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

IgniteUI / igniteui-angular / 15160041151

21 May 2025 10:39AM UTC coverage: 91.662% (-0.007%) from 91.669%
15160041151

push

github

web-flow
fix(AdvancedFiltering): Adding missing resource strings in AFD from the redesigned QB (#15817)

* fix(AdvanceFiltering): Adding missing resource strings.

13415 of 15685 branches covered (85.53%)

26956 of 29408 relevant lines covered (91.66%)

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

186
interface IMatchInfoCache {
187
    row: any;
188
    index: number;
189
    column: string;
190
    metadata: Map<string, boolean>;
191
}
192

193
let FAKE_ROW_ID = -1;
3✔
194
const DEFAULT_ITEMS_PER_PAGE = 15;
3✔
195
const MINIMUM_COLUMN_WIDTH = 136;
3✔
196
// By default row editing overlay outlet is inside grid body so that overlay is hidden below grid header when scrolling.
197
// 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.
198
// Default row editing overlay height is higher then row height that is why the case is valid also for row with 2 rows.
199
// More accurate calculation is not possible, cause row editing overlay is still not shown and we don't know its height,
200
// but in the same time we need to set row editing overlay outlet before opening the overlay itself.
201
const MIN_ROW_EDITING_COUNT_THRESHOLD = 2;
3✔
202

203
/* blazorIndirectRender
204
   blazorComponent
205
   omitModule
206
   wcSkipComponentSuffix */
207
@Directive()
208
export abstract class IgxGridBaseDirective implements GridType,
3✔
209
    OnInit, DoCheck, OnDestroy, AfterContentInit, AfterViewInit {
210

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

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

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

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

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

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

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

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

302
    public get summaryRowHeight(): number {
303
        if (this.hasSummarizedColumns && this.rootSummariesEnabled) {
257,963✔
304
            return this._summaryRowHeight || this.summaryService.calcMaxSummaryHeight();
175,288✔
305
        }
306
        return 0;
82,675✔
307
    }
308

309
    /** @hidden @internal */
310
    public get hasColumnsToAutosize() {
311
        return this._columns.some(x => x.width === 'fit-content');
89,517✔
312
    }
313

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

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

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

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

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

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

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

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

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

450
    public set primaryKey(value: string) {
451
        this._primaryKey = value;
1,734✔
452
        this.checkPrimaryKeyField();
1,734✔
453
    }
454

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

472
    /** @hidden @internal */
473
    @ContentChildren(IgxGridExcelStyleFilteringComponent, { read: IgxGridExcelStyleFilteringComponent, descendants: false })
474
    public excelStyleFilteringComponents: QueryList<IgxGridExcelStyleFilteringComponent>;
475

476
    /** @hidden @internal */
477
    public get excelStyleFilteringComponent() {
478
        return this.excelStyleFilteringComponents?.first;
269✔
479
    }
480

481
    /** @hidden @internal */
482
    public get headerGroups() {
483
        return this.theadRow.groups;
3✔
484
    }
485

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

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

512

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

737
    /* blazorInclude */
738
    /**
739
     * @hidden @internal
740
     */
741
    @Output()
742
    public columnsAutogenerated = new EventEmitter<IColumnsAutoGeneratedEventArgs>();
4,117✔
743

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

996
    /* blazorCSSuppress */
997
    /**
998
     * Emitted when the rows are expanded or collapsed.
999
     *
1000
     * @example
1001
     * ```html
1002
     * <igx-grid [data]="employeeData" (expansionStatesChange)="expansionStatesChange($event)" [autoGenerate]="true"></igx-grid>
1003
     * ```
1004
     */
1005
    @Output()
1006
    public expansionStatesChange = new EventEmitter<Map<any, boolean>>();
4,117✔
1007

1008
    /* blazorInclude */
1009
    /**
1010
     * Emitted when the rows are selected or deselected.
1011
     *
1012
     * @example
1013
     * ```html
1014
     * <igx-grid [data]="employeeData" (selectedRowsChange)="selectedRowsChange($event)" [autoGenerate]="true"></igx-grid>
1015
     * ```
1016
     */
1017
    @Output()
1018
    public selectedRowsChange = new EventEmitter<any[]>();
4,117✔
1019

1020
    /**
1021
     * Emitted when the expanded state of a row gets changed.
1022
     *
1023
     * @example
1024
     * ```html
1025
     * <igx-grid [data]="employeeData" (rowToggle)="rowToggle($event)" [autoGenerate]="true"></igx-grid>
1026
     * ```
1027
     */
1028
    @Output()
1029
    public rowToggle = new EventEmitter<IRowToggleEventArgs>();
4,117✔
1030

1031
    /**
1032
     * Emitted when the pinned state of a row is changed.
1033
     *
1034
     * @example
1035
     * ```html
1036
     * <igx-grid [data]="employeeData" (rowPinning)="rowPin($event)" [autoGenerate]="true"></igx-grid>
1037
     * ```
1038
     */
1039
    @Output()
1040
    public rowPinning = new EventEmitter<IPinRowEventArgs>();
4,117✔
1041

1042
    /**
1043
     * Emitted when the pinned state of a row is changed.
1044
     *
1045
     * @example
1046
     * ```html
1047
     * <igx-grid [data]="employeeData" (rowPinned)="rowPin($event)" [autoGenerate]="true"></igx-grid>
1048
     * ```
1049
     */
1050
    @Output()
1051
    public rowPinned = new EventEmitter<IPinRowEventArgs>();
4,117✔
1052

1053
    /**
1054
     * Emitted when the active node is changed.
1055
     *
1056
     * @example
1057
     * ```
1058
     * <igx-grid [data]="data" [autoGenerate]="true" (activeNodeChange)="activeNodeChange($event)"></igx-grid>
1059
     * ```
1060
     */
1061
    @Output()
1062
    public activeNodeChange = new EventEmitter<IActiveNodeChangeEventArgs>();
4,117✔
1063

1064
    /**
1065
     * Emitted before sorting is performed.
1066
     *
1067
     * @remarks
1068
     * Returns the sorting expressions.
1069
     * @example
1070
     * ```html
1071
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingExpressionsChange)="sortingExprChange($event)"></igx-grid>
1072
     * ```
1073
     */
1074
    @Output()
1075
    public sortingExpressionsChange = new EventEmitter<ISortingExpression[]>();
4,117✔
1076

1077

1078
    /**
1079
     * Emitted when an export process is initiated by the user.
1080
     *
1081
     * @example
1082
     * ```typescript
1083
     * toolbarExporting(event: IGridToolbarExportEventArgs){
1084
     *     const toolbarExporting = event;
1085
     * }
1086
     * ```
1087
     */
1088
    @Output()
1089
    public toolbarExporting = new EventEmitter<IGridToolbarExportEventArgs>();
4,117✔
1090

1091
    /* End of toolbar related definitions */
1092

1093
    /**
1094
     * Emitted when making a range selection.
1095
     *
1096
     * @remarks
1097
     * Range selection can be made either through drag selection or through keyboard selection.
1098
     */
1099
    @Output()
1100
    public rangeSelected = new EventEmitter<GridSelectionRange>();
4,117✔
1101

1102
    /** Emitted after the ngAfterViewInit hook. At this point the grid exists in the DOM */
1103
    @Output()
1104
    public rendered = new EventEmitter<boolean>();
4,117✔
1105

1106
    /**
1107
     * @hidden @internal
1108
     */
1109
    @Output()
1110
    public localeChange = new EventEmitter<boolean>();
4,117✔
1111

1112
    /**
1113
     * Emitted before the grid's data view is changed because of a data operation, rebinding, etc.
1114
     *
1115
     * @example
1116
     * ```typescript
1117
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanging)='handleDataChangingEvent()'></igx-grid>
1118
     * ```
1119
     */
1120
    @Output()
1121
    public dataChanging = new EventEmitter<IForOfDataChangeEventArgs>();
4,117✔
1122

1123
    /**
1124
     * Emitted after the grid's data view is changed because of a data operation, rebinding, etc.
1125
     *
1126
     * @example
1127
     * ```typescript
1128
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanged)='handleDataChangedEvent()'></igx-grid>
1129
     * ```
1130
     */
1131
    @Output()
1132
    public dataChanged = new EventEmitter<IForOfDataChangeEventArgs>();
4,117✔
1133

1134

1135
    /**
1136
     * @hidden @internal
1137
     */
1138
    @ViewChild(IgxSnackbarComponent)
1139
    public addRowSnackbar: IgxSnackbarComponent;
1140

1141
    /**
1142
     * @hidden @internal
1143
     */
1144
    @ViewChild(IgxGridColumnResizerComponent)
1145
    public resizeLine: IgxGridColumnResizerComponent;
1146

1147
    /**
1148
     * @hidden @internal
1149
     */
1150
    @ViewChild('loadingOverlay', { read: IgxToggleDirective, static: true })
1151
    public loadingOverlay: IgxToggleDirective;
1152

1153
    /**
1154
     * @hidden @internal
1155
     */
1156
    @ViewChild('igxLoadingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1157
    public loadingOutlet: IgxOverlayOutletDirective;
1158

1159
    /* reactContentChildren */
1160
    /* blazorInclude */
1161
    /* blazorTreatAsCollection */
1162
    /* blazorCollectionName: ColumnCollection */
1163
    /* ngQueryListName: columnList */
1164
    /**
1165
     * @hidden @internal
1166
     */
1167
    @ContentChildren(IgxColumnComponent, { read: IgxColumnComponent, descendants: true })
1168
    public columnList: QueryList<IgxColumnComponent> = new QueryList<IgxColumnComponent>();
4,117✔
1169

1170
    /* contentChildren */
1171
    /* blazorInclude */
1172
    /* blazorTreatAsCollection */
1173
    /* blazorCollectionName: ActionStripCollection */
1174
    /* blazorCollectionItemName: ActionStrip */
1175
    /* ngQueryListName: actionStripComponents */
1176
    /** @hidden @internal */
1177
    @ContentChildren(IgxActionStripToken)
1178
    protected actionStripComponents: QueryList<IgxActionStripToken>;
1179

1180
    /** @hidden @internal */
1181
    public get actionStrip() {
1182
        return this.actionStripComponents?.first;
4,760✔
1183
    }
1184

1185
    /**
1186
     * @hidden @internal
1187
     */
1188
    @ContentChild(IgxExcelStyleLoadingValuesTemplateDirective, { read: IgxExcelStyleLoadingValuesTemplateDirective, static: true })
1189
    public excelStyleLoadingValuesTemplateDirective: IgxExcelStyleLoadingValuesTemplateDirective;
1190

1191
    /** @hidden @internal */
1192
    @ViewChild('emptyFilteredGrid', { read: TemplateRef, static: true })
1193
    public emptyFilteredGridTemplate: TemplateRef<any>;
1194

1195
    /** @hidden @internal */
1196
    @ViewChild('defaultEmptyGrid', { read: TemplateRef, static: true })
1197
    public emptyGridDefaultTemplate: TemplateRef<any>;
1198

1199
    /**
1200
     * @hidden @internal
1201
     */
1202
    @ViewChild('defaultLoadingGrid', { read: TemplateRef, static: true })
1203
    public loadingGridDefaultTemplate: TemplateRef<any>;
1204

1205
    /**
1206
     * @hidden @internal
1207
     */
1208
    @ViewChild('scrollContainer', { read: IgxGridForOfDirective, static: true })
1209
    public parentVirtDir: IgxGridForOfDirective<any, any[]>;
1210

1211
    /**
1212
     * @hidden
1213
     * @internal
1214
     */
1215
    @ContentChildren(IgxHeadSelectorDirective, { read: TemplateRef, descendants: false })
1216
    public headSelectorsTemplates: QueryList<TemplateRef<IgxHeadSelectorTemplateContext>>;
1217

1218
    /**
1219
     * @hidden
1220
     * @internal
1221
     */
1222
    @ContentChildren(IgxRowSelectorDirective, { read: TemplateRef, descendants: false })
1223
    public rowSelectorsTemplates: QueryList<TemplateRef<IgxRowSelectorTemplateContext>>;
1224

1225
    /**
1226
     * @hidden
1227
     * @internal
1228
     */
1229
    @ContentChildren(IgxRowDragGhostDirective, { read: TemplateRef, descendants: false })
1230
    public dragGhostCustomTemplates: QueryList<TemplateRef<IgxGridRowDragGhostContext>>;
1231

1232

1233
    /**
1234
     * Gets the custom template, if any, used for row drag ghost.
1235
     */
1236
    @Input()
1237
    public get dragGhostCustomTemplate() {
1238
        return this._dragGhostCustomTemplate || this.dragGhostCustomTemplates?.first;
1,979✔
1239
    }
1240

1241
    /**
1242
     * Sets a custom template for the row drag ghost.
1243
     *```html
1244
     * <ng-template #template igxRowDragGhost>
1245
     *    <igx-icon>menu</igx-icon>
1246
     * </ng-template>
1247
     * ```
1248
     * ```typescript
1249
     * @ViewChild("'template'", {read: TemplateRef })
1250
     * public template: TemplateRef<any>;
1251
     * this.grid.dragGhostCustomTemplate = this.template;
1252
     * ```
1253
     */
1254
    public set dragGhostCustomTemplate(template: TemplateRef<IgxGridRowDragGhostContext>) {
1255
        this._dragGhostCustomTemplate = template;
1✔
1256
    }
1257

1258

1259
    /**
1260
     * @hidden @internal
1261
     */
1262
    @ViewChild('verticalScrollContainer', { read: IgxGridForOfDirective, static: true })
1263
    public verticalScrollContainer: IgxGridForOfDirective<any, any[]>;
1264

1265
    /**
1266
     * @hidden @internal
1267
     */
1268
    @ViewChild('verticalScrollHolder', { read: IgxGridForOfDirective, static: true })
1269
    public verticalScroll: IgxGridForOfDirective<any, any[]>;
1270

1271
    /**
1272
     * @hidden @internal
1273
     */
1274
    @ViewChild('scr', { read: ElementRef, static: true })
1275
    public scr: ElementRef;
1276

1277
    /** @hidden @internal */
1278
    @ViewChild('headSelectorBaseTemplate', { read: TemplateRef, static: true })
1279
    public headerSelectorBaseTemplate: TemplateRef<any>;
1280

1281
    /**
1282
     * @hidden @internal
1283
     */
1284
    @ViewChild('footer', { read: ElementRef })
1285
    public footer: ElementRef;
1286

1287
    /** @hidden @internal */
1288
    public get headerContainer() {
1289
        return this.theadRow?.headerForOf;
14,731✔
1290
    }
1291

1292
    /** @hidden @internal */
1293
    public get headerSelectorContainer() {
1294
        return this.theadRow?.headerSelectorContainer;
30,563✔
1295
    }
1296

1297
    /** @hidden @internal */
1298
    public get headerDragContainer() {
1299
        return this.theadRow?.headerDragContainer;
475✔
1300
    }
1301

1302
    /** @hidden @internal */
1303
    public get headerGroupContainer() {
1304
        return this.theadRow?.headerGroupContainer;
29,815✔
1305
    }
1306

1307
    /** @hidden @internal */
1308
    public get filteringRow(): IgxGridFilteringRowComponent {
1309
        return this.theadRow?.filterRow;
1,324✔
1310
    }
1311

1312
    /** @hidden @internal */
1313
    @ViewChild(IgxGridHeaderRowComponent, { static: true })
1314
    public theadRow: IgxGridHeaderRowComponent;
1315

1316
    /** @hidden @internal */
1317
    @ViewChild(IgxGridGroupByAreaComponent)
1318
    public groupArea: IgxGridGroupByAreaComponent;
1319

1320
    /**
1321
     * @hidden @internal
1322
     */
1323
    @ViewChild('tbody', { static: true })
1324
    public tbody: ElementRef;
1325

1326
    @ViewChild(IgxGridBodyDirective, { static: true, read: ElementRef })
1327
    protected tbodyContainer: ElementRef;
1328

1329
    /**
1330
     * @hidden @internal
1331
     */
1332
    @ViewChild('pinContainer', { read: ElementRef })
1333
    public pinContainer: ElementRef;
1334

1335
    /**
1336
     * @hidden @internal
1337
     */
1338
    @ViewChild('tfoot', { static: true })
1339
    public tfoot: ElementRef<HTMLElement>;
1340

1341
    /**
1342
     * @hidden @internal
1343
     */
1344
    @ViewChild('igxRowEditingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1345
    public rowEditingOutletDirective: IgxOverlayOutletDirective;
1346

1347
    /**
1348
     * @hidden @internal
1349
     */
1350
    @ViewChildren(IgxTemplateOutletDirective, { read: IgxTemplateOutletDirective })
1351
    public tmpOutlets: QueryList<any> = new QueryList<any>();
4,117✔
1352

1353
    /**
1354
     * @hidden
1355
     * @internal
1356
     */
1357
    @ViewChild('dragIndicatorIconBase', { read: TemplateRef, static: true })
1358
    public dragIndicatorIconBase: TemplateRef<any>;
1359

1360
    /**
1361
     * @hidden @internal
1362
     */
1363
    @ContentChildren(IgxRowEditTemplateDirective, { descendants: false, read: TemplateRef })
1364
    public rowEditCustomDirectives: QueryList<TemplateRef<IgxGridRowEditTemplateContext>>;
1365

1366
    /**
1367
     * @hidden @internal
1368
     */
1369
    @ContentChildren(IgxRowEditTextDirective, { descendants: false, read: TemplateRef })
1370
    public rowEditTextDirectives: QueryList<TemplateRef<IgxGridRowEditTextTemplateContext>>;
1371

1372
    /**
1373
     * Gets the row edit text template.
1374
     */
1375
    @Input()
1376
    public get rowEditTextTemplate(): TemplateRef<IgxGridRowEditTextTemplateContext> {
1377
        return this._rowEditTextTemplate || this.rowEditTextDirectives?.first;
5,595✔
1378
    }
1379
    /**
1380
     * Sets the row edit text template.
1381
     *```html
1382
     * <ng-template #template igxRowEditText let-rowChangesCount>
1383
     * Changes: {{rowChangesCount}}
1384
     * </ng-template>
1385
     * ```
1386
     *```typescript
1387
     * @ViewChild('template', {read: TemplateRef })
1388
     * public template: TemplateRef<any>;
1389
     * this.grid.rowEditTextTemplate = this.template;
1390
     * ```
1391
     */
1392
    public set rowEditTextTemplate(template: TemplateRef<IgxGridRowEditTextTemplateContext>) {
1393
        this._rowEditTextTemplate = template;
1✔
1394
    }
1395

1396
    /**
1397
     * @hidden @internal
1398
     */
1399
    @ContentChild(IgxRowAddTextDirective, { read: TemplateRef })
1400
    public rowAddText: TemplateRef<IgxGridEmptyTemplateContext>;
1401

1402
    /**
1403
     * Gets the row add text template.
1404
     */
1405
    @Input()
1406
    public get rowAddTextTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
1407
        return this._rowAddTextTemplate || this.rowAddText;
372✔
1408
    }
1409
    /**
1410
     * Sets the row add text template.
1411
     *```html
1412
     * <ng-template #template igxRowAddText>
1413
     * Adding Row
1414
     * </ng-template>
1415
     * ```
1416
     *```typescript
1417
     * @ViewChild('template', {read: TemplateRef })
1418
     * public template: TemplateRef<any>;
1419
     * this.grid.rowAddTextTemplate = this.template;
1420
     * ```
1421
     */
1422
    public set rowAddTextTemplate(template: TemplateRef<IgxGridEmptyTemplateContext>) {
1423
        this._rowAddTextTemplate = template;
1✔
1424
    }
1425

1426
    /**
1427
     * @hidden @internal
1428
     */
1429
    @ContentChildren(IgxRowEditActionsDirective, { descendants: false, read: TemplateRef })
1430
    public rowEditActionsDirectives: QueryList<TemplateRef<IgxGridRowEditActionsTemplateContext>>;
1431

1432
    /**
1433
     * Gets the row edit actions template.
1434
     */
1435
    @Input()
1436
    public get rowEditActionsTemplate(): TemplateRef<IgxGridRowEditActionsTemplateContext> {
1437
        return this._rowEditActionsTemplate || this.rowEditActionsDirectives?.first;
5,971✔
1438
    }
1439
    /**
1440
     * Sets the row edit actions template.
1441
     *```html
1442
     * <ng-template #template igxRowEditActions let-endRowEdit>
1443
     *     <button type="button" igxButton igxRowEditTabStop (click)="endRowEdit(false)">Cancel</button>
1444
     *     <button type="button" igxButton igxRowEditTabStop (click)="endRowEdit(true)">Apply</button>
1445
     * </ng-template>
1446
     * ```
1447
     *```typescript
1448
     * @ViewChild('template', {read: TemplateRef })
1449
     * public template: TemplateRef<any>;
1450
     * this.grid.rowEditActionsTemplate = this.template;
1451
     * ```
1452
     */
1453
    public set rowEditActionsTemplate(template: TemplateRef<IgxGridRowEditActionsTemplateContext>) {
1454
        this._rowEditActionsTemplate = template;
1✔
1455
    }
1456

1457
    /**
1458
     * The custom template, if any, that should be used when rendering a row expand indicator.
1459
     */
1460
    @ContentChild(IgxRowExpandedIndicatorDirective, { read: TemplateRef })
1461
    protected rowExpandedIndicatorDirectiveTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
4,117✔
1462

1463
    /**
1464
     * Gets the row expand indicator template.
1465
    */
1466
    @Input()
1467
    public get rowExpandedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1468
        return this._rowExpandedIndicatorTemplate || this.rowExpandedIndicatorDirectiveTemplate;
12,233✔
1469
    }
1470

1471
    /**
1472
     * Sets the row expand indicator template.
1473
     *```html
1474
     *<ng-template igxRowExpandedIndicator>
1475
     *  <igx-icon role="button">remove</igx-icon>
1476
     *</ng-template>
1477
     * ```
1478
     *```typescript
1479
     * @ViewChild('template', {read: TemplateRef })
1480
     * public template: TemplateRef<any>;
1481
     * this.grid.rowExpandedIndicatorTemplate = this.template;
1482
     * ```
1483
    */
1484
    public set rowExpandedIndicatorTemplate(template: TemplateRef<IgxGridRowTemplateContext>) {
1485
        this._rowExpandedIndicatorTemplate = template;
908✔
1486
    }
1487

1488
    /**
1489
     * The custom template, if any, that should be used when rendering a row collapse indicator.
1490
     */
1491
    @ContentChild(IgxRowCollapsedIndicatorDirective, { read: TemplateRef })
1492
    protected rowCollapsedIndicatorDirectiveTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
4,117✔
1493

1494
    /**
1495
     * Gets the row collapse indicator template.
1496
    */
1497
    @Input()
1498
    public get rowCollapsedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1499
        return this._rowCollapsedIndicatorTemplate || this.rowCollapsedIndicatorDirectiveTemplate;
51,145✔
1500
    }
1501

1502
    /**
1503
     * Sets the row collapse indicator template.
1504
     *```html
1505
     *<ng-template igxRowCollapsedIndicator>
1506
     *  <igx-icon role="button">add</igx-icon>
1507
     *</ng-template>
1508
     * ```
1509
     *```typescript
1510
     * @ViewChild('template', {read: TemplateRef })
1511
     * public template: TemplateRef<any>;
1512
     * this.grid.rowCollapsedIndicatorTemplate = this.template;
1513
     * ```
1514
    */
1515
    public set rowCollapsedIndicatorTemplate(template: TemplateRef<IgxGridRowTemplateContext>) {
1516
        this._rowCollapsedIndicatorTemplate = template;
908✔
1517
    }
1518

1519
    /**
1520
     * The custom template, if any, that should be used when rendering a header expand indicator.
1521
     */
1522
    @ContentChild(IgxHeaderExpandedIndicatorDirective, { read: TemplateRef })
1523
    protected headerExpandedIndicatorDirectiveTemplate: TemplateRef<IgxGridTemplateContext> = null;
4,117✔
1524

1525
    /**
1526
     * Gets the header expand indicator template.
1527
    */
1528
    @Input()
1529
    public get headerExpandedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
1530
        return this._headerExpandIndicatorTemplate || this.headerExpandedIndicatorDirectiveTemplate;
13,955✔
1531
    }
1532

1533
    /**
1534
     * Sets the header expand indicator template.
1535
     *```html
1536
     *<ng-template igxHeaderExpandedIndicator>
1537
     *  <igx-icon role="button">remove</igx-icon>
1538
     *</ng-template>
1539
     * ```
1540
     *```typescript
1541
     * @ViewChild('template', {read: TemplateRef })
1542
     * public template: TemplateRef<any>;
1543
     * this.grid.headerExpandedIndicatorTemplate = this.template;
1544
     * ```
1545
    */
1546
    public set headerExpandedIndicatorTemplate(template: TemplateRef<IgxGridTemplateContext>) {
1547
        this._headerExpandIndicatorTemplate = template;
908✔
1548
    }
1549

1550
    /**
1551
     * The custom template, if any, that should be used when rendering a header collapse indicator.
1552
     */
1553
    @ContentChild(IgxHeaderCollapsedIndicatorDirective, { read: TemplateRef })
1554
    protected headerCollapsedIndicatorDirectiveTemplate: TemplateRef<IgxGridTemplateContext> = null;
4,117✔
1555

1556
    /**
1557
     * Gets the row collapse indicator template.
1558
    */
1559
    @Input()
1560
    public get headerCollapsedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
1561
        return this._headerCollapseIndicatorTemplate || this.headerCollapsedIndicatorDirectiveTemplate;
1,054✔
1562
    }
1563

1564
    /**
1565
     * Sets the row collapse indicator template.
1566
     *```html
1567
     *<ng-template igxHeaderCollapsedIndicator>
1568
     *  <igx-icon role="button">add</igx-icon>
1569
     *</ng-template>
1570
     * ```
1571
     *```typescript
1572
     * @ViewChild('template', {read: TemplateRef })
1573
     * public template: TemplateRef<any>;
1574
     * this.grid.headerCollapsedIndicatorTemplate = this.template;
1575
     * ```
1576
    */
1577
    public set headerCollapsedIndicatorTemplate(template: TemplateRef<IgxGridTemplateContext>) {
1578
        this._headerCollapseIndicatorTemplate = template;
908✔
1579
    }
1580

1581
    /** @hidden @internal */
1582
    @ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef })
1583
    public excelStyleHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,117✔
1584

1585
    /**
1586
     * Gets the excel style header icon.
1587
    */
1588
    @Input()
1589
    public get excelStyleHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1590
        return this._excelStyleHeaderIconTemplate || this.excelStyleHeaderIconDirectiveTemplate;
12,362✔
1591
    }
1592

1593
    /**
1594
     * Sets the excel style header icon.
1595
     *```html
1596
     *<ng-template #template igxExcelStyleHeaderIcon>
1597
     * <igx-icon>filter_alt</igx-icon>
1598
     *</ng-template>
1599
     * ```
1600
     *```typescript
1601
     * @ViewChild('template', {read: TemplateRef })
1602
     * public template: TemplateRef<any>;
1603
     * this.grid.excelStyleHeaderIconTemplate = this.template;
1604
     * ```
1605
    */
1606
    public set excelStyleHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1607
        this._excelStyleHeaderIconTemplate = template;
909✔
1608
    }
1609

1610

1611
    /**
1612
     * @hidden
1613
     * @internal
1614
     */
1615
    @ContentChild(IgxSortAscendingHeaderIconDirective, { read: TemplateRef })
1616
    public sortAscendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,117✔
1617

1618
    /**
1619
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1620
     */
1621
    @Input()
1622
    public get sortAscendingHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1623
        return this._sortAscendingHeaderIconTemplate;
1,064✔
1624
    }
1625

1626
    /**
1627
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1628
     *```html
1629
     * <ng-template #template igxSortAscendingHeaderIcon>
1630
     *    <igx-icon>expand_less</igx-icon>
1631
     * </ng-template>
1632
     * ```
1633
     * ```typescript
1634
     * @ViewChild("'template'", {read: TemplateRef })
1635
     * public template: TemplateRef<any>;
1636
     * this.grid.sortAscendingHeaderIconTemplate = this.template;
1637
     * ```
1638
     */
1639
    public set sortAscendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1640
        this._sortAscendingHeaderIconTemplate = template;
914✔
1641
    }
1642

1643
    /** @hidden @internal */
1644
    @ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef })
1645
    public sortDescendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,117✔
1646

1647
    /**
1648
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1649
     */
1650
    @Input()
1651
    public get sortDescendingHeaderIconTemplate() {
1652
        return this._sortDescendingHeaderIconTemplate;
970✔
1653
    }
1654

1655
    /**
1656
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1657
     *```html
1658
     * <ng-template #template igxSortDescendingHeaderIcon>
1659
     *    <igx-icon>expand_more</igx-icon>
1660
     * </ng-template>
1661
     * ```
1662
     * ```typescript
1663
     * @ViewChild("'template'", {read: TemplateRef })
1664
     * public template: TemplateRef<any>;
1665
     * this.grid.sortDescendingHeaderIconTemplate = this.template;
1666
     * ```
1667
     */
1668
    public set sortDescendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1669
        this._sortDescendingHeaderIconTemplate = template;
914✔
1670
    }
1671

1672
    /**
1673
     * @hidden
1674
     * @internal
1675
     */
1676
    @ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef })
1677
    public sortHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,117✔
1678

1679
    /**
1680
     * Gets custom template, if any, that should be used when rendering a header sorting indicator when columns are not sorted.
1681
     */
1682
    @Input()
1683
    public get sortHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1684
        return this._sortHeaderIconTemplate;
29,722✔
1685
    }
1686

1687
    /**
1688
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are not sorted.
1689
     *```html
1690
     * <ng-template #template igxSortHeaderIcon>
1691
     *    <igx-icon>unfold_more</igx-icon>
1692
     * </ng-template>
1693
     * ```
1694
     * ```typescript
1695
     * @ViewChild("'template'", {read: TemplateRef })
1696
     * public template: TemplateRef<any>;
1697
     * this.grid.sortHeaderIconTemplate = this.template;
1698
     * ```
1699
     */
1700
    public set sortHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1701
        this._sortHeaderIconTemplate = template;
914✔
1702
    }
1703

1704
    /**
1705
     * @hidden
1706
     * @internal
1707
     */
1708
    @ContentChildren(IgxDragIndicatorIconDirective, { read: TemplateRef, descendants: false })
1709
    public dragIndicatorIconTemplates: QueryList<TemplateRef<IgxGridEmptyTemplateContext>>;
1710

1711
    /**
1712
     * @hidden @internal
1713
     */
1714
    @ViewChildren(IgxRowEditTabStopDirective)
1715
    public rowEditTabsDEFAULT: QueryList<IgxRowEditTabStopDirective>;
1716

1717
    /**
1718
     * @hidden @internal
1719
     */
1720
    @ContentChildren(IgxRowEditTabStopDirective, { descendants: true })
1721
    public rowEditTabsCUSTOM: QueryList<IgxRowEditTabStopDirective>;
1722

1723
    /**
1724
     * @hidden @internal
1725
     */
1726
    @ViewChild('rowEditingOverlay', { read: IgxToggleDirective })
1727
    public rowEditingOverlay: IgxToggleDirective;
1728

1729
    /**
1730
     * @hidden @internal
1731
     */
1732
    @HostBinding('attr.tabindex')
1733
    public tabindex = 0;
4,117✔
1734

1735
    /**
1736
     * @hidden @internal
1737
     */
1738
    @HostBinding('attr.role')
1739
    public hostRole = 'grid';
4,117✔
1740

1741
    /* contentChildren */
1742
    /* blazorInclude */
1743
    /* blazorTreatAsCollection */
1744
    /* blazorCollectionName: GridToolbarCollection */
1745
    /* ngQueryListName: toolbar */
1746
    /** @hidden @internal */
1747
    @ContentChildren(IgxToolbarToken)
1748
    public toolbar: QueryList<IgxGridToolbarComponent>;
1749

1750
    /* contentChildren */
1751
    /* blazorInclude */
1752
    /* blazorTreatAsCollection */
1753
    /* blazorCollectionName: PaginatorCollection */
1754
    /* ngQueryListName: paginationComponents */
1755
    /** @hidden @internal */
1756
    @ContentChildren(IgxPaginatorToken)
1757
    protected paginationComponents: QueryList<IgxPaginatorComponent>;
1758

1759
    /**
1760
     * @hidden @internal
1761
     */
1762
    @ViewChild('igxFilteringOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1763
    protected _outletDirective: IgxOverlayOutletDirective;
1764

1765
    /**
1766
     * @hidden @internal
1767
     * @igxElementsAnchor
1768
     */
1769
    @ViewChild('sink', { read: ViewContainerRef, static: true })
1770
    public anchor: ViewContainerRef;
1771

1772
    /**
1773
     * @hidden @internal
1774
     */
1775
    @ViewChild('defaultExpandedTemplate', { read: TemplateRef, static: true })
1776
    protected defaultExpandedTemplate: TemplateRef<any>;
1777

1778
    /**
1779
     * @hidden @internal
1780
     */
1781
    @ViewChild('defaultCollapsedTemplate', { read: TemplateRef, static: true })
1782
    protected defaultCollapsedTemplate: TemplateRef<any>;
1783

1784
    /**
1785
     * @hidden @internal
1786
     */
1787
    @ViewChild('defaultESFHeaderIcon', { read: TemplateRef, static: true })
1788
    protected defaultESFHeaderIconTemplate: TemplateRef<any>;
1789

1790
    @ViewChildren('summaryRow', { read: IgxSummaryRowComponent })
1791
    protected _summaryRowList: QueryList<IgxSummaryRowComponent>;
1792

1793
    @ViewChildren('row')
1794
    private _rowList: QueryList<IgxGridRowComponent>;
1795

1796
    @ViewChildren('pinnedRow')
1797
    private _pinnedRowList: QueryList<IgxGridRowComponent>;
1798

1799
    /**
1800
     * @hidden @internal
1801
     */
1802
    @ViewChild('defaultRowEditTemplate', { read: TemplateRef, static: true })
1803
    private defaultRowEditTemplate: TemplateRef<IgxGridRowEditTemplateContext>;
1804

1805
    @ViewChildren(IgxRowDirective, { read: IgxRowDirective })
1806
    private _dataRowList: QueryList<IgxRowDirective>;
1807

1808
    @HostBinding('class.igx-grid')
1809
    protected baseClass = 'igx-grid';
4,117✔
1810

1811

1812
    /**
1813
     * Gets/Sets the resource strings.
1814
     *
1815
     * @remarks
1816
     * By default it uses EN resources.
1817
     */
1818
    @Input()
1819
    public set resourceStrings(value: IGridResourceStrings) {
1820
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
3✔
1821
    }
1822

1823
    public get resourceStrings(): IGridResourceStrings {
1824
        return this._resourceStrings;
232,339✔
1825
    }
1826

1827
    /**
1828
     * Gets/Sets the filtering logic of the `IgxGridComponent`.
1829
     *
1830
     * @remarks
1831
     * The default is AND.
1832
     * @example
1833
     * ```html
1834
     * <igx-grid [data]="Data" [autoGenerate]="true" [filteringLogic]="filtering"></igx-grid>
1835
     * ```
1836
     */
1837
    @WatchChanges()
1838
    @Input()
1839
    public get filteringLogic() {
1840
        return this._filteringExpressionsTree.operator;
330✔
1841
    }
1842

1843
    public set filteringLogic(value: FilteringLogic) {
1844
        this._filteringExpressionsTree.operator = value;
3✔
1845
    }
1846

1847
    /* mustSetInCodePlatforms: WebComponents;Blazor */
1848
    /**
1849
     * Gets/Sets the filtering state.
1850
     *
1851
     * @example
1852
     * ```html
1853
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(filteringExpressionsTree)]="model.filteringExpressions"></igx-grid>
1854
     * ```
1855
     * @remarks
1856
     * Supports two-way binding.
1857
     */
1858
    @WatchChanges()
1859
    @Input()
1860
    public get filteringExpressionsTree() {
1861
        return this._filteringExpressionsTree;
128,295✔
1862
    }
1863

1864
    public set filteringExpressionsTree(value) {
1865
        if (value && isTree(value)) {
919✔
1866
            for (let index = 0; index < value.filteringOperands.length; index++) {
919✔
1867
                if (!(isTree(value.filteringOperands[index]))) {
475✔
1868
                    const newExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, value.filteringOperands[index].fieldName);
7✔
1869
                    newExpressionsTree.filteringOperands.push(value.filteringOperands[index]);
7✔
1870
                    value.filteringOperands[index] = newExpressionsTree;
7✔
1871
                }
1872
            }
1873

1874
            value.type = FilteringExpressionsTreeType.Regular;
919✔
1875
            if (value && this._columns?.length > 0) {
919✔
1876
                this._filteringExpressionsTree = this.getRecreatedTree(value);
768✔
1877
            } else {
1878
                this._filteringExpressionsTree = value;
151✔
1879
            }
1880
            this.filteringPipeTrigger++;
919✔
1881
            this.filteringExpressionsTreeChange.emit(this._filteringExpressionsTree);
919✔
1882

1883
            if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
919✔
1884
                this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1885
                this._filteredData = null;
465✔
1886
            }
1887

1888
            this.filteringService.refreshExpressions();
919✔
1889
            this.selectionService.clearHeaderCBState();
919✔
1890
            this.summaryService.clearSummaryCache();
919✔
1891
            this.notifyChanges();
919✔
1892
        }
1893
    }
1894

1895
    /**
1896
     * Gets/Sets the advanced filtering state.
1897
     *
1898
     * @example
1899
     * ```typescript
1900
     * let advancedFilteringExpressionsTree = this.grid.advancedFilteringExpressionsTree;
1901
     * this.grid.advancedFilteringExpressionsTree = logic;
1902
     * ```
1903
     */
1904
    @WatchChanges()
1905
    @Input()
1906
    public get advancedFilteringExpressionsTree() {
1907
        return this._advancedFilteringExpressionsTree;
69,360✔
1908
    }
1909

1910
    public set advancedFilteringExpressionsTree(value) {
1911
        const filteringEventArgs: IFilteringEventArgs = {
75✔
1912
            owner: this,
1913
            filteringExpressions: value,
1914
            cancel: false
1915
        };
1916

1917
        this.filtering.emit(filteringEventArgs);
75✔
1918

1919
        if (filteringEventArgs.cancel) {
75✔
1920
            return;
1✔
1921
        }
1922

1923
        if (value && isTree(value)) {
74✔
1924
            value.type = FilteringExpressionsTreeType.Advanced;
60✔
1925
            if (this._columns && this._columns.length > 0) {
60✔
1926
                this._advancedFilteringExpressionsTree = this.getRecreatedTree(value);
54✔
1927
            } else {
1928
                this._advancedFilteringExpressionsTree = value;
6✔
1929
            }
1930
            this.filteringPipeTrigger++;
60✔
1931
        } else {
1932
            this._advancedFilteringExpressionsTree = null;
14✔
1933
        }
1934
        this.advancedFilteringExpressionsTreeChange.emit(this._advancedFilteringExpressionsTree);
74✔
1935

1936
        if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
74✔
1937
            this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1938
            this._filteredData = null;
19✔
1939
        }
1940

1941
        this.selectionService.clearHeaderCBState();
74✔
1942
        this.summaryService.clearSummaryCache();
74✔
1943
        this.notifyChanges();
74✔
1944

1945
        // Wait for the change detection to update filtered data through the pipes and then emit the event.
1946
        requestAnimationFrame(() => this.filteringDone.emit(this._advancedFilteringExpressionsTree));
74✔
1947
    }
1948

1949
    /**
1950
     * Gets/Sets the locale.
1951
     *
1952
     * @remarks
1953
     * If not set, returns browser's language.
1954
     */
1955
    @Input()
1956
    public get locale(): string {
1957
        return this._locale;
1,544,951✔
1958
    }
1959

1960
    public set locale(value: string) {
1961
        if (value !== this._locale) {
4,134✔
1962
            this._locale = value;
4,128✔
1963
            this._currencyPositionLeft = undefined;
4,128✔
1964
            this.summaryService.clearSummaryCache();
4,128✔
1965
            this.pipeTrigger++;
4,128✔
1966
            this.notifyChanges();
4,128✔
1967
            this.localeChange.emit();
4,128✔
1968
        }
1969
    }
1970

1971
    @Input()
1972
    public get pagingMode() {
1973
        return this._pagingMode;
8,419✔
1974
    }
1975

1976
    public set pagingMode(val: GridPagingMode) {
1977
        this._pagingMode = val;
2✔
1978
        this.pipeTrigger++;
2✔
1979
        this.notifyChanges(true);
2✔
1980
    }
1981

1982
    /** @hidden @internal */
1983
    public get page(): number {
1984
        return this.paginator?.page || 0;
2,235,365✔
1985
    }
1986

1987
    public set page(val: number) {
1988
        if (this.paginator) {
713✔
1989
            this.paginator.page = val;
330✔
1990
        }
1991
    }
1992

1993
    /** @hidden @internal */
1994
    public get perPage(): number {
1995
        return this.paginator?.perPage || DEFAULT_ITEMS_PER_PAGE;
2,235,278✔
1996
    }
1997

1998
    public set perPage(val: number) {
1999
        if (this.paginator) {
20✔
2000
            this.paginator.perPage = val;
20✔
2001
        }
2002
    }
2003

2004
    /**
2005
     * Gets/Sets if the row selectors are hidden.
2006
     *
2007
     * @remarks
2008
     *  By default row selectors are shown
2009
     */
2010
    @WatchChanges()
2011
    @Input({ transform: booleanAttribute })
2012
    public get hideRowSelectors() {
2013
        return this._hideRowSelectors;
31,751✔
2014
    }
2015

2016
    public set hideRowSelectors(value: boolean) {
2017
        this._hideRowSelectors = value;
21✔
2018
        this.notifyChanges(true);
21✔
2019
    }
2020

2021
    /**
2022
     * Gets/Sets whether rows can be moved.
2023
     *
2024
     * @example
2025
     * ```html
2026
     * <igx-grid #grid [rowDraggable]="true"></igx-grid>
2027
     * ```
2028
     */
2029
    @Input({ transform: booleanAttribute })
2030
    public get rowDraggable(): boolean {
2031
        return this._rowDrag && this.hasVisibleColumns;
327,616✔
2032
    }
2033

2034
    public set rowDraggable(val: boolean) {
2035
        this._rowDrag = val;
47✔
2036
        this.notifyChanges(true);
47✔
2037
    }
2038

2039
    /**
2040
     * Gets/Sets the trigger for validators used when editing the grid.
2041
     *
2042
     * @example
2043
     * ```html
2044
     * <igx-grid #grid validationTrigger='blur'></igx-grid>
2045
     * ```
2046
     */
2047
    @Input()
2048
    public validationTrigger: GridValidationTrigger = 'change';
4,117✔
2049

2050
    /**
2051
     * @hidden
2052
     * @internal
2053
     */
2054
    public rowDragging = false;
4,117✔
2055

2056
    /** @hidden @internal */
2057
    public dragRowID = null;
4,117✔
2058

2059
    /**
2060
     * Gets/Sets whether the rows are editable.
2061
     *
2062
     * @remarks
2063
     * By default it is set to false.
2064
     * @example
2065
     * ```html
2066
     * <igx-grid #grid [rowEditable]="true" [primaryKey]="'ProductID'" ></igx-grid>
2067
     * ```
2068
     */
2069
    @WatchChanges()
2070
    @Input({ transform: booleanAttribute })
2071
    public get rowEditable(): boolean {
2072
        return this._rowEditable;
13,394,223✔
2073
    }
2074

2075
    public set rowEditable(val: boolean) {
2076
        if (!this._init) {
552✔
2077
            this.refreshGridState();
35✔
2078
        }
2079
        this._rowEditable = val;
552✔
2080
        this.notifyChanges();
552✔
2081
    }
2082

2083
    /**
2084
     * Gets/Sets the height.
2085
     *
2086
     * @example
2087
     * ```html
2088
     * <igx-grid #grid [data]="Data" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2089
     * ```
2090
     */
2091
    @WatchChanges()
2092
    @HostBinding('style.height')
2093
    @Input()
2094
    public get height(): string | null {
2095
        return this._height;
36,712✔
2096
    }
2097

2098
    public set height(value: string | null) {
2099
        if (this._height !== value) {
3,072✔
2100
            this._height = value;
3,070✔
2101
            this.nativeElement.style.height = value;
3,070✔
2102
            this.notifyChanges(true);
3,070✔
2103
        }
2104
    }
2105

2106
    /**
2107
     * @hidden @internal
2108
     */
2109
    @HostBinding('style.width')
2110
    public get hostWidth() {
2111
        return this._width || this._hostWidth;
33,144✔
2112
    }
2113

2114
    /**
2115
     * Gets/Sets the width of the grid.
2116
     *
2117
     * @example
2118
     * ```typescript
2119
     * let gridWidth = this.grid.width;
2120
     * ```
2121
     */
2122
    @WatchChanges()
2123
    @Input()
2124
    public get width(): string | null {
2125
        return this._width;
144,602✔
2126
    }
2127

2128
    public set width(value: string | null) {
2129
        if (this._width !== value) {
2,079✔
2130
            this._width = value;
2,079✔
2131
            this.nativeElement.style.width = value;
2,079✔
2132
            this.notifyChanges(true);
2,079✔
2133
        }
2134
    }
2135

2136
    /** @hidden @internal */
2137
    public get headerWidth() {
2138
        return parseInt(this.width, 10) - 17;
×
2139
    }
2140

2141
    /**
2142
     * Gets/Sets the row height.
2143
     *
2144
     * @example
2145
     * ```html
2146
     * <igx-grid #grid [data]="localData" [rowHeight]="100" [autoGenerate]="true"></igx-grid>
2147
     * ```
2148
     */
2149
    @WatchChanges()
2150
    @Input()
2151
    public get rowHeight(): number {
2152
        return this._rowHeight ? this._rowHeight : this.defaultRowHeight;
347,543✔
2153
    }
2154

2155
    public set rowHeight(value: number | string) {
2156
        if (typeof value !== 'number') {
2✔
2157
            value = parseInt(value, 10);
1✔
2158
        }
2159
        this._rowHeight = value;
2✔
2160
    }
2161

2162
    /**
2163
     * Gets/Sets the default width of the columns.
2164
     *
2165
     * @example
2166
     * ```html
2167
     * <igx-grid #grid [data]="localData" [columnWidth]="100" [autoGenerate]="true"></igx-grid>
2168
     * ```
2169
     */
2170
    @WatchChanges()
2171
    @Input()
2172
    public get columnWidth(): string {
2173
        return this._columnWidth;
610✔
2174
    }
2175
    public set columnWidth(value: string) {
2176
        this._columnWidth = value;
230✔
2177
        this.columnWidthSetByUser = true;
230✔
2178
        this.notifyChanges(true);
230✔
2179
    }
2180

2181
    /**
2182
     * Get/Sets the message displayed when there are no records.
2183
     *
2184
     * @example
2185
     * ```html
2186
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
2187
     * ```
2188
     */
2189
    @Input()
2190
    public set emptyGridMessage(value: string) {
2191
        this._emptyGridMessage = value;
×
2192
    }
2193
    public get emptyGridMessage(): string {
2194
        return this._emptyGridMessage || this.resourceStrings.igx_grid_emptyGrid_message;
732✔
2195
    }
2196

2197
    /**
2198
     * Gets/Sets whether the grid is going to show a loading indicator.
2199
     *
2200
     * @example
2201
     * ```html
2202
     * <igx-grid #grid [data]="Data" [isLoading]="true" [autoGenerate]="true"></igx-grid>
2203
     * ```
2204
     */
2205
    @WatchChanges()
2206
    @Input({ transform: booleanAttribute })
2207
    public set isLoading(value: boolean) {
2208
        if (this._isLoading !== value) {
14✔
2209
            this._isLoading = value;
14✔
2210
            if (this.data) {
14✔
2211
                this.evaluateLoadingState();
8✔
2212
            }
2213
        }
2214
        Promise.resolve().then(() => {
14✔
2215
            // wait for the current detection cycle to end before triggering a new one.
2216
            this.notifyChanges();
14✔
2217
        });
2218
    }
2219

2220
    public get isLoading(): boolean {
2221
        return this._isLoading;
94,342✔
2222
    }
2223

2224
    /**
2225
     * Gets/Sets whether the columns should be auto-generated once again after the initialization of the grid
2226
     *
2227
     * @remarks
2228
     * This will allow to bind the grid to remote data and having auto-generated columns at the same time.
2229
     * Note that after generating the columns, this property would be disabled to avoid re-creating
2230
     * columns each time a new data is assigned.
2231
     * @example
2232
     * ```typescript
2233
     *  this.grid.shouldGenerate = true;
2234
     * ```
2235
     * @deprecated in version 18.2.0. Column re-creation now relies on `autoGenerate` instead.
2236
     */
2237
    public get shouldGenerate(): boolean {
2238
        return this.autoGenerate;
×
2239
    }
2240

2241
    public set shouldGenerate(value: boolean) {
2242
        this.autoGenerate = value;
×
2243
    }
2244

2245
    /**
2246
     * Gets/Sets the message displayed when there are no records and the grid is filtered.
2247
     *
2248
     * @example
2249
     * ```html
2250
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
2251
     * ```
2252
     */
2253
    @Input()
2254
    public set emptyFilteredGridMessage(value: string) {
2255
        this._emptyFilteredGridMessage = value;
×
2256
    }
2257

2258
    public get emptyFilteredGridMessage(): string {
2259
        return this._emptyFilteredGridMessage || this.resourceStrings.igx_grid_emptyFilteredGrid_message;
205✔
2260
    }
2261

2262
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
2263
    /**
2264
     * Gets/Sets the initial pinning configuration.
2265
     *
2266
     * @remarks
2267
     * Allows to apply pinning the columns to the start or the end.
2268
     * Note that pinning to both sides at a time is not allowed.
2269
     * @example
2270
     * ```html
2271
     * <igx-grid [pinning]="pinningConfig"></igx-grid>
2272
     * ```
2273
     */
2274
    @Input()
2275
    public get pinning() {
2276
        return this._pinning;
2,944,395✔
2277
    }
2278
    public set pinning(value) {
2279
        if (value !== this._pinning) {
135✔
2280
            this.resetCaches();
135✔
2281
        }
2282
        this._pinning = value;
135✔
2283
    }
2284

2285
    /**
2286
     * Gets/Sets if the filtering is enabled.
2287
     *
2288
     * @example
2289
     * ```html
2290
     * <igx-grid #grid [data]="localData" [allowFiltering]="true" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2291
     * ```
2292
     */
2293
    @Input({ transform: booleanAttribute })
2294
    public get allowFiltering() {
2295
        return this._allowFiltering;
408,476✔
2296
    }
2297

2298
    public set allowFiltering(value) {
2299
        if (this._allowFiltering !== value) {
745✔
2300
            this._allowFiltering = value;
734✔
2301
            this.filteringService.registerSVGIcons();
734✔
2302

2303

2304
            this.filteringService.isFilterRowVisible = false;
734✔
2305
            this.filteringService.filteredColumn = null;
734✔
2306

2307
            this.notifyChanges(true);
734✔
2308
        }
2309
    }
2310

2311
    /**
2312
     * Gets/Sets a value indicating whether the advanced filtering is enabled.
2313
     *
2314
     * @example
2315
     * ```html
2316
     * <igx-grid #grid [data]="localData" [allowAdvancedFiltering]="true" [autoGenerate]="true"></igx-grid>
2317
     * ```
2318
     */
2319
    @Input({ transform: booleanAttribute })
2320
    public get allowAdvancedFiltering() {
2321
        return this._allowAdvancedFiltering;
567✔
2322
    }
2323

2324
    public set allowAdvancedFiltering(value) {
2325
        if (this._allowAdvancedFiltering !== value) {
55✔
2326
            this._allowAdvancedFiltering = value;
54✔
2327
            this.filteringService.registerSVGIcons();
54✔
2328

2329
            if (!this._init) {
54✔
2330
                this.notifyChanges(true);
5✔
2331
            }
2332
        }
2333
    }
2334

2335
    /**
2336
     * Gets/Sets the filter mode.
2337
     *
2338
     * @example
2339
     * ```html
2340
     * <igx-grid #grid [data]="localData" [filterMode]="'quickFilter'" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2341
     * ```
2342
     * @remarks
2343
     * By default it's set to FilterMode.quickFilter.
2344
     */
2345
    @Input()
2346
    public get filterMode() {
2347
        return this._filterMode;
95,072✔
2348
    }
2349

2350
    public set filterMode(value: FilterMode) {
2351
        switch (value) {
188!
2352
            case FilterMode.excelStyleFilter:
2353
            case FilterMode.quickFilter:
2354
                this._filterMode = value;
188✔
2355
                break;
188✔
2356
            default:
2357
                break;
×
2358
        }
2359

2360
        if (this.filteringService.isFilterRowVisible) {
188✔
2361
            this.filteringRow.close();
1✔
2362
        }
2363
        this.notifyChanges(true);
188✔
2364
    }
2365

2366
    /**
2367
     * Gets/Sets the summary position.
2368
     *
2369
     * @example
2370
     * ```html
2371
     * <igx-grid #grid [data]="localData" summaryPosition="top" [autoGenerate]="true"></igx-grid>
2372
     * ```
2373
     * @remarks
2374
     * By default it is bottom.
2375
     */
2376
    @Input()
2377
    public get summaryPosition() {
2378
        return this._summaryPosition;
32,261✔
2379
    }
2380

2381
    public set summaryPosition(value: GridSummaryPosition) {
2382
        this._summaryPosition = value;
18✔
2383
        this.notifyChanges();
18✔
2384
    }
2385

2386
    /**
2387
     * Gets/Sets the summary calculation mode.
2388
     *
2389
     * @example
2390
     * ```html
2391
     * <igx-grid #grid [data]="localData" summaryCalculationMode="rootLevelOnly" [autoGenerate]="true"></igx-grid>
2392
     * ```
2393
     * @remarks
2394
     * By default it is rootAndChildLevels which means the summaries are calculated for the root level and each child level.
2395
     */
2396
    @Input()
2397
    public get summaryCalculationMode() {
2398
        return this._summaryCalculationMode;
276,413✔
2399
    }
2400

2401
    public set summaryCalculationMode(value: GridSummaryCalculationMode) {
2402
        this._summaryCalculationMode = value;
63✔
2403
        if (!this._init) {
63✔
2404
            this.crudService.endEdit(false);
25✔
2405
            this.summaryService.resetSummaryHeight();
25✔
2406
            this.notifyChanges(true);
25✔
2407
        }
2408
    }
2409

2410
    /**
2411
     * Controls whether the summary row is visible when groupBy/parent row is collapsed.
2412
     *
2413
     * @example
2414
     * ```html
2415
     * <igx-grid #grid [data]="localData" [showSummaryOnCollapse]="true" [autoGenerate]="true"></igx-grid>
2416
     * ```
2417
     * @remarks
2418
     * By default showSummaryOnCollapse is set to 'false' which means that the summary row is not visible
2419
     * when the groupBy/parent row is collapsed.
2420
     */
2421
    @Input({ transform: booleanAttribute })
2422
    public get showSummaryOnCollapse() {
2423
        return this._showSummaryOnCollapse;
32,254✔
2424
    }
2425

2426
    public set showSummaryOnCollapse(value: boolean) {
2427
        this._showSummaryOnCollapse = value;
9✔
2428
        this.notifyChanges();
9✔
2429
    }
2430

2431
    /**
2432
     * Gets/Sets the filtering strategy of the grid.
2433
     *
2434
     * @example
2435
     * ```html
2436
     *  <igx-grid #grid [data]="localData" [filterStrategy]="filterStrategy"></igx-grid>
2437
     * ```
2438
     */
2439
    @Input()
2440
    public get filterStrategy(): IFilteringStrategy {
2441
        return this._filterStrategy;
46,488✔
2442
    }
2443

2444
    public set filterStrategy(classRef: IFilteringStrategy) {
2445
        this._filterStrategy = classRef;
38✔
2446
    }
2447

2448
    /**
2449
     * Gets/Sets the sorting strategy of the grid.
2450
     *
2451
     * @example
2452
     * ```html
2453
     *  <igx-grid #grid [data]="localData" [sortStrategy]="sortStrategy"></igx-grid>
2454
     * ```
2455
     */
2456
    @Input()
2457
    public get sortStrategy(): IGridSortingStrategy {
2458
        return this._sortingStrategy;
48,590✔
2459
    }
2460

2461
    public set sortStrategy(value: IGridSortingStrategy) {
2462
        this._sortingStrategy = value;
14✔
2463
    }
2464

2465
    /**
2466
     * Gets/Sets the sorting options - single or multiple sorting.
2467
     * Accepts an `ISortingOptions` object with any of the `mode` properties.
2468
     *
2469
     * @example
2470
     * ```typescript
2471
     * const _sortingOptions: ISortingOptions = {
2472
     *      mode: 'single'
2473
     * }
2474
     * ```html
2475
     * <igx-grid [sortingOptions]="sortingOptions"><igx-grid>
2476
     * ```
2477
     */
2478
    @Input()
2479
    public set sortingOptions(value: ISortingOptions) {
2480
        if (!this._init) {
3✔
2481
            // clear sort only if option is changed runtime. No need to clear on initial load.
2482
            this.clearSort();
2✔
2483
        }
2484
        this._sortingOptions = Object.assign(this._sortingOptions, value);
3✔
2485
    }
2486

2487
    public get sortingOptions() {
2488
        return this._sortingOptions;
28,909✔
2489
    }
2490

2491
    /* blazorByValueArray */
2492
    /* blazorAlwaysWriteback */
2493
    /* @tsTwoWayProperty (true, "SelectedRowsChange", "Detail", false) */
2494
    /* blazorPrimitiveValue */
2495
    /**
2496
     * Gets/Sets the current selection state.
2497
     *
2498
     * @remarks
2499
     * Represents the selected rows' IDs (primary key or rowData)
2500
     * @example
2501
     * ```html
2502
     * <igx-grid [data]="localData" primaryKey="ID" rowSelection="multiple" [selectedRows]="[0, 1, 2]"><igx-grid>
2503
     * ```
2504
     */
2505
    @Input()
2506
    public set selectedRows(rowIDs: any[]) {
2507
        this.selectRows(rowIDs || [], true);
138!
2508
    }
2509

2510
    public get selectedRows(): any[] {
2511
        return this.selectionService.getSelectedRows();
731✔
2512
    }
2513

2514

2515
    /** @hidden @internal */
2516
    public get headerGroupsList(): IgxGridHeaderGroupComponent[] {
2517
        return this.theadRow.groups;
20,611✔
2518
    }
2519

2520
    /** @hidden @internal */
2521
    public get headerCellList(): IgxGridHeaderComponent[] {
2522
        return this.headerGroupsList.map(headerGroup => headerGroup.header).filter(header => header);
5,016✔
2523
    }
2524

2525
    /** @hidden @internal */
2526
    public get filterCellList(): IgxGridFilteringCellComponent[] {
2527
        return this.headerGroupsList.map(group => group.filter).filter(cell => cell);
14,499✔
2528
    }
2529

2530
    /**
2531
     * @hidden @internal
2532
     */
2533
    public get summariesRowList() {
2534
        const res = new QueryList<any>();
2,658✔
2535
        if (!this._summaryRowList) {
2,658!
2536
            return res;
×
2537
        }
2538
        const sumList = this._summaryRowList.filter((item) => item.element.nativeElement.parentElement !== null);
2,658✔
2539
        res.reset(sumList);
2,658✔
2540
        return res;
2,658✔
2541
    }
2542

2543
    /* csSuppress */
2544
    /**
2545
     * A list of `IgxGridRowComponent`.
2546
     *
2547
     * @example
2548
     * ```typescript
2549
     * const rowList = this.grid.rowList;
2550
     * ```
2551
     */
2552
    public get rowList() {
2553
        const res = new QueryList<IgxRowDirective>();
11,865✔
2554
        if (!this._rowList) {
11,865!
2555
            return res;
×
2556
        }
2557
        const rList = this._rowList
11,865✔
2558
            .filter((item) => item.element.nativeElement.parentElement !== null)
91,938✔
2559
            .sort((a, b) => a.index - b.index);
101,245✔
2560
        res.reset(rList);
11,865✔
2561
        return res;
11,865✔
2562
    }
2563

2564
    /* csSuppress */
2565
    /**
2566
     * A list of currently rendered `IgxGridRowComponent`'s.
2567
     *
2568
     * @example
2569
     * ```typescript
2570
     * const dataList = this.grid.dataRowList;
2571
     * ```
2572
     */
2573
    public get dataRowList(): QueryList<IgxRowDirective> {
2574
        const res = new QueryList<IgxRowDirective>();
56,787✔
2575
        if (!this._dataRowList) {
56,787✔
2576
            return res;
9,398✔
2577
        }
2578
        const rList = this._dataRowList.filter(item => item.element.nativeElement.parentElement !== null).sort((a, b) => a.index - b.index);
233,700✔
2579
        res.reset(rList);
47,389✔
2580
        return res;
47,389✔
2581
    }
2582

2583
    /**
2584
     * Gets the header row selector template.
2585
     */
2586
    @Input()
2587
    public get headSelectorTemplate(): TemplateRef<IgxHeadSelectorTemplateContext> {
2588
        return this._headSelectorTemplate || this.headSelectorsTemplates?.first;
3,334✔
2589
    }
2590

2591
    /**
2592
     * Sets the header row selector template.
2593
     * ```html
2594
     * <ng-template #template igxHeadSelector let-headContext>
2595
     * {{ headContext.selectedCount }} / {{ headContext.totalCount  }}
2596
     * </ng-template>
2597
     * ```
2598
     * ```typescript
2599
     * @ViewChild("'template'", {read: TemplateRef })
2600
     * public template: TemplateRef<any>;
2601
     * this.grid.headSelectorTemplate = this.template;
2602
     * ```
2603
     */
2604
    public set headSelectorTemplate(template: TemplateRef<IgxHeadSelectorTemplateContext>) {
2605
        this._headSelectorTemplate = template;
1✔
2606
    }
2607

2608
    /**
2609
     * @hidden
2610
     * @internal
2611
     */
2612
    public get isPinningToStart() {
2613
        return this.pinning.columns !== ColumnPinningPosition.End;
2,448,368✔
2614
    }
2615

2616
    /**
2617
     * @hidden
2618
     * @internal
2619
     */
2620
    public get isRowPinningToTop() {
2621
        return this.pinning.rows !== RowPinningPosition.Bottom;
676,476✔
2622
    }
2623

2624
    /**
2625
     * Gets the row selector template.
2626
     */
2627
    @Input()
2628
    public get rowSelectorTemplate(): TemplateRef<IgxRowSelectorTemplateContext> {
2629
        return this._rowSelectorTemplate || this.rowSelectorsTemplates?.first;
20,196✔
2630
    }
2631

2632
    /**
2633
         * Sets a custom template for the row selectors.
2634
         * ```html
2635
         * <ng-template #template igxRowSelector let-rowContext>
2636
         *    <igx-checkbox [checked]="rowContext.selected"></igx-checkbox>
2637
         * </ng-template>
2638
         * ```
2639
         * ```typescript
2640
         * @ViewChild("'template'", {read: TemplateRef })
2641
         * public template: TemplateRef<any>;
2642
         * this.grid.rowSelectorTemplate = this.template;
2643
         * ```
2644
         */
2645
    public set rowSelectorTemplate(template: TemplateRef<IgxRowSelectorTemplateContext>) {
2646
        this._rowSelectorTemplate = template;
1✔
2647
    }
2648

2649
    /**
2650
     * @hidden @internal
2651
     */
2652
    public get rowOutletDirective() {
2653
        return this.rowEditingOutletDirective;
4,394✔
2654
    }
2655

2656
    /**
2657
     * @hidden @internal
2658
     */
2659
    public get parentRowOutletDirective() {
2660
        return this.outlet;
1✔
2661
    }
2662

2663
    /**
2664
     * @hidden @internal
2665
     */
2666
    public get rowEditCustom(): TemplateRef<IgxGridRowEditTemplateContext> {
2667
        if (this.rowEditCustomDirectives && this.rowEditCustomDirectives.first) {
6,560✔
2668
            return this.rowEditCustomDirectives.first;
62✔
2669
        }
2670
        return null;
6,498✔
2671
    }
2672

2673
    /**
2674

2675
    /**
2676
     * @hidden @internal
2677
     */
2678
    public get rowEditContainer(): TemplateRef<IgxGridRowEditTemplateContext> {
2679
        return this.rowEditCustom ? this.rowEditCustom : this.defaultRowEditTemplate;
5,991✔
2680
    }
2681

2682
    /**
2683
     * The custom template, if any, that should be used when rendering the row drag indicator icon
2684
     */
2685
    @Input()
2686
    public get dragIndicatorIconTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
2687
        return this._customDragIndicatorIconTemplate || this.dragIndicatorIconTemplates?.first;
3,355✔
2688
    }
2689

2690
    /**
2691
     * Sets a custom template that should be used when rendering the row drag indicator icon.
2692
     *```html
2693
     * <ng-template #template igxDragIndicatorIcon>
2694
     *    <igx-icon>expand_less</igx-icon>
2695
     * </ng-template>
2696
     * ```
2697
     * ```typescript
2698
     * @ViewChild("'template'", {read: TemplateRef })
2699
     * public template: TemplateRef<any>;
2700
     * this.grid.dragIndicatorIconTemplate = this.template;
2701
     * ```
2702
     */
2703
    public set dragIndicatorIconTemplate(val: TemplateRef<IgxGridEmptyTemplateContext>) {
2704
        this._customDragIndicatorIconTemplate = val;
909✔
2705
    }
2706

2707
    /**
2708
     * @hidden @internal
2709
     */
2710
    public get firstEditableColumnIndex(): number {
2711
        const index = this.visibleColumns.filter(col => col.editable)
26✔
2712
            .map(c => c.visibleIndex).sort((a, b) => a - b);
21✔
2713
        return index.length ? index[0] : null;
4!
2714
    }
2715

2716
    /**
2717
     * @hidden @internal
2718
     */
2719
    public get lastEditableColumnIndex(): number {
2720
        const index = this.visibleColumns.filter(col => col.editable)
47✔
2721
            .map(c => c.visibleIndex).sort((a, b) => a > b ? -1 : 1);
55✔
2722
        return index.length ? index[0] : null;
6!
2723
    }
2724

2725
    /**
2726
     * @hidden @internal
2727
     * TODO: Nav service logic doesn't handle 0 results from this querylist
2728
     */
2729
    public get rowEditTabs(): QueryList<IgxRowEditTabStopDirective> {
2730
        return this.rowEditTabsCUSTOM.length ? this.rowEditTabsCUSTOM : this.rowEditTabsDEFAULT;
141✔
2731
    }
2732

2733
    /** @hidden @internal */
2734
    public get activeDescendant() {
2735
        const activeElem = this.navigation.activeNode;
134,314✔
2736

2737
        if (!activeElem || !Object.keys(activeElem).length) {
134,314✔
2738
            return this.id;
118,839✔
2739
        }
2740

2741
        return activeElem.row < 0 ?
15,475✔
2742
            `${this.id}_${activeElem.row}_${activeElem.mchCache.level}_${activeElem.column}` :
2743
            `${this.id}_${activeElem.row}_${activeElem.column}`;
2744
    }
2745

2746
    /** @hidden @internal */
2747
    public get bannerClass(): string {
2748
        const position = this.rowEditPositioningStrategy.isTop ? 'igx-banner__border-top' : 'igx-banner__border-bottom';
5,991✔
2749
        return `igx-banner ${position}`;
5,991✔
2750
    }
2751

2752
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
2753
    /**
2754
     * Gets/Sets the sorting state.
2755
     *
2756
     * @remarks
2757
     * Supports two-way data binding.
2758
     * @example
2759
     * ```html
2760
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(sortingExpressions)]="model.sortingExpressions"></igx-grid>
2761
     * ```
2762
     */
2763
    @WatchChanges()
2764
    @Input()
2765
    public get sortingExpressions(): ISortingExpression[] {
2766
        return this._sortingExpressions;
298,175✔
2767
    }
2768

2769
    public set sortingExpressions(value: ISortingExpression[]) {
2770
        this._sortingExpressions = cloneArray(value);
453✔
2771
        this.sortingExpressionsChange.emit(this._sortingExpressions);
453✔
2772
        this.notifyChanges();
453✔
2773
    }
2774

2775
    /**
2776
     * @hidden @internal
2777
     */
2778
    public get maxLevelHeaderDepth() {
2779
        if (this._maxLevelHeaderDepth === null) {
×
2780
            this._maxLevelHeaderDepth = this.hasColumnLayouts ?
×
2781
                this._columns.reduce((acc, col) => Math.max(acc, col.rowStart), 0) :
×
2782
                this._columns.reduce((acc, col) => Math.max(acc, col.level), 0);
×
2783
        }
2784
        return this._maxLevelHeaderDepth;
×
2785
    }
2786

2787
    /**
2788
     * Gets the number of hidden columns.
2789
     *
2790
     * @example
2791
     * ```typescript
2792
     * const hiddenCol = this.grid.hiddenColumnsCount;
2793
     * ``
2794
     */
2795
    public get hiddenColumnsCount() {
2796
        return this._columns.filter((col) => col.columnGroup === false && col.hidden === true).length;
28,701✔
2797
    }
2798

2799
    /**
2800
     * Gets the number of pinned columns.
2801
     */
2802
    public get pinnedColumnsCount() {
2803
        return this.pinnedColumns.filter(col => !col.columnLayout).length;
101✔
2804
    }
2805

2806
    /**
2807
     * Gets/Sets whether the grid has batch editing enabled.
2808
     * When batch editing is enabled, changes are not made directly to the underlying data.
2809
     * Instead, they are stored as transactions, which can later be committed w/ the `commit` method.
2810
     *
2811
     * @example
2812
     * ```html
2813
     * <igx-grid [batchEditing]="true" [data]="someData">
2814
     * </igx-grid>
2815
     * ```
2816
     */
2817
    @Input({ transform: booleanAttribute })
2818
    public get batchEditing(): boolean {
2819
        return this._batchEditing;
535,223✔
2820
    }
2821

2822
    public set batchEditing(val: boolean) {
2823
        if (val !== this._batchEditing) {
163✔
2824
            delete this._transactions;
152✔
2825
            this._batchEditing = val;
152✔
2826
            this.switchTransactionService(val);
152✔
2827
            this.subscribeToTransactions();
152✔
2828
        }
2829
    }
2830

2831
    /* blazorSuppress */
2832
    /**
2833
     * Get transactions service for the grid.
2834
     */
2835
    public get transactions(): TransactionService<Transaction, State> {
2836
        if (this._diTransactions && !this.batchEditing) {
7,268,871✔
2837
            return this._diTransactions;
3,804✔
2838
        }
2839
        return this._transactions;
7,265,067✔
2840
    }
2841

2842
    /**
2843
     * @hidden @internal
2844
     */
2845
    public get currentRowState(): any {
2846
        return this._currentRowState;
×
2847
    }
2848

2849
    /**
2850
     * @hidden @internal
2851
     */
2852
    public get currencyPositionLeft(): boolean {
2853
        if (this._currencyPositionLeft !== undefined) {
8✔
2854
            return this._currencyPositionLeft;
6✔
2855
        }
2856
        const format = getLocaleNumberFormat(this.locale, NumberFormatStyle.Currency);
2✔
2857
        const formatParts = format.split(',');
2✔
2858
        const i = formatParts.indexOf(formatParts.find(c => c.includes('¤')));
3✔
2859
        return this._currencyPositionLeft = i < 1;
2✔
2860
    }
2861

2862
    /**
2863
     * Gets/Sets cell selection mode.
2864
     *
2865
     * @remarks
2866
     * By default the cell selection mode is multiple
2867
     * @param selectionMode: GridSelectionMode
2868
     */
2869
    @WatchChanges()
2870
    @Input()
2871
    public get cellSelection() {
2872
        return this._cellSelectionMode;
1,214,361✔
2873
    }
2874

2875
    public set cellSelection(selectionMode: GridSelectionMode) {
2876
        this._cellSelectionMode = selectionMode;
33✔
2877
        // if (this.gridAPI.grid) {
2878
        this.selectionService.clear(true);
33✔
2879
        this.notifyChanges();
33✔
2880
        // }
2881
    }
2882

2883
    /**
2884
     * Gets/Sets row selection mode
2885
     *
2886
     * @remarks
2887
     * By default the row selection mode is 'none'
2888
     * Note that in IgxGrid and IgxHierarchicalGrid 'multipleCascade' behaves like 'multiple'
2889
     */
2890
    @WatchChanges()
2891
    @Input()
2892
    public get rowSelection() {
2893
        return this._rowSelectionMode;
300,469✔
2894
    }
2895

2896
    public set rowSelection(selectionMode: GridSelectionMode) {
2897
        this._rowSelectionMode = selectionMode;
524✔
2898
        if (!this._init) {
524✔
2899
            this.selectionService.clearAllSelectedRows();
72✔
2900
            this.notifyChanges(true);
72✔
2901
        }
2902
    }
2903

2904
    /**
2905
     * Gets/Sets column selection mode
2906
     *
2907
     * @remarks
2908
     * By default the row selection mode is none
2909
     * @param selectionMode: GridSelectionMode
2910
     */
2911
    @WatchChanges()
2912
    @Input()
2913
    public get columnSelection() {
2914
        return this._columnSelectionMode;
222,303✔
2915
    }
2916

2917
    public set columnSelection(selectionMode: GridSelectionMode) {
2918
        this._columnSelectionMode = selectionMode;
158✔
2919
        // if (this.gridAPI.grid) {
2920
        this.selectionService.clearAllSelectedColumns();
158✔
2921
        this.notifyChanges(true);
158✔
2922
        // }
2923
    }
2924

2925
    /**
2926
     * @hidden @internal
2927
     */
2928
    public set pagingState(value) {
2929
        this._pagingState = value;
580✔
2930
        if (this.paginator && !this._init) {
580✔
2931
            this.paginator.totalRecords = value.metadata.countRecords;
427✔
2932
        }
2933
    }
2934

2935
    public get pagingState() {
2936
        return this._pagingState;
174✔
2937
    }
2938

2939
    /**
2940
     * @hidden @internal
2941
     */
2942
    public rowEditMessage;
2943

2944
    /**
2945
     * @hidden @internal
2946
     */
2947
    public calcWidth: number;
2948
    /**
2949
     * @hidden @internal
2950
     */
2951
    public calcHeight = 0;
4,117✔
2952
    /**
2953
     * @hidden @internal
2954
     */
2955
    public tfootHeight: number;
2956

2957
    /**
2958
     * @hidden @internal
2959
     */
2960
    public disableTransitions = false;
4,117✔
2961

2962
    /**
2963
     * Represents the last search information.
2964
     */
2965
    public get lastSearchInfo(): ISearchInfo {
2966
        return this._lastSearchInfo;
1,484,046✔
2967
    }
2968

2969
    /**
2970
     * @hidden @internal
2971
     */
2972
    public columnWidthSetByUser = false;
4,117✔
2973

2974
    /**
2975
     * @hidden @internal
2976
     */
2977
    public pinnedRecords: any[];
2978

2979
    /**
2980
     * @hidden @internal
2981
     */
2982
    public unpinnedRecords: any[];
2983

2984
    /**
2985
     * @hidden @internal
2986
     */
2987
    public rendered$ = this.rendered.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true }));
4,117✔
2988

2989
    /** @hidden @internal */
2990
    public resizeNotify = new Subject<void>();
4,117✔
2991

2992
    /** @hidden @internal */
2993
    public rowAddedNotifier = new Subject<IRowDataEventArgs>();
4,117✔
2994

2995
    /** @hidden @internal */
2996
    public rowDeletedNotifier = new Subject<IRowDataEventArgs>();
4,117✔
2997

2998
    /** @hidden @internal */
2999
    public pipeTriggerNotifier = new Subject();
4,117✔
3000

3001
    /** @hidden @internal */
3002
    public _filteredSortedPinnedData: any[];
3003

3004
    /** @hidden @internal */
3005
    public _filteredSortedUnpinnedData: any[];
3006

3007
    /** @hidden @internal */
3008
    public _filteredPinnedData: any[];
3009

3010
    /**
3011
     * @hidden
3012
     */
3013
    public _filteredUnpinnedData;
3014
    /**
3015
     * @hidden @internal
3016
     */
3017
    public _destroyed = false;
4,117✔
3018
    /**
3019
     * @hidden @internal
3020
     */
3021
    public _totalRecords = -1;
4,117✔
3022
    /**
3023
     * @hidden @internal
3024
     */
3025
    public columnsWithNoSetWidths = null;
4,117✔
3026
    /**
3027
     * @hidden @internal
3028
     */
3029
    public pipeTrigger = 0;
4,117✔
3030
    /**
3031
     * @hidden @internal
3032
     */
3033
    public filteringPipeTrigger = 0;
4,117✔
3034

3035
    /**
3036
     * @hidden @internal
3037
     */
3038
    public isColumnWidthSum = false;
4,117✔
3039

3040
    /**
3041
     * @hidden @internal
3042
     */
3043
    public summaryPipeTrigger = 0;
4,117✔
3044
    /**
3045
     * @hidden @internal
3046
     */
3047
    public groupablePipeTrigger = 0;
4,117✔
3048

3049
    /**
3050
    * @hidden @internal
3051
    */
3052
    public EMPTY_DATA = [];
4,117✔
3053

3054
    /** @hidden @internal */
3055
    public get type(): GridType["type"] {
3056
        return 'flat';
24,441✔
3057
    }
3058

3059
    /** @hidden @internal */
3060
    public _baseFontSize: number;
3061

3062
    /**
3063
     * @hidden
3064
     */
3065
    public destroy$ = new Subject<any>();
4,117✔
3066
    /**
3067
     * @hidden
3068
     */
3069
    protected _pagingMode: GridPagingMode = 'local';
4,117✔
3070
    /**
3071
     * @hidden
3072
     */
3073
    protected _pagingState;
3074
    /**
3075
     * @hidden
3076
     */
3077
    protected _hideRowSelectors = false;
4,117✔
3078
    /**
3079
     * @hidden
3080
     */
3081
    protected _rowDrag = false;
4,117✔
3082
    /**
3083
     * @hidden
3084
     */
3085
    protected _columns: IgxColumnComponent[] = [];
4,117✔
3086
    /**
3087
     * @hidden
3088
     */
3089
    protected _pinnedColumns: IgxColumnComponent[] = [];
4,117✔
3090
    /**
3091
     * @hidden
3092
     */
3093
    protected _unpinnedColumns: IgxColumnComponent[] = [];
4,117✔
3094
    /**
3095
     * @hidden
3096
     */
3097
    protected _filteringExpressionsTree: IFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And);
4,117✔
3098
    /**
3099
     * @hidden
3100
     */
3101
    protected _advancedFilteringExpressionsTree: IFilteringExpressionsTree;
3102
    /**
3103
     * @hidden
3104
     */
3105
    protected _sortingExpressions: Array<ISortingExpression> = [];
4,117✔
3106
    /**
3107
     * @hidden
3108
     */
3109
    protected _maxLevelHeaderDepth = null;
4,117✔
3110
    /**
3111
     * @hidden
3112
     */
3113
    protected _columnHiding = false;
4,117✔
3114
    /**
3115
     * @hidden
3116
     */
3117
    protected _columnPinning = false;
4,117✔
3118

3119
    protected _pinnedRecordIDs = [];
4,117✔
3120

3121
    /**
3122
     * @hidden
3123
     */
3124
    protected _hasVisibleColumns;
3125
    protected _allowFiltering = false;
4,117✔
3126
    protected _allowAdvancedFiltering = false;
4,117✔
3127
    protected _filterMode: FilterMode = FilterMode.quickFilter;
4,117✔
3128

3129

3130
    protected _defaultTargetRecordNumber = 10;
4,117✔
3131
    protected _expansionStates: Map<any, boolean> = new Map<any, boolean>();
4,117✔
3132
    protected _defaultExpandState = false;
4,117✔
3133
    protected _headerFeaturesWidth = NaN;
4,117✔
3134
    protected _init = true;
4,117✔
3135
    protected _firstAutoResize = true;
4,117✔
3136
    protected _autoSizeColumnsNotify = new Subject<void>();
4,117✔
3137
    protected _cdrRequestRepaint = false;
4,117✔
3138
    protected _userOutletDirective: IgxOverlayOutletDirective;
3139
    protected _transactions: TransactionService<Transaction, State>;
3140
    protected _batchEditing = false;
4,117✔
3141
    protected _sortingOptions: ISortingOptions = { mode: 'multiple' };
4,117✔
3142
    protected _filterStrategy: IFilteringStrategy = new FilteringStrategy();
4,117✔
3143
    protected _autoGeneratedCols = [];
4,117✔
3144
    protected _autoGeneratedColsRefs: ComponentRef<IgxColumnComponent>[] = [];
4,117✔
3145
    protected _dataView = [];
4,117✔
3146
    protected _lastSearchInfo: ISearchInfo = {
4,117✔
3147
        searchText: '',
3148
        caseSensitive: false,
3149
        exactMatch: false,
3150
        activeMatchIndex: 0,
3151
        matchInfoCache: [],
3152
        matchCount: 0,
3153
        content: ''
3154
    };
3155
    protected _hGridSchema: EntityType[];
3156
    protected gridComputedStyles;
3157

3158
    /** @hidden @internal */
3159
    public get paginator() {
3160
        return this.paginationComponents?.first;
3,734,605✔
3161
    }
3162

3163
    /**
3164
     * @hidden @internal
3165
     */
3166
    public get scrollSize() {
3167
        return this.verticalScrollContainer.getScrollNativeSize();
187,328✔
3168
    }
3169

3170
    private _primaryKey: string;
3171
    private _rowEditable = false;
4,117✔
3172
    private _currentRowState: any;
3173
    private _filteredSortedData = null;
4,117✔
3174
    private _filteredData = null;
4,117✔
3175

3176
    private _customDragIndicatorIconTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3177
    private _excelStyleHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext>;
3178
    private _rowSelectorTemplate: TemplateRef<IgxRowSelectorTemplateContext>;
3179
    private _headSelectorTemplate: TemplateRef<IgxHeadSelectorTemplateContext>;
3180
    private _rowEditTextTemplate: TemplateRef<IgxGridRowEditTextTemplateContext>;
3181
    private _rowAddTextTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3182
    private _rowEditActionsTemplate: TemplateRef<IgxGridRowEditActionsTemplateContext>;
3183
    private _dragGhostCustomTemplate: TemplateRef<IgxGridRowDragGhostContext>;
3184
    private _rowExpandedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3185
    private _rowCollapsedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3186
    private _headerExpandIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
3187
    private _headerCollapseIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
3188

3189
    private _cdrRequests = false;
4,117✔
3190
    private _resourceStrings = getCurrentResourceStrings(GridResourceStringsEN);
4,117✔
3191
    private _emptyGridMessage = null;
4,117✔
3192
    private _emptyFilteredGridMessage = null;
4,117✔
3193
    private _isLoading = false;
4,117✔
3194
    private _locale: string;
3195
    private overlayIDs = [];
4,117✔
3196
    private _sortingStrategy: IGridSortingStrategy;
3197
    private _pinning: IPinningConfig = { columns: ColumnPinningPosition.Start };
4,117✔
3198
    private _shouldRecalcRowHeight = false;
4,117✔
3199

3200
    private _hostWidth;
3201
    private _advancedFilteringOverlayId: string;
3202
    private _advancedFilteringPositionSettings: PositionSettings = {
4,117✔
3203
        verticalDirection: VerticalAlignment.Middle,
3204
        horizontalDirection: HorizontalAlignment.Center,
3205
        horizontalStartPoint: HorizontalAlignment.Center,
3206
        verticalStartPoint: VerticalAlignment.Middle
3207
    };
3208

3209
    private _advancedFilteringOverlaySettings: OverlaySettings = {
4,117✔
3210
        closeOnOutsideClick: false,
3211
        modal: false,
3212
        positionStrategy: new ConnectedPositioningStrategy(this._advancedFilteringPositionSettings),
3213
    };
3214

3215
    private columnListDiffer;
3216
    private rowListDiffer;
3217
    private _height: string | null = '100%';
4,117✔
3218
    private _width: string | null = '100%';
4,117✔
3219
    private _rowHeight: number | undefined;
3220
    private _horizontalForOfs: Array<IgxGridForOfDirective<any, any[]>> = [];
4,117✔
3221
    private _multiRowLayoutRowSize = 1;
4,117✔
3222
    // Caches
3223
    private _totalWidth = NaN;
4,117✔
3224
    private _pinnedVisible = [];
4,117✔
3225
    private _unpinnedVisible = [];
4,117✔
3226
    private _pinnedWidth = NaN;
4,117✔
3227
    private _unpinnedWidth = NaN;
4,117✔
3228
    private _visibleColumns = [];
4,117✔
3229
    private _columnGroups = false;
4,117✔
3230

3231
    private _columnWidth: string;
3232

3233
    private _summaryPosition: GridSummaryPosition = GridSummaryPosition.bottom;
4,117✔
3234
    private _summaryCalculationMode: GridSummaryCalculationMode = GridSummaryCalculationMode.rootAndChildLevels;
4,117✔
3235
    private _showSummaryOnCollapse = false;
4,117✔
3236
    private _summaryRowHeight = 0;
4,117✔
3237
    private _cellSelectionMode: GridSelectionMode = GridSelectionMode.multiple;
4,117✔
3238
    private _rowSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,117✔
3239
    private _selectRowOnClick = true;
4,117✔
3240
    private _columnSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,117✔
3241

3242
    private lastAddedRowIndex;
3243

3244
    private _currencyPositionLeft: boolean;
3245

3246
    private rowEditPositioningStrategy = new RowEditPositionStrategy({
4,117✔
3247
        horizontalDirection: HorizontalAlignment.Right,
3248
        verticalDirection: VerticalAlignment.Bottom,
3249
        horizontalStartPoint: HorizontalAlignment.Left,
3250
        verticalStartPoint: VerticalAlignment.Bottom,
3251
        closeAnimation: null
3252
    });
3253

3254
    private rowEditSettings: OverlaySettings = {
4,117✔
3255
        scrollStrategy: new AbsoluteScrollStrategy(),
3256
        modal: false,
3257
        closeOnOutsideClick: false,
3258
        outlet: this.rowOutletDirective,
3259
        positionStrategy: this.rowEditPositioningStrategy
3260
    };
3261

3262
    private transactionChange$ = new Subject<void>();
4,117✔
3263
    private _rendered = false;
4,117✔
3264
    private readonly DRAG_SCROLL_DELTA = 10;
4,117✔
3265
    private _dataCloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy();
4,117✔
3266
    private _autoSize = false;
4,117✔
3267
    private _sortHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,117✔
3268
    private _sortAscendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,117✔
3269
    private _sortDescendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,117✔
3270
    private _gridSize: Size = Size.Large;
4,117✔
3271
    private _defaultRowHeight = 50;
4,117✔
3272

3273
    /**
3274
     * @hidden @internal
3275
     */
3276
    public get minColumnWidth() {
3277
        return MINIMUM_COLUMN_WIDTH;
65,618✔
3278
    }
3279

3280
    protected get isCustomSetRowHeight(): boolean {
3281
        return !isNaN(this._rowHeight);
1,205,212✔
3282
    }
3283

3284
    /**
3285
     * @hidden @internal
3286
     */
3287
    public abstract id: string;
3288
    /* blazorSuppress */
3289
    public abstract data: any[] | null;
3290

3291
    /**
3292
     * Returns an array of objects containing the filtered data.
3293
     *
3294
     * @example
3295
     * ```typescript
3296
     * let filteredData = this.grid.filteredData;
3297
     * ```
3298
     */
3299
    public get filteredData() {
3300
        return this._filteredData;
48,830✔
3301
    }
3302

3303
    /**
3304
     * Returns an array containing the filtered sorted data.
3305
     *
3306
     * @example
3307
     * ```typescript
3308
     * const filteredSortedData = this.grid1.filteredSortedData;
3309
     * ```
3310
     */
3311
    public get filteredSortedData(): any[] {
3312
        return this._filteredSortedData;
128,569✔
3313
    }
3314

3315
    /**
3316
     * @hidden @internal
3317
     */
3318
    public get rowChangesCount() {
3319
        if (!this.crudService.row) {
17,152✔
3320
            return 0;
14,424✔
3321
        }
3322
        const f = (obj: any) => {
2,728✔
3323
            let changes = 0;
470✔
3324
            Object.keys(obj).forEach(key => isObject(obj[key]) ? changes += f(obj[key]) : changes++);
492✔
3325
            return changes;
470✔
3326
        };
3327
        if (this.transactions.getState(this.crudService.row.id)?.type === TransactionType.ADD) {
2,728✔
3328
            return this._columns.filter(c => c.field).length;
77✔
3329
        }
3330
        const rowChanges = this.transactions.getAggregatedValue(this.crudService.row.id, false);
2,709✔
3331
        return rowChanges ? f(rowChanges) : 0;
2,709✔
3332
    }
3333

3334
    /**
3335
     * @hidden @internal
3336
     */
3337
    public get dataWithAddedInTransactionRows() {
3338
        const result = cloneArray(this.gridAPI.get_all_data());
11,404✔
3339
        if (this.transactions.enabled) {
11,404✔
3340
            result.push(...this.transactions.getAggregatedChanges(true)
7,024✔
3341
                .filter(t => t.type === TransactionType.ADD)
1,228✔
3342
                .map(t => t.newValue));
356✔
3343
        }
3344

3345
        if (this.crudService.row && this.crudService.row.getClassName() === IgxAddRow.name) {
11,404✔
3346
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
385✔
3347
        }
3348

3349
        return result;
11,404✔
3350
    }
3351

3352
    /**
3353
     * @hidden @internal
3354
     */
3355
    public get dataLength() {
3356
        return this.transactions.enabled ? this.dataWithAddedInTransactionRows.length : this.gridAPI.get_all_data().length;
45,963✔
3357
    }
3358

3359
    /**
3360
     * @hidden @internal
3361
     */
3362
    public get template(): TemplateRef<IgxGridTemplateContext> {
3363
        if (this.isLoading && (this.hasZeroResultFilter || this.hasNoData)) {
45,496✔
3364
            return this.loadingGridTemplate ? this.loadingGridTemplate : this.loadingGridDefaultTemplate;
98✔
3365
        }
3366

3367
        if (this.hasZeroResultFilter) {
45,398✔
3368
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
193!
3369
        }
3370

3371
        if (this.hasNoData) {
45,205✔
3372
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate;
737✔
3373
        }
3374
    }
3375

3376
    /**
3377
     * @hidden @internal
3378
     */
3379
    private get hasZeroResultFilter(): boolean {
3380
        return this.filteredData && this.filteredData.length === 0;
45,721✔
3381
    }
3382
    protected get totalCalcWidth() {
3383
        return this.platform.isBrowser ? this.calcWidth : undefined;
43,410!
3384
    }
3385

3386
    protected get renderData() {
3387
        // omit data if not in the browser and size is %
3388
        return !this.platform.isBrowser && this.isPercentHeight ? undefined : this.data;
45,551!
3389
    }
3390

3391
    @HostBinding('style.display')
3392
    protected displayStyle = 'grid';
4,117✔
3393

3394
    @HostBinding('style.grid-template-rows')
3395
    protected templateRows = 'auto auto auto 1fr auto auto';
4,117✔
3396

3397
    /**
3398
     * @hidden @internal
3399
     */
3400
    private get hasNoData(): boolean {
3401
        return !this.data || this.dataLength === 0 || !this.platform.isBrowser;
45,611✔
3402
    }
3403

3404
    /**
3405
     * @hidden @internal
3406
     */
3407
    public get shouldOverlayLoading(): boolean {
3408
        return this.isLoading && !this.hasNoData && !this.hasZeroResultFilter;
48,832✔
3409
    }
3410

3411
    /**
3412
     * @hidden @internal
3413
     */
3414
    public get isMultiRowSelectionEnabled(): boolean {
3415
        return this.rowSelection === GridSelectionMode.multiple
3,393✔
3416
            || this.rowSelection === GridSelectionMode.multipleCascade;
3417
    }
3418

3419
    /**
3420
     * @hidden @internal
3421
     */
3422
    public get isRowSelectable(): boolean {
3423
        return this.rowSelection !== GridSelectionMode.none;
291,300✔
3424
    }
3425

3426
    /**
3427
     * @hidden @internal
3428
     */
3429
    public get isCellSelectable() {
3430
        return this.cellSelection !== GridSelectionMode.none;
8,475✔
3431
    }
3432

3433
    /**
3434
     * @hidden @internal
3435
     */
3436
    public get columnInDrag() {
3437
        return this.gridAPI.cms.column;
271,660✔
3438
    }
3439

3440
    constructor(
3441
        public readonly validation: IgxGridValidationService,
4,117✔
3442
        /** @hidden @internal */
3443
        public readonly selectionService: IgxGridSelectionService,
4,117✔
3444
        protected colResizingService: IgxColumnResizingService,
4,117✔
3445
        @Inject(IGX_GRID_SERVICE_BASE) public readonly gridAPI: GridServiceType,
4,117✔
3446
        protected transactionFactory: IgxFlatTransactionFactory,
4,117✔
3447
        private elementRef: ElementRef<HTMLElement>,
4,117✔
3448
        protected zone: NgZone,
4,117✔
3449
        /** @hidden @internal */
3450
        @Inject(DOCUMENT) public document: any,
4,117✔
3451
        public readonly cdr: ChangeDetectorRef,
4,117✔
3452
        protected differs: IterableDiffers,
4,117✔
3453
        protected viewRef: ViewContainerRef,
4,117✔
3454
        protected injector: Injector,
4,117✔
3455
        protected envInjector: EnvironmentInjector,
4,117✔
3456
        public navigation: IgxGridNavigationService,
4,117✔
3457
        /** @hidden @internal */
3458
        public filteringService: IgxFilteringService,
4,117✔
3459
        protected textHighlightService: IgxTextHighlightService,
4,117✔
3460
        @Inject(IgxOverlayService) protected overlayService: IgxOverlayService,
4,117✔
3461
        /** @hidden @internal */
3462
        public summaryService: IgxGridSummaryService,
4,117✔
3463
        @Inject(LOCALE_ID) private localeId: string,
4,117✔
3464
        protected platform: PlatformUtil,
4,117✔
3465
        @Optional() @Inject(IgxGridTransaction) protected _diTransactions?: TransactionService<Transaction, State>,
4,117✔
3466
    ) {
3467
        this.locale = this.locale || this.localeId;
4,117✔
3468
        this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
4,117✔
3469
        this._transactions.cloneStrategy = this.dataCloneStrategy;
4,117✔
3470
        this.cdr.detach();
4,117✔
3471
        this.selectionService.selectedRowsChange.pipe(takeUntil(this.destroy$)).subscribe((args: any[]) => {
4,117✔
3472
            this.selectedRowsChange.emit(args);
696✔
3473
        });
3474
        IgcTrialWatermark.register();
4,117✔
3475
    }
3476

3477
    /**
3478
     * @hidden
3479
     * @internal
3480
     */
3481
    @HostListener('mouseleave')
3482
    public hideActionStrip() {
3483
        this.actionStrip?.hide();
3✔
3484
    }
3485

3486
    /**
3487
     * @hidden
3488
     * @internal
3489
     */
3490
    public get headerFeaturesWidth() {
3491
        return this._headerFeaturesWidth;
1,305✔
3492
    }
3493

3494
    /**
3495
     * @hidden
3496
     * @internal
3497
     */
3498
    public isDetailRecord(_rec) {
3499
        return false;
1,237✔
3500
    }
3501

3502
    /**
3503
     * @hidden
3504
     * @internal
3505
     */
3506
    public isGroupByRecord(_rec) {
3507
        return false;
1,237✔
3508
    }
3509

3510
    /**
3511
     * @hidden @internal
3512
     */
3513
    public isGhostRecord(record: any): boolean {
3514
        return record.ghostRecord !== undefined;
542,920✔
3515
    }
3516
    /**
3517
     * @hidden @internal
3518
     */
3519
    public isAddRowRecord(record: any): boolean {
3520
        return record.addRow !== undefined;
×
3521
    }
3522

3523
    /**
3524
     * @hidden
3525
     * Returns the row index of a row that takes into account the full view data like pinning.
3526
     */
3527
    public getDataViewIndex(rowIndex, pinned) {
3528
        if (pinned && !this.isRowPinningToTop) {
254,741✔
3529
            rowIndex = rowIndex + this.unpinnedDataView.length;
198✔
3530
        } else if (!pinned && this.isRowPinningToTop) {
254,543✔
3531
            rowIndex = rowIndex + this.pinnedDataView.length;
252,792✔
3532
        }
3533
        return rowIndex;
254,741✔
3534
    }
3535

3536
    /**
3537
     * @hidden
3538
     * @internal
3539
     */
3540
    public get hasDetails() {
3541
        return false;
375✔
3542
    }
3543

3544
    /**
3545
     * Returns the state of the grid virtualization.
3546
     *
3547
     * @remarks
3548
     * Includes the start index and how many records are rendered.
3549
     * @example
3550
     * ```typescript
3551
     * const gridVirtState = this.grid1.virtualizationState;
3552
     * ```
3553
     */
3554
    public get virtualizationState() {
3555
        return this.verticalScrollContainer.state;
1,238✔
3556
    }
3557

3558
    /**
3559
     * @hidden
3560
     * @internal
3561
     */
3562
    public hideOverlays() {
3563
        this.overlayIDs.forEach(overlayID => {
522✔
3564
            const overlay = this.overlayService.getOverlayById(overlayID);
1✔
3565

3566
            if (overlay?.visible && !overlay.closeAnimationPlayer?.hasStarted()) {
1✔
3567
                this.overlayService.hide(overlayID);
1✔
3568

3569
                this.nativeElement.focus();
1✔
3570
            }
3571
        });
3572
    }
3573

3574
    /**
3575
     * Returns whether the record is pinned or not.
3576
     *
3577
     * @param rowIndex Index of the record in the `dataView` collection.
3578
     *
3579
     * @hidden
3580
     * @internal
3581
     */
3582
    public isRecordPinnedByViewIndex(rowIndex: number) {
3583
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this.pinnedDataView.length) ||
303,120✔
3584
            (!this.isRowPinningToTop && rowIndex >= this.unpinnedDataView.length);
3585
    }
3586

3587
    /**
3588
     * Returns whether the record is pinned or not.
3589
     *
3590
     * @param rowIndex Index of the record in the `filteredSortedData` collection.
3591
     */
3592
    public isRecordPinnedByIndex(rowIndex: number) {
3593
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this._filteredSortedPinnedData.length) ||
2,419!
3594
            (!this.isRowPinningToTop && rowIndex >= this._filteredSortedUnpinnedData.length);
3595
    }
3596

3597
    /**
3598
     * @hidden
3599
     * @internal
3600
     */
3601
    public isRecordPinned(rec) {
3602
        return this.getInitialPinnedIndex(rec) !== -1;
1,099,710✔
3603
    }
3604

3605
    /**
3606
     * @hidden
3607
     * @internal
3608
     * Returns the record index in order of pinning by the user. Does not consider sorting/filtering.
3609
     */
3610
    public getInitialPinnedIndex(rec) {
3611
        const id = this.gridAPI.get_row_id(rec);
1,099,818✔
3612
        return this._pinnedRecordIDs.indexOf(id);
1,099,818✔
3613
    }
3614

3615
    /**
3616
     * @hidden
3617
     * @internal
3618
     */
3619
    public get hasPinnedRecords() {
3620
        return this._pinnedRecordIDs.length > 0;
494,410✔
3621
    }
3622

3623
    /**
3624
     * @hidden
3625
     * @internal
3626
     */
3627
    public get pinnedRecordsCount() {
3628
        return this._pinnedRecordIDs.length;
7,357✔
3629
    }
3630

3631
    /**
3632
     * @hidden
3633
     * @internal
3634
     */
3635
    public get crudService() {
3636
        return this.gridAPI.crudService;
3,442,167✔
3637
    }
3638

3639
    /**
3640
     * @hidden
3641
     * @internal
3642
     */
3643
    public _setupServices() {
3644
        this.gridAPI.grid = this as any;
3,571✔
3645
        this.crudService.grid = this as any;
3,571✔
3646
        this.selectionService.grid = this as any;
3,571✔
3647
        this.validation.grid = this as any;
3,571✔
3648
        this.navigation.grid = this as any;
3,571✔
3649
        this.filteringService.grid = this as any;
3,571✔
3650
        this.summaryService.grid = this as any;
3,571✔
3651
    }
3652

3653
    /**
3654
     * @hidden
3655
     * @internal
3656
     */
3657
    public _setupListeners() {
3658
        const destructor = takeUntil<any>(this.destroy$);
3,571✔
3659
        fromEvent(this.nativeElement, 'focusout').pipe(filter(() => !!this.navigation.activeNode), destructor).subscribe((event) => {
3,571✔
3660
            const activeNode = this.navigation.activeNode;
679✔
3661
            if (!this.crudService.cell && !!activeNode &&
679!
3662
                ((event.target === this.tbody.nativeElement && activeNode.row >= 0 &&
3663
                    activeNode.row < this.dataView.length)
3664
                    || (event.target === this.theadRow.nativeElement && activeNode.row === -1)
3665
                    || (event.target === this.tfoot.nativeElement && activeNode.row === this.dataView.length)) &&
3666
                !(this.rowEditable && this.crudService.rowEditingBlocked && this.crudService.rowInEditMode)) {
79!
3667
                this.clearActiveNode();
72✔
3668
            }
3669
        });
3670
        this.rowAddedNotifier.pipe(destructor).subscribe(args => this.refreshGridState(args));
3,571✔
3671
        this.rowDeletedNotifier.pipe(destructor).subscribe(args => {
3,571✔
3672
            this.summaryService.deleteOperation = true;
185✔
3673
            this.summaryService.clearSummaryCache(args);
185✔
3674
        });
3675

3676
        this.subscribeToTransactions();
3,571✔
3677

3678
        this.resizeNotify.pipe(
3,571✔
3679
            filter(() => !this._init),
2,930✔
3680
            throttleTime(40, animationFrameScheduler, { leading: true, trailing: true }),
3681
            destructor
3682
        )
3683
        .subscribe(() => {
3684
            this.zone.run(() => {
2,153✔
3685
                // do not trigger reflow if element is detached.
3686
                if (this.nativeElement.isConnected) {
2,153✔
3687
                    if (this.shouldResize) {
1,770✔
3688
                        // resizing occurs due to the change of --ig-size css var
3689
                        this._gridSize = this.gridSize;
71✔
3690
                        this.updateDefaultRowHeight();
71✔
3691
                        this._autoSize = this.isPercentHeight && this.calcHeight !== this.getDataBasedBodyHeight();
71✔
3692
                        this.crudService.endEdit(false);
71✔
3693
                        if (this._summaryRowHeight === 0) {
71✔
3694
                            this.summaryService.summaryHeight = 0;
71✔
3695
                        }
3696
                    }
3697
                    this.notifyChanges(true);
1,770✔
3698
                }
3699
            });
3700
        });
3701

3702
        this.pipeTriggerNotifier.pipe(takeUntil(this.destroy$)).subscribe(() => this.pipeTrigger++);
3,571✔
3703
        this.columnMovingEnd.pipe(destructor).subscribe(() => this.crudService.endEdit(false));
3,571✔
3704

3705
        this.overlayService.opening.pipe(destructor).subscribe((event) => {
3,571✔
3706
            if (this._advancedFilteringOverlayId === event.id) {
841✔
3707
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
53✔
3708
                if (instance) {
53✔
3709
                    instance.initialize(this as any, this.overlayService, event.id);
53✔
3710
                }
3711
            }
3712
        });
3713

3714
        this.overlayService.opened.pipe(destructor).subscribe((event) => {
3,571✔
3715
            const overlaySettings = this.overlayService.getOverlayById(event.id)?.settings;
576✔
3716

3717
            // do not hide the advanced filtering overlay on scroll
3718
            if (this._advancedFilteringOverlayId === event.id) {
576✔
3719
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
51✔
3720
                if (instance) {
51✔
3721
                    instance.lastActiveNode = this.navigation.activeNode;
51✔
3722
                    instance.queryBuilder.setAddButtonFocus();
51✔
3723
                }
3724
                return;
51✔
3725
            }
3726

3727
            // do not hide the overlay if it's attached to a row
3728
            if (this.rowEditingOverlay?.overlayId === event.id) {
525✔
3729
                return;
21✔
3730
            }
3731

3732
            if (overlaySettings?.outlet === this.outlet && this.overlayIDs.indexOf(event.id) === -1) {
504✔
3733
                this.overlayIDs.push(event.id);
354✔
3734
            }
3735
        });
3736

3737
        this.overlayService.closed.pipe(filter(() => !this._init), destructor).subscribe((event) => {
3,571✔
3738
            if (this._advancedFilteringOverlayId === event.id) {
463✔
3739
                this.overlayService.detach(this._advancedFilteringOverlayId);
28✔
3740
                this._advancedFilteringOverlayId = null;
28✔
3741
                return;
28✔
3742
            }
3743

3744
            const ind = this.overlayIDs.indexOf(event.id);
435✔
3745
            if (ind !== -1) {
435✔
3746
                this.overlayIDs.splice(ind, 1);
196✔
3747
            }
3748
        });
3749

3750
        this.verticalScrollContainer.dataChanging.pipe(filter(() => !this._init), destructor).subscribe(($event) => {
6,759✔
3751
            const shouldRecalcSize = this.isPercentHeight &&
3,273✔
3752
                (!this.calcHeight || this.calcHeight === this.getDataBasedBodyHeight() ||
3753
                    this.calcHeight === this.renderedRowHeight * this._defaultTargetRecordNumber);
3754
            if (shouldRecalcSize) {
3,273✔
3755
                this.calculateGridHeight();
110✔
3756
                $event.containerSize = this.calcHeight;
110✔
3757
            }
3758
            this.evaluateLoadingState();
3,273✔
3759
        });
3760

3761
        this.verticalScrollContainer.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3,571✔
3762
            // called to recalc all widths that may have changes as a result of
3763
            // the vert. scrollbar showing/hiding
3764
            this.notifyChanges(true);
867✔
3765
            this.cdr.detectChanges();
867✔
3766
            Promise.resolve().then(() => this.headerContainer.updateScroll());
867✔
3767
        });
3768

3769

3770
        this.headerContainer?.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3,571✔
3771
            // the horizontal scrollbar showing/hiding
3772
            // update scrollbar visibility and recalc heights
3773
            this.notifyChanges(true);
1,333✔
3774
            this.cdr.detectChanges();
1,333✔
3775
        });
3776

3777
        this.verticalScrollContainer.contentSizeChange.pipe(filter(() => !this._init), throttleTime(30), destructor).subscribe(() => {
3,571✔
3778
            this.notifyChanges(true);
699✔
3779
        });
3780

3781
        // notifier for column autosize requests
3782
        this._autoSizeColumnsNotify.pipe(
3,571✔
3783
            throttleTime(0, animationFrameScheduler, { leading: false, trailing: true }),
3784
            destructor
3785
        )
3786
        .subscribe(() => {
3787
            this.autoSizeColumnsInView();
15✔
3788
            this._firstAutoResize = false;
15✔
3789
        });
3790
    }
3791

3792
    /**
3793
     * @hidden
3794
     */
3795
    public ngOnInit() {
3796
        this._setupServices();
3,571✔
3797
        this._setupListeners();
3,571✔
3798
        this.rowListDiffer = this.differs.find([]).create(null);
3,571✔
3799
        // compare based on field, not on object ref.
3800
        this.columnListDiffer = this.differs.find([]).create((_index, col: ColumnType) => col.field);
17,453✔
3801
        this.calcWidth = this.width && this.width.indexOf('%') === -1 ? parseInt(this.width, 10) : 0;
3,571✔
3802
        this.gridComputedStyles = this.document.defaultView.getComputedStyle(this.nativeElement);
3,571✔
3803
    }
3804

3805
    /**
3806
     * @hidden
3807
     * @internal
3808
     */
3809
    public resetColumnsCaches() {
3810
        this._columns.forEach(column => column.resetCaches());
215,706✔
3811
    }
3812

3813
    /**
3814
     * @hidden @internal
3815
     */
3816
    public generateRowID(): string | number {
3817
        const primaryColumn = this._columns.find(col => col.field === this.primaryKey);
62✔
3818
        const idType = this.data.length ?
60✔
3819
            this.resolveDataTypes(this.data[0][this.primaryKey]) : primaryColumn ? primaryColumn.dataType : 'string';
1!
3820
        return idType === 'string' ? getUUID() : FAKE_ROW_ID--;
60✔
3821
    }
3822

3823
    /**
3824
     * @hidden
3825
     * @internal
3826
     */
3827
    public resetForOfCache() {
3828
        const firstVirtRow = this.dataRowList.first;
34,339✔
3829
        if (firstVirtRow) {
34,339✔
3830
            if (this._cdrRequests) {
16,969✔
3831
                firstVirtRow.virtDirRow.cdr.detectChanges();
6,725✔
3832
            }
3833
            firstVirtRow.virtDirRow.assumeMaster();
16,969✔
3834
        }
3835
    }
3836

3837
    /**
3838
     * @hidden
3839
     * @internal
3840
     */
3841
    public setFilteredData(data, pinned: boolean) {
3842
        if (this.hasPinnedRecords && pinned) {
1,530✔
3843
            this._filteredPinnedData = data || [];
49✔
3844
            const filteredUnpinned = this._filteredUnpinnedData || [];
49✔
3845
            const filteredData = [... this._filteredPinnedData, ...filteredUnpinned];
49✔
3846
            this._filteredData = filteredData.length > 0 ? filteredData : this._filteredUnpinnedData;
49✔
3847
        } else if (this.hasPinnedRecords && !pinned) {
1,481✔
3848
            this._filteredUnpinnedData = data;
47✔
3849
        } else {
3850
            this._filteredData = data;
1,434✔
3851
        }
3852
    }
3853

3854
    /**
3855
     * @hidden
3856
     * @internal
3857
     */
3858
    public resetColumnCollections() {
3859
        if (this.hasColumnLayouts) {
34,480✔
3860
            this._columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
12,012✔
3861
        }
3862
        this._visibleColumns.length = 0;
34,480✔
3863
        this._pinnedVisible.length = 0;
34,480✔
3864
        this._unpinnedVisible.length = 0;
34,480✔
3865
    }
3866

3867
    /**
3868
     * @hidden
3869
     * @internal
3870
     */
3871
    public resetCachedWidths() {
3872
        this._unpinnedWidth = NaN;
46,193✔
3873
        this._pinnedWidth = NaN;
46,193✔
3874
        this._totalWidth = NaN;
46,193✔
3875
    }
3876

3877
    /**
3878
     * @hidden
3879
     * @internal
3880
     */
3881
    public resetCaches(recalcFeatureWidth = true) {
10,591✔
3882
        if (recalcFeatureWidth) {
34,339✔
3883
            this._headerFeaturesWidth = NaN;
34,330✔
3884
            this.summaryService.summaryHeight = 0;
34,330✔
3885
        }
3886
        this.resetColumnsCaches();
34,339✔
3887
        this.resetColumnCollections();
34,339✔
3888
        this.resetForOfCache();
34,339✔
3889
        this.resetCachedWidths();
34,339✔
3890
        this.hasVisibleColumns = undefined;
34,339✔
3891
        this._columnGroups = this._columns.some(col => col.columnGroup);
161,651✔
3892
    }
3893

3894
    /**
3895
     * @hidden
3896
     */
3897
    public ngAfterContentInit() {
3898
        if (this.sortHeaderIconDirectiveTemplate) {
3,440✔
3899
            this.sortHeaderIconTemplate = this.sortHeaderIconDirectiveTemplate;
5✔
3900
        }
3901

3902
        if (this.sortAscendingHeaderIconDirectiveTemplate) {
3,440✔
3903
            this.sortAscendingHeaderIconTemplate = this.sortAscendingHeaderIconDirectiveTemplate;
5✔
3904
        }
3905

3906
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
3,440✔
3907
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
5✔
3908
        }
3909

3910
        this.setupColumns();
3,440✔
3911
        this.toolbar.changes.pipe(filter(() => !this._init), takeUntil(this.destroy$)).subscribe(() => this.notifyChanges(true));
3,440✔
3912
        this.setUpPaginator();
3,440✔
3913
        this.paginationComponents.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,440✔
3914
            this.setUpPaginator();
64✔
3915
        });
3916
        if (this.actionStrip) {
3,440✔
3917
            this.actionStrip.menuOverlaySettings.outlet = this.outlet;
115✔
3918
        }
3919
    }
3920

3921
    /**
3922
     * @hidden @internal
3923
     */
3924
    public dataRebinding(event: IForOfDataChangeEventArgs) {
3925
        if (event.state.chunkSize == 0) {
6,759✔
3926
            this._shouldRecalcRowHeight = true;
3,600✔
3927
        }
3928
        this.dataChanging.emit(event);
6,759✔
3929
    }
3930

3931
    /**
3932
     * @hidden @internal
3933
     */
3934
    public dataRebound(event: IForOfDataChangeEventArgs) {
3935
        this.selectionService.clearHeaderCBState();
6,759✔
3936
        if (this._shouldRecalcRowHeight) {
6,759✔
3937
            this._shouldRecalcRowHeight = false;
3,600✔
3938
            this.updateDefaultRowHeight();
3,600✔
3939
        }
3940
        this.dataChanged.emit(event);
6,759✔
3941
    }
3942

3943
    /** @hidden @internal */
3944
    public createFilterDropdown(column: ColumnType, options: OverlaySettings) {
3945
        options.outlet = this.outlet;
205✔
3946
        if (this.excelStyleFilteringComponent) {
205✔
3947
            this.excelStyleFilteringComponent.initialize(column, this.overlayService);
21✔
3948
            const id = this.overlayService.attach(this.excelStyleFilteringComponent.element, options);
21✔
3949
            this.excelStyleFilteringComponent.overlayComponentId = id;
21✔
3950
            return id;
21✔
3951
        }
3952
        const id = this.overlayService.attach(IgxGridExcelStyleFilteringComponent, this.viewRef, options);
184✔
3953
        return id;
184✔
3954
    }
3955

3956
    /** @hidden @internal */
3957
    public setUpPaginator() {
3958
        if (this.paginator) {
3,507✔
3959
            this.paginator.pageChange
220✔
3960
                .pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
114✔
3961
                .pipe(takeUntil(this.destroy$))
3962
                .subscribe(() => {
3963
                    this.selectionService.clear(true);
114✔
3964
                    this.crudService.endEdit(false);
114✔
3965
                    this.pipeTrigger++;
114✔
3966
                    this.navigateTo(0);
114✔
3967
                    this.notifyChanges();
114✔
3968
                });
3969
            this.paginator.perPageChange
220✔
3970
                .pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
70✔
3971
                .pipe(takeUntil(this.destroy$))
3972
                .subscribe(() => {
3973
                    this.selectionService.clear(true);
70✔
3974
                    this.page = 0;
70✔
3975
                    this.crudService.endEdit(false);
70✔
3976
                    this.notifyChanges();
70✔
3977
                });
3978
        } else {
3979
            this.markForCheck();
3,287✔
3980
        }
3981
    }
3982

3983
    /**
3984
     * @hidden
3985
     * @internal
3986
     */
3987
    public setFilteredSortedData(data, pinned: boolean) {
3988
        data = data || [];
6,500✔
3989
        if (this.pinnedRecordsCount > 0) {
6,500✔
3990
            if (pinned) {
341✔
3991
                this._filteredSortedPinnedData = data;
174✔
3992
                this.pinnedRecords = data;
174✔
3993
                this._filteredSortedData = this.isRowPinningToTop ? [... this._filteredSortedPinnedData, ... this._filteredSortedUnpinnedData] :
174✔
3994
                    [... this._filteredSortedUnpinnedData, ... this._filteredSortedPinnedData];
3995
                this.refreshSearch(true, false);
174✔
3996
            } else {
3997
                this._filteredSortedUnpinnedData = data;
167✔
3998
            }
3999
        } else {
4000
            this._filteredSortedData = data;
6,159✔
4001
            this.refreshSearch(true, false);
6,159✔
4002
        }
4003
        this.buildDataView(data);
6,500✔
4004
    }
4005

4006
    /**
4007
     * @hidden @internal
4008
     */
4009
    public resetHorizontalVirtualization() {
4010
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
49,547✔
4011
        this._horizontalForOfs = [
6,729✔
4012
            ...this._dataRowList.filter(elementFilter).map(item => item.virtDirRow),
46,406✔
4013
            ...this._summaryRowList.filter(elementFilter).map(item => item.virtDirRow)
3,141✔
4014
        ];
4015
    }
4016

4017
    /**
4018
     * @hidden @internal
4019
     */
4020
    public _setupRowObservers() {
4021
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
17,726✔
4022
        const extractForOfs = pipe(map((collection: any[]) => collection.filter(elementFilter).map(item => item.virtDirRow)));
17,726✔
4023
        const rowListObserver = extractForOfs(this._dataRowList.changes);
3,584✔
4024
        const summaryRowObserver = extractForOfs(this._summaryRowList.changes);
3,584✔
4025
        rowListObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,584✔
4026
            this.resetHorizontalVirtualization();
2,729✔
4027
        });
4028
        summaryRowObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,584✔
4029
            this.resetHorizontalVirtualization();
318✔
4030
        });
4031
        this.resetHorizontalVirtualization();
3,584✔
4032
    }
4033

4034
    /**
4035
     * @hidden @internal
4036
     */
4037
    public _zoneBegoneListeners() {
4038
        this.zone.runOutsideAngular(() => {
3,584✔
4039
            this.verticalScrollHandler = this.verticalScrollHandler.bind(this);
3,584✔
4040
            this.horizontalScrollHandler = this.horizontalScrollHandler.bind(this);
3,584✔
4041
            this.verticalScrollContainer.getScroll().addEventListener('scroll', this.verticalScrollHandler);
3,584✔
4042
            this.headerContainer?.getScroll().addEventListener('scroll', this.horizontalScrollHandler);
3,584✔
4043
            if (this.hasColumnsToAutosize) {
3,584✔
4044
                this.headerContainer?.dataChanged.pipe(takeUntil(this.destroy$)).subscribe(() => {
15✔
4045
                    this.cdr.detectChanges();
34✔
4046
                    this.zone.onStable.pipe(first()).subscribe(() => {
34✔
4047
                        this.autoSizeColumnsInView();
34✔
4048
                    });
4049
                });
4050
            }
4051
            // Window resize observer not needed because when you resize the window element the tbody container always resize so
4052
            // it would always notify resizing, thus a change detection and recalculation of sizes will occur
4053
            resizeObservable(this.nativeElement).pipe(first(), takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
3,584✔
4054
            resizeObservable(this.tbodyContainer.nativeElement).pipe(takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
3,584✔
4055
        });
4056
    }
4057

4058
    /**
4059
     * @hidden
4060
     */
4061
    public ngAfterViewInit() {
4062
        this.initPinning();
3,584✔
4063
        this.calculateGridSizes();
3,584✔
4064
        this._init = false;
3,584✔
4065
        this.cdr.reattach();
3,584✔
4066
        this._setupRowObservers();
3,584✔
4067
        this._zoneBegoneListeners();
3,584✔
4068

4069
        const vertScrDC = this.verticalScrollContainer.displayContainer;
3,584✔
4070
        vertScrDC.addEventListener('scroll', this.preventContainerScroll);
3,584✔
4071

4072
        this._pinnedRowList.changes
3,584✔
4073
            .pipe(takeUntil(this.destroy$))
4074
            .subscribe((change: QueryList<IgxGridRowComponent>) => {
4075
                this.onPinnedRowsChanged(change);
182✔
4076
            });
4077

4078
        this.addRowSnackbar?.clicked.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,584✔
4079
            const rec = this.filteredSortedData[this.lastAddedRowIndex];
4✔
4080
            this.scrollTo(rec, 0);
4✔
4081
            this.addRowSnackbar.close();
4✔
4082
        });
4083

4084
        // Keep the stream open for future subscribers
4085
        this.rendered$.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,584✔
4086
            if (this.paginator) {
2,003✔
4087
                this.paginator.totalRecords = this.totalRecords ? this.totalRecords : this.paginator.totalRecords;
73✔
4088
                this.paginator.overlaySettings = { outlet: this.outlet };
73✔
4089
            }
4090
            if (this.hasColumnsToAutosize) {
2,003✔
4091
                this.autoSizeColumnsInView();
14✔
4092
            }
4093
            this._rendered = true;
2,003✔
4094
        });
4095
        Promise.resolve().then(() => this.rendered.next(true));
3,584✔
4096

4097
    }
4098

4099
    /**
4100
     * @hidden @internal
4101
     */
4102
    public notifyChanges(repaint = false) {
14,998✔
4103
        this._cdrRequests = true;
494,264✔
4104
        this._cdrRequestRepaint = repaint;
494,264✔
4105
        this.cdr.markForCheck();
494,264✔
4106
    }
4107

4108
    /**
4109
     * @hidden @internal
4110
     */
4111
    public ngDoCheck() {
4112
        if (this._init) {
17,937✔
4113
            return;
4,159✔
4114
        }
4115

4116
        if (this._cdrRequestRepaint) {
13,778✔
4117
            this.resetNotifyChanges();
3,989✔
4118
            this.calculateGridSizes();
3,989✔
4119
            this.refreshSearch(true);
3,989✔
4120
            return;
3,989✔
4121
        }
4122

4123
        if (this._cdrRequests) {
9,789✔
4124
            this.resetNotifyChanges();
4,234✔
4125
            this.cdr.detectChanges();
4,234✔
4126
        }
4127
    }
4128

4129
    /**
4130
     * @hidden
4131
     * @internal
4132
     */
4133
    public getDragGhostCustomTemplate() {
4134

4135
        return this.dragGhostCustomTemplate;
1,979✔
4136
    }
4137

4138
    /**
4139
     * @hidden @internal
4140
     */
4141
    public ngOnDestroy() {
4142
        this.tmpOutlets.forEach((tmplOutlet) => {
3,406✔
4143
            tmplOutlet.cleanCache();
25,056✔
4144
        });
4145
        this._autoGeneratedColsRefs.forEach(ref => ref.destroy());
4,129✔
4146
        this._autoGeneratedColsRefs = [];
3,406✔
4147

4148
        this.destroy$.next(true);
3,406✔
4149
        this.destroy$.complete();
3,406✔
4150
        this.transactionChange$.next();
3,406✔
4151
        this.transactionChange$.complete();
3,406✔
4152
        this._destroyed = true;
3,406✔
4153

4154
        this.textHighlightService.destroyGroup(this.id);
3,406✔
4155

4156
        if (this._advancedFilteringOverlayId) {
3,406!
4157
            this.overlayService.detach(this._advancedFilteringOverlayId);
×
4158
            delete this._advancedFilteringOverlayId;
×
4159
        }
4160

4161
        this.overlayIDs.forEach(overlayID => {
3,406✔
4162
            const overlay = this.overlayService.getOverlayById(overlayID);
23✔
4163

4164
            if (overlay && !overlay.detached) {
23✔
4165
                this.overlayService.detach(overlayID);
13✔
4166
            }
4167
        });
4168

4169

4170
        this.zone.runOutsideAngular(() => {
3,406✔
4171
            this.verticalScrollContainer?.getScroll()?.removeEventListener('scroll', this.verticalScrollHandler);
3,406✔
4172
            this.headerContainer?.getScroll()?.removeEventListener('scroll', this.horizontalScrollHandler);
3,406✔
4173
            const vertScrDC = this.verticalScrollContainer?.displayContainer;
3,406✔
4174
            vertScrDC?.removeEventListener('scroll', this.preventContainerScroll);
3,406✔
4175
        });
4176
    }
4177

4178
    /**
4179
     * Toggles the specified column's visibility.
4180
     *
4181
     * @example
4182
     * ```typescript
4183
     * this.grid1.toggleColumnVisibility({
4184
     *       column: this.grid1.columns[0],
4185
     *       newValue: true
4186
     * });
4187
     * ```
4188
     */
4189
    public toggleColumnVisibility(args: IColumnVisibilityChangedEventArgs) {
4190
        const col = args.column ? this._columns.find((c) => c === args.column) : undefined;
×
4191

4192
        if (!col) {
×
4193
            return;
×
4194
        }
4195
        col.toggleVisibility(args.newValue);
×
4196
    }
4197

4198
    /* blazorCSSuppress */
4199
    /**
4200
     * Gets/Sets a list of key-value pairs [row ID, expansion state].
4201
     *
4202
     * @remarks
4203
     * Includes only states that differ from the default one.
4204
     * Supports two-way binding.
4205
     * @example
4206
     * ```html
4207
     * <igx-grid #grid [data]="data" [(expansionStates)]="model.expansionStates">
4208
     * </igx-grid>
4209
     * ```
4210
     */
4211
    @Input()
4212
    public get expansionStates() {
4213
        return this._expansionStates;
230,308✔
4214
    }
4215

4216
    /* blazorCSSuppress */
4217
    public set expansionStates(value) {
4218
        this._expansionStates = new Map<any, boolean>(value);
691✔
4219
        this.expansionStatesChange.emit(this._expansionStates);
691✔
4220
        this.notifyChanges(true);
691✔
4221
        if (this.gridAPI.grid) {
691✔
4222
            this.cdr.detectChanges();
598✔
4223
        }
4224
    }
4225

4226
    /**
4227
     * Expands all rows.
4228
     *
4229
     * @example
4230
     * ```typescript
4231
     * this.grid.expandAll();
4232
     * ```
4233
     */
4234
    public expandAll() {
4235
        this._defaultExpandState = true;
8✔
4236
        this.expansionStates = new Map<any, boolean>();
8✔
4237
    }
4238

4239
    /**
4240
     * Collapses all rows.
4241
     *
4242
     * @example
4243
     * ```typescript
4244
     * this.grid.collapseAll();
4245
     * ```
4246
     */
4247
    public collapseAll() {
4248
        this._defaultExpandState = false;
2✔
4249
        this.expansionStates = new Map<any, boolean>();
2✔
4250
    }
4251

4252
    /**
4253
     * Expands the row by its id.
4254
     *
4255
     * @remarks
4256
     * ID is either the primaryKey value or the data record instance.
4257
     * @example
4258
     * ```typescript
4259
     * this.grid.expandRow(rowID);
4260
     * ```
4261
     * @param rowID The row id - primaryKey value or the data record instance.
4262
     */
4263
    public expandRow(rowID: any) {
4264
        this.gridAPI.set_row_expansion_state(rowID, true);
56✔
4265
    }
4266

4267
    /**
4268
     * Collapses the row by its id.
4269
     *
4270
     * @remarks
4271
     * ID is either the primaryKey value or the data record instance.
4272
     * @example
4273
     * ```typescript
4274
     * this.grid.collapseRow(rowID);
4275
     * ```
4276
     * @param rowID The row id - primaryKey value or the data record instance.
4277
     */
4278
    public collapseRow(rowID: any) {
4279
        this.gridAPI.set_row_expansion_state(rowID, false);
9✔
4280
    }
4281

4282

4283
    /**
4284
     * Toggles the row by its id.
4285
     *
4286
     * @remarks
4287
     * ID is either the primaryKey value or the data record instance.
4288
     * @example
4289
     * ```typescript
4290
     * this.grid.toggleRow(rowID);
4291
     * ```
4292
     * @param rowID The row id - primaryKey value or the data record instance.
4293
     */
4294
    public toggleRow(rowID: any) {
4295
        const rec = this.gridAPI.get_rec_by_id(rowID);
86✔
4296
        const state = this.gridAPI.get_row_expansion_state(rec);
86✔
4297
        this.gridAPI.set_row_expansion_state(rowID, !state);
86✔
4298
    }
4299

4300
    /**
4301
     * @hidden
4302
     * @internal
4303
     */
4304
    public getDefaultExpandState(_rec: any) {
4305
        return this._defaultExpandState;
9,610✔
4306
    }
4307

4308
    /**
4309
     * Gets the native element.
4310
     *
4311
     * @example
4312
     * ```typescript
4313
     * const nativeEl = this.grid.nativeElement.
4314
     * ```
4315
     */
4316
    public get nativeElement() {
4317
        return this.elementRef.nativeElement;
42,041✔
4318
    }
4319

4320
    /**
4321
     * Gets/Sets the outlet used to attach the grid's overlays to.
4322
     *
4323
     * @remarks
4324
     * If set, returns the outlet defined outside the grid. Otherwise returns the grid's internal outlet directive.
4325
     */
4326
    @Input()
4327
    public get outlet() {
4328
        return this.resolveOutlet();
199,210✔
4329
    }
4330

4331
    public set outlet(val: IgxOverlayOutletDirective) {
4332
        this._userOutletDirective = val;
×
4333
    }
4334

4335

4336
    /**
4337
     * Gets the default row height.
4338
     *
4339
     * @example
4340
     * ```typescript
4341
     * const rowHeigh = this.grid.defaultRowHeight;
4342
     * ```
4343
     */
4344
    public get defaultRowHeight(): number {
4345
        return this._defaultRowHeight;
347,043✔
4346
    }
4347

4348
    /**
4349
     * @hidden @internal
4350
     */
4351
    public get defaultSummaryHeight(): number {
4352
        switch (this.gridSize) {
15,148✔
4353
            case Size.Medium:
4354
                return 30;
29✔
4355
            case Size.Small:
4356
                return 24;
123✔
4357
            default:
4358
                return 36;
14,996✔
4359
        }
4360
    }
4361

4362
    /**
4363
     * Returns the `IgxGridHeaderGroupComponent`'s minimum allowed width.
4364
     *
4365
     * @remarks
4366
     * Used internally for restricting header group component width.
4367
     * The values below depend on the header cell default right/left padding values.
4368
     */
4369
    public get defaultHeaderGroupMinWidth(): number {
4370
        switch (this.gridSize) {
472,930✔
4371
            case Size.Medium:
4372
                return 32;
38,079✔
4373
            case Size.Small:
4374
                return 24;
3,385✔
4375
            default:
4376
                return 48;
431,466✔
4377
        }
4378
    }
4379

4380
    /** @hidden @internal */
4381
    public get pinnedWidth() {
4382
        if (!isNaN(this._pinnedWidth)) {
224,022✔
4383
            return this._pinnedWidth;
202,875✔
4384
        }
4385
        this._pinnedWidth = this.getPinnedWidth();
21,147✔
4386
        return this._pinnedWidth;
21,147✔
4387
    }
4388

4389
    /** @hidden @internal */
4390
    public get unpinnedWidth() {
4391
        if (!isNaN(this._unpinnedWidth)) {
370,330✔
4392
            return this._unpinnedWidth;
346,805✔
4393
        }
4394
        this._unpinnedWidth = this.getUnpinnedWidth();
23,525✔
4395
        return this._unpinnedWidth;
23,525✔
4396
    }
4397

4398
    /**
4399
     * @hidden @internal
4400
     */
4401
    public isHorizontalScrollHidden = false;
4,117✔
4402

4403
    /**
4404
     * @hidden @internal
4405
     * Gets the header cell inner width for auto-sizing.
4406
     */
4407
    public getHeaderCellWidth(element: HTMLElement): ISizeInfo {
4408
        const range = this.document.createRange();
35✔
4409
        const headerWidth = this.platform.getNodeSizeViaRange(range,
35✔
4410
            element,
4411
            element.parentElement);
4412

4413
        const headerStyle = this.document.defaultView.getComputedStyle(element);
35✔
4414
        const headerPadding = parseFloat(headerStyle.paddingLeft) + parseFloat(headerStyle.paddingRight) +
35✔
4415
            parseFloat(headerStyle.borderRightWidth);
4416

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

4423
    /**
4424
     * @hidden @internal
4425
     * Gets the combined width of the columns that are specific to the enabled grid features. They are fixed.
4426
     */
4427
    public featureColumnsWidth(expander?: ElementRef) {
4428
        if (Number.isNaN(this._headerFeaturesWidth)) {
130,006✔
4429
            // TODO: platformUtil.isBrowser check
4430
            const rowSelectArea = this.headerSelectorContainer?.nativeElement?.getBoundingClientRect ?
28,815✔
4431
                this.headerSelectorContainer.nativeElement.getBoundingClientRect().width : 0;
4432
            const rowDragArea = this.rowDraggable && this.headerDragContainer?.nativeElement?.getBoundingClientRect ?
28,815✔
4433
                this.headerDragContainer.nativeElement.getBoundingClientRect().width : 0;
4434
            const groupableArea = this.headerGroupContainer?.nativeElement?.getBoundingClientRect ?
28,815✔
4435
                this.headerGroupContainer.nativeElement.getBoundingClientRect().width : 0;
4436
            const expanderWidth = expander?.nativeElement?.getBoundingClientRect ? expander.nativeElement.getBoundingClientRect().width : 0;
28,815✔
4437
            this._headerFeaturesWidth = rowSelectArea + rowDragArea + groupableArea + expanderWidth;
28,815✔
4438
        }
4439
        return this._headerFeaturesWidth;
130,006✔
4440
    }
4441

4442
    /**
4443
     * @hidden @internal
4444
     */
4445
    public get summariesMargin() {
4446
        return this.featureColumnsWidth();
22,177✔
4447
    }
4448

4449
    /**
4450
     * Gets an array of `IgxColumnComponent`s.
4451
     *
4452
     * @example
4453
     * ```typescript
4454
     * const colums = this.grid.columns.
4455
     * ```
4456
     */
4457
    public get columns(): IgxColumnComponent[] {
4458
        return this._columns || [];
32,354!
4459
    }
4460

4461
    /**
4462
     * Gets an array of the pinned `IgxColumnComponent`s.
4463
     *
4464
     * @example
4465
     * ```typescript
4466
     * const pinnedColumns = this.grid.pinnedColumns.
4467
     * ```
4468
     */
4469
    public get pinnedColumns(): IgxColumnComponent[] {
4470
        if (this._pinnedVisible.length) {
4,109,564✔
4471
            return this._pinnedVisible;
332,700✔
4472
        }
4473
        this._pinnedVisible = this._pinnedColumns.filter(col => !col.hidden);
3,776,864✔
4474
        return this._pinnedVisible;
3,776,864✔
4475
    }
4476

4477
    /* csSuppress */
4478
    /**
4479
     * Gets an array of the pinned `IgxRowComponent`s.
4480
     *
4481
     * @example
4482
     * ```typescript
4483
     * const pinnedRow = this.grid.pinnedRows;
4484
     * ```
4485
     */
4486
    public get pinnedRows(): IgxGridRowComponent[] {
4487
        return this._pinnedRowList.toArray().sort((a, b) => a.index - b.index);
147✔
4488
    }
4489

4490
    /**
4491
     * Gets an array of unpinned `IgxColumnComponent`s.
4492
     *
4493
     * @example
4494
     * ```typescript
4495
     * const unpinnedColumns = this.grid.unpinnedColumns.
4496
     * ```
4497
     */
4498
    public get unpinnedColumns(): IgxColumnComponent[] {
4499
        if (this._unpinnedVisible.length) {
762,422✔
4500
            return this._unpinnedVisible;
735,414✔
4501
        }
4502
        this._unpinnedVisible = this._unpinnedColumns.filter((col) => !col.hidden);
156,240✔
4503
        return this._unpinnedVisible;
27,008✔
4504
    }
4505

4506
    /**
4507
     * Gets the `width` to be set on `IgxGridHeaderGroupComponent`.
4508
     */
4509
    public getHeaderGroupWidth(column: IgxColumnComponent): string {
4510
        return this.hasColumnLayouts
×
4511
            ? ''
4512
            : `${Math.max(parseFloat(column.calcWidth), this.defaultHeaderGroupMinWidth)}px`;
4513
    }
4514

4515
    /**
4516
     * Returns the `IgxColumnComponent` by field name.
4517
     *
4518
     * @example
4519
     * ```typescript
4520
     * const myCol = this.grid1.getColumnByName("ID");
4521
     * ```
4522
     * @param name
4523
     */
4524
    public getColumnByName(name: string): IgxColumnComponent {
4525
        return this._columns.find((col) => col.field === name);
136,418✔
4526
    }
4527

4528
    public getColumnByVisibleIndex(index: number): IgxColumnComponent {
4529
        return this.visibleColumns.find((col) =>
1,963✔
4530
            !col.columnGroup && !col.columnLayout &&
11,573✔
4531
            col.visibleIndex === index
4532
        );
4533
    }
4534

4535
    /**
4536
     * Recalculates all widths of columns that have size set to `auto`.
4537
     *
4538
     * @example
4539
     * ```typescript
4540
     * this.grid1.recalculateAutoSizes();
4541
     * ```
4542
     */
4543
    public recalculateAutoSizes() {
4544
        // reset auto-size and calculate it again.
4545
        this._columns.forEach(x => x.autoSize = undefined);
12✔
4546
        this.resetCaches();
2✔
4547
        this.zone.onStable.pipe(first()).subscribe(() => {
2✔
4548
            this.cdr.detectChanges();
2✔
4549
            this.autoSizeColumnsInView();
2✔
4550
        });
4551
    }
4552

4553
    /**
4554
     * Returns an array of visible `IgxColumnComponent`s.
4555
     *
4556
     * @example
4557
     * ```typescript
4558
     * const visibleColumns = this.grid.visibleColumns.
4559
     * ```
4560
     */
4561
    public get visibleColumns(): IgxColumnComponent[] {
4562
        if (this._visibleColumns.length) {
161,890✔
4563
            return this._visibleColumns;
134,920✔
4564
        }
4565
        this._visibleColumns = this._columns.filter(c => !c.hidden);
151,844✔
4566
        return this._visibleColumns;
26,970✔
4567
    }
4568

4569
    /**
4570
     * Returns the total number of records.
4571
     *
4572
     * @remarks
4573
     * Only functions when paging is enabled.
4574
     * @example
4575
     * ```typescript
4576
     * const totalRecords = this.grid.totalRecords;
4577
     * ```
4578
     */
4579
    @Input()
4580
    public get totalRecords(): number {
4581
        return this._totalRecords >= 0 ? this._totalRecords : this.pagingState?.metadata.countRecords;
145✔
4582
    }
4583

4584
    public set totalRecords(total: number) {
4585
        if (total >= 0) {
1✔
4586
            if (this.paginator) {
1✔
4587
                this.paginator.totalRecords = total;
1✔
4588
            }
4589
            this._totalRecords = total;
1✔
4590
            this.pipeTrigger++;
1✔
4591
            this.notifyChanges();
1✔
4592
        }
4593
    }
4594

4595
    /** @hidden @internal */
4596
    public get totalWidth(): number {
4597
        if (!isNaN(this._totalWidth)) {
11,349✔
4598
            return this._totalWidth;
3,216✔
4599
        }
4600
        // Take only top level columns
4601
        const cols = this.visibleColumns.filter(col => col.level === 0 && !col.pinned);
50,087✔
4602
        let totalWidth = 0;
8,133✔
4603
        let i = 0;
8,133✔
4604
        for (i; i < cols.length; i++) {
8,133✔
4605
            totalWidth += parseFloat(cols[i].calcWidth) || 0;
40,138!
4606
        }
4607
        this._totalWidth = totalWidth;
8,133✔
4608
        return totalWidth;
8,133✔
4609
    }
4610

4611
    /**
4612
     * @hidden
4613
     * @internal
4614
     */
4615
    public get showRowSelectors(): boolean {
4616
        return this.isRowSelectable && this.hasVisibleColumns && !this.hideRowSelectors;
279,686✔
4617
    }
4618

4619
    /**
4620
     * @hidden
4621
     * @internal
4622
     */
4623
    public get showAddButton() {
4624
        return this.rowEditable && this.dataView.length === 0 && this._columns.length > 0;
912✔
4625
    }
4626

4627
    /**
4628
     * @hidden
4629
     * @internal
4630
     */
4631
    public get showDragIcons(): boolean {
4632
        return this.rowDraggable && this._columns.length > this.hiddenColumnsCount;
×
4633
    }
4634

4635
    /**
4636
     * @hidden
4637
     * @internal
4638
     */
4639
    protected _getDataViewIndex(index: number): number {
4640
        let newIndex = index;
56,288✔
4641
        if ((index < 0 || index >= this.dataView.length) && this.pagingMode === 'remote' && this.page !== 0) {
56,288!
4642
            newIndex = index - this.perPage * this.page;
×
4643
        } else if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
56,288!
4644
            newIndex = index - this.gridAPI.grid.virtualizationState.startIndex;
×
4645
        }
4646
        return newIndex;
56,288✔
4647
    }
4648

4649
    /**
4650
     * @hidden
4651
     * @internal
4652
     */
4653
    protected getDataIndex(dataViewIndex: number): number {
4654
        let newIndex = dataViewIndex;
23✔
4655
        if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
23!
4656
            newIndex = dataViewIndex + this.gridAPI.grid.virtualizationState.startIndex;
×
4657
        }
4658
        return newIndex;
23✔
4659
    }
4660

4661
    /**
4662
     * Places a column before or after the specified target column.
4663
     *
4664
     * @example
4665
     * ```typescript
4666
     * grid.moveColumn(column, target);
4667
     * ```
4668
     */
4669
    public moveColumn(column: IgxColumnComponent, target: IgxColumnComponent, pos: DropPosition = DropPosition.AfterDropTarget) {
51✔
4670
        // M.A. May 11th, 2021 #9508 Make the event cancelable
4671
        const eventArgs: IColumnMovingEndEventArgs = { source: column, target, cancel: false };
151✔
4672

4673
        this.columnMovingEnd.emit(eventArgs);
151✔
4674

4675
        if (eventArgs.cancel) {
151✔
4676
            return;
1✔
4677
        }
4678

4679
        if (column === target || (column.level !== target.level) ||
150✔
4680
            (column.topLevelParent !== target.topLevelParent)) {
4681
            return;
22✔
4682
        }
4683

4684
        if (column.level) {
128✔
4685
            this._moveChildColumns(column.parent, column, target, pos);
16✔
4686
        }
4687

4688
        // let columnPinStateChanged;
4689
        // pinning and unpinning will work correctly even without passing index
4690
        // but is easier to calclulate the index here, and later use it in the pinning event args
4691
        if (target.pinned && !column.pinned) {
128✔
4692
            const pinnedIndex = this._pinnedColumns.indexOf(target);
8✔
4693
            const index = pos === DropPosition.AfterDropTarget ? pinnedIndex + 1 : pinnedIndex;
8✔
4694
            column.pin(index);
8✔
4695
        }
4696

4697
        if (!target.pinned && column.pinned) {
128✔
4698
            const unpinnedIndex = this._unpinnedColumns.indexOf(target);
3✔
4699
            const index = pos === DropPosition.AfterDropTarget ? unpinnedIndex + 1 : unpinnedIndex;
3✔
4700
            column.unpin(index);
3✔
4701
        }
4702

4703
        // if (target.pinned && column.pinned && !columnPinStateChanged) {
4704
        //     this._reorderColumns(column, target, pos, this._pinnedColumns);
4705
        // }
4706

4707
        // if (!target.pinned && !column.pinned && !columnPinStateChanged) {
4708
        //     this._reorderColumns(column, target, pos, this._unpinnedColumns);
4709
        // }
4710

4711
        this._moveColumns(column, target, pos);
128✔
4712
        this._columnsReordered(column);
128✔
4713
    }
4714

4715
    /**
4716
     * Triggers change detection for the `IgxGridComponent`.
4717
     * Calling markForCheck also triggers the grid pipes explicitly, resulting in all updates being processed.
4718
     * May degrade performance if used when not needed, or if misused:
4719
     * ```typescript
4720
     * // DON'Ts:
4721
     * // don't call markForCheck from inside a loop
4722
     * // don't call markForCheck when a primitive has changed
4723
     * grid.data.forEach(rec => {
4724
     *  rec = newValue;
4725
     *  grid.markForCheck();
4726
     * });
4727
     *
4728
     * // DOs
4729
     * // call markForCheck after updating a nested property
4730
     * grid.data.forEach(rec => {
4731
     *  rec.nestedProp1.nestedProp2 = newValue;
4732
     * });
4733
     * grid.markForCheck();
4734
     * ```
4735
     *
4736
     * @example
4737
     * ```typescript
4738
     * grid.markForCheck();
4739
     * ```
4740
     */
4741
    public markForCheck() {
4742
        this.pipeTrigger++;
3,290✔
4743
        this.cdr.detectChanges();
3,290✔
4744
    }
4745

4746
    /* csSuppress */
4747
    /**
4748
     * Creates a new `IgxGridRowComponent` and adds the data record to the end of the data source.
4749
     *
4750
     * @example
4751
     * ```typescript
4752
     * this.grid1.addRow(record);
4753
     * ```
4754
     * @param data
4755
     */
4756
    public addRow(data: any): void {
4757
        // commit pending states prior to adding a row
4758
        this.crudService.endEdit(true);
171✔
4759
        this.gridAPI.addRowToData(data);
171✔
4760

4761
        this.pipeTrigger++;
171✔
4762
        this.rowAddedNotifier.next({ data: data, rowData: data, owner: this, primaryKey: data[this.primaryKey], rowKey: data[this.primaryKey] });
171✔
4763
        this.notifyChanges();
171✔
4764
    }
4765

4766
    /* blazorCSSuppress */
4767
    /**
4768
     * Removes the `IgxGridRowComponent` and the corresponding data record by primary key.
4769
     *
4770
     * @remarks
4771
     * Requires that the `primaryKey` property is set.
4772
     * The method accept rowSelector as a parameter, which is the rowID.
4773
     * @example
4774
     * ```typescript
4775
     * this.grid1.deleteRow(0);
4776
     * ```
4777
     * @param rowSelector
4778
     */
4779
    public deleteRow(rowSelector: any): any {
4780
        if (this.primaryKey !== undefined && this.primaryKey !== null) {
82✔
4781
            return this.deleteRowById(rowSelector);
82✔
4782
        }
4783
    }
4784

4785
    /** @hidden */
4786
    public deleteRowById(rowId: any): any {
4787
        const args: IRowDataCancelableEventArgs = {
79✔
4788
            rowID: rowId,
4789
            primaryKey: rowId,
4790
            rowKey: rowId,
4791
            rowData: this.getRowData(rowId),
4792
            data: this.getRowData(rowId),
4793
            oldValue: this.getRowData(rowId),
4794
            owner: this,
4795
            isAddRow: false,
4796
            cancel: false
4797
        };
4798
        this.rowDelete.emit(args);
79✔
4799
        if (args.cancel) {
79!
4800
            return;
×
4801
        }
4802

4803
        const record = this.gridAPI.deleteRowById(rowId);
79✔
4804
        if (record !== null && record !== undefined) {
79✔
4805
            const rowDeletedEventArgs: IRowDataEventArgs = {
78✔
4806
                data: record,
4807
                rowData: record,
4808
                owner: this,
4809
                primaryKey: record[this.primaryKey],
4810
                rowKey: record[this.primaryKey]
4811
            };
4812
            this.rowDeleted.emit(rowDeletedEventArgs);
78✔
4813
        }
4814
        return record;
79✔
4815
    }
4816

4817
    /* blazorCSSuppress */
4818
    /**
4819
     * Updates the `IgxGridRowComponent` and the corresponding data record by primary key.
4820
     *
4821
     * @remarks
4822
     * Requires that the `primaryKey` property is set.
4823
     * @example
4824
     * ```typescript
4825
     * this.gridWithPK.updateCell('Updated', 1, 'ProductName');
4826
     * ```
4827
     * @param value the new value which is to be set.
4828
     * @param rowSelector corresponds to rowID.
4829
     * @param column corresponds to column field.
4830
     */
4831
    public updateCell(value: any, rowSelector: any, column: string): void {
4832
        if (this.isDefined(this.primaryKey)) {
19✔
4833
            const col = this._columns.find(c => c.field === column);
47✔
4834
            if (col) {
19✔
4835
                // Simplify
4836
                const rowData = this.gridAPI.getRowData(rowSelector);
19✔
4837
                const index = this.gridAPI.get_row_index_in_data(rowSelector);
19✔
4838
                // If row passed is invalid
4839
                if (index < 0) {
19✔
4840
                    return;
1✔
4841
                }
4842

4843
                const id = {
18✔
4844
                    rowID: rowSelector,
4845
                    columnID: col.index,
4846
                    rowIndex: index
4847
                };
4848

4849
                const cell = new IgxCell(id, index, col, rowData[col.field], value, rowData, this as any);
18✔
4850
                const formControl = this.validation.getFormControl(cell.id.rowID, cell.column.field);
18✔
4851
                formControl.setValue(value);
18✔
4852
                this.gridAPI.update_cell(cell);
18✔
4853
                this.cdr.detectChanges();
18✔
4854
            }
4855
        }
4856
    }
4857

4858
    /* blazorCSSuppress */
4859
    /**
4860
     * Updates the `IgxGridRowComponent`
4861
     *
4862
     * @remarks
4863
     * The row is specified by
4864
     * rowSelector parameter and the data source record with the passed value.
4865
     * This method will apply requested update only if primary key is specified in the grid.
4866
     * @example
4867
     * ```typescript
4868
     * grid.updateRow({
4869
     *       ProductID: 1, ProductName: 'Spearmint', InStock: true, UnitsInStock: 1, OrderDate: new Date('2005-03-21')
4870
     *   }, 1);
4871
     * ```
4872
     * @param value–
4873
     * @param rowSelector correspond to rowID
4874
     */
4875
    // TODO: prevent event invocation
4876
    public updateRow(value: any, rowSelector: any): void {
4877
        if (this.isDefined(this.primaryKey)) {
32✔
4878
            const editableCell = this.crudService.cell;
32✔
4879
            if (editableCell && editableCell.id.rowID === rowSelector) {
32!
4880
                this.crudService.endCellEdit();
×
4881
            }
4882
            const row = new IgxEditRow(rowSelector, -1, this.gridAPI.getRowData(rowSelector), this as any);
32✔
4883
            this.gridAPI.update_row(row, value);
32✔
4884

4885
            // TODO: fix for #5934 and probably break for #5763
4886
            // consider adding of third optional boolean parameter in updateRow.
4887
            // If developer set this parameter to true we should call notifyChanges(true), and
4888
            // vise-versa if developer set it to false we should call notifyChanges(false).
4889
            // The parameter should default to false
4890
            this.notifyChanges();
32✔
4891
        }
4892
    }
4893

4894
    /**
4895
     * Returns the data that is contained in the row component.
4896
     *
4897
     * @remarks
4898
     * If the primary key is not specified the row selector match the row data.
4899
     * @example
4900
     * ```typescript
4901
     * const data = grid.getRowData(94741);
4902
     * ```
4903
     * @param rowSelector correspond to rowID
4904
     */
4905
    public getRowData(rowSelector: any): any {
4906
        if (!this.primaryKey) {
427✔
4907
            return rowSelector;
19✔
4908
        }
4909
        const data = this.gridAPI.get_all_data(this.transactions.enabled);
408✔
4910
        const index = this.gridAPI.get_row_index_in_data(rowSelector);
408✔
4911
        return index < 0 ? {} : data[index];
408✔
4912
    }
4913

4914
    /**
4915
     * Sort a single `IgxColumnComponent`.
4916
     *
4917
     * @remarks
4918
     * Sort the `IgxGridComponent`'s `IgxColumnComponent` based on the provided array of sorting expressions.
4919
     * @example
4920
     * ```typescript
4921
     * this.grid.sort({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false });
4922
     * ```
4923
     */
4924
    public sort(expression: ISortingExpression | Array<ISortingExpression>): void {
4925
        const sortingState = cloneArray(this.sortingExpressions);
159✔
4926

4927
        if (expression instanceof Array) {
159✔
4928
            for (const each of expression) {
3✔
4929
                this.gridAPI.prepare_sorting_expression([sortingState], each);
6✔
4930
            }
4931
        } else {
4932
            if (this._sortingOptions.mode === 'single') {
156✔
4933
                this._columns.forEach((col) => {
4✔
4934
                    if (!(col.field === expression.fieldName)) {
12✔
4935
                        this.clearSort(col.field);
8✔
4936
                    }
4937
                });
4938
            }
4939
            this.gridAPI.prepare_sorting_expression([sortingState], expression);
156✔
4940
        }
4941

4942
        const eventArgs: ISortingEventArgs = { owner: this, sortingExpressions: sortingState, cancel: false };
159✔
4943
        this.sorting.emit(eventArgs);
159✔
4944

4945
        if (eventArgs.cancel) {
159!
4946
            return;
×
4947
        }
4948

4949
        this.crudService.endEdit(false);
159✔
4950
        if (expression instanceof Array) {
159✔
4951
            this.gridAPI.sort_multiple(expression);
3✔
4952
        } else {
4953
            this.gridAPI.sort(expression);
156✔
4954
        }
4955
        requestAnimationFrame(() => this.sortingDone.emit(expression));
159✔
4956
    }
4957

4958
    /**
4959
     * Filters a single `IgxColumnComponent`.
4960
     *
4961
     * @example
4962
     * ```typescript
4963
     * public filter(term) {
4964
     *      this.grid.filter("ProductName", term, IgxStringFilteringOperand.instance().condition("contains"));
4965
     * }
4966
     * ```
4967
     * @param name
4968
     * @param value
4969
     * @param conditionOrExpressionTree
4970
     * @param ignoreCase
4971
     */
4972
    public filter(name: string, value: any, conditionOrExpressionTree?: IFilteringOperation | IFilteringExpressionsTree,
4973
        ignoreCase?: boolean) {
4974
        this.filteringService.filter(name, value, conditionOrExpressionTree, ignoreCase);
274✔
4975
    }
4976

4977
    /**
4978
     * Filters all the `IgxColumnComponent` in the `IgxGridComponent` with the same condition.
4979
     *
4980
     * @example
4981
     * ```typescript
4982
     * grid.filterGlobal('some', IgxStringFilteringOperand.instance().condition('contains'));
4983
     * ```
4984
     * @param value
4985
     * @param condition
4986
     * @param ignoreCase
4987
     * @deprecated in version 19.0.0.
4988
     */
4989
    public filterGlobal(value: any, condition, ignoreCase?) {
4990
        this.filteringService.filterGlobal(value, condition, ignoreCase);
3✔
4991
    }
4992

4993
    /**
4994
     * Enables summaries for the specified column and applies your customSummary.
4995
     *
4996
     * @remarks
4997
     * If you do not provide the customSummary, then the default summary for the column data type will be applied.
4998
     * @example
4999
     * ```typescript
5000
     * grid.enableSummaries([{ fieldName: 'ProductName' }, { fieldName: 'ID' }]);
5001
     * ```
5002
     * Enable summaries for the listed columns.
5003
     * @example
5004
     * ```typescript
5005
     * grid.enableSummaries('ProductName');
5006
     * ```
5007
     * @param rest
5008
     */
5009
    public enableSummaries(...rest) {
5010
        if (rest.length === 1 && Array.isArray(rest[0])) {
10✔
5011
            this._multipleSummaries(rest[0], true);
7✔
5012
        } else {
5013
            this._summaries(rest[0], true, rest[1]);
3✔
5014
        }
5015
    }
5016

5017
    /**
5018
     * Disable summaries for the specified column.
5019
     *
5020
     * @example
5021
     * ```typescript
5022
     * grid.disableSummaries('ProductName');
5023
     * ```
5024
     * @remarks
5025
     * Disable summaries for the listed columns.
5026
     * @example
5027
     * ```typescript
5028
     * grid.disableSummaries([{ fieldName: 'ProductName' }]);
5029
     * ```
5030
     */
5031
    public disableSummaries(...rest) {
5032
        if (rest.length === 1 && Array.isArray(rest[0])) {
7✔
5033
            this._disableMultipleSummaries(rest[0]);
5✔
5034
        } else {
5035
            this._summaries(rest[0], false);
2✔
5036
        }
5037
    }
5038

5039
    /**
5040
     * If name is provided, clears the filtering state of the corresponding `IgxColumnComponent`.
5041
     *
5042
     * @remarks
5043
     * Otherwise clears the filtering state of all `IgxColumnComponent`s.
5044
     * @example
5045
     * ```typescript
5046
     * this.grid.clearFilter();
5047
     * ```
5048
     * @param name
5049
     */
5050
    public clearFilter(name?: string) {
5051
        this.filteringService.clearFilter(name);
147✔
5052
    }
5053

5054
    /**
5055
     * If name is provided, clears the sorting state of the corresponding `IgxColumnComponent`.
5056
     *
5057
     * @remarks
5058
     * otherwise clears the sorting state of all `IgxColumnComponent`.
5059
     * @example
5060
     * ```typescript
5061
     * this.grid.clearSort();
5062
     * ```
5063
     * @param name
5064
     */
5065
    public clearSort(name?: string) {
5066
        if (!name) {
35✔
5067
            this.sortingExpressions = [];
22✔
5068
            return;
22✔
5069
        }
5070
        if (!this.gridAPI.get_column_by_name(name)) {
13!
5071
            return;
×
5072
        }
5073
        this.gridAPI.clear_sort(name);
13✔
5074
    }
5075

5076
    /**
5077
     * @hidden @internal
5078
     */
5079
    public refreshGridState(_args?) {
5080
        this.crudService.endEdit(true);
287✔
5081
        this.selectionService.clearHeaderCBState();
287✔
5082
        this.summaryService.clearSummaryCache();
287✔
5083
        this.summaryPipeTrigger++;
287✔
5084
        this.cdr.detectChanges();
287✔
5085
    }
5086

5087
    // TODO: We have return values here. Move them to event args ??
5088

5089
    /**
5090
     * Pins a column by field name.
5091
     *
5092
     * @remarks
5093
     * Returns whether the operation is successful.
5094
     * @example
5095
     * ```typescript
5096
     * this.grid.pinColumn("ID");
5097
     * ```
5098
     * @param columnName
5099
     * @param index
5100
     */
5101
    public pinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
5102
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
37✔
5103
        return col.pin(index);
37✔
5104
    }
5105

5106
    /**
5107
     * Unpins a column by field name. Returns whether the operation is successful.
5108
     *
5109
     * @example
5110
     * ```typescript
5111
     * this.grid.pinColumn("ID");
5112
     * ```
5113
     * @param columnName
5114
     * @param index
5115
     */
5116
    public unpinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
5117
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
19✔
5118
        return col.unpin(index);
19✔
5119
    }
5120

5121
    /* csSuppress */
5122
    /**
5123
     * Pin the row by its id.
5124
     *
5125
     * @remarks
5126
     * ID is either the primaryKey value or the data record instance.
5127
     * @example
5128
     * ```typescript
5129
     * this.grid.pinRow(rowID);
5130
     * ```
5131
     * @param rowID The row id - primaryKey value or the data record instance.
5132
     * @param index The index at which to insert the row in the pinned collection.
5133
     */
5134
    public pinRow(rowID: any, index?: number, row?: RowType): boolean {
5135
        if (this._pinnedRecordIDs.indexOf(rowID) !== -1) {
134✔
5136
            return false;
1✔
5137
        }
5138
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, index, row, true);
133✔
5139
        this.rowPinning.emit(eventArgs);
133✔
5140

5141
        if (eventArgs.cancel) {
133✔
5142
            return;
1✔
5143
        }
5144
        this.crudService.endEdit(false);
132✔
5145

5146
        const insertIndex = typeof eventArgs.insertAtIndex === 'number' ? eventArgs.insertAtIndex : this._pinnedRecordIDs.length;
132✔
5147
        this._pinnedRecordIDs.splice(insertIndex, 0, rowID);
132✔
5148
        this.pipeTrigger++;
132✔
5149
        if (this.gridAPI.grid) {
132✔
5150
            this.cdr.detectChanges();
130✔
5151
            this.rowPinned.emit(eventArgs);
130✔
5152
        }
5153

5154
        return true;
132✔
5155
    }
5156

5157
    /* csSuppress */
5158
    /**
5159
     * Unpin the row by its id.
5160
     *
5161
     * @remarks
5162
     * ID is either the primaryKey value or the data record instance.
5163
     * @example
5164
     * ```typescript
5165
     * this.grid.unpinRow(rowID);
5166
     * ```
5167
     * @param rowID The row id - primaryKey value or the data record instance.
5168
     */
5169
    public unpinRow(rowID: any, row?: RowType): boolean {
5170
        const index = this._pinnedRecordIDs.indexOf(rowID);
24✔
5171
        if (index === -1) {
24!
5172
            return false;
×
5173
        }
5174

5175
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, null, row, false);
24✔
5176
        this.rowPinning.emit(eventArgs);
24✔
5177

5178
        if (eventArgs.cancel) {
24✔
5179
            return;
1✔
5180
        }
5181

5182
        this.crudService.endEdit(false);
23✔
5183
        this._pinnedRecordIDs.splice(index, 1);
23✔
5184
        this.pipeTrigger++;
23✔
5185
        if (this.gridAPI.grid) {
23✔
5186
            this.cdr.detectChanges();
23✔
5187
            this.rowPinned.emit(eventArgs);
23✔
5188
        }
5189

5190
        return true;
23✔
5191
    }
5192

5193
    /** @hidden @internal */
5194
    public get pinnedRowHeight() {
5195
        const containerHeight = this.pinContainer ? this.pinContainer.nativeElement.offsetHeight : 0;
82,479✔
5196
        return this.hasPinnedRecords ? containerHeight : 0;
82,479✔
5197
    }
5198

5199
    /** @hidden @internal */
5200
    public get totalHeight() {
5201
        const height = this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
43,410✔
5202
        return this.platform.isBrowser ? height : undefined;
43,410!
5203
    }
5204

5205
    /**
5206
     * Recalculates grid width/height dimensions.
5207
     *
5208
     * @remarks
5209
     * Should be run when changing DOM elements dimentions manually that affect the grid's size.
5210
     * @example
5211
     * ```typescript
5212
     * this.grid.reflow();
5213
     * ```
5214
     */
5215
    public reflow() {
5216
        this.calculateGridSizes();
322✔
5217
    }
5218

5219
    /**
5220
     * Finds the next occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
5221
     *
5222
     * @remarks
5223
     * Returns how many times the grid contains the string.
5224
     * @example
5225
     * ```typescript
5226
     * this.grid.findNext("financial");
5227
     * ```
5228
     * @param text the string to search.
5229
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
5230
     * @param exactMatch optionally, if the text should match the entire value  (defaults to false).
5231
     */
5232
    public findNext(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
5233
        return this.find(text, 1, caseSensitive, exactMatch);
155✔
5234
    }
5235

5236
    /**
5237
     * Finds the previous occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
5238
     *
5239
     * @remarks
5240
     * Returns how many times the grid contains the string.
5241
     * @example
5242
     * ```typescript
5243
     * this.grid.findPrev("financial");
5244
     * ```
5245
     * @param text the string to search.
5246
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
5247
     * @param exactMatch optionally, if the text should match the entire value (defaults to false).
5248
     */
5249
    public findPrev(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
5250
        return this.find(text, -1, caseSensitive, exactMatch);
40✔
5251
    }
5252

5253
    /**
5254
     * Reapplies the existing search.
5255
     *
5256
     * @remarks
5257
     * Returns how many times the grid contains the last search.
5258
     * @example
5259
     * ```typescript
5260
     * this.grid.refreshSearch();
5261
     * ```
5262
     * @param updateActiveInfo
5263
     */
5264
    public refreshSearch(updateActiveInfo?: boolean, endEdit = true): number {
4,080✔
5265
        if (this._lastSearchInfo.searchText) {
10,025✔
5266
            this.rebuildMatchCache();
127✔
5267

5268
            if (updateActiveInfo) {
127✔
5269
                const activeInfo = this.textHighlightService.highlightGroupsMap.get(this.id);
126✔
5270
                this._lastSearchInfo.matchInfoCache.forEach((match, i) => {
126✔
5271
                    if (match.column === activeInfo.column &&
1,552✔
5272
                        match.row === activeInfo.row &&
5273
                        match.index === activeInfo.index &&
5274
                        compareMaps(match.metadata, activeInfo.metadata)) {
5275
                        this._lastSearchInfo.activeMatchIndex = i;
111✔
5276
                    }
5277
                });
5278
            }
5279

5280
            return this.find(this._lastSearchInfo.searchText,
127✔
5281
                0,
5282
                this._lastSearchInfo.caseSensitive,
5283
                this._lastSearchInfo.exactMatch,
5284
                false,
5285
                endEdit);
5286
        } else {
5287
            return 0;
9,898✔
5288
        }
5289
    }
5290

5291
    /**
5292
     * Removes all the highlights in the cell.
5293
     *
5294
     * @example
5295
     * ```typescript
5296
     * this.grid.clearSearch();
5297
     * ```
5298
     */
5299
    public clearSearch() {
5300
        this._lastSearchInfo = {
1✔
5301
            searchText: '',
5302
            caseSensitive: false,
5303
            exactMatch: false,
5304
            activeMatchIndex: 0,
5305
            matchInfoCache: [],
5306
            matchCount: 0,
5307
            content: ''
5308
        };
5309

5310
        this.rowList.forEach((row) => {
1✔
5311
            if (row.cells) {
10✔
5312
                row.cells.forEach((c: IgxGridCellComponent) => {
10✔
5313
                    c.clearHighlight();
40✔
5314
                });
5315
            }
5316
        });
5317
    }
5318

5319
    /** @hidden @internal */
5320
    public get hasEditableColumns(): boolean {
5321
        return this._columns.some((col) => col.editable);
6✔
5322
    }
5323

5324
    /** @hidden @internal */
5325
    public get hasSummarizedColumns(): boolean {
5326
        const summarizedColumns = this._columns.filter(col => col.hasSummary && !col.hidden);
2,106,286✔
5327
        return summarizedColumns.length > 0;
333,827✔
5328
    }
5329

5330
    /**
5331
     * @hidden @internal
5332
     */
5333
    public get rootSummariesEnabled(): boolean {
5334
        return this.summaryCalculationMode !== GridSummaryCalculationMode.childLevelsOnly;
244,123✔
5335
    }
5336

5337
    /**
5338
     * @hidden @internal
5339
     */
5340
    public get hasVisibleColumns(): boolean {
5341
        if (this._hasVisibleColumns === undefined) {
78,046✔
5342
            return this._columns ? this._columns.some(c => !c.hidden) : false;
79,168!
5343
        }
5344
        return this._hasVisibleColumns;
×
5345
    }
5346

5347
    public set hasVisibleColumns(value) {
5348
        this._hasVisibleColumns = value;
34,339✔
5349
    }
5350

5351
    /** @hidden @internal */
5352
    public get hasMovableColumns(): boolean {
5353
        return this.moving;
×
5354
    }
5355

5356
    /** @hidden @internal */
5357
    public get hasColumnGroups(): boolean {
5358
        return this._columnGroups;
859✔
5359
    }
5360

5361
    /** @hidden @internal */
5362
    public get hasColumnLayouts() {
5363
        return !!this._columns.some(col => col.columnLayout);
22,349,948✔
5364
    }
5365

5366

5367
    /**
5368
     * @hidden @internal
5369
     */
5370
    public get multiRowLayoutRowSize() {
5371
        return this._multiRowLayoutRowSize;
20,000✔
5372
    }
5373

5374
    /**
5375
     * @hidden
5376
     */
5377
    protected get rowBasedHeight() {
5378
        return this.dataLength * this.rowHeight;
×
5379
    }
5380

5381
    /**
5382
     * @hidden
5383
     */
5384
    protected get isPercentWidth() {
5385
        return this.width && this.width.indexOf('%') !== -1;
42,131✔
5386
    }
5387

5388
    protected get shouldResize(): boolean {
5389
        return this._gridSize !== this.gridSize;
1,770✔
5390
    }
5391

5392
    /**
5393
     * @hidden @internal
5394
     */
5395
    public get isPercentHeight() {
5396
        return this._height && this._height.indexOf('%') !== -1;
15,837✔
5397
    }
5398

5399
    /**
5400
     * @hidden
5401
     */
5402
    protected get defaultTargetBodyHeight(): number {
5403
        const allItems = this.dataLength;
325✔
5404
        return this.renderedActualRowHeight * Math.min(this._defaultTargetRecordNumber,
325✔
5405
            this.paginator ? Math.min(allItems, this.paginator.perPage) : allItems);
325✔
5406
    }
5407

5408
    /**
5409
     * @hidden @internal
5410
     * The rowHeight input is bound to min-height css prop of rows that adds a 1px border in all cases
5411
     */
5412
    public get renderedRowHeight(): number {
5413
        return this.rowHeight + 1;
85,218✔
5414
    }
5415

5416
    /**
5417
     * @hidden @internal
5418
     */
5419
    public get outerWidth() {
5420
        return this.hasVerticalScroll() ? this.calcWidth + this.scrollSize : this.calcWidth;
3,858✔
5421
    }
5422

5423
    /**
5424
     * @hidden @internal
5425
     * Gets the size of the grid
5426
     */
5427
    public get gridSize(): Size {
5428
        return this.gridComputedStyles?.getPropertyValue('--component-size') || Size.Large;
887,664✔
5429
    }
5430

5431
    /**
5432
     * @hidden @internal
5433
     * Gets the visible content height that includes header + tbody + footer.
5434
     */
5435
    public getVisibleContentHeight() {
5436
        let height = this.theadRow.nativeElement.clientHeight + this.tbody.nativeElement.clientHeight;
48✔
5437
        if (this.hasSummarizedColumns) {
48✔
5438
            height += this.tfoot.nativeElement.clientHeight;
5✔
5439
        }
5440
        return height;
48✔
5441
    }
5442

5443
    /**
5444
     * @hidden @internal
5445
     */
5446
    public getPossibleColumnWidth(baseWidth: number = null) {
72,784✔
5447
        let computedWidth;
5448
        if (baseWidth !== null) {
72,784!
5449
            computedWidth = baseWidth;
×
5450
        } else {
5451
            computedWidth = this.calcWidth ||
72,784✔
5452
                parseFloat(this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width'));
5453
        }
5454

5455
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
682,119✔
5456

5457

5458
        // Column layouts related
5459
        let visibleCols = [];
72,784✔
5460
        const columnBlocks = this.visibleColumns.filter(c => c.columnGroup);
682,119✔
5461
        const colsPerBlock = columnBlocks.map(block => block.getInitialChildColumnSizes(block.children));
122,367✔
5462
        const combinedBlocksSize = colsPerBlock.reduce((acc, item) => acc + item.length, 0);
122,367✔
5463
        colsPerBlock.forEach(blockCols => visibleCols = visibleCols.concat(blockCols));
122,367✔
5464
        //
5465

5466
        const columnsWithSetWidths = this.hasColumnLayouts ?
72,784✔
5467
            visibleCols.filter(c => c.widthSetByUser) :
164,920✔
5468
            visibleChildColumns.filter(c => (c.widthSetByUser || c.widthConstrained) && c.width !== 'fit-content');
296,743✔
5469

5470
        const columnsToSize = this.hasColumnLayouts ?
72,784✔
5471
            combinedBlocksSize - columnsWithSetWidths.length :
5472
            visibleChildColumns.length - columnsWithSetWidths.length;
5473
        const sumExistingWidths = columnsWithSetWidths
72,784✔
5474
            .reduce((prev, curr) => {
5475
                const colInstance =  this.hasColumnLayouts ? curr.ref : curr;
37,618✔
5476
                const colWidth = !colInstance.widthConstrained ? curr.width : colInstance.calcPixelWidth;
37,618✔
5477
                let widthValue = parseFloat(colWidth);
37,618✔
5478
                if (isNaN(widthValue)) {
37,618!
5479
                    widthValue = MINIMUM_COLUMN_WIDTH;
×
5480
                }
5481
                const currWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1 ?
37,618✔
5482
                    widthValue / 100 * computedWidth :
5483
                    widthValue;
5484
                // apply constraints, since constraint may change width
5485
                const constrainedWidth = this.hasColumnLayouts ? currWidth : colInstance.getConstrainedSizePx(currWidth);
37,618✔
5486
                return prev + constrainedWidth;
37,618✔
5487
            }, 0);
5488

5489
        // When all columns are hidden, return 0px width
5490
        if (!sumExistingWidths && !columnsToSize) {
72,784✔
5491
            return '0px';
1,592✔
5492
        }
5493
        computedWidth -= this.featureColumnsWidth();
71,192✔
5494

5495
        const columnWidth = !Number.isFinite(sumExistingWidths) ?
71,192!
5496
            Math.max(computedWidth / columnsToSize, this.minColumnWidth) :
5497
            Math.max((computedWidth - sumExistingWidths) / columnsToSize, this.minColumnWidth);
5498

5499
        return columnWidth + 'px';
71,192✔
5500
    }
5501

5502
    /**
5503
     * @hidden @internal
5504
     */
5505
    public hasVerticalScroll() {
5506
        if (this._init) {
188,503✔
5507
            return false;
83,200✔
5508
        }
5509
        const isScrollable = this.verticalScrollContainer ? this.verticalScrollContainer.isScrollable() : false;
105,303✔
5510
        return !!(this.calcWidth && this.dataView && this.dataView.length > 0 && isScrollable);
105,303✔
5511
    }
5512

5513
    /**
5514
     * Gets calculated width of the pinned area.
5515
     *
5516
     * @example
5517
     * ```typescript
5518
     * const pinnedWidth = this.grid.getPinnedWidth();
5519
     * ```
5520
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
5521
     */
5522
    public getPinnedWidth(takeHidden = false) {
20,560✔
5523
        const fc = takeHidden ? this._pinnedColumns : this.pinnedColumns;
44,672!
5524
        let sum = 0;
44,672✔
5525
        for (const col of fc) {
44,672✔
5526
            if (col.level === 0) {
6,579✔
5527
                sum += parseInt(col.calcWidth, 10);
3,870✔
5528
            }
5529
        }
5530
        if (this.isPinningToStart) {
44,672✔
5531
            sum += this.featureColumnsWidth();
44,548✔
5532
        }
5533

5534
        return sum;
44,672✔
5535
    }
5536

5537
    /**
5538
     * @hidden @internal
5539
     */
5540
    public isColumnGrouped(_fieldName: string): boolean {
5541
        return false;
×
5542
    }
5543

5544
    /**
5545
     * @hidden @internal
5546
     * TODO: REMOVE
5547
     */
5548
    public onHeaderSelectorClick(event) {
5549
        if (!this.isMultiRowSelectionEnabled) {
7!
5550
            return;
×
5551
        }
5552
        if (this.selectionService.areAllRowSelected()) {
7✔
5553
            this.selectionService.clearRowSelection(event);
1✔
5554
        } else {
5555
            this.selectionService.selectAllRows(event);
6✔
5556
        }
5557
    }
5558

5559
    /**
5560
     * @hidden @internal
5561
     */
5562
    public get headSelectorBaseAriaLabel() {
5563
        if (this._filteringExpressionsTree.filteringOperands.length > 0) {
3,169✔
5564
            return this.selectionService.areAllRowSelected() ? 'Deselect all filtered' : 'Select all filtered';
112✔
5565
        }
5566

5567
        return this.selectionService.areAllRowSelected() ? 'Deselect all' : 'Select all';
3,057✔
5568
    }
5569

5570
    /**
5571
     * @hidden
5572
     * @internal
5573
     */
5574
    public get totalRowsCountAfterFilter() {
5575
        if (this.data) {
3,334✔
5576
            return this.selectionService.allData.length;
3,334✔
5577
        }
5578

5579
        return 0;
×
5580
    }
5581

5582
    /** @hidden @internal */
5583
    public get pinnedDataView(): any[] {
5584
        return this.pinnedRecords ? this.pinnedRecords : [];
263,986✔
5585
    }
5586

5587
    /** @hidden @internal */
5588
    public get unpinnedDataView(): any[] {
5589
        return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer?.igxForOf || [];
15,650✔
5590
    }
5591

5592
    /**
5593
     * Returns the currently transformed paged/filtered/sorted/grouped/pinned/unpinned row data, displayed in the grid.
5594
     *
5595
     * @example
5596
     * ```typescript
5597
     *      const dataView = this.grid.dataView;
5598
     * ```
5599
     */
5600
    public get dataView() {
5601
        return this._dataView;
402,075✔
5602
    }
5603

5604
    /**
5605
     * Gets/Sets whether clicking over a row should select/deselect it
5606
     *
5607
     * @remarks
5608
     * By default it is set to true
5609
     * @param enabled: boolean
5610
     */
5611
    @WatchChanges()
5612
    @Input({ transform: booleanAttribute })
5613
    public get selectRowOnClick() {
5614
        return this._selectRowOnClick;
110✔
5615
    }
5616

5617
    public set selectRowOnClick(enabled: boolean) {
5618
        this._selectRowOnClick = enabled;
13✔
5619
    }
5620

5621
    /**
5622
     * Select specified rows by ID.
5623
     *
5624
     * @example
5625
     * ```typescript
5626
     * this.grid.selectRows([1,2,5], true);
5627
     * ```
5628
     * @param rowIDs
5629
     * @param clearCurrentSelection if true clears the current selection
5630
     */
5631
    public selectRows(rowIDs: any[], clearCurrentSelection?: boolean) {
5632
        this.selectionService.selectRowsWithNoEvent(rowIDs, clearCurrentSelection);
264✔
5633
        this.notifyChanges();
264✔
5634
    }
5635

5636
    /**
5637
     * Deselect specified rows by ID.
5638
     *
5639
     * @example
5640
     * ```typescript
5641
     * this.grid.deselectRows([1,2,5]);
5642
     * ```
5643
     * @param rowIDs
5644
     */
5645
    public deselectRows(rowIDs: any[]) {
5646
        this.selectionService.deselectRowsWithNoEvent(rowIDs);
19✔
5647
        this.notifyChanges();
19✔
5648
    }
5649

5650
    /**
5651
     * Selects all rows
5652
     *
5653
     * @remarks
5654
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
5655
     * If you set the parameter onlyFilterData to false that will select all rows in the grid exept deleted rows.
5656
     * @example
5657
     * ```typescript
5658
     * this.grid.selectAllRows();
5659
     * this.grid.selectAllRows(false);
5660
     * ```
5661
     * @param onlyFilterData
5662
     */
5663
    public selectAllRows(onlyFilterData = true) {
29✔
5664
        const data = onlyFilterData && this.filteredData ? this.filteredData : this.gridAPI.get_all_data(true);
30✔
5665
        const rowIDs = this.selectionService.getRowIDs(data).filter(rID => !this.gridAPI.row_deleted_transaction(rID));
429✔
5666
        this.selectRows(rowIDs);
30✔
5667
    }
5668

5669
    /**
5670
     * Deselects all rows
5671
     *
5672
     * @remarks
5673
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
5674
     * If you set the parameter onlyFilterData to false that will deselect all rows in the grid exept deleted rows.
5675
     * @example
5676
     * ```typescript
5677
     * this.grid.deselectAllRows();
5678
     * ```
5679
     * @param onlyFilterData
5680
     */
5681
    public deselectAllRows(onlyFilterData = true) {
5✔
5682
        if (onlyFilterData && this.filteredData && this.filteredData.length > 0) {
6✔
5683
            this.deselectRows(this.selectionService.getRowIDs(this.filteredData));
1✔
5684
        } else {
5685
            this.selectionService.clearAllSelectedRows();
5✔
5686
            this.notifyChanges();
5✔
5687
        }
5688
    }
5689

5690
    /**
5691
     * Deselect selected cells.
5692
     * @example
5693
     * ```typescript
5694
     * this.grid.clearCellSelection();
5695
     * ```
5696
     */
5697
    public clearCellSelection(): void {
5698
        this.selectionService.clear(true);
8✔
5699
        this.notifyChanges();
8✔
5700
    }
5701

5702
    /**
5703
     * @hidden @internal
5704
     */
5705
    public dragScroll(delta: { left: number; top: number }): void {
5706
        const horizontal = this.headerContainer.getScroll();
1✔
5707
        const vertical = this.verticalScrollContainer.getScroll();
1✔
5708
        const { left, top } = delta;
1✔
5709

5710
        horizontal.scrollLeft += left * this.DRAG_SCROLL_DELTA;
1✔
5711
        vertical.scrollTop += top * this.DRAG_SCROLL_DELTA;
1✔
5712
    }
5713

5714
    /**
5715
     * @hidden @internal
5716
     */
5717
    public isDefined(arg: any): boolean {
5718
        return arg !== undefined && arg !== null;
67,494✔
5719
    }
5720

5721
    /**
5722
     * Select range(s) of cells between certain rows and columns of the grid.
5723
     */
5724
    public selectRange(arg: GridSelectionRange | GridSelectionRange[] | null | undefined): void {
5725
        if (!this.isDefined(arg)) {
170✔
5726
            this.clearCellSelection();
6✔
5727
            return;
6✔
5728
        }
5729
        if (arg instanceof Array) {
164✔
5730
            arg.forEach(range => this.setSelection(range));
6✔
5731
        } else {
5732
            this.setSelection(arg);
161✔
5733
        }
5734
        this.notifyChanges();
161✔
5735
    }
5736

5737
    /**
5738
     * @hidden @internal
5739
     */
5740
    public columnToVisibleIndex(field: string | number): number {
5741
        const visibleColumns = this.visibleColumns;
341✔
5742
        if (typeof field === 'number') {
341✔
5743
            return field;
208✔
5744
        }
5745
        return visibleColumns.find(column => column.field === field).visibleIndex;
322✔
5746
    }
5747

5748
    /**
5749
     * @hidden @internal
5750
     */
5751
    public setSelection(range: GridSelectionRange): void {
5752
        const startNode = { row: range.rowStart, column: this.columnToVisibleIndex(range.columnStart) };
169✔
5753
        const endNode = { row: range.rowEnd, column: this.columnToVisibleIndex(range.columnEnd) };
167✔
5754

5755
        this.selectionService.pointerState.node = startNode;
166✔
5756
        this.selectionService.selectRange(endNode, this.selectionService.pointerState);
166✔
5757
        this.selectionService.addRangeMeta(endNode, this.selectionService.pointerState);
166✔
5758
        this.selectionService.initPointerState();
166✔
5759
    }
5760

5761
    /**
5762
     * Get the currently selected ranges in the grid.
5763
     */
5764
    public getSelectedRanges(): GridSelectionRange[] {
5765
        return this.selectionService.ranges;
348✔
5766
    }
5767

5768
    /**
5769
     *
5770
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
5771
     *
5772
     * @remarks
5773
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5774
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5775
     */
5776
    public getSelectedData(formatters = false, headers = false) {
4✔
5777
        const source = this.filteredSortedData;
167✔
5778
        return this.extractDataFromSelection(source, formatters, headers);
167✔
5779
    }
5780

5781
    /**
5782
     * Get current selected columns.
5783
     *
5784
     * @example
5785
     * Returns an array with selected columns
5786
     * ```typescript
5787
     * const selectedColumns = this.grid.selectedColumns();
5788
     * ```
5789
     */
5790
    public selectedColumns(): ColumnType[] {
5791
        const fields = this.selectionService.getSelectedColumns();
67✔
5792
        return fields.map(field => this.getColumnByName(field)).filter(field => field);
67✔
5793
    }
5794

5795
    /**
5796
     * Select specified columns.
5797
     *
5798
     * @example
5799
     * ```typescript
5800
     * this.grid.selectColumns(['ID','Name'], true);
5801
     * ```
5802
     * @param columns
5803
     * @param clearCurrentSelection if true clears the current selection
5804
     */
5805
    public selectColumns(columns: string[] | ColumnType[], clearCurrentSelection?: boolean) {
5806
        let fieldToSelect: string[] = [];
12✔
5807
        if (columns.length === 0 || typeof columns[0] === 'string') {
12✔
5808
            fieldToSelect = columns as string[];
7✔
5809
        } else {
5810
            (columns as ColumnType[]).forEach(col => {
5✔
5811
                if (col.columnGroup) {
18!
5812
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
×
5813
                    fieldToSelect = [...fieldToSelect, ...children];
×
5814
                } else {
5815
                    fieldToSelect.push(col.field);
18✔
5816
                }
5817
            });
5818
        }
5819

5820
        this.selectionService.selectColumnsWithNoEvent(fieldToSelect, clearCurrentSelection);
12✔
5821
        this.notifyChanges();
12✔
5822
    }
5823

5824
    /**
5825
     * Deselect specified columns by field.
5826
     *
5827
     * @example
5828
     * ```typescript
5829
     * this.grid.deselectColumns(['ID','Name']);
5830
     * ```
5831
     * @param columns
5832
     */
5833
    public deselectColumns(columns: string[] | ColumnType[]) {
5834
        let fieldToDeselect: string[] = [];
3✔
5835
        if (columns.length === 0 || typeof columns[0] === 'string') {
3✔
5836
            fieldToDeselect = columns as string[];
2✔
5837
        } else {
5838
            (columns as ColumnType[]).forEach(col => {
1✔
5839
                if (col.columnGroup) {
2!
5840
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
×
5841
                    fieldToDeselect = [...fieldToDeselect, ...children];
×
5842
                } else {
5843
                    fieldToDeselect.push(col.field);
2✔
5844
                }
5845
            });
5846
        }
5847
        this.selectionService.deselectColumnsWithNoEvent(fieldToDeselect);
3✔
5848
        this.notifyChanges();
3✔
5849
    }
5850

5851
    /**
5852
     * Deselects all columns
5853
     *
5854
     * @example
5855
     * ```typescript
5856
     * this.grid.deselectAllColumns();
5857
     * ```
5858
     */
5859
    public deselectAllColumns() {
5860
        this.selectionService.clearAllSelectedColumns();
3✔
5861
        this.notifyChanges();
3✔
5862
    }
5863

5864
    /**
5865
     * Selects all columns
5866
     *
5867
     * @example
5868
     * ```typescript
5869
     * this.grid.deselectAllColumns();
5870
     * ```
5871
     */
5872
    public selectAllColumns() {
5873
        this.selectColumns(this._columns.filter(c => !c.columnGroup));
15✔
5874
    }
5875

5876
    /**
5877
     *
5878
     * Returns an array of the current columns selection in the form of `[{ column.field: cell.value }, ...]`.
5879
     *
5880
     * @remarks
5881
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5882
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5883
     */
5884
    public getSelectedColumnsData(formatters = false, headers = false) {
16✔
5885
        const source = this.filteredSortedData ? this.filteredSortedData : this.data;
20!
5886
        return this.extractDataFromColumnsSelection(source, formatters, headers);
20✔
5887
    }
5888

5889

5890
    /** @hidden @internal **/
5891
    public combineSelectedCellAndColumnData(columnData: any[], formatters = false, headers = false) {
×
5892
        const source = this.filteredSortedData;
×
5893
        return this.extractDataFromSelection(source, formatters, headers, columnData);
×
5894
    }
5895

5896
    /**
5897
     * @hidden @internal
5898
     */
5899
    public preventContainerScroll = (evt) => {
4,117✔
5900
        if (evt.target.scrollTop !== 0) {
×
5901
            this.verticalScrollContainer.addScroll(evt.target.scrollTop);
×
5902
            evt.target.scrollTop = 0;
×
5903
        }
5904
        if (evt.target.scrollLeft !== 0) {
×
5905
            this.headerContainer.scrollPosition += evt.target.scrollLeft;
×
5906
            evt.target.scrollLeft = 0;
×
5907
        }
5908
    };
5909

5910
    /**
5911
     * @hidden
5912
     * @internal
5913
     */
5914
    public copyHandler(event) {
5915
        const eventPathElements = event.composedPath().map(el => el.tagName?.toLowerCase());
98✔
5916
        if (eventPathElements.includes('igx-grid-filtering-row') ||
13✔
5917
            eventPathElements.includes('igx-grid-filtering-cell')) {
5918
            return;
1✔
5919
        }
5920

5921
        const selectedColumns = this.gridAPI.grid.selectedColumns();
12✔
5922
        const columnData = this.getSelectedColumnsData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
5923
        let selectedData;
5924
        if (event.type === 'copy') {
12✔
5925
            selectedData = this.getSelectedData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
5926
        }
5927

5928
        let data = [];
12✔
5929
        let result;
5930

5931
        if (event.code === 'KeyC' && (event.ctrlKey || event.metaKey) && event.currentTarget.className === 'igx-grid-thead__wrapper') {
12!
5932
            if (selectedData.length) {
×
5933
                if (columnData.length === 0) {
×
5934
                    result = this.prepareCopyData(event, selectedData);
×
5935
                } else {
5936
                    data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
×
5937
                        this.clipboardOptions.copyHeaders);
5938
                    result = this.prepareCopyData(event, data[0], data[1]);
×
5939
                }
5940
            } else {
5941
                data = columnData;
×
5942
                result = this.prepareCopyData(event, data);
×
5943
            }
5944

5945
            navigator.clipboard.writeText(result).then().catch(e => console.error(e));
×
5946
        } else if (!this.clipboardOptions.enabled || this.crudService.cellInEditMode || event.type === 'keydown') {
12✔
5947
            return;
2✔
5948
        } else {
5949
            if (selectedColumns.length) {
10!
5950
                data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
×
5951
                    this.clipboardOptions.copyHeaders);
5952
                result = this.prepareCopyData(event, data[0], data[1]);
×
5953
            } else {
5954
                data = selectedData;
10✔
5955
                result = this.prepareCopyData(event, data);
10✔
5956
            }
5957
            event.clipboardData.setData('text/plain', result);
10✔
5958
        }
5959
    }
5960

5961
    /**
5962
     * @hidden @internal
5963
     */
5964
    public prepareCopyData(event, data, keys?) {
5965
        const ev = { data, cancel: false } as IGridClipboardEvent;
10✔
5966
        this.gridCopy.emit(ev);
10✔
5967

5968
        if (ev.cancel) {
10✔
5969
            return;
1✔
5970
        }
5971

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

5975
        if (!this.clipboardOptions.copyHeaders) {
9✔
5976
            result = result.substring(result.indexOf('\n') + 1);
2✔
5977
        }
5978

5979
        if (data && data.length > 0 && Object.values(data[0]).length === 1) {
9✔
5980
            result = result.slice(0, -2);
4✔
5981
        }
5982

5983
        event.preventDefault();
9✔
5984

5985
        /* Necessary for the hiearachical case but will probably have to
5986
           change how getSelectedData is propagated in the hiearachical grid
5987
        */
5988
        event.stopPropagation();
9✔
5989

5990
        return result;
9✔
5991
    }
5992

5993
    /**
5994
     * @hidden @internal
5995
     */
5996
    public showSnackbarFor(index: number) {
5997
        this.addRowSnackbar.actionText = index === -1 ? '' : this.resourceStrings.igx_grid_snackbar_addrow_actiontext;
29✔
5998
        this.lastAddedRowIndex = index;
29✔
5999
        this.addRowSnackbar.open();
29✔
6000
    }
6001

6002
    /* blazorCsSuppress */
6003
    /**
6004
     * Navigates to a position in the grid based on provided `rowindex` and `visibleColumnIndex`.
6005
     *
6006
     * @remarks
6007
     * Also can execute a custom logic over the target element,
6008
     * through a callback function that accepts { targetType: GridKeydownTargetType, target: Object }
6009
     * @example
6010
     * ```typescript
6011
     *  this.grid.navigateTo(10, 3, (args) => { args.target.nativeElement.focus(); });
6012
     * ```
6013
     */
6014
    public navigateTo(rowIndex: number, visibleColIndex = -1, cb: (args: any) => void = null) {
410✔
6015
        const totalItems = (this as any).totalItemCount ?? this.dataView.length - 1;
834✔
6016
        if (rowIndex < 0 || rowIndex > totalItems || (visibleColIndex !== -1
834!
6017
            && this._columns.map(col => col.visibleIndex).indexOf(visibleColIndex) === -1)) {
5,050✔
6018
            return;
×
6019
        }
6020
        if (this.dataView.slice(rowIndex, rowIndex + 1).find(rec => rec.expression || rec.childGridsData)) {
834✔
6021
            visibleColIndex = -1;
43✔
6022
        }
6023
        // If the target row is pinned no need to scroll as well.
6024
        const shouldScrollVertically = this.navigation.shouldPerformVerticalScroll(rowIndex, visibleColIndex);
834✔
6025
        const shouldScrollHorizontally = this.navigation.shouldPerformHorizontalScroll(visibleColIndex, rowIndex);
834✔
6026
        if (shouldScrollVertically) {
834✔
6027
            this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () => {
143✔
6028
                if (shouldScrollHorizontally) {
139✔
6029
                    this.navigation.performHorizontalScrollToCell(visibleColIndex, () =>
16✔
6030
                        this.executeCallback(rowIndex, visibleColIndex, cb));
16✔
6031
                } else {
6032
                    this.executeCallback(rowIndex, visibleColIndex, cb);
123✔
6033
                }
6034
            });
6035
        } else if (shouldScrollHorizontally) {
691✔
6036
            this.navigation.performHorizontalScrollToCell(visibleColIndex, () => {
93✔
6037
                if (shouldScrollVertically) {
93!
6038
                    this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () =>
×
6039
                        this.executeCallback(rowIndex, visibleColIndex, cb));
×
6040
                } else {
6041
                    this.executeCallback(rowIndex, visibleColIndex, cb);
93✔
6042
                }
6043
            });
6044
        } else {
6045
            this.executeCallback(rowIndex, visibleColIndex, cb);
598✔
6046
        }
6047
    }
6048

6049
    /* blazorCsSuppress */
6050
    /**
6051
     * Returns `ICellPosition` which defines the next cell,
6052
     * according to the current position, that match specific criteria.
6053
     *
6054
     * @remarks
6055
     * You can pass callback function as a third parameter of `getPreviousCell` method.
6056
     * The callback function accepts IgxColumnComponent as a param
6057
     * @example
6058
     * ```typescript
6059
     *  const nextEditableCellPosition = this.grid.getNextCell(0, 3, (column) => column.editable);
6060
     * ```
6061
     */
6062
    public getNextCell(currRowIndex: number, curVisibleColIndex: number,
6063
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
×
6064
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
477✔
6065
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
56✔
6066
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
56✔
6067
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
2✔
6068
        }
6069
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => a - b) :
460!
6070
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => a - b);
×
6071
        const nextCellIndex = colIndexes.find(index => index > curVisibleColIndex);
147✔
6072
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
54✔
6073
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && nextCellIndex !== undefined) {
54✔
6074
            return { rowIndex: currRowIndex, visibleColumnIndex: nextCellIndex };
39✔
6075
        } else {
6076
            const nextIndex = this.getNextDataRowIndex(currRowIndex)
15✔
6077
            if (colIndexes.length === 0 || nextIndex === currRowIndex) {
15!
6078
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
×
6079
            } else {
6080
                return { rowIndex: nextIndex, visibleColumnIndex: colIndexes[0] };
15✔
6081
            }
6082
        }
6083
    }
6084

6085
    /* blazorCsSuppress */
6086
    /**
6087
     * Returns `ICellPosition` which defines the previous cell,
6088
     * according to the current position, that match specific criteria.
6089
     *
6090
     * @remarks
6091
     * You can pass callback function as a third parameter of `getPreviousCell` method.
6092
     * The callback function accepts IgxColumnComponent as a param
6093
     * @example
6094
     * ```typescript
6095
     *  const previousEditableCellPosition = this.grid.getPreviousCell(0, 3, (column) => column.editable);
6096
     * ```
6097
     */
6098
    public getPreviousCell(currRowIndex: number, curVisibleColIndex: number,
6099
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
1✔
6100
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
300✔
6101
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
41✔
6102
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
41✔
6103
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
2✔
6104
        }
6105
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => b - a) :
238✔
6106
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => b - a);
4✔
6107
        const prevCellIndex = colIndexes.find(index => index < curVisibleColIndex);
141✔
6108
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
39✔
6109
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && prevCellIndex !== undefined) {
39✔
6110
            return { rowIndex: currRowIndex, visibleColumnIndex: prevCellIndex };
24✔
6111
        } else {
6112
            const prevIndex = this.getNextDataRowIndex(currRowIndex, true);
15✔
6113
            if (colIndexes.length === 0 || prevIndex === currRowIndex) {
15✔
6114
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
7✔
6115
            } else {
6116
                return { rowIndex: prevIndex, visibleColumnIndex: colIndexes[0] };
8✔
6117
            }
6118
        }
6119
    }
6120

6121
    /**
6122
     * @hidden
6123
     * @internal
6124
     */
6125
    public endRowEditTabStop(commit = true, event?: Event) {
×
6126
        const canceled = this.crudService.endEdit(commit, event);
15✔
6127

6128
        if (canceled) {
15✔
6129
            return true;
3✔
6130
        }
6131

6132
        this.navigation.restoreActiveNodeFocus();
12✔
6133
    }
6134

6135
    /**
6136
     * @hidden @internal
6137
     */
6138
    public trackColumnChanges(_index, col) {
6139
        return col.field + col._calcWidth;
1,706,471✔
6140
    }
6141

6142
    /**
6143
     * @hidden
6144
     */
6145
    public isExpandedGroup(_group: IGroupByRecord): boolean {
6146
        return undefined;
×
6147
    }
6148

6149
    /**
6150
     * @hidden @internal
6151
     * TODO: MOVE to CRUD
6152
     */
6153
    public openRowOverlay(id) {
6154
        this.configureRowEditingOverlay(id, this.rowList.length <= MIN_ROW_EDITING_COUNT_THRESHOLD);
208✔
6155

6156
        this.rowEditingOverlay.open(this.rowEditSettings);
208✔
6157
        this.rowEditingOverlay.element.addEventListener('wheel', this.rowEditingWheelHandler);
208✔
6158
    }
6159

6160
    /**
6161
     * @hidden @internal
6162
     */
6163
    public closeRowEditingOverlay() {
6164
        this.rowEditingOverlay.element.removeEventListener('wheel', this.rowEditingWheelHandler);
130✔
6165
        this.rowEditPositioningStrategy.isTopInitialPosition = null;
130✔
6166
        this.rowEditingOverlay.close();
130✔
6167
        this.rowEditingOverlay.element.parentElement.style.display = '';
130✔
6168
    }
6169

6170
    /**
6171
     * @hidden @internal
6172
     */
6173
    public toggleRowEditingOverlay(show) {
6174
        const rowStyle = this.rowEditingOverlay.element.style;
278✔
6175
        if (show) {
278!
6176
            rowStyle.display = 'block';
278✔
6177
        } else {
6178
            rowStyle.display = 'none';
×
6179
        }
6180
    }
6181

6182
    /**
6183
     * @hidden @internal
6184
     */
6185
    public repositionRowEditingOverlay(row: RowType) {
6186
        if (row && !this.rowEditingOverlay.collapsed) {
888✔
6187
            const rowStyle = this.rowEditingOverlay.element.parentElement.style;
70✔
6188
            if (row) {
70!
6189
                rowStyle.display = '';
70✔
6190
                this.configureRowEditingOverlay(row.key);
70✔
6191
                this.rowEditingOverlay.reposition();
70✔
6192
            } else {
6193
                rowStyle.display = 'none';
×
6194
            }
6195
        }
6196
    }
6197

6198
    /**
6199
     * @hidden @internal
6200
     */
6201
    public cachedViewLoaded(args: ICachedViewLoadedEventArgs) {
6202
        if (this.hasHorizontalScroll()) {
629✔
6203
            const tmplId = args.context.templateID.type;
444✔
6204
            const index = args.context.index;
444✔
6205
            args.view.detectChanges();
444✔
6206
            this.zone.onStable.pipe(first()).subscribe(() => {
444✔
6207
                const row = tmplId === 'dataRow' ? this.gridAPI.get_row_by_index(index) : null;
444✔
6208
                const summaryRow = tmplId === 'summaryRow' ? this.summariesRowList.find((sr) => sr.dataRowIndex === index) : null;
444✔
6209
                if (row && row instanceof IgxRowDirective) {
444✔
6210
                    this._restoreVirtState(row);
267✔
6211
                } else if (summaryRow) {
177✔
6212
                    this._restoreVirtState(summaryRow);
43✔
6213
                }
6214
            });
6215
        }
6216
    }
6217

6218
    /**
6219
     * Opens the advanced filtering dialog.
6220
     */
6221
    public openAdvancedFilteringDialog(overlaySettings?: OverlaySettings) {
6222
        const settings = overlaySettings ? overlaySettings : this._advancedFilteringOverlaySettings;
56!
6223
        if (!this._advancedFilteringOverlayId) {
56✔
6224
            this._advancedFilteringOverlaySettings.target =
53✔
6225
                (this as any).rootGrid ? (this as any).rootGrid.nativeElement : this.nativeElement;
53✔
6226
            this._advancedFilteringOverlaySettings.outlet = this.outlet;
53✔
6227

6228
            this._advancedFilteringOverlayId = this.overlayService.attach(
53✔
6229
                IgxAdvancedFilteringDialogComponent,
6230
                this.viewRef,
6231
                settings);
6232
            this.overlayService.show(this._advancedFilteringOverlayId);
53✔
6233
        }
6234
    }
6235

6236
    /**
6237
     * Closes the advanced filtering dialog.
6238
     *
6239
     * @param applyChanges indicates whether the changes should be applied
6240
     */
6241
    public closeAdvancedFilteringDialog(applyChanges: boolean) {
6242
        if (this._advancedFilteringOverlayId) {
10✔
6243
            const advancedFilteringOverlay = this.overlayService.getOverlayById(this._advancedFilteringOverlayId);
10✔
6244
            const advancedFilteringDialog = advancedFilteringOverlay.componentRef.instance as IgxAdvancedFilteringDialogComponent;
10✔
6245

6246
            if (applyChanges) {
10✔
6247
                advancedFilteringDialog.applyChanges();
2✔
6248
            }
6249
            advancedFilteringDialog.closeDialog();
10✔
6250
        }
6251
    }
6252

6253
    /**
6254
     * @hidden @internal
6255
     */
6256
    public getEmptyRecordObjectFor(inRow: RowType) {
6257
        const row = { ...inRow?.data };
49✔
6258
        Object.keys(row).forEach(key => row[key] = undefined);
213✔
6259
        const id = this.generateRowID();
49✔
6260
        row[this.primaryKey] = id;
49✔
6261
        return { rowID: id, data: row, recordRef: row };
49✔
6262
    }
6263

6264
    /**
6265
     * @hidden @internal
6266
     */
6267
    public hasHorizontalScroll() {
6268
        return this.totalWidth - this.unpinnedWidth > 0 && this.width !== null;
11,349✔
6269
    }
6270

6271
    /**
6272
     * @hidden @internal
6273
     */
6274
    public isSummaryRow(rowData): boolean {
6275
        return rowData && rowData.summaries && (rowData.summaries instanceof Map);
436,309✔
6276
    }
6277

6278
    /**
6279
     * @hidden @internal
6280
     */
6281
    public triggerPipes() {
6282
        this.pipeTrigger++;
129✔
6283
        this.cdr.detectChanges();
129✔
6284
    }
6285

6286
    /**
6287
     * @hidden
6288
     */
6289
    public rowEditingWheelHandler = (event: WheelEvent) => {
4,117✔
6290
        if (event.deltaY > 0) {
×
6291
            this.verticalScrollContainer.scrollNext();
×
6292
        } else {
6293
            this.verticalScrollContainer.scrollPrev();
×
6294
        }
6295
    }
6296

6297
    /**
6298
     * @hidden
6299
     */
6300
    public getUnpinnedIndexById(id) {
6301
        return this.unpinnedRecords.findIndex(x => x[this.primaryKey] === id);
102✔
6302
    }
6303

6304
    /**
6305
     * Finishes the row transactions on the current row and returns whether the grid editing was canceled.
6306
     *
6307
     * @remarks
6308
     * If `commit === true`, passes them from the pending state to the data (or transaction service)
6309
     * @example
6310
     * ```html
6311
     * <button type="button" igxButton (click)="grid.endEdit(true)">Commit Row</button>
6312
     * ```
6313
     * @param commit
6314
     */
6315
    // TODO: Facade for crud service refactoring. To be removed
6316
    // TODO: do not remove this, as it is used in rowEditTemplate, but mark is as internal and hidden
6317
    /* blazorCSSuppress */
6318
    public endEdit(commit = true, event?: Event): boolean {
1✔
6319
        const document = this.nativeElement?.getRootNode() as Document | ShadowRoot;
6✔
6320
        const focusWithin = this.nativeElement?.contains(document.activeElement);
6✔
6321

6322
        const success = this.crudService.endEdit(commit, event);
6✔
6323

6324
        if (focusWithin) {
6✔
6325
            // restore focus for navigation
6326
            this.navigation.restoreActiveNodeFocus();
2✔
6327
        } else if (this.navigation.activeNode) {
4✔
6328
            // grid already lost focus, clear active node
6329
            this.clearActiveNode();
4✔
6330
        }
6331

6332
        return success;
6✔
6333
    }
6334

6335
    /**
6336
     * Enters add mode by spawning the UI under the specified row by rowID.
6337
     *
6338
     * @remarks
6339
     * If null is passed as rowID, the row adding UI is spawned as the first record in the data view
6340
     * @remarks
6341
     * Spawning the UI to add a child for a record only works if you provide a rowID
6342
     * @example
6343
     * ```typescript
6344
     * this.grid.beginAddRowById('ALFKI');
6345
     * this.grid.beginAddRowById('ALFKI', true);
6346
     * this.grid.beginAddRowById(null);
6347
     * ```
6348
     * @param rowID - The rowID to spawn the add row UI for, or null to spawn it as the first record in the data view
6349
     * @param asChild - Whether the record should be added as a child. Only applicable to igxTreeGrid.
6350
     */
6351
    public beginAddRowById(rowID: any, asChild?: boolean): void {
6352
        let index = rowID;
4✔
6353
        if (rowID == null) {
4✔
6354
            if (asChild) {
3!
6355
                console.warn('The record cannot be added as a child to an unspecified record.');
×
6356
                return;
×
6357
            }
6358
            index = null;
3✔
6359
        } else {
6360
            // find the index of the record with that PK
6361
            index = this.gridAPI.get_rec_index_by_id(rowID, this.dataView);
1✔
6362
            if (index === -1) {
1!
6363
                console.warn('No row with the specified ID was found.');
×
6364
                return;
×
6365
            }
6366
        }
6367

6368
        this._addRowForIndex(index, asChild);
4✔
6369
    }
6370

6371
    protected _addRowForIndex(index: number, asChild?: boolean) {
6372
        if (!this.dataView.length) {
4!
6373
            this.beginAddRowForIndex(index, asChild);
×
6374
            return;
×
6375
        }
6376
        // check if the index is valid - won't support anything outside the data view
6377
        if (index >= 0 && index < this.dataView.length) {
4!
6378
            // check if the index is in the view port
6379
            if ((index < this.virtualizationState.startIndex ||
4✔
6380
                index >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) &&
6381
                !this.isRecordPinnedByViewIndex(index)) {
6382
                this.verticalScrollContainer.chunkLoad
1✔
6383
                    .pipe(first(), takeUntil(this.destroy$))
6384
                    .subscribe(() => {
6385
                        this.beginAddRowForIndex(index, asChild);
1✔
6386
                    });
6387
                this.navigateTo(index);
1✔
6388
                this.notifyChanges(true);
1✔
6389
                return;
1✔
6390
            }
6391
            this.beginAddRowForIndex(index, asChild);
3✔
6392
        } else {
6393
            console.warn('The row with the specified PK or index is outside of the current data view.');
×
6394
        }
6395
    }
6396

6397
    /* csSuppress */
6398
    /**
6399
     * Enters add mode by spawning the UI at the specified index.
6400
     *
6401
     * @remarks
6402
     * Accepted values for index are integers from 0 to this.grid.dataView.length
6403
     * @example
6404
     * ```typescript
6405
     * this.grid.beginAddRowByIndex(0);
6406
     * ```
6407
     * @param index - The index to spawn the UI at. Accepts integers from 0 to this.grid.dataView.length
6408
     */
6409
    public beginAddRowByIndex(index: number): void {
6410
        if (index === 0) {
1✔
6411
            return this.beginAddRowById(null);
1✔
6412
        }
6413
        return this._addRowForIndex(index - 1);
×
6414
    }
6415

6416
    /**
6417
     * @hidden
6418
     */
6419
    public preventHeaderScroll(args) {
6420
        if (args.target.scrollLeft !== 0) {
×
6421
            (this.navigation as any).forOfDir().getScroll().scrollLeft = args.target.scrollLeft;
×
6422
            args.target.scrollLeft = 0;
×
6423
        }
6424
    }
6425

6426
    protected beginAddRowForIndex(index: number, asChild = false) {
3✔
6427
        // TODO is row from rowList suitable for enterAddRowMode
6428
        const row = index == null ?
4✔
6429
            null : this.rowList.find(r => r.index === index);
2✔
6430
        if (row !== undefined) {
4!
6431
            this.crudService.enterAddRowMode(row, asChild);
4✔
6432
        } else {
6433
            console.warn('No row with the specified PK or index was found.');
×
6434
        }
6435
    }
6436

6437
    protected switchTransactionService(val: boolean) {
6438
        if (val) {
244✔
6439
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);
243✔
6440
        } else {
6441
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
1✔
6442
        }
6443

6444
        if (this.dataCloneStrategy) {
244✔
6445
            this._transactions.cloneStrategy = this.dataCloneStrategy;
244✔
6446
        }
6447
    }
6448

6449
    protected subscribeToTransactions(): void {
6450
        this.transactionChange$.next();
3,815✔
6451
        this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$)))
3,815✔
6452
            .subscribe(this.transactionStatusUpdate.bind(this));
6453
    }
6454

6455
    protected transactionStatusUpdate(event: StateUpdateEvent) {
6456
        let actions: Action<Transaction>[] = [];
324✔
6457
        if (event.origin === TransactionEventOrigin.REDO) {
324✔
6458
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.DELETE) : [];
44!
6459
        } else if (event.origin === TransactionEventOrigin.UNDO) {
285✔
6460
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.ADD) : [];
54!
6461
        }
6462
        if (actions.length > 0) {
324✔
6463
            for (const action of actions) {
22✔
6464
                if (this.selectionService.isRowSelected(action.transaction.id)) {
27!
6465
                    this.selectionService.deselectRow(action.transaction.id);
×
6466
                }
6467
            }
6468
        }
6469
        if (event.origin === TransactionEventOrigin.REDO || event.origin === TransactionEventOrigin.UNDO) {
324✔
6470
            event.actions.forEach(x => {
88✔
6471
                if (x.transaction.type === TransactionType.UPDATE) {
98✔
6472
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
43✔
6473
                    this.validation.update(x.transaction.id, value ?? x.recordRef);
43✔
6474
                } else if (x.transaction.type === TransactionType.DELETE || x.transaction.type === TransactionType.ADD) {
55✔
6475
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
55✔
6476
                    if (value) {
55✔
6477
                        this.validation.create(x.transaction.id, value ?? x.recordRef);
29!
6478
                        this.validation.update(x.transaction.id, value ?? x.recordRef);
29!
6479
                        this.validation.markAsTouched(x.transaction.id);
29✔
6480
                    } else {
6481
                        this.validation.clear(x.transaction.id);
26✔
6482
                    }
6483
                }
6484

6485
            });
6486
        }
6487

6488
        this.selectionService.clearHeaderCBState();
324✔
6489
        this.summaryService.clearSummaryCache();
324✔
6490
        this.pipeTrigger++;
324✔
6491
        this.notifyChanges();
324✔
6492
    }
6493

6494
    protected writeToData(rowIndex: number, value: any) {
6495
        mergeObjects(this.gridAPI.get_all_data()[rowIndex], value);
×
6496
    }
6497

6498
    protected _restoreVirtState(row) {
6499
        // check virtualization state of data record added from cache
6500
        // in case state is no longer valid - update it.
6501
        const rowForOf = row.virtDirRow;
310✔
6502
        const gridScrLeft = rowForOf.getScroll().scrollLeft;
310✔
6503
        rowForOf.onHScroll(gridScrLeft);
310✔
6504
        rowForOf.cdr.detectChanges();
310✔
6505
    }
6506

6507
    protected changeRowEditingOverlayStateOnScroll(row: RowType) {
6508
        if (!this.rowEditable || !this.rowEditingOverlay || this.rowEditingOverlay.collapsed) {
12✔
6509
            return;
12✔
6510
        }
6511
        if (!row) {
×
6512
            this.toggleRowEditingOverlay(false);
×
6513
        } else {
6514
            this.repositionRowEditingOverlay(row);
×
6515
        }
6516
    }
6517

6518
    /**
6519
     * Should be called when data and/or isLoading input changes so that the overlay can be
6520
     * hidden/shown based on the current value of shouldOverlayLoading
6521
     */
6522
    protected evaluateLoadingState() {
6523
        if (this.shouldOverlayLoading) {
3,281✔
6524
            // a new overlay should be shown
6525
            const overlaySettings: OverlaySettings = {
15✔
6526
                outlet: this.loadingOutlet,
6527
                closeOnOutsideClick: false,
6528
                positionStrategy: new ContainerPositionStrategy()
6529
            };
6530
            this.loadingOverlay.open(overlaySettings);
15✔
6531
        } else {
6532
            this.loadingOverlay.close();
3,266✔
6533
        }
6534
    }
6535

6536
    /**
6537
     * @hidden
6538
     * Sets grid width i.e. this.calcWidth
6539
     */
6540
    protected calculateGridWidth() {
6541
        let width;
6542

6543
        if (this.isPercentWidth) {
11,834✔
6544
            /* width in %*/
6545
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width');
5,413✔
6546
            width = computed.indexOf('%') === -1 ? parseFloat(computed) : null;
5,413✔
6547
        } else {
6548
            width = parseInt(this.width, 10);
6,421✔
6549
        }
6550

6551
        if (!width && this.nativeElement) {
11,834✔
6552
            width = this.nativeElement.offsetWidth;
32✔
6553
        }
6554

6555

6556
        if (this.width === null || !width) {
11,834✔
6557
            this.isColumnWidthSum = true;
32✔
6558
            width = this.getColumnWidthSum();
32✔
6559
        } else {
6560
            this.isColumnWidthSum = false;
11,802✔
6561
        }
6562

6563
        if (this.hasVerticalScroll() && this.width !== null) {
11,834✔
6564
            width -= this.scrollSize;
2,419✔
6565
        }
6566
        if ((Number.isFinite(width) || width === null) && width !== this.calcWidth) {
11,834!
6567
            this.calcWidth = width;
3,816✔
6568
        }
6569
        this._derivePossibleWidth();
11,834✔
6570
    }
6571

6572
    /**
6573
     * @hidden
6574
     * Sets columns defaultWidth property
6575
     */
6576
    protected _derivePossibleWidth() {
6577
        if (!this.columnWidthSetByUser) {
11,834✔
6578
            this._columnWidth = this.width !== null ? this.getPossibleColumnWidth() : this.minColumnWidth + 'px';
11,075✔
6579
        }
6580
        this._columns.forEach((column: IgxColumnComponent) => {
11,834✔
6581
            if (this.hasColumnLayouts && parseFloat(this._columnWidth)) {
74,538✔
6582
                const columnWidthCombined = parseFloat(this._columnWidth) * (column.colEnd ? column.colEnd - column.colStart : 1);
3,825✔
6583
                column.defaultWidth = columnWidthCombined + 'px';
3,825✔
6584
            } else {
6585
                // D.K. March 29th, 2021 #9145 Consider min/max width when setting defaultWidth property
6586
                column.defaultWidth = this.getExtremumBasedColWidth(column);
70,713✔
6587
                column.resetCaches();
70,713✔
6588
            }
6589
        });
6590
        this.resetCachedWidths();
11,834✔
6591
    }
6592

6593
    /**
6594
     * @hidden
6595
     * @internal
6596
     */
6597
    protected getExtremumBasedColWidth(column: IgxColumnComponent): string {
6598
        let width = this._columnWidth;
70,713✔
6599
        if (width && typeof width !== 'string') {
70,713!
6600
            width = String(width);
×
6601
        }
6602
        const minWidth = width.indexOf('%') === -1 ? column.userSetMinWidthPx : column.minWidthPercent;
70,713✔
6603
        const maxWidth = width.indexOf('%') === -1 ? column.maxWidthPx : column.maxWidthPercent;
70,713✔
6604
        if (column.hidden) {
70,713✔
6605
            return width;
1,337✔
6606
        }
6607

6608
        if (minWidth > parseFloat(width)) {
69,376✔
6609
            width = String(column.minWidth);
6✔
6610
        } else if (maxWidth < parseFloat(width)) {
69,370✔
6611
            width = String(column.maxWidth);
1,120✔
6612
        }
6613

6614
        // if no px or % are defined in maxWidth/minWidth consider it px
6615
        if (width.indexOf('%') === -1 && width.indexOf('px') === -1) {
69,376✔
6616
            width += 'px';
276✔
6617
        }
6618
        return width;
69,376✔
6619
    }
6620

6621
    protected resetNotifyChanges() {
6622
        this._cdrRequestRepaint = false;
8,223✔
6623
        this._cdrRequests = false;
8,223✔
6624
    }
6625

6626
    /** @hidden @internal */
6627
    public resolveOutlet() {
6628
        return this._userOutletDirective ? this._userOutletDirective : this._outletDirective;
238,686!
6629
    }
6630

6631
    /**
6632
     * Reorder columns in the main columnList and _columns collections.
6633
     *
6634
     * @hidden
6635
     */
6636
    protected _moveColumns(from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6637
        const orderedList = this._pinnedColumns.concat(this._unpinnedColumns);
138✔
6638
        const list = orderedList;
138✔
6639
        this._reorderColumns(from, to, pos, list);
138✔
6640
        const newList = this._resetColumnList(list);
138✔
6641
        this.updateColumns(newList);
138✔
6642
    }
6643

6644

6645
    /**
6646
     * Update internal column's collection.
6647
     * @hidden
6648
     */
6649
    public updateColumns(newColumns: IgxColumnComponent[]) {
6650
        // update internal collections to retain order.
6651
        this._pinnedColumns = newColumns
5,760✔
6652
            .filter((c) => c.pinned);
34,591✔
6653
        this._unpinnedColumns = newColumns.filter((c) => !c.pinned);
34,591✔
6654
        this._columns = newColumns;
5,760✔
6655
        if (this._columns && this._columns.length && this._filteringExpressionsTree) {
5,760✔
6656
            this._filteringExpressionsTree = this.getRecreatedTree(this._filteringExpressionsTree);
5,324✔
6657
        }
6658
        if (this._columns && this._columns.length && this._advancedFilteringExpressionsTree) {
5,760✔
6659
            this._advancedFilteringExpressionsTree = this.getRecreatedTree(this._advancedFilteringExpressionsTree);
7✔
6660
        }
6661
        this.resetCaches();
5,760✔
6662
    }
6663

6664
    /**
6665
     * @hidden
6666
     */
6667
    protected _resetColumnList(list?) {
6668
        if (!list) {
138!
6669
            list = this._columns;
×
6670
        }
6671
        let newList = [];
138✔
6672
        list.filter(c => c.level === 0).forEach(p => {
1,189✔
6673
            newList.push(p);
814✔
6674
            if (p.columnGroup) {
814✔
6675
                newList = newList.concat(p.allChildren);
138✔
6676
            }
6677
        });
6678
        return newList;
138✔
6679
    }
6680

6681
    /**
6682
     * Reorders columns inside the passed column collection.
6683
     * When reordering column group collection, the collection is not flattened.
6684
     * In all other cases, the columns collection is flattened, this is why adittional calculations on the dropIndex are done.
6685
     *
6686
     * @hidden
6687
     */
6688
    protected _reorderColumns(from: IgxColumnComponent, to: IgxColumnComponent, position: DropPosition, columnCollection: any[],
6689
        inGroup = false) {
138✔
6690
        const fromIndex = columnCollection.indexOf(from);
154✔
6691
        const childColumnsCount = inGroup ? 1 : from.allChildren.length + 1;
154✔
6692
        columnCollection.splice(fromIndex, childColumnsCount);
154✔
6693
        let dropIndex = columnCollection.indexOf(to);
154✔
6694
        if (position === DropPosition.AfterDropTarget) {
154✔
6695
            dropIndex++;
87✔
6696
            if (!inGroup && to.columnGroup) {
87✔
6697
                dropIndex += to.allChildren.length;
21✔
6698
            }
6699
        }
6700
        columnCollection.splice(dropIndex, 0, from);
154✔
6701
    }
6702

6703
    /**
6704
     * Reorder column group collection.
6705
     *
6706
     * @hidden
6707
     */
6708
    protected _moveChildColumns(parent: IgxColumnComponent, from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6709
        const buffer = parent.children.toArray();
16✔
6710
        this._reorderColumns(from, to, pos, buffer, true);
16✔
6711
        parent.children.reset(buffer);
16✔
6712
    }
6713

6714
    /**
6715
     * @hidden @internal
6716
     */
6717
    protected setupColumns() {
6718
        if (this.autoGenerate) {
3,372✔
6719
            this.autogenerateColumns();
909✔
6720
        } else {
6721
            this._columns = this.getColumnList();
2,463✔
6722
        }
6723
        if (this._columns && this._columns.length && this._filteringExpressionsTree) {
3,372✔
6724
            this._filteringExpressionsTree = this.getRecreatedTree(this._filteringExpressionsTree);
3,315✔
6725
        }
6726
        if (this._columns && this._columns.length && this._advancedFilteringExpressionsTree) {
3,372✔
6727
            this._advancedFilteringExpressionsTree = this.getRecreatedTree(this._advancedFilteringExpressionsTree);
6✔
6728
        }
6729

6730
        this.initColumns(this._columns, (col: IgxColumnComponent) => this.columnInit.emit(col));
21,658✔
6731
        this.columnListDiffer.diff(this.columnList);
3,372✔
6732

6733
        this.columnList.changes
3,372✔
6734
            .pipe(takeUntil(this.destroy$))
6735
            .subscribe((change: QueryList<IgxColumnComponent>) => {
6736
                this.onColumnsChanged(change);
74✔
6737
            });
6738
    }
6739

6740
    protected getColumnList() {
6741
        return this.columnList.toArray();
2,263✔
6742
    }
6743

6744
    /**
6745
     * @hidden
6746
     */
6747
    protected deleteRowFromData(rowID: any, index: number) {
6748
        //  if there is a row (index !== 0) delete it
6749
        //  if there is a row in ADD or UPDATE state change it's state to DELETE
6750
        if (index !== -1) {
×
6751
            if (this.transactions.enabled) {
×
6752
                const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null };
×
6753
                this.transactions.add(transaction, this.data[index]);
×
6754
            } else {
6755
                this.data.splice(index, 1);
×
6756
            }
6757
        } else {
6758
            const state: State = this.transactions.getState(rowID);
×
6759
            this.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef);
×
6760
        }
6761
    }
6762

6763

6764
    /**
6765
     * @hidden @internal
6766
     */
6767
    protected getDataBasedBodyHeight(): number {
6768
        return !this.data || (this.data.length < this._defaultTargetRecordNumber) ?
1,057✔
6769
            0 : this.defaultTargetBodyHeight;
6770
    }
6771

6772
    /**
6773
     * @hidden @internal
6774
     */
6775
    protected onPinnedRowsChanged(change: QueryList<IgxGridRowComponent>) {
6776
        const diff = this.rowListDiffer.diff(change);
182✔
6777
        if (diff) {
182✔
6778
            this.notifyChanges(true);
182✔
6779
        }
6780
    }
6781

6782
    /**
6783
     * @hidden
6784
     */
6785
    protected onColumnsChanged(change: QueryList<IgxColumnComponent>) {
6786
        const diff = this.columnListDiffer.diff(change);
63✔
6787

6788
        if (this.autoGenerate && this._columns.length === 0 && this._autoGeneratedCols.length > 0) {
63!
6789
            // In Ivy if there are nested conditional templates the content children are re-evaluated
6790
            // hence autogenerated columns are cleared and need to be reset.
6791
            this.updateColumns(this._autoGeneratedCols);
×
6792
            return;
×
6793
        }
6794
        if (diff) {
63✔
6795
            let added = false;
63✔
6796
            let removed = false;
63✔
6797
            let pinning = false;
63✔
6798
            diff.forEachAddedItem((record: IterableChangeRecord<IgxColumnComponent>) => {
63✔
6799
                added = true;
1,031✔
6800
                if (record.item.pinned) {
1,031✔
6801
                    this._pinnedColumns.push(record.item);
1✔
6802
                    pinning = true;
1✔
6803
                } else {
6804
                    this._unpinnedColumns.push(record.item);
1,030✔
6805
                }
6806
            });
6807

6808
            this.initColumns(this.columnList.toArray(), (col: IgxColumnComponent) => this.columnInit.emit(col));
1,277✔
6809
            if (pinning) {
63✔
6810
                this.initPinning();
1✔
6811
            }
6812

6813
            diff.forEachRemovedItem((record: IterableChangeRecord<IgxColumnComponent | IgxColumnGroupComponent>) => {
63✔
6814
                const isColumnGroup = record.item instanceof IgxColumnGroupComponent;
130✔
6815
                if (!isColumnGroup) {
130✔
6816
                    // Clear Grouping
6817
                    this.gridAPI.clear_groupby(record.item.field);
127✔
6818

6819
                    // Clear Filtering
6820
                    this.filteringService.clear_filter(record.item.field);
127✔
6821

6822
                    // Close filter row
6823
                    if (this.filteringService.isFilterRowVisible
127!
6824
                        && this.filteringService.filteredColumn
6825
                        && this.filteringService.filteredColumn.field === record.item.field) {
6826
                        this.filteringRow.close();
×
6827
                    }
6828

6829
                    // Clear Sorting
6830
                    this.gridAPI.clear_sort(record.item.field);
127✔
6831

6832
                    // Remove column selection
6833
                    this.selectionService.deselectColumnsWithNoEvent([record.item.field]);
127✔
6834
                }
6835
                removed = true;
130✔
6836
            });
6837

6838
            this.resetCaches();
63✔
6839

6840
            if (added || removed) {
63✔
6841
                this.onColumnsAddedOrRemoved();
62✔
6842
            }
6843
        }
6844
    }
6845

6846
    protected checkPrimaryKeyField() {
6847
        if (this.primaryKey && this.data?.length && !(this.primaryKey in this.data[0])) {
4,680✔
6848
            console.warn(`Field "${this.primaryKey}" is not defined in the data. Set \`primaryKey\` to a valid field.`);
9✔
6849
        }
6850
    }
6851

6852
    /**
6853
     * @hidden @internal
6854
     */
6855
    protected onColumnsAddedOrRemoved() {
6856
        this.summaryService.clearSummaryCache();
62✔
6857
        Promise.resolve().then(() => {
62✔
6858
            // `onColumnsChanged` can be executed midway a current detectChange cycle and markForCheck will be ignored then.
6859
            // This ensures that we will wait for the current cycle to end so we can trigger a new one and ngDoCheck to fire.
6860
            this.notifyChanges(true);
62✔
6861
        });
6862
    }
6863

6864
    /**
6865
     * @hidden
6866
     */
6867
    protected calculateGridSizes(recalcFeatureWidth = true) {
7,419✔
6868
        /*
6869
            TODO: (R.K.) This layered lasagne should be refactored
6870
            ASAP. The reason I have to reset the caches so many times is because
6871
            after teach `detectChanges` call they are filled with invalid
6872
            state. Of course all of this happens midway through the grid
6873
            sizing process which of course, uses values from the caches, thus resulting
6874
            in a broken layout.
6875
        */
6876
        this.cdr.detectChanges();
7,916✔
6877
        this.resetCaches(recalcFeatureWidth);
7,916✔
6878
        const hasScroll = this.hasVerticalScroll();
7,916✔
6879
        const hasHScroll = !this.isHorizontalScrollHidden;
7,916✔
6880
        this.calculateGridWidth();
7,916✔
6881
        this.resetCaches(recalcFeatureWidth);
7,916✔
6882
        this.cdr.detectChanges();
7,916✔
6883
        this.calculateGridHeight();
7,916✔
6884

6885
        if (this.rowEditable) {
7,916✔
6886
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
881✔
6887
        }
6888

6889
        if (this.filteringService.isFilterRowVisible) {
7,916✔
6890
            this.filteringRow.resetChipsArea();
153✔
6891
        }
6892

6893
        this.cdr.detectChanges();
7,916✔
6894
        // in case scrollbar has appeared recalc to size correctly.
6895
        if (hasScroll !== this.hasVerticalScroll()) {
7,916✔
6896
            this.calculateGridWidth();
167✔
6897
            this.cdr.detectChanges();
167✔
6898
        }
6899

6900
        // in case horizontal scrollbar has appeared recalc to size correctly.
6901
        if (hasHScroll !== this.hasHorizontalScroll()) {
7,916✔
6902
            this.isHorizontalScrollHidden = !this.hasHorizontalScroll();
2,795✔
6903
            this.cdr.detectChanges();
2,795✔
6904
            this.calculateGridHeight();
2,795✔
6905
            this.cdr.detectChanges();
2,795✔
6906
        }
6907
        if (this.zone.isStable) {
7,916✔
6908
            this.zone.run(() => {
225✔
6909
                this._applyWidthHostBinding();
225✔
6910
                this.cdr.detectChanges();
225✔
6911
            });
6912
        } else {
6913
            this.zone.onStable.pipe(first()).subscribe(() => {
7,691✔
6914
                this.zone.run(() => {
7,691✔
6915
                    this._applyWidthHostBinding();
7,691✔
6916
                });
6917
            });
6918
        }
6919
        this.resetCaches(recalcFeatureWidth);
7,916✔
6920
        if (this.hasColumnsToAutosize) {
7,916✔
6921
            this.cdr.detectChanges();
20✔
6922
            this.zone.onStable.pipe(first()).subscribe(() => {
20✔
6923
                this._autoSizeColumnsNotify.next();
20✔
6924
            });
6925
        }
6926
    }
6927

6928
    /**
6929
     * @hidden
6930
     * Sets TBODY height i.e. this.calcHeight
6931
     */
6932
    protected calculateGridHeight() {
6933

6934
        this.calcHeight = this._calculateGridBodyHeight();
10,457✔
6935
        if (this.pinnedRowHeight && this.calcHeight) {
10,457✔
6936
            this.calcHeight -= this.pinnedRowHeight;
107✔
6937
        }
6938
    }
6939

6940
    /**
6941
     * @hidden
6942
     */
6943
    protected getGroupAreaHeight(): number {
6944
        return 0;
2,771✔
6945
    }
6946

6947
    /**
6948
     * @hidden
6949
     */
6950
    protected getComputedHeight(elem) {
6951
        return elem.offsetHeight ? parseFloat(this.document.defaultView.getComputedStyle(elem).getPropertyValue('height')) : 0;
37,167✔
6952
    }
6953
    /**
6954
     * @hidden
6955
     */
6956
    protected getFooterHeight(): number {
6957
        return this.summaryRowHeight || this.getComputedHeight(this.tfoot.nativeElement);
9,659✔
6958
    }
6959
    /**
6960
     * @hidden
6961
     */
6962
    protected getTheadRowHeight(): number {
6963
        // D.P.: Before CSS loads,theadRow computed height will be 'auto'->NaN, so use 0 fallback
6964
        const height = this.getComputedHeight(this.theadRow.nativeElement) || 0;
9,659✔
6965
        return (!this.allowFiltering || (this.allowFiltering && this.filterMode !== FilterMode.quickFilter)) ?
9,659✔
6966
            height - this.getFilterCellHeight() :
6967
            height;
6968
    }
6969

6970
    /**
6971
     * @hidden
6972
     */
6973
    protected getToolbarHeight(): number {
6974
        let toolbarHeight = 0;
9,659✔
6975
        if (this.toolbar.first) {
9,659✔
6976
            toolbarHeight = this.getComputedHeight(this.toolbar.first.nativeElement);
258✔
6977
        }
6978
        return toolbarHeight;
9,659✔
6979
    }
6980

6981
    /**
6982
     * @hidden
6983
     */
6984
    protected getPagingFooterHeight(): number {
6985
        let pagingHeight = 0;
9,659✔
6986
        if (this.footer) {
9,659✔
6987
            const height = this.getComputedHeight(this.footer.nativeElement);
9,659✔
6988
            pagingHeight = this.footer.nativeElement.firstElementChild ?
9,659✔
6989
                height : 0;
6990
        }
6991
        return pagingHeight;
9,659✔
6992
    }
6993

6994
    /**
6995
     * @hidden
6996
     */
6997
    protected getFilterCellHeight(): number {
6998
        const headerGroupNativeEl = (this.headerGroupsList.length !== 0) ?
7,966✔
6999
            this.headerGroupsList[0].nativeElement : null;
7000
        const filterCellNativeEl = (headerGroupNativeEl) ?
7,966✔
7001
            headerGroupNativeEl.querySelector('igx-grid-filtering-cell') as HTMLElement : null;
7002
        return (filterCellNativeEl) ? filterCellNativeEl.offsetHeight : 0;
7,966✔
7003
    }
7004

7005
    /**
7006
     * @hidden
7007
     */
7008
    protected _calculateGridBodyHeight(): number {
7009
        if (!this._height) {
10,457✔
7010
            return null;
798✔
7011
        }
7012
        const actualTheadRow = this.getTheadRowHeight();
9,659✔
7013
        const footerHeight = this.getFooterHeight();
9,659✔
7014
        const toolbarHeight = this.getToolbarHeight();
9,659✔
7015
        const pagingHeight = this.getPagingFooterHeight();
9,659✔
7016
        const groupAreaHeight = this.getGroupAreaHeight();
9,659✔
7017
        const scrHeight = this.getComputedHeight(this.scr.nativeElement);
9,659✔
7018
        const renderedHeight = toolbarHeight + actualTheadRow +
9,659✔
7019
            footerHeight + pagingHeight + groupAreaHeight +
7020
            scrHeight;
7021

7022
        let gridHeight = 0;
9,659✔
7023

7024
        if (this.isPercentHeight) {
9,659✔
7025
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('height');
2,084✔
7026
            const autoSize = this._shouldAutoSize(renderedHeight);
2,084✔
7027
            if (autoSize || computed.indexOf('%') !== -1) {
2,084✔
7028
                const bodyHeight = this.getDataBasedBodyHeight();
857✔
7029
                return bodyHeight > 0 ? bodyHeight : null;
857✔
7030
            }
7031
            gridHeight = parseFloat(computed);
1,227✔
7032
        } else {
7033
            gridHeight = parseInt(this._height, 10);
7,575✔
7034
        }
7035
        const height = Math.abs(gridHeight - renderedHeight);
8,802✔
7036

7037
        if (Math.round(height) === 0 || isNaN(gridHeight)) {
8,802✔
7038
            const bodyHeight = this.defaultTargetBodyHeight;
18✔
7039
            return bodyHeight > 0 ? bodyHeight : null;
18✔
7040
        }
7041
        return height;
8,784✔
7042
    }
7043

7044
    protected checkContainerSizeChange() {
7045
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
98!
7046
        const origHeight = parentElement.offsetHeight;
98✔
7047
        this.nativeElement.style.display = 'none';
98✔
7048
        const height = parentElement.offsetHeight;
98✔
7049
        this.nativeElement.style.display = '';
98✔
7050
        return origHeight !== height;
98✔
7051
    }
7052

7053
    protected _shouldAutoSize(renderedHeight) {
7054
        this.tbody.nativeElement.style.display = 'none';
1,342✔
7055
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
1,342✔
7056
        let res = !parentElement ||
1,342✔
7057
            parentElement.clientHeight === 0 ||
7058
            parentElement.clientHeight === renderedHeight;
7059
        if (parentElement && (res || this._autoSize)) {
1,342✔
7060
            // If grid causes the parent container to extend (for example when container is flex)
7061
            // we should always auto-size since the actual size of the container will continuously change as the grid renders elements.
7062
            this._autoSize = false;
98✔
7063
            res = this.checkContainerSizeChange();
98✔
7064
        }
7065
        this.tbody.nativeElement.style.display = '';
1,342✔
7066
        return res;
1,342✔
7067
    }
7068

7069
    /**
7070
     * @hidden
7071
     * Gets calculated width of the unpinned area
7072
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
7073
     */
7074
    protected getUnpinnedWidth(takeHidden = false) {
23,525✔
7075
        let width = this.isPercentWidth ?
23,525✔
7076
            this.calcWidth :
7077
            parseInt(this.width, 10) || parseInt(this.hostWidth, 10) || this.calcWidth;
13,254✔
7078
        if (this.hasVerticalScroll() && !this.isPercentWidth) {
23,525✔
7079
            width -= this.scrollSize;
4,341✔
7080
        }
7081
        if (!this.isPinningToStart) {
23,525✔
7082
            width -= this.featureColumnsWidth();
62✔
7083
        }
7084

7085
        return width - this.getPinnedWidth(takeHidden);
23,525✔
7086
    }
7087

7088
    /**
7089
     * @hidden
7090
     */
7091
    protected _summaries(fieldName: string, hasSummary: boolean, summaryOperand?: any) {
7092
        const column = this.gridAPI.get_column_by_name(fieldName);
27✔
7093
        if (column) {
27✔
7094
            column.hasSummary = hasSummary;
27✔
7095
            if (summaryOperand) {
27✔
7096
                if (this.rootSummariesEnabled) {
2✔
7097
                    this.summaryService.retriggerRootPipe++;
2✔
7098
                }
7099
                column.summaries = summaryOperand;
2✔
7100
            }
7101
        }
7102
    }
7103

7104
    /**
7105
     * @hidden
7106
     */
7107
    protected _multipleSummaries(expressions: ISummaryExpression[], hasSummary: boolean) {
7108
        expressions.forEach((element) => {
7✔
7109
            this._summaries(element.fieldName, hasSummary, element.customSummary);
9✔
7110
        });
7111
    }
7112
    /**
7113
     * @hidden
7114
     */
7115
    protected _disableMultipleSummaries(expressions) {
7116
        expressions.forEach((column) => {
5✔
7117
            const columnName = column && column.fieldName ? column.fieldName : column;
13✔
7118
            this._summaries(columnName, false);
13✔
7119
        });
7120
    }
7121

7122
    /**
7123
     * @hidden
7124
     */
7125
    public resolveDataTypes(rec) {
7126
        if (typeof rec === 'number') {
6,514✔
7127
            return GridColumnDataType.Number;
3,850✔
7128
        } else if (typeof rec === 'boolean') {
2,664✔
7129
            return GridColumnDataType.Boolean;
152✔
7130
        } else if (typeof rec === 'object' && rec instanceof Date) {
2,512✔
7131
            return GridColumnDataType.Date;
143✔
7132
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
2,369✔
7133
            return GridColumnDataType.Image;
1✔
7134
        }
7135
        return GridColumnDataType.String;
2,368✔
7136
    }
7137

7138
    /**
7139
     * @hidden
7140
     */
7141
    protected autogenerateColumns() {
7142
        const data = this.gridAPI.get_data();
663✔
7143
        const fields = this.generateDataFields(data);
663✔
7144
        const columns = [];
663✔
7145

7146
        this._autoGeneratedColsRefs.forEach(ref => ref.destroy());
663✔
7147
        this._autoGeneratedColsRefs = [];
663✔
7148
        fields.forEach((field) => {
663✔
7149
            const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
4,273✔
7150
            ref.instance.field = field;
4,273✔
7151
            ref.instance.dataType = this.resolveDataTypes(data[0][field]);
4,273✔
7152
            ref.changeDetectorRef.detectChanges();
4,273✔
7153
            this._autoGeneratedColsRefs.push(ref);
4,273✔
7154
            columns.push(ref.instance);
4,273✔
7155
        });
7156
        this._autoGeneratedCols = columns;
663✔
7157

7158
        this.updateColumns(columns);
663✔
7159
        this.columnsAutogenerated.emit({ columns: this._autoGeneratedCols });
663✔
7160
    }
7161

7162
    protected generateDataFields(data: any[]): string[] {
7163
        return Object.keys(data && data.length !== 0 ? data[0] : [])
669✔
7164
            .filter(key => !this.autoGenerateExclude.includes(key));
4,549✔
7165
    }
7166

7167
    /**
7168
     * @hidden
7169
     */
7170
    protected initColumns(collection: IgxColumnComponent[], cb: (args: any) => void = null) {
×
7171
        this._columnGroups = collection.some(col => col.columnGroup);
17,597✔
7172
        if (this.hasColumnLayouts) {
3,645✔
7173
            // Set overall row layout size
7174
            collection.forEach((col) => {
142✔
7175
                if (col.columnLayout) {
1,371✔
7176
                    const layoutSize = col.children ?
285!
7177
                        col.children.reduce((acc, val) => Math.max(val.rowStart + val.gridRowSpan - 1, acc), 1) :
1,076✔
7178
                        1;
7179
                    this._multiRowLayoutRowSize = Math.max(layoutSize, this._multiRowLayoutRowSize);
285✔
7180
                }
7181
            });
7182
        }
7183
        if (this.hasColumnLayouts && this.hasColumnGroups) {
3,645✔
7184
            // invalid configuration - multi-row and column groups
7185
            // remove column groups
7186
            const columnLayoutColumns = collection.filter((col) => col.columnLayout || col.columnLayoutChild);
1,371✔
7187
            collection = columnLayoutColumns;
142✔
7188
        }
7189
        this._maxLevelHeaderDepth = null;
3,645✔
7190
        collection.forEach((column: IgxColumnComponent) => {
3,645✔
7191
            column.defaultWidth = this.columnWidthSetByUser ? this._columnWidth : column.defaultWidth ? column.defaultWidth : '';
23,632✔
7192

7193
            if (cb) {
23,632✔
7194
                cb(column);
23,632✔
7195
            }
7196
        });
7197

7198
        this.updateColumns(collection);
3,645✔
7199

7200
        if (this.hasColumnLayouts) {
3,645✔
7201
            collection.forEach((column: IgxColumnComponent) => {
142✔
7202
                column.populateVisibleIndexes();
1,361✔
7203
            });
7204
        }
7205
    }
7206

7207
    /**
7208
     * @hidden
7209
     */
7210
    protected reinitPinStates() {
7211
        this._pinnedColumns = this._columns
206✔
7212
            .filter((c) => c.pinned).sort((a, b) => this._pinnedColumns.indexOf(a) - this._pinnedColumns.indexOf(b));
2,559✔
7213
        this._unpinnedColumns = this.hasColumnGroups ? this._columns.filter((c) => !c.pinned) :
2,259✔
7214
            this._columns.filter((c) => !c.pinned)
300✔
7215
                .sort((a, b) => this._unpinnedColumns.indexOf(a) - this._unpinnedColumns.indexOf(b));
214✔
7216
    }
7217

7218
    protected extractDataFromSelection(source: any[], formatters = false, headers = false, columnData?: any[]): any[] {
×
7219
        let columnsArray: IgxColumnComponent[];
7220
        let record = {};
246✔
7221
        let selectedData = [];
246✔
7222
        let keys = [];
246✔
7223
        const selectionCollection = new Map();
246✔
7224
        const keysAndData = [];
246✔
7225
        const activeEl = this.selectionService.activeElement;
246✔
7226

7227
        if (this.type === 'hierarchical') {
246✔
7228
            const expansionRowIndexes = [];
2✔
7229
            for (const [key, value] of this.expansionStates.entries()) {
2✔
7230
                if (value) {
×
7231
                    const rowIndex = this.gridAPI.get_rec_index_by_id(key, this.dataView);
×
7232
                    expansionRowIndexes.push(rowIndex);
×
7233
                }
7234
            }
7235
            if (this.selectionService.selection.size > 0) {
2!
7236
                if (expansionRowIndexes.length > 0) {
2!
7237
                    for (const [key, value] of this.selectionService.selection.entries()) {
×
7238
                        const updatedKey = key;
×
7239
                        let subtract = 0;
×
7240
                        expansionRowIndexes.forEach((row) => {
×
7241
                            if (updatedKey > Number(row)) {
×
7242
                                subtract++;
×
7243
                            }
7244
                        });
7245
                        selectionCollection.set(updatedKey - subtract, value);
×
7246
                    }
7247
                }
7248
            } else if (activeEl) {
×
7249
                let subtract = 0;
×
7250
                if (expansionRowIndexes.length > 0) {
×
7251
                    expansionRowIndexes.forEach(row => {
×
7252
                        if (activeEl.row > Number(row)) {
×
7253
                            subtract++;
×
7254
                        }
7255
                    });
7256
                    activeEl.row -= subtract;
×
7257
                }
7258
            }
7259
        }
7260

7261
        const totalItems = (this as any).totalItemCount ?? 0;
246✔
7262
        const isRemote = totalItems && totalItems > this.dataView.length;
246!
7263
        let selectionMap;
7264
        if (this.type === 'hierarchical' && selectionCollection.size > 0) {
246!
7265
            selectionMap = isRemote ? Array.from(selectionCollection) :
×
7266
                Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length);
×
7267
        } else {
7268
            selectionMap = isRemote ? Array.from(this.selectionService.selection) :
246!
7269
                Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length);
883✔
7270
        }
7271

7272
        if (this.cellSelection === GridSelectionMode.single && activeEl) {
246✔
7273
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
10✔
7274
        }
7275

7276
        if (this.cellSelection === GridSelectionMode.none && activeEl) {
246✔
7277
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
5✔
7278
        }
7279

7280
        if (columnData) {
246!
7281
            selectedData = columnData;
×
7282
        }
7283

7284
        // eslint-disable-next-line prefer-const
7285
        for (let [row, set] of selectionMap) {
246✔
7286
            row = this.paginator && (this.pagingMode === 'local' && source === this.filteredSortedData) ? row + (this.perPage * this.page) : row;
788✔
7287
            row = isRemote ? row - this.virtualizationState.startIndex : row;
788!
7288
            if (!source[row] || source[row].detailsData !== undefined) {
788✔
7289
                continue;
40✔
7290
            }
7291
            const temp = Array.from(set);
748✔
7292
            for (const each of temp) {
748✔
7293
                columnsArray = this.getSelectableColumnsAt(each);
2,512✔
7294
                columnsArray.forEach((col) => {
2,512✔
7295
                    if (col) {
2,518✔
7296
                        const key = this.type !== 'pivot' && headers ? col.header || col.field : col.field;
2,316!
7297
                        const rowData = source[row].ghostRecord ? source[row].recordRef : source[row];
2,316!
7298
                        const value = this.type === 'pivot' ? rowData.aggregationValues.get(col.field)
2,316!
7299
                            : resolveNestedPath(rowData, col.field);
7300
                        record[key] = formatters && col.formatter ? col.formatter(value, rowData) : value;
2,316✔
7301
                        if (columnData) {
2,316!
7302
                            if (!record[key]) {
×
7303
                                record[key] = '';
×
7304
                            }
7305
                            record[key] = record[key].toString().concat('recordRow-' + row);
×
7306
                        }
7307
                    }
7308
                });
7309
            }
7310
            if (Object.keys(record).length) {
748✔
7311
                if (columnData) {
746!
7312
                    if (!keys.length) {
×
7313
                        keys = Object.keys(columnData[0]);
×
7314
                    }
7315
                    for (const [key, value] of Object.entries(record)) {
×
7316
                        if (!keys.includes(key)) {
×
7317
                            keys.push(key);
×
7318
                        }
7319
                        let c: any = value;
×
7320
                        const rowNumber = +c.split('recordRow-')[1];
×
7321
                        c = c.split('recordRow-')[0];
×
7322
                        record[key] = c;
×
7323
                        const mergedObj = Object.assign(selectedData[rowNumber], record);
×
7324
                        selectedData[rowNumber] = mergedObj;
×
7325
                    }
7326
                } else {
7327
                    selectedData.push(record);
746✔
7328
                }
7329
            }
7330
            record = {};
748✔
7331
        }
7332

7333
        if (keys.length) {
246!
7334
            keysAndData.push(selectedData);
×
7335
            keysAndData.push(keys);
×
7336
            return keysAndData;
×
7337
        } else {
7338
            return selectedData;
246✔
7339
        }
7340
    }
7341

7342
    protected getSelectableColumnsAt(index) {
7343
        if (this.hasColumnLayouts) {
2,512✔
7344
            const visibleLayoutColumns = this.visibleColumns
2✔
7345
                .filter(col => col.columnLayout)
20✔
7346
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
2✔
7347
            const colLayout = visibleLayoutColumns[index];
2✔
7348
            return colLayout ? colLayout.children.toArray() : [];
2!
7349
        } else {
7350
            const visibleColumns = this.visibleColumns
2,510✔
7351
                .filter(col => !col.columnGroup)
14,120✔
7352
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
11,790✔
7353
            return [visibleColumns[index]];
2,510✔
7354
        }
7355
    }
7356

7357
    protected autoSizeColumnsInView() {
7358
        if (!this.hasColumnsToAutosize) return;
371✔
7359
        const vState = this.headerContainer.state;
29✔
7360
        let colResized = false;
29✔
7361
        const unpinnedInView = this.headerContainer.igxGridForOf.slice(vState.startIndex, vState.startIndex + vState.chunkSize).flatMap(x => x.columnGroup ? x.allChildren : x);
117✔
7362
        const columnsInView = this.pinnedColumns.concat(unpinnedInView as IgxColumnComponent[]);
29✔
7363
        for (const col of columnsInView) {
29✔
7364
            if (!col.autoSize && col.headerCell) {
120✔
7365
                const cellsContentWidths = [];
84✔
7366
                if (col._cells.length !== this.rowList.length) {
84!
7367
                    this.rowList.forEach(x => x.cdr.detectChanges());
×
7368
                }
7369
                const cells = this._dataRowList.map(x => x.cells.find(c => c.column === col));
2,397✔
7370
                cells.forEach((cell) => cellsContentWidths.push(cell?.nativeElement?.offsetWidth || 0));
606!
7371
                let maxForCells = Math.max(...cellsContentWidths);
84✔
7372
                const header = this.headerCellList.find(x => x.column === col);
316✔
7373
                cellsContentWidths.push(header.nativeElement.offsetWidth);
84✔
7374
                const max = Math.max(...cellsContentWidths);
84✔
7375
                // in cases with template contains something, like a webcomponent,
7376
                // that renders fully only after it is already injected in the DOM,
7377
                // and initially renders as empty, skip measuring it.
7378
                let emptyCellWithPaddingOnly = 0;
84✔
7379
                if (cells.length > 0 && !!col.bodyTemplate) {
84✔
7380
                    const cellStyle = this.document.defaultView.getComputedStyle(cells[0].nativeElement);
1✔
7381
                    emptyCellWithPaddingOnly = parseFloat(cellStyle.paddingLeft) + parseFloat(cellStyle.paddingRight);
1✔
7382
                } else {
7383
                    maxForCells = max;
83✔
7384
                }
7385

7386
                if (max === 0 || (maxForCells <= emptyCellWithPaddingOnly && this._firstAutoResize)) {
84!
7387
                    // cells not in DOM yet or content not fully initialized.
7388
                    continue;
6✔
7389
                }
7390
                let maxSize = Math.ceil(Math.max(...cellsContentWidths)) + 1;
78✔
7391
                if (col.maxWidth && maxSize > col.maxWidthPx) {
78✔
7392
                    maxSize = col.maxWidthPx;
3✔
7393
                } else if (maxSize < col.userSetMinWidthPx) {
75✔
7394
                    maxSize = col.userSetMinWidthPx;
1✔
7395
                }
7396
                col.autoSize = maxSize;
78✔
7397
                col.resetCaches();
78✔
7398
                colResized = true;
78✔
7399
            }
7400
        }
7401
        if (colResized) {
29✔
7402
            this.resetCachedWidths();
20✔
7403
            this.cdr.detectChanges();
20✔
7404
        }
7405

7406
        if (this.isColumnWidthSum) {
29✔
7407
            this.calcWidth = this.getColumnWidthSum();
2✔
7408
        }
7409
    }
7410

7411
    protected extractDataFromColumnsSelection(source: any[], formatters = false, headers = false): any[] {
×
7412
        let record = {};
20✔
7413
        const selectedData = [];
20✔
7414
        const selectedColumns = this.selectedColumns();
20✔
7415
        if (selectedColumns.length === 0) {
20✔
7416
            return [];
12✔
7417
        }
7418

7419
        for (const data of source) {
8✔
7420
            selectedColumns.forEach((col) => {
71✔
7421
                const key = headers ? col.header || col.field : col.field;
142!
7422
                record[key] = formatters && col.formatter ? col.formatter(data[col.field], data)
142!
7423
                    : data[col.field];
7424
            });
7425

7426
            if (Object.keys(record).length) {
71✔
7427
                selectedData.push(record);
71✔
7428
            }
7429
            record = {};
71✔
7430
        }
7431
        return selectedData;
8✔
7432
    }
7433

7434
    /**
7435
     * @hidden
7436
     */
7437
    protected initPinning() {
7438
        this.calculateGridWidth();
3,937✔
7439
        this.resetCaches();
3,937✔
7440
        this.handleColumnPinningForGroups();
3,937✔
7441
        this.notifyChanges();
3,937✔
7442
    }
7443

7444
    /**
7445
     * @hidden
7446
     */
7447
    protected scrollTo(row: any | number, column: any | number, inCollection = this._filteredSortedUnpinnedData): void {
3✔
7448
        let delayScrolling = false;
106✔
7449

7450
        if (this.paginator && typeof (row) !== 'number') {
106✔
7451
            const rowIndex = inCollection.indexOf(row);
16✔
7452
            const page = Math.floor(rowIndex / this.perPage);
16✔
7453

7454
            if (this.page !== page) {
16✔
7455
                delayScrolling = true;
5✔
7456
                this.page = page;
5✔
7457
            }
7458
        }
7459

7460
        if (delayScrolling) {
106✔
7461
            this.verticalScrollContainer.dataChanged.pipe(first(), takeUntil(this.destroy$)).subscribe(() => {
5✔
7462
                this.scrollDirective(this.verticalScrollContainer,
5✔
7463
                    typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
5!
7464
            });
7465
        } else {
7466
            this.scrollDirective(this.verticalScrollContainer,
101✔
7467
                typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
101✔
7468
        }
7469

7470
        this.scrollToHorizontally(column);
106✔
7471
    }
7472

7473
    /**
7474
     * @hidden
7475
     */
7476
    protected scrollToHorizontally(column: any | number) {
7477
        let columnIndex = typeof column === 'number' ? column : this.getColumnByName(column).visibleIndex;
191✔
7478
        const scrollRow = this.rowList.find(r => !!r.virtDirRow);
233✔
7479
        const virtDir = scrollRow ? scrollRow.virtDirRow : null;
191✔
7480
        if (this.isPinningToStart && this.pinnedColumns.length) {
191✔
7481
            if (columnIndex >= this.pinnedColumns.length) {
1!
7482
                columnIndex -= this.pinnedColumns.length;
×
7483
                this.scrollDirective(virtDir, columnIndex);
×
7484
            }
7485
        } else {
7486
            this.scrollDirective(virtDir, columnIndex);
190✔
7487
        }
7488
    }
7489

7490
    /**
7491
     * @hidden
7492
     */
7493
    protected scrollDirective(directive: IgxGridForOfDirective<any, any[]>, goal: number): void {
7494
        if (!directive) {
381✔
7495
            return;
1✔
7496
        }
7497
        directive.scrollTo(goal);
380✔
7498
    }
7499

7500

7501
    /**
7502
     * @hidden
7503
     */
7504
    protected getColumnWidthSum(): number {
7505
        let colSum = 0;
34✔
7506
        const cols = this.hasColumnLayouts ?
34!
7507
            this.visibleColumns.filter(x => x.columnLayout) : this.visibleColumns.filter(x => !x.columnGroup);
234✔
7508
        cols.forEach((item) => {
34✔
7509
            colSum += parseInt((item.calcWidth || item.defaultWidth), 10) || this.minColumnWidth;
201!
7510
        });
7511
        if (!colSum) {
34!
7512
            return null;
×
7513
        }
7514
        this.cdr.detectChanges();
34✔
7515
        colSum += this.featureColumnsWidth();
34✔
7516
        return colSum;
34✔
7517
    }
7518

7519
    /**
7520
     * Notify changes, reset cache and populateVisibleIndexes.
7521
     *
7522
     * @hidden
7523
     */
7524
    private _columnsReordered(column: IgxColumnComponent) {
7525
        this.notifyChanges();
128✔
7526
        // after reordering is done reset cached column collections.
7527
        this.resetColumnCollections();
128✔
7528
        column.resetCaches();
128✔
7529
    }
7530

7531
    protected buildDataView(_data: any[]) {
7532
        this._dataView = this.isRowPinningToTop ?
13,658✔
7533
            [...this.pinnedDataView, ...this.unpinnedDataView] :
7534
            [...this.unpinnedDataView, ...this.pinnedDataView];
7535
    }
7536

7537
    private _applyWidthHostBinding() {
7538
        let width = this._width;
7,916✔
7539
        if (width === null) {
7,916✔
7540
            let currentWidth = this.calcWidth;
2✔
7541
            if (this.hasVerticalScroll()) {
2!
7542
                currentWidth += this.scrollSize;
×
7543
            }
7544
            width = currentWidth + 'px';
2✔
7545
            this.resetCaches();
2✔
7546
        }
7547
        this._hostWidth = width;
7,916✔
7548
        this.cdr.markForCheck();
7,916✔
7549
    }
7550

7551
    protected verticalScrollHandler(event) {
7552
        this.verticalScrollContainer.onScroll(event);
315✔
7553
        this.disableTransitions = true;
315✔
7554

7555
        this.zone.run(() => {
315✔
7556
            this.zone.onStable.pipe(first()).subscribe(() => {
315✔
7557
                this.verticalScrollContainer.chunkLoad.emit(this.verticalScrollContainer.state);
315✔
7558
                if (this.rowEditable) {
315✔
7559
                    this.changeRowEditingOverlayStateOnScroll(this.crudService.rowInEditMode);
12✔
7560
                }
7561
            });
7562
        });
7563
        this.disableTransitions = false;
315✔
7564

7565
        this.hideOverlays();
315✔
7566
        this.actionStrip?.hide();
315✔
7567
        if (this.actionStrip) {
315✔
7568
            this.actionStrip.context = null;
7✔
7569
        }
7570
        const args: IGridScrollEventArgs = {
315✔
7571
            direction: 'vertical',
7572
            event,
7573
            scrollPosition: this.verticalScrollContainer.scrollPosition
7574
        };
7575
        this.gridScroll.emit(args);
315✔
7576
    }
7577

7578
    protected horizontalScrollHandler(event) {
7579
        const scrollLeft = event.target.scrollLeft;
308✔
7580
        this.headerContainer.onHScroll(scrollLeft);
308✔
7581
        this._horizontalForOfs.forEach(vfor => vfor.onHScroll(scrollLeft));
1,940✔
7582
        this.cdr.markForCheck();
308✔
7583

7584
        this.zone.run(() => {
308✔
7585
            this.zone.onStable.pipe(first()).subscribe(() => {
308✔
7586
                this.parentVirtDir.chunkLoad.emit(this.headerContainer.state);
306✔
7587
                requestAnimationFrame(() => {
306✔
7588
                    this.autoSizeColumnsInView();
306✔
7589
                });
7590
            });
7591
        });
7592
        if (!this.navigation.isColumnFullyVisible(this.navigation.lastColumnIndex)) {
308✔
7593
            this.hideOverlays();
207✔
7594
        }
7595
        const args: IGridScrollEventArgs = { direction: 'horizontal', event, scrollPosition: this.headerContainer.scrollPosition };
308✔
7596
        this.gridScroll.emit(args);
308✔
7597
    }
7598

7599
    protected get renderedActualRowHeight() {
7600
        let border = 1;
503✔
7601
        if (this.rowList.toArray().length > 0) {
503✔
7602
            const rowStyles = this.document.defaultView.getComputedStyle(this.rowList.first.nativeElement);
353✔
7603
            border = rowStyles.borderBottomWidth ? Math.ceil(parseFloat(rowStyles.borderBottomWidth)) : border;
353✔
7604
        }
7605
        return this.rowHeight + border;
503✔
7606
    }
7607

7608
    private executeCallback(rowIndex, visibleColIndex = -1, cb: (args: any) => void = null) {
×
7609
        if (!cb) {
830✔
7610
            return;
280✔
7611
        }
7612
        let row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
1,618✔
7613
        if (!row) {
550✔
7614
            if ((this as any).totalItemCount) {
8!
7615
                this.verticalScrollContainer.dataChanged.pipe(first(), takeUntil(this.destroy$)).subscribe(() => {
×
7616
                    this.cdr.detectChanges();
×
7617
                    row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
×
7618
                    const cbArgs = this.getNavigationArguments(row, visibleColIndex);
×
7619
                    cb(cbArgs);
×
7620
                });
7621
            }
7622
            const dataViewIndex = this._getDataViewIndex(rowIndex);
8✔
7623
            if (this.dataView[dataViewIndex].detailsData) {
8✔
7624
                this.navigation.setActiveNode({ row: rowIndex });
8✔
7625
                this.cdr.detectChanges();
8✔
7626
            }
7627

7628
            return;
8✔
7629
        }
7630
        const args = this.getNavigationArguments(row, visibleColIndex);
542✔
7631
        cb(args);
542✔
7632
    }
7633

7634
    private getNavigationArguments(row, visibleColIndex) {
7635
        let targetType: GridKeydownTargetType; let target;
7636
        switch (row.nativeElement.tagName.toLowerCase()) {
542!
7637
            case 'igx-grid-groupby-row':
7638
                targetType = 'groupRow';
30✔
7639
                target = row;
30✔
7640
                break;
30✔
7641
            case 'igx-grid-summary-row':
7642
                targetType = 'summaryCell';
50✔
7643
                target = visibleColIndex !== -1 ?
50!
7644
                    row.summaryCells.find(c => c.visibleColumnIndex === visibleColIndex) : row.summaryCells.first;
139✔
7645
                break;
50✔
7646
            case 'igx-child-grid-row':
7647
                targetType = 'hierarchicalRow';
×
7648
                target = row;
×
7649
                break;
×
7650
            default:
7651
                targetType = 'dataCell';
462✔
7652
                target = visibleColIndex !== -1 ? row.cells.find(c => c.visibleColumnIndex === visibleColIndex) : row.cells.first;
1,285!
7653
                break;
462✔
7654
        }
7655
        return { targetType, target };
542✔
7656
    }
7657

7658
    private getNextDataRowIndex(currentRowIndex, previous = false): number {
15✔
7659
        const resolvedIndex = this._getDataViewIndex(currentRowIndex);
30✔
7660
        if (currentRowIndex < 0 || (currentRowIndex === 0 && previous) || (resolvedIndex >= this.dataView.length - 1 && !previous)) {
30!
7661
            return currentRowIndex;
7✔
7662
        }
7663
        // find next/prev record that is editable.
7664
        const nextRowIndex = previous ? this.findPrevEditableDataRowIndex(currentRowIndex) :
23✔
7665
            this.dataView.findIndex((_rec, index) =>
7666
                index > resolvedIndex && this.isEditableDataRecordAtIndex(index));
56✔
7667
        const nextDataIndex = this.getDataIndex(nextRowIndex);
23✔
7668
        return nextDataIndex !== -1 ? nextDataIndex : currentRowIndex;
23!
7669
    }
7670

7671
    /**
7672
     * Returns the previous editable row index or -1 if no such row is found.
7673
     *
7674
     * @param currentIndex The index of the current editable record.
7675
     */
7676
    private findPrevEditableDataRowIndex(currentIndex): number {
7677
        let i = this.dataView.length;
8✔
7678
        const resolvedIndex = this._getDataViewIndex(currentIndex);
8✔
7679
        while (i--) {
8✔
7680
            if (i < resolvedIndex && this.isEditableDataRecordAtIndex(i)) {
178✔
7681
                return i;
8✔
7682
            }
7683
        }
7684
        return -1;
×
7685
    }
7686

7687

7688
    /**
7689
     * Returns if the record at the specified data view index is a an editable data record.
7690
     * If record is group rec, summary rec, child rec, ghost rec. etc. it is not editable.
7691
     *
7692
     * @param dataViewIndex The index of that record in the data view.
7693
     *
7694
     */
7695
    // TODO: Consider moving it into CRUD
7696
    private isEditableDataRecordAtIndex(dataViewIndex) {
7697
        const rec = this.dataView[dataViewIndex];
29✔
7698
        return !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData &&
29✔
7699
            !this.isGhostRecordAtIndex(dataViewIndex);
7700
    }
7701

7702
    /**
7703
     * Returns if the record at the specified data view index is a ghost.
7704
     * If record is pinned but is not in pinned area then it is a ghost record.
7705
     *
7706
     * @param dataViewIndex The index of that record in the data view.
7707
     */
7708
    private isGhostRecordAtIndex(dataViewIndex) {
7709
        const isPinned = this.isRecordPinned(this.dataView[dataViewIndex]);
27✔
7710
        const isInPinnedArea = this.isRecordPinnedByViewIndex(dataViewIndex);
27✔
7711
        return isPinned && !isInPinnedArea;
27✔
7712
    }
7713

7714
    private isValidPosition(rowIndex, colIndex): boolean {
7715
        const rows = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).length;
97✔
7716
        const cols = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0 && !col.hidden).length;
777✔
7717
        if (rows < 1 || cols < 1) {
97✔
7718
            return false;
2✔
7719
        }
7720
        if (rowIndex > -1 && rowIndex < this.dataView.length &&
95✔
7721
            colIndex > - 1 && colIndex <= Math.max(...this.visibleColumns.map(c => c.visibleIndex))) {
694✔
7722
            return true;
93✔
7723
        }
7724
        return false;
2✔
7725
    }
7726

7727
    private find(text: string, increment: number, caseSensitive?: boolean, exactMatch?: boolean, scroll?: boolean, endEdit = true) {
195✔
7728
        if (!this.rowList) {
322!
7729
            return 0;
×
7730
        }
7731

7732
        if (endEdit) {
322✔
7733
            this.crudService.endEdit(false);
283✔
7734
        }
7735

7736
        if (!text) {
322!
7737
            this.clearSearch();
×
7738
            return 0;
×
7739
        }
7740

7741
        const caseSensitiveResolved = caseSensitive ? true : false;
322✔
7742
        const exactMatchResolved = exactMatch ? true : false;
322✔
7743
        let rebuildCache = false;
322✔
7744

7745
        if (this._lastSearchInfo.searchText !== text ||
322✔
7746
            this._lastSearchInfo.caseSensitive !== caseSensitiveResolved ||
7747
            this._lastSearchInfo.exactMatch !== exactMatchResolved) {
7748
            this._lastSearchInfo = {
84✔
7749
                searchText: text,
7750
                activeMatchIndex: 0,
7751
                caseSensitive: caseSensitiveResolved,
7752
                exactMatch: exactMatchResolved,
7753
                matchInfoCache: [],
7754
                matchCount: 0,
7755
                content: ''
7756
            };
7757

7758
            rebuildCache = true;
84✔
7759
        } else {
7760
            this._lastSearchInfo.activeMatchIndex += increment;
238✔
7761
        }
7762

7763
        if (rebuildCache) {
322✔
7764
            this.rowList.forEach((row) => {
84✔
7765
                if (row.cells) {
784✔
7766
                    row.cells.forEach((c: IgxGridCellComponent) => {
740✔
7767
                        c.highlightText(text, caseSensitiveResolved, exactMatchResolved);
3,097✔
7768
                    });
7769
                }
7770
            });
7771

7772
            this.rebuildMatchCache();
84✔
7773
        }
7774

7775
        if (this._lastSearchInfo.activeMatchIndex >= this._lastSearchInfo.matchCount) {
322✔
7776
            this._lastSearchInfo.activeMatchIndex = 0;
28✔
7777
        } else if (this._lastSearchInfo.activeMatchIndex < 0) {
294✔
7778
            this._lastSearchInfo.activeMatchIndex = this._lastSearchInfo.matchCount - 1;
8✔
7779
        }
7780

7781
        if (this._lastSearchInfo.matchCount > 0) {
322✔
7782
            const matchInfo = this._lastSearchInfo.matchInfoCache[this._lastSearchInfo.activeMatchIndex];
304✔
7783
            this._lastSearchInfo = { ...this._lastSearchInfo };
304✔
7784

7785
            if (scroll !== false) {
304✔
7786
                this.scrollTo(matchInfo.row, matchInfo.column);
182✔
7787
            }
7788

7789
            this.textHighlightService.setActiveHighlight(this.id, {
304✔
7790
                column: matchInfo.column,
7791
                row: matchInfo.row,
7792
                index: matchInfo.index,
7793
                metadata: matchInfo.metadata,
7794
            });
7795

7796
        } else {
7797
            this.textHighlightService.clearActiveHighlight(this.id);
18✔
7798
        }
7799

7800
        return this._lastSearchInfo.matchCount;
322✔
7801
    }
7802

7803
    private rebuildMatchCache() {
7804
        this._lastSearchInfo.matchInfoCache = [];
211✔
7805

7806
        const caseSensitive = this._lastSearchInfo.caseSensitive;
211✔
7807
        const exactMatch = this._lastSearchInfo.exactMatch;
211✔
7808
        const searchText = caseSensitive ? this._lastSearchInfo.searchText : this._lastSearchInfo.searchText.toLowerCase();
211✔
7809
        const data = this.filteredSortedData;
211✔
7810
        const columnItems = this.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex);
908✔
7811

7812
        data.forEach((dataRow, rowIndex) => {
211✔
7813
            columnItems.forEach((c) => {
3,474✔
7814
                const pipeArgs = this.getColumnByName(c.field).pipeArgs;
14,791✔
7815
                const value = c.formatter ? c.formatter(resolveNestedPath(dataRow, c.field), dataRow) :
14,791!
7816
                    c.dataType === 'number' ? formatNumber(resolveNestedPath(dataRow, c.field), this.locale, pipeArgs.digitsInfo) :
14,791✔
7817
                        c.dataType === 'date'
11,115✔
7818
                            ? formatDate(resolveNestedPath(dataRow, c.field), pipeArgs.format, this.locale, pipeArgs.timezone)
7819
                            : resolveNestedPath(dataRow, c.field);
7820
                if (value !== undefined && value !== null && c.searchable) {
14,791✔
7821
                    let searchValue = caseSensitive ? String(value) : String(value).toLowerCase();
14,576✔
7822

7823
                    if (exactMatch) {
14,576✔
7824
                        if (searchValue === searchText) {
536✔
7825
                            const mic: IMatchInfoCache = {
9✔
7826
                                row: dataRow,
7827
                                column: c.field,
7828
                                index: 0,
7829
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7830
                            };
7831

7832
                            this._lastSearchInfo.matchInfoCache.push(mic);
9✔
7833
                        }
7834
                    } else {
7835
                        let occurrenceIndex = 0;
14,040✔
7836
                        let searchIndex = searchValue.indexOf(searchText);
14,040✔
7837

7838
                        while (searchIndex !== -1) {
14,040✔
7839
                            const mic: IMatchInfoCache = {
2,410✔
7840
                                row: dataRow,
7841
                                column: c.field,
7842
                                index: occurrenceIndex++,
7843
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7844
                            };
7845

7846
                            this._lastSearchInfo.matchInfoCache.push(mic);
2,410✔
7847

7848
                            searchValue = searchValue.substring(searchIndex + searchText.length);
2,410✔
7849
                            searchIndex = searchValue.indexOf(searchText);
2,410✔
7850
                        }
7851
                    }
7852
                }
7853
            });
7854
        });
7855

7856
        this._lastSearchInfo.matchCount = this._lastSearchInfo.matchInfoCache.length;
211✔
7857
    }
7858

7859
    protected updateDefaultRowHeight() {
7860
        if (this.dataRowList.length > 0 && this.dataRowList.first.cells && this.dataRowList.first.cells.length > 0) {
3,671✔
7861
            const height = parseFloat(this.document.defaultView.getComputedStyle(this.dataRowList.first.cells.first.nativeElement)?.getPropertyValue('height'));
121✔
7862
            if (height) {
121!
7863
                this._defaultRowHeight = height;
121✔
7864
            } else {
7865
                this._shouldRecalcRowHeight = true;
×
7866
            }
7867
        }
7868
    }
7869

7870
    // TODO: About to Move to CRUD
7871
    private configureRowEditingOverlay(rowID: any, useOuter = false) {
70✔
7872
        let settings = this.rowEditSettings;
278✔
7873
        const overlay = this.overlayService.getOverlayById(this.rowEditingOverlay.overlayId);
278✔
7874
        if (overlay) {
278✔
7875
            settings = overlay.settings;
72✔
7876
        }
7877
        settings.outlet = useOuter ? this.parentRowOutletDirective : this.rowOutletDirective;
278✔
7878
        this.rowEditPositioningStrategy.settings.container = this.tbody.nativeElement;
278✔
7879
        const pinned = this._pinnedRecordIDs.indexOf(rowID) !== -1;
278✔
7880
        const targetRow = !pinned ?
278✔
7881
            this.gridAPI.get_row_by_key(rowID) as IgxRowDirective
7882
            : this.pinnedRows.find(x => x.key === rowID) as IgxRowDirective;
8✔
7883
        if (!targetRow) {
278!
7884
            return;
×
7885
        }
7886
        settings.target = targetRow.element.nativeElement;
278✔
7887
        this.toggleRowEditingOverlay(true);
278✔
7888
    }
7889

7890
    private handleColumnPinningForGroups(): void {
7891
        // When a column is a group or is inside a group, pin all related.
7892
        const pinnedColumns = [];
3,937✔
7893
        const unpinnedColumns = [];
3,937✔
7894

7895
        this._pinnedColumns.forEach(col => {
3,937✔
7896
            if (col.parent) {
492✔
7897
                col.parent.pinned = true;
113✔
7898
            }
7899
            if (col.columnGroup) {
492✔
7900
                col.children.forEach(child => child.pinned = true);
112✔
7901
            }
7902
        });
7903

7904
        // Make sure we don't exceed unpinned area min width and get pinned and unpinned col collections.
7905
        // We take into account top level columns (top level groups and non groups).
7906
        // If top level is unpinned the pinning handles all children to be unpinned as well.
7907
        for (const column of this._columns) {
3,937✔
7908
            if (column.pinned && !column.parent) {
23,282✔
7909
                pinnedColumns.push(column);
380✔
7910
            } else if (column.pinned && column.parent) {
22,902✔
7911
                if (column.topLevelParent.pinned) {
115!
7912
                    pinnedColumns.push(column);
115✔
7913
                } else {
7914
                    column.pinned = false;
×
7915
                    unpinnedColumns.push(column);
×
7916
                }
7917
            } else {
7918
                unpinnedColumns.push(column);
22,787✔
7919
            }
7920
        }
7921
        // Assign the applicable collections.
7922
        this._pinnedColumns = pinnedColumns;
3,937✔
7923
        this._unpinnedColumns = unpinnedColumns;
3,937✔
7924
    }
7925

7926
    protected shouldRecreateColumns(oldData: any[] | null | undefined, newData: any[] | null | undefined): boolean {
7927
        if (!oldData || !oldData.length) return true;
72✔
7928
        if (!newData || !newData.length) return false;
45!
7929
        return Object.keys(oldData[0]).join() !== Object.keys(newData[0]).join();
45✔
7930
    }
7931

7932
    /**
7933
     * Clears the current navigation service active node
7934
     */
7935
    private clearActiveNode() {
7936
        this.navigation.lastActiveNode = this.navigation.activeNode;
76✔
7937
        this.navigation.activeNode = {} as IActiveNode;
76✔
7938
        this.notifyChanges();
76✔
7939
    }
7940

7941
    private getRecreatedTree(value: IFilteringExpressionsTree): IFilteringExpressionsTree {
7942
        if (this._hGridSchema) {
9,474✔
7943
            return recreateTree(value, this._hGridSchema, true) as IFilteringExpressionsTree;
16✔
7944
        } else {
7945
            return recreateTreeFromFields(value, this._columns) as IFilteringExpressionsTree;
9,458✔
7946
        }
7947
    }
7948
}
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

© 2026 Coveralls, Inc