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

IgniteUI / igniteui-angular / 15734072093

18 Jun 2025 01:23PM UTC coverage: 91.699% (-0.007%) from 91.706%
15734072093

push

github

web-flow
fix(combo): clear button foreground and abckground parameters (#15946)

Co-authored-by: Simeon Simeonoff <sim.simeonoff@gmail.com>

13423 of 15692 branches covered (85.54%)

26987 of 29430 relevant lines covered (91.7%)

34504.98 hits per line

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

92.45
/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 { columnFieldPath, 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 } 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,123✔
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,123✔
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,123✔
249

250
    /**
251
     * Controls whether columns moving is enabled in the grid.
252
     *
253
     */
254
    @Input({ transform: booleanAttribute })
255
    public moving = false;
4,123✔
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) {
258,177✔
304
            return this._summaryRowHeight || this.summaryService.calcMaxSummaryHeight();
175,336✔
305
        }
306
        return 0;
82,841✔
307
    }
308

309
    /** @hidden @internal */
310
    public get hasColumnsToAutosize() {
311
        return this._columns.some(x => x.width === 'fit-content');
89,689✔
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,010✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,367,773✔
448
    }
449

450
    public set primaryKey(value: string) {
451
        this._primaryKey = value;
1,735✔
452
        this.checkPrimaryKeyField();
1,735✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
736

737
    /* blazorInclude */
738
    /**
739
     * @hidden @internal
740
     */
741
    @Output()
742
    public columnsAutogenerated = new EventEmitter<IColumnsAutoGeneratedEventArgs>();
4,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
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,123✔
1105

1106
    /**
1107
     * @hidden @internal
1108
     */
1109
    @Output()
1110
    public localeChange = new EventEmitter<boolean>();
4,123✔
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,123✔
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,123✔
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,123✔
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,770✔
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,744✔
1290
    }
1291

1292
    /** @hidden @internal */
1293
    public get headerSelectorContainer() {
1294
        return this.theadRow?.headerSelectorContainer;
30,624✔
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,880✔
1305
    }
1306

1307
    /** @hidden @internal */
1308
    public get filteringRow(): IgxGridFilteringRowComponent {
1309
        return this.theadRow?.filterRow;
1,325✔
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,123✔
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,123✔
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,289✔
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;
909✔
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,123✔
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,254✔
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;
909✔
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,123✔
1524

1525
    /**
1526
     * Gets the header expand indicator template.
1527
    */
1528
    @Input()
1529
    public get headerExpandedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
1530
        return this._headerExpandIndicatorTemplate || this.headerExpandedIndicatorDirectiveTemplate;
14,007✔
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;
909✔
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,123✔
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,055✔
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;
909✔
1579
    }
1580

1581
    /** @hidden @internal */
1582
    @ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef })
1583
    public excelStyleHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,123✔
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,363✔
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;
910✔
1608
    }
1609

1610

1611
    /**
1612
     * @hidden
1613
     * @internal
1614
     */
1615
    @ContentChild(IgxSortAscendingHeaderIconDirective, { read: TemplateRef })
1616
    public sortAscendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,123✔
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,065✔
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;
915✔
1641
    }
1642

1643
    /** @hidden @internal */
1644
    @ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef })
1645
    public sortDescendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,123✔
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;
971✔
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;
915✔
1670
    }
1671

1672
    /**
1673
     * @hidden
1674
     * @internal
1675
     */
1676
    @ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef })
1677
    public sortHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,123✔
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,735✔
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;
915✔
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,123✔
1734

1735
    /**
1736
     * @hidden @internal
1737
     */
1738
    @HostBinding('attr.role')
1739
    public hostRole = 'grid';
4,123✔
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,123✔
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,666✔
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,430✔
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,465✔
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,547,461✔
1958
    }
1959

1960
    public set locale(value: string) {
1961
        if (value !== this._locale) {
4,140✔
1962
            this._locale = value;
4,134✔
1963
            this._currencyPositionLeft = undefined;
4,134✔
1964
            this.summaryService.clearSummaryCache();
4,134✔
1965
            this.pipeTrigger++;
4,134✔
1966
            this.notifyChanges();
4,134✔
1967
            this.localeChange.emit();
4,134✔
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,240,090✔
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,240,003✔
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,762✔
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;
328,325✔
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,123✔
2049

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

2056
    /** @hidden @internal */
2057
    public dragRowID = null;
4,123✔
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,422,938✔
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,764✔
2096
    }
2097

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

2106
    /**
2107
     * @hidden @internal
2108
     */
2109
    @HostBinding('style.width')
2110
    public get hostWidth() {
2111
        return this._width || this._hostWidth;
33,194✔
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,885✔
2126
    }
2127

2128
    public set width(value: string | null) {
2129
        if (this._width !== value) {
2,081✔
2130
            this._width = value;
2,081✔
2131
            this.nativeElement.style.width = value;
2,081✔
2132
            this.notifyChanges(true);
2,081✔
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;
348,130✔
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;
738✔
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,525✔
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,949,846✔
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;
409,275✔
2296
    }
2297

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

2303

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

2307
            this.notifyChanges(true);
735✔
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) {
56✔
2326
            this._allowAdvancedFiltering = value;
55✔
2327
            this.filteringService.registerSVGIcons();
55✔
2328

2329
            if (!this._init) {
55✔
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,148✔
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,304✔
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,492✔
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,297✔
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,581✔
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,683✔
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,921✔
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,657✔
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,250✔
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,859✔
2575
        if (!this._dataRowList) {
56,859✔
2576
            return res;
9,409✔
2577
        }
2578
        const rList = this._dataRowList.filter(item => item.element.nativeElement.parentElement !== null).sort((a, b) => a.index - b.index);
233,979✔
2579
        res.reset(rList);
47,450✔
2580
        return res;
47,450✔
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,452,913✔
2614
    }
2615

2616
    /**
2617
     * @hidden
2618
     * @internal
2619
     */
2620
    public get isRowPinningToTop() {
2621
        return this.pinning.rows !== RowPinningPosition.Bottom;
677,382✔
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,400✔
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,356✔
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;
910✔
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,587✔
2736

2737
        if (!activeElem || !Object.keys(activeElem).length) {
134,587✔
2738
            return this.id;
119,085✔
2739
        }
2740

2741
        return activeElem.row < 0 ?
15,502✔
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,667✔
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,761✔
2797
    }
2798

2799
    /**
2800
     * Gets the number of pinned columns.
2801
     */
2802
    public get pinnedColumnsCount() {
2803
        return this.pinnedColumns.filter(col => !col.columnLayout).length;
113✔
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,286,279✔
2837
            return this._diTransactions;
3,804✔
2838
        }
2839
        return this._transactions;
7,282,475✔
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,216,777✔
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;
301,107✔
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,699✔
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,123✔
2952
    /**
2953
     * @hidden @internal
2954
     */
2955
    public tfootHeight: number;
2956

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

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

2969
    /**
2970
     * @hidden @internal
2971
     */
2972
    public columnWidthSetByUser = false;
4,123✔
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,123✔
2988

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

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

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

2998
    /** @hidden @internal */
2999
    public pipeTriggerNotifier = new Subject();
4,123✔
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,123✔
3018
    /**
3019
     * @hidden @internal
3020
     */
3021
    public _totalRecords = -1;
4,123✔
3022
    /**
3023
     * @hidden @internal
3024
     */
3025
    public columnsWithNoSetWidths = null;
4,123✔
3026
    /**
3027
     * @hidden @internal
3028
     */
3029
    public pipeTrigger = 0;
4,123✔
3030
    /**
3031
     * @hidden @internal
3032
     */
3033
    public filteringPipeTrigger = 0;
4,123✔
3034

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

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

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

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

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

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

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

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

3129

3130
    protected _defaultTargetRecordNumber = 10;
4,123✔
3131
    protected _expansionStates: Map<any, boolean> = new Map<any, boolean>();
4,123✔
3132
    protected _defaultExpandState = false;
4,123✔
3133
    protected _headerFeaturesWidth = NaN;
4,123✔
3134
    protected _init = true;
4,123✔
3135
    protected _firstAutoResize = true;
4,123✔
3136
    protected _autoSizeColumnsNotify = new Subject<void>();
4,123✔
3137
    protected _cdrRequestRepaint = false;
4,123✔
3138
    protected _userOutletDirective: IgxOverlayOutletDirective;
3139
    protected _transactions: TransactionService<Transaction, State>;
3140
    protected _batchEditing = false;
4,123✔
3141
    protected _sortingOptions: ISortingOptions = { mode: 'multiple' };
4,123✔
3142
    protected _filterStrategy: IFilteringStrategy = new FilteringStrategy();
4,123✔
3143
    protected _autoGeneratedCols = [];
4,123✔
3144
    protected _autoGeneratedColsRefs: ComponentRef<IgxColumnComponent>[] = [];
4,123✔
3145
    protected _dataView = [];
4,123✔
3146
    protected _lastSearchInfo: ISearchInfo = {
4,123✔
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,740,639✔
3161
    }
3162

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

3170
    private _primaryKey: string;
3171
    private _rowEditable = false;
4,123✔
3172
    private _currentRowState: any;
3173
    private _filteredSortedData = null;
4,123✔
3174
    private _filteredData = null;
4,123✔
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,123✔
3190
    private _resourceStrings = getCurrentResourceStrings(GridResourceStringsEN);
4,123✔
3191
    private _emptyGridMessage = null;
4,123✔
3192
    private _emptyFilteredGridMessage = null;
4,123✔
3193
    private _isLoading = false;
4,123✔
3194
    private _locale: string;
3195
    private overlayIDs = [];
4,123✔
3196
    private _sortingStrategy: IGridSortingStrategy;
3197
    private _pinning: IPinningConfig = { columns: ColumnPinningPosition.Start };
4,123✔
3198
    private _shouldRecalcRowHeight = false;
4,123✔
3199

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

3209
    private _advancedFilteringOverlaySettings: OverlaySettings = {
4,123✔
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,123✔
3218
    private _width: string | null = '100%';
4,123✔
3219
    private _rowHeight: number | undefined;
3220
    private _horizontalForOfs: Array<IgxGridForOfDirective<any, any[]>> = [];
4,123✔
3221
    private _multiRowLayoutRowSize = 1;
4,123✔
3222
    // Caches
3223
    private _totalWidth = NaN;
4,123✔
3224
    private _pinnedVisible = [];
4,123✔
3225
    private _unpinnedVisible = [];
4,123✔
3226
    private _pinnedWidth = NaN;
4,123✔
3227
    private _unpinnedWidth = NaN;
4,123✔
3228
    private _visibleColumns = [];
4,123✔
3229
    private _columnGroups = false;
4,123✔
3230

3231
    private _columnWidth: string;
3232

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

3242
    private lastAddedRowIndex;
3243

3244
    private _currencyPositionLeft: boolean;
3245

3246
    private rowEditPositioningStrategy = new RowEditPositionStrategy({
4,123✔
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,123✔
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,123✔
3263
    private _rendered = false;
4,123✔
3264
    private readonly DRAG_SCROLL_DELTA = 10;
4,123✔
3265
    private _dataCloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy();
4,123✔
3266
    private _autoSize = false;
4,123✔
3267
    private _sortHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,123✔
3268
    private _sortAscendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,123✔
3269
    private _sortDescendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,123✔
3270
    private _gridSize: Size = Size.Large;
4,123✔
3271
    private _defaultRowHeight = 50;
4,123✔
3272

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

3280
    protected get isCustomSetRowHeight(): boolean {
3281
        return !isNaN(this._rowHeight);
1,207,630✔
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,921✔
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,771✔
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,406✔
3339
        if (this.transactions.enabled) {
11,406✔
3340
            result.push(...this.transactions.getAggregatedChanges(true)
7,026✔
3341
                .filter(t => t.type === TransactionType.ADD)
1,230✔
3342
                .map(t => t.newValue));
358✔
3343
        }
3344

3345
        if (this.crudService.row && this.crudService.row.isAddRow) {
11,406✔
3346
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
385✔
3347
        }
3348

3349
        return result;
11,406✔
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;
46,048✔
3357
    }
3358

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

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

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

3376
    /**
3377
     * @hidden @internal
3378
     */
3379
    private get hasZeroResultFilter(): boolean {
3380
        return this.filteredData && this.filteredData.length === 0;
45,812✔
3381
    }
3382
    protected get totalCalcWidth() {
3383
        return this.platform.isBrowser ? this.calcWidth : undefined;
43,501!
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,642!
3389
    }
3390

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

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

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

3404
    /**
3405
     * @hidden @internal
3406
     */
3407
    public get shouldOverlayLoading(): boolean {
3408
        return this.isLoading && !this.hasNoData && !this.hasZeroResultFilter;
48,924✔
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,937✔
3424
    }
3425

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

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

3440
    constructor(
3441
        public readonly validation: IgxGridValidationService,
4,123✔
3442
        /** @hidden @internal */
3443
        public readonly selectionService: IgxGridSelectionService,
4,123✔
3444
        protected colResizingService: IgxColumnResizingService,
4,123✔
3445
        @Inject(IGX_GRID_SERVICE_BASE) public readonly gridAPI: GridServiceType,
4,123✔
3446
        protected transactionFactory: IgxFlatTransactionFactory,
4,123✔
3447
        private elementRef: ElementRef<HTMLElement>,
4,123✔
3448
        protected zone: NgZone,
4,123✔
3449
        /** @hidden @internal */
3450
        @Inject(DOCUMENT) public document: any,
4,123✔
3451
        public readonly cdr: ChangeDetectorRef,
4,123✔
3452
        protected differs: IterableDiffers,
4,123✔
3453
        protected viewRef: ViewContainerRef,
4,123✔
3454
        protected injector: Injector,
4,123✔
3455
        protected envInjector: EnvironmentInjector,
4,123✔
3456
        public navigation: IgxGridNavigationService,
4,123✔
3457
        /** @hidden @internal */
3458
        public filteringService: IgxFilteringService,
4,123✔
3459
        protected textHighlightService: IgxTextHighlightService,
4,123✔
3460
        @Inject(IgxOverlayService) protected overlayService: IgxOverlayService,
4,123✔
3461
        /** @hidden @internal */
3462
        public summaryService: IgxGridSummaryService,
4,123✔
3463
        @Inject(LOCALE_ID) private localeId: string,
4,123✔
3464
        protected platform: PlatformUtil,
4,123✔
3465
        @Optional() @Inject(IgxGridTransaction) protected _diTransactions?: TransactionService<Transaction, State>,
4,123✔
3466
    ) {
3467
        this.locale = this.locale || this.localeId;
4,123✔
3468
        this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
4,123✔
3469
        this._transactions.cloneStrategy = this.dataCloneStrategy;
4,123✔
3470
        this.cdr.detach();
4,123✔
3471
        this.selectionService.selectedRowsChange.pipe(takeUntil(this.destroy$)).subscribe((args: any[]) => {
4,123✔
3472
            this.selectedRowsChange.emit(args);
696✔
3473
        });
3474
        IgcTrialWatermark.register();
4,123✔
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;
544,179✔
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) {
255,246✔
3529
            rowIndex = rowIndex + this.unpinnedDataView.length;
198✔
3530
        } else if (!pinned && this.isRowPinningToTop) {
255,048✔
3531
            rowIndex = rowIndex + this.pinnedDataView.length;
253,295✔
3532
        }
3533
        return rowIndex;
255,246✔
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 => {
524✔
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,321✔
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,102,790✔
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,102,898✔
3612
        return this._pinnedRecordIDs.indexOf(id);
1,102,898✔
3613
    }
3614

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

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

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

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

3653
    /**
3654
     * @hidden
3655
     * @internal
3656
     */
3657
    public _setupListeners() {
3658
        const destructor = takeUntil<any>(this.destroy$);
3,575✔
3659
        fromEvent(this.nativeElement, 'focusout').pipe(filter(() => !!this.navigation.activeNode), destructor).subscribe((event) => {
3,575✔
3660
            const activeNode = this.navigation.activeNode;
680✔
3661
            if (!this.crudService.cell && !!activeNode &&
680!
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,575✔
3671
        this.rowDeletedNotifier.pipe(destructor).subscribe(args => {
3,575✔
3672
            this.summaryService.deleteOperation = true;
185✔
3673
            this.summaryService.clearSummaryCache(args);
185✔
3674
        });
3675

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

3678
        this.resizeNotify.pipe(
3,575✔
3679
            filter(() => !this._init),
2,937✔
3680
            throttleTime(40, animationFrameScheduler, { leading: true, trailing: true }),
3681
            destructor
3682
        )
3683
            .subscribe(() => {
3684
                this.zone.run(() => {
2,163✔
3685
                    // do not trigger reflow if element is detached.
3686
                    if (this.nativeElement.isConnected) {
2,163✔
3687
                        if (this.shouldResize) {
1,776✔
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,776✔
3698
                    }
3699
                });
3700
            });
3701

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

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

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

3717
            // do not hide the advanced filtering overlay on scroll
3718
            if (this._advancedFilteringOverlayId === event.id) {
577✔
3719
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
52✔
3720
                if (instance) {
52✔
3721
                    instance.lastActiveNode = this.navigation.activeNode;
52✔
3722
                    instance.queryBuilder.setAddButtonFocus();
52✔
3723
                }
3724
                return;
52✔
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,575✔
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,763✔
3751
            const shouldRecalcSize = this.isPercentHeight &&
3,274✔
3752
                (!this.calcHeight || this.calcHeight === this.getDataBasedBodyHeight() ||
3753
                    this.calcHeight === this.renderedRowHeight * this._defaultTargetRecordNumber);
3754
            if (shouldRecalcSize) {
3,274✔
3755
                this.calculateGridHeight();
110✔
3756
                $event.containerSize = this.calcHeight;
110✔
3757
            }
3758
            this.evaluateLoadingState();
3,274✔
3759
        });
3760

3761
        this.verticalScrollContainer.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3,575✔
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,575✔
3771
            // the horizontal scrollbar showing/hiding
3772
            // update scrollbar visibility and recalc heights
3773
            this.notifyChanges(true);
1,340✔
3774
            this.cdr.detectChanges();
1,340✔
3775
        });
3776

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

3781
        // notifier for column autosize requests
3782
        this._autoSizeColumnsNotify.pipe(
3,575✔
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,575✔
3797
        this._setupListeners();
3,575✔
3798
        this.rowListDiffer = this.differs.find([]).create(null);
3,575✔
3799
        // compare based on field, not on object ref.
3800
        this.columnListDiffer = this.differs.find([]).create((_index, col: ColumnType) => col.field);
17,463✔
3801
        this.calcWidth = this.width && this.width.indexOf('%') === -1 ? parseInt(this.width, 10) : 0;
3,575✔
3802
        this.gridComputedStyles = this.document.defaultView.getComputedStyle(this.nativeElement);
3,575✔
3803
    }
3804

3805
    /**
3806
     * @hidden
3807
     * @internal
3808
     */
3809
    public resetColumnsCaches() {
3810
        this._columns.forEach(column => column.resetCaches());
216,165✔
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,408✔
3829
        if (firstVirtRow) {
34,408✔
3830
            if (this._cdrRequests) {
17,017✔
3831
                firstVirtRow.virtDirRow.cdr.detectChanges();
6,727✔
3832
            }
3833
            firstVirtRow.virtDirRow.assumeMaster();
17,017✔
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,549✔
3860
            this._columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
12,051✔
3861
        }
3862
        this._visibleColumns.length = 0;
34,549✔
3863
        this._pinnedVisible.length = 0;
34,549✔
3864
        this._unpinnedVisible.length = 0;
34,549✔
3865
    }
3866

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

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

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

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

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

3910
        this.setupColumns();
3,444✔
3911
        this.toolbar.changes.pipe(filter(() => !this._init), takeUntil(this.destroy$)).subscribe(() => this.notifyChanges(true));
3,444✔
3912
        this.setUpPaginator();
3,444✔
3913
        this.paginationComponents.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,444✔
3914
            this.setUpPaginator();
64✔
3915
        });
3916
        if (this.actionStrip) {
3,444✔
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,763✔
3926
            this._shouldRecalcRowHeight = true;
3,603✔
3927
        }
3928
        this.dataChanging.emit(event);
6,763✔
3929
    }
3930

3931
    /**
3932
     * @hidden @internal
3933
     */
3934
    public dataRebound(event: IForOfDataChangeEventArgs) {
3935
        this.selectionService.clearHeaderCBState();
6,763✔
3936
        if (this._shouldRecalcRowHeight) {
6,763✔
3937
            this._shouldRecalcRowHeight = false;
3,603✔
3938
            this.updateDefaultRowHeight();
3,603✔
3939
        }
3940
        this.dataChanged.emit(event);
6,763✔
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,511✔
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,291✔
3980
        }
3981
    }
3982

3983
    /**
3984
     * @hidden
3985
     * @internal
3986
     */
3987
    public setFilteredSortedData(data, pinned: boolean) {
3988
        data = data || [];
6,504✔
3989
        if (this.pinnedRecordsCount > 0) {
6,504✔
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,163✔
4001
            this.refreshSearch(true, false);
6,163✔
4002
        }
4003
        this.buildDataView(data);
6,504✔
4004
    }
4005

4006
    /**
4007
     * @hidden @internal
4008
     */
4009
    public resetHorizontalVirtualization() {
4010
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
49,591✔
4011
        this._horizontalForOfs = [
6,735✔
4012
            ...this._dataRowList.filter(elementFilter).map(item => item.virtDirRow),
46,450✔
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,748✔
4022
        const extractForOfs = pipe(map((collection: any[]) => collection.filter(elementFilter).map(item => item.virtDirRow)));
17,748✔
4023
        const rowListObserver = extractForOfs(this._dataRowList.changes);
3,588✔
4024
        const summaryRowObserver = extractForOfs(this._summaryRowList.changes);
3,588✔
4025
        rowListObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,588✔
4026
            this.resetHorizontalVirtualization();
2,731✔
4027
        });
4028
        summaryRowObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,588✔
4029
            this.resetHorizontalVirtualization();
318✔
4030
        });
4031
        this.resetHorizontalVirtualization();
3,588✔
4032
    }
4033

4034
    /**
4035
     * @hidden @internal
4036
     */
4037
    public _zoneBegoneListeners() {
4038
        this.zone.runOutsideAngular(() => {
3,588✔
4039
            this.verticalScrollHandler = this.verticalScrollHandler.bind(this);
3,588✔
4040
            this.horizontalScrollHandler = this.horizontalScrollHandler.bind(this);
3,588✔
4041
            this.verticalScrollContainer.getScroll().addEventListener('scroll', this.verticalScrollHandler);
3,588✔
4042
            this.headerContainer?.getScroll().addEventListener('scroll', this.horizontalScrollHandler);
3,588✔
4043
            if (this.hasColumnsToAutosize) {
3,588✔
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,588✔
4054
            resizeObservable(this.tbodyContainer.nativeElement).pipe(takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
3,588✔
4055
        });
4056
    }
4057

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

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

4072
        this._pinnedRowList.changes
3,588✔
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,588✔
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,588✔
4086
            if (this.paginator) {
2,006✔
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,006✔
4091
                this.autoSizeColumnsInView();
14✔
4092
            }
4093
            this._rendered = true;
2,006✔
4094
        });
4095
        Promise.resolve().then(() => this.rendered.next(true));
3,588✔
4096

4097
    }
4098

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

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

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

4123
        if (this._cdrRequests) {
9,799✔
4124
            this.resetNotifyChanges();
4,240✔
4125
            this.cdr.detectChanges();
4,240✔
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,409✔
4143
            tmplOutlet.cleanCache();
25,076✔
4144
        });
4145
        this._autoGeneratedColsRefs.forEach(ref => ref.destroy());
4,134✔
4146
        this._autoGeneratedColsRefs = [];
3,409✔
4147

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

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

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

4161
        this.overlayIDs.forEach(overlayID => {
3,409✔
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,409✔
4171
            this.verticalScrollContainer?.getScroll()?.removeEventListener('scroll', this.verticalScrollHandler);
3,409✔
4172
            this.headerContainer?.getScroll()?.removeEventListener('scroll', this.horizontalScrollHandler);
3,409✔
4173
            const vertScrDC = this.verticalScrollContainer?.displayContainer;
3,409✔
4174
            vertScrDC?.removeEventListener('scroll', this.preventContainerScroll);
3,409✔
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,933✔
4214
    }
4215

4216
    /* blazorCSSuppress */
4217
    public set expansionStates(value) {
4218
        this._expansionStates = new Map<any, boolean>(value);
692✔
4219
        this.expansionStatesChange.emit(this._expansionStates);
692✔
4220
        this.notifyChanges(true);
692✔
4221
        if (this.gridAPI.grid) {
692✔
4222
            this.cdr.detectChanges();
599✔
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);
87✔
4296
        const state = this.gridAPI.get_row_expansion_state(rec);
87✔
4297
        this.gridAPI.set_row_expansion_state(rowID, !state);
87✔
4298
    }
4299

4300
    /**
4301
     * @hidden
4302
     * @internal
4303
     */
4304
    public getDefaultExpandState(_rec: any) {
4305
        return this._defaultExpandState;
9,742✔
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,109✔
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,422✔
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,630✔
4346
    }
4347

4348
    /**
4349
     * @hidden @internal
4350
     */
4351
    public get defaultSummaryHeight(): number {
4352
        switch (this.gridSize) {
15,147✔
4353
            case Size.Medium:
4354
                return 30;
29✔
4355
            case Size.Small:
4356
                return 24;
123✔
4357
            default:
4358
                return 36;
14,995✔
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) {
473,680✔
4371
            case Size.Medium:
4372
                return 32;
38,079✔
4373
            case Size.Small:
4374
                return 24;
3,385✔
4375
            default:
4376
                return 48;
432,216✔
4377
        }
4378
    }
4379

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

4389
    /** @hidden @internal */
4390
    public get unpinnedWidth() {
4391
        if (!isNaN(this._unpinnedWidth)) {
371,033✔
4392
            return this._unpinnedWidth;
347,470✔
4393
        }
4394
        this._unpinnedWidth = this.getUnpinnedWidth();
23,563✔
4395
        return this._unpinnedWidth;
23,563✔
4396
    }
4397

4398
    /**
4399
     * @hidden @internal
4400
     */
4401
    public isHorizontalScrollHidden = false;
4,123✔
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,133✔
4429
            // TODO: platformUtil.isBrowser check
4430
            const rowSelectArea = this.headerSelectorContainer?.nativeElement?.getBoundingClientRect ?
28,876✔
4431
                this.headerSelectorContainer.nativeElement.getBoundingClientRect().width : 0;
4432
            const rowDragArea = this.rowDraggable && this.headerDragContainer?.nativeElement?.getBoundingClientRect ?
28,876✔
4433
                this.headerDragContainer.nativeElement.getBoundingClientRect().width : 0;
4434
            const groupableArea = this.headerGroupContainer?.nativeElement?.getBoundingClientRect ?
28,876✔
4435
                this.headerGroupContainer.nativeElement.getBoundingClientRect().width : 0;
4436
            const expanderWidth = expander?.nativeElement?.getBoundingClientRect ? expander.nativeElement.getBoundingClientRect().width : 0;
28,876✔
4437
            this._headerFeaturesWidth = rowSelectArea + rowDragArea + groupableArea + expanderWidth;
28,876✔
4438
        }
4439
        return this._headerFeaturesWidth;
130,133✔
4440
    }
4441

4442
    /**
4443
     * @hidden @internal
4444
     */
4445
    public get summariesMargin() {
4446
        return this.featureColumnsWidth();
22,183✔
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,357!
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,117,989✔
4471
            return this._pinnedVisible;
333,363✔
4472
        }
4473
        this._pinnedVisible = this._pinnedColumns.filter(col => !col.hidden);
3,784,626✔
4474
        return this._pinnedVisible;
3,784,626✔
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) {
764,031✔
4500
            return this._unpinnedVisible;
737,011✔
4501
        }
4502
        this._unpinnedVisible = this._unpinnedColumns.filter((col) => !col.hidden);
156,500✔
4503
        return this._unpinnedVisible;
27,020✔
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,439✔
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,997✔
4563
            return this._visibleColumns;
134,970✔
4564
        }
4565
        this._visibleColumns = this._columns.filter(c => !c.hidden);
152,176✔
4566
        return this._visibleColumns;
27,027✔
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,374✔
4598
            return this._totalWidth;
3,220✔
4599
        }
4600
        // Take only top level columns
4601
        const cols = this.visibleColumns.filter(col => col.level === 0 && !col.pinned);
50,239✔
4602
        let totalWidth = 0;
8,154✔
4603
        let i = 0;
8,154✔
4604
        for (i; i < cols.length; i++) {
8,154✔
4605
            totalWidth += parseFloat(cols[i].calcWidth) || 0;
40,272!
4606
        }
4607
        this._totalWidth = totalWidth;
8,154✔
4608
        return totalWidth;
8,154✔
4609
    }
4610

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

4619
    /**
4620
     * @hidden
4621
     * @internal
4622
     */
4623
    public get showAddButton() {
4624
        return this.rowEditable && this.dataView.length === 0 && this._columns.length > 0;
918✔
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,305✔
4641
        if ((index < 0 || index >= this.dataView.length) && this.pagingMode === 'remote' && this.page !== 0) {
56,305!
4642
            newIndex = index - this.perPage * this.page;
×
4643
        } else if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
56,305!
4644
            newIndex = index - this.gridAPI.grid.virtualizationState.startIndex;
×
4645
        }
4646
        return newIndex;
56,305✔
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,294✔
4743
        this.cdr.detectChanges();
3,294✔
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,669✔
5196
        return this.hasPinnedRecords ? containerHeight : 0;
82,669✔
5197
    }
5198

5199
    /** @hidden @internal */
5200
    public get totalHeight() {
5201
        const height = this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
43,501✔
5202
        return this.platform.isBrowser ? height : undefined;
43,501!
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,097✔
5265
        if (this._lastSearchInfo.searchText) {
10,046✔
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,919✔
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,108,525✔
5327
        return summarizedColumns.length > 0;
334,175✔
5328
    }
5329

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

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

5347
    public set hasVisibleColumns(value) {
5348
        this._hasVisibleColumns = value;
34,408✔
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;
853✔
5359
    }
5360

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

5366

5367
    /**
5368
     * @hidden @internal
5369
     */
5370
    public get multiRowLayoutRowSize() {
5371
        return this._multiRowLayoutRowSize;
20,060✔
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,218✔
5386
    }
5387

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

5392
    /**
5393
     * @hidden @internal
5394
     */
5395
    public get isPercentHeight() {
5396
        return this._height && this._height.indexOf('%') !== -1;
15,864✔
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,306✔
5414
    }
5415

5416
    /**
5417
     * @hidden @internal
5418
     */
5419
    public get outerWidth() {
5420
        return this.hasVerticalScroll() ? this.calcWidth + this.scrollSize : this.calcWidth;
3,861✔
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;
889,077✔
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,827✔
5447
        let computedWidth;
5448
        if (baseWidth !== null) {
72,827!
5449
            computedWidth = baseWidth;
×
5450
        } else {
5451
            computedWidth = this.calcWidth ||
72,827✔
5452
                parseFloat(this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width'));
5453
        }
5454

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

5457

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

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

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

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

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

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

5502
    /**
5503
     * @hidden @internal
5504
     */
5505
    public hasVerticalScroll() {
5506
        if (this._init) {
188,884✔
5507
            return false;
83,297✔
5508
        }
5509
        const isScrollable = this.verticalScrollContainer ? this.verticalScrollContainer.isScrollable() : false;
105,587✔
5510
        return !!(this.calcWidth && this.dataView && this.dataView.length > 0 && isScrollable);
105,587✔
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,604✔
5523
        const fc = takeHidden ? this._pinnedColumns : this.pinnedColumns;
44,754!
5524
        let sum = 0;
44,754✔
5525
        for (const col of fc) {
44,754✔
5526
            if (col.level === 0) {
6,557✔
5527
                sum += parseInt(col.calcWidth, 10);
3,848✔
5528
            }
5529
        }
5530
        if (this.isPinningToStart) {
44,754✔
5531
            sum += this.featureColumnsWidth();
44,630✔
5532
        }
5533

5534
        return sum;
44,754✔
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 : [];
264,499✔
5585
    }
5586

5587
    /** @hidden @internal */
5588
    public get unpinnedDataView(): any[] {
5589
        return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer?.igxForOf || [];
15,660✔
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,811✔
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,560✔
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,123✔
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,710,169✔
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;
57!
6223
        if (!this._advancedFilteringOverlayId) {
57✔
6224
            this._advancedFilteringOverlaySettings.target =
54✔
6225
                (this as any).rootGrid ? (this as any).rootGrid.nativeElement : this.nativeElement;
54✔
6226
            this._advancedFilteringOverlaySettings.outlet = this.outlet;
54✔
6227

6228
            this._advancedFilteringOverlayId = this.overlayService.attach(
54✔
6229
                IgxAdvancedFilteringDialogComponent,
6230
                this.viewRef,
6231
                settings);
6232
            this.overlayService.show(this._advancedFilteringOverlayId);
54✔
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,374✔
6269
    }
6270

6271
    /**
6272
     * @hidden @internal
6273
     */
6274
    public isSummaryRow(rowData): boolean {
6275
        return rowData && rowData.summaries && (rowData.summaries instanceof Map);
436,965✔
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,123✔
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,819✔
6451
        this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$)))
3,819✔
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,282✔
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,267✔
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,859✔
6544
            /* width in %*/
6545
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width');
5,431✔
6546
            width = computed.indexOf('%') === -1 ? parseFloat(computed) : null;
5,431✔
6547
        } else {
6548
            width = parseInt(this.width, 10);
6,428✔
6549
        }
6550

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

6555

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

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

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

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

6608
        if (minWidth > parseFloat(width)) {
69,526✔
6609
            width = String(column.minWidth);
6✔
6610
        } else if (maxWidth < parseFloat(width)) {
69,520✔
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,526✔
6616
            width += 'px';
276✔
6617
        }
6618
        return width;
69,526✔
6619
    }
6620

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

6626
    /** @hidden @internal */
6627
    public resolveOutlet() {
6628
        return this._userOutletDirective ? this._userOutletDirective : this._outletDirective;
239,085!
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,768✔
6652
            .filter((c) => c.pinned);
34,613✔
6653
        this._unpinnedColumns = newColumns.filter((c) => !c.pinned);
34,613✔
6654
        this._columns = newColumns;
5,768✔
6655
        if (this._columns && this._columns.length && this._filteringExpressionsTree) {
5,768✔
6656
            this._filteringExpressionsTree = this.getRecreatedTree(this._filteringExpressionsTree);
5,332✔
6657
        }
6658
        if (this._columns && this._columns.length && this._advancedFilteringExpressionsTree) {
5,768✔
6659
            this._advancedFilteringExpressionsTree = this.getRecreatedTree(this._advancedFilteringExpressionsTree);
7✔
6660
        }
6661
        this.resetCaches();
5,768✔
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,376✔
6719
            this.autogenerateColumns();
910✔
6720
        } else {
6721
            this._columns = this.getColumnList();
2,466✔
6722
        }
6723
        if (this._columns && this._columns.length && this._filteringExpressionsTree) {
3,376✔
6724
            this._filteringExpressionsTree = this.getRecreatedTree(this._filteringExpressionsTree);
3,319✔
6725
        }
6726
        if (this._columns && this._columns.length && this._advancedFilteringExpressionsTree) {
3,376✔
6727
            this._advancedFilteringExpressionsTree = this.getRecreatedTree(this._advancedFilteringExpressionsTree);
6✔
6728
        }
6729

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

6733
        this.columnList.changes
3,376✔
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().filter((col) => col.grid === this);
15,433✔
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,058✔
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
                if (record.item.grid !== this) {
1,031!
6800
                    return;
×
6801
                }
6802
                added = true;
1,031✔
6803
                if (record.item.pinned) {
1,031✔
6804
                    this._pinnedColumns.push(record.item);
1✔
6805
                    pinning = true;
1✔
6806
                } else {
6807
                    this._unpinnedColumns.push(record.item);
1,030✔
6808
                }
6809
            });
6810

6811
            this.initColumns(this.getColumnList(), (col: IgxColumnComponent) => this.columnInit.emit(col));
1,277✔
6812
            if (pinning) {
63✔
6813
                this.initPinning();
1✔
6814
            }
6815

6816
            diff.forEachRemovedItem((record: IterableChangeRecord<IgxColumnComponent | IgxColumnGroupComponent>) => {
63✔
6817
                if (record.item.grid !== this) {
130!
6818
                    return;
×
6819
                }
6820
                const isColumnGroup = record.item instanceof IgxColumnGroupComponent;
130✔
6821
                if (!isColumnGroup) {
130✔
6822
                    // Clear Grouping
6823
                    this.gridAPI.clear_groupby(record.item.field);
127✔
6824

6825
                    // Clear Filtering
6826
                    this.filteringService.clear_filter(record.item.field);
127✔
6827

6828
                    // Close filter row
6829
                    if (this.filteringService.isFilterRowVisible
127!
6830
                        && this.filteringService.filteredColumn
6831
                        && this.filteringService.filteredColumn.field === record.item.field) {
6832
                        this.filteringRow.close();
×
6833
                    }
6834

6835
                    // Clear Sorting
6836
                    this.gridAPI.clear_sort(record.item.field);
127✔
6837

6838
                    // Remove column selection
6839
                    this.selectionService.deselectColumnsWithNoEvent([record.item.field]);
127✔
6840
                }
6841
                removed = true;
130✔
6842
            });
6843

6844
            this.resetCaches();
63✔
6845

6846
            if (added || removed) {
63✔
6847
                this.onColumnsAddedOrRemoved();
62✔
6848
            }
6849
        }
6850
    }
6851

6852
    protected checkPrimaryKeyField() {
6853
        if (this.primaryKey && this.data?.length && !(this.primaryKey in this.data[0])) {
4,684✔
6854
            console.warn(`Field "${this.primaryKey}" is not defined in the data. Set \`primaryKey\` to a valid field.`);
9✔
6855
        }
6856
    }
6857

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

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

6891
        if (this.rowEditable) {
7,937✔
6892
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
881✔
6893
        }
6894

6895
        if (this.filteringService.isFilterRowVisible) {
7,937✔
6896
            this.filteringRow.resetChipsArea();
153✔
6897
        }
6898

6899
        this.cdr.detectChanges();
7,937✔
6900
        // in case scrollbar has appeared recalc to size correctly.
6901
        if (hasScroll !== this.hasVerticalScroll()) {
7,937✔
6902
            this.calculateGridWidth();
167✔
6903
            this.cdr.detectChanges();
167✔
6904
        }
6905

6906
        // in case horizontal scrollbar has appeared recalc to size correctly.
6907
        if (hasHScroll !== this.hasHorizontalScroll()) {
7,937✔
6908
            this.isHorizontalScrollHidden = !this.hasHorizontalScroll();
2,799✔
6909
            this.cdr.detectChanges();
2,799✔
6910
            this.calculateGridHeight();
2,799✔
6911
            this.cdr.detectChanges();
2,799✔
6912
        }
6913
        if (this.zone.isStable) {
7,937✔
6914
            this.zone.run(() => {
226✔
6915
                this._applyWidthHostBinding();
226✔
6916
                this.cdr.detectChanges();
226✔
6917
            });
6918
        } else {
6919
            this.zone.onStable.pipe(first()).subscribe(() => {
7,711✔
6920
                this.zone.run(() => {
7,711✔
6921
                    this._applyWidthHostBinding();
7,711✔
6922
                });
6923
            });
6924
        }
6925
        this.resetCaches(recalcFeatureWidth);
7,937✔
6926
        if (this.hasColumnsToAutosize) {
7,937✔
6927
            this.cdr.detectChanges();
20✔
6928
            this.zone.onStable.pipe(first()).subscribe(() => {
20✔
6929
                this._autoSizeColumnsNotify.next();
20✔
6930
            });
6931
        }
6932
    }
6933

6934
    /**
6935
     * @hidden
6936
     * Sets TBODY height i.e. this.calcHeight
6937
     */
6938
    protected calculateGridHeight() {
6939

6940
        this.calcHeight = this._calculateGridBodyHeight();
10,482✔
6941
        if (this.pinnedRowHeight && this.calcHeight) {
10,482✔
6942
            this.calcHeight -= this.pinnedRowHeight;
108✔
6943
        }
6944
    }
6945

6946
    /**
6947
     * @hidden
6948
     */
6949
    protected getGroupAreaHeight(): number {
6950
        return 0;
2,786✔
6951
    }
6952

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

6976
    /**
6977
     * @hidden
6978
     */
6979
    protected getToolbarHeight(): number {
6980
        let toolbarHeight = 0;
9,683✔
6981
        if (this.toolbar.first) {
9,683✔
6982
            toolbarHeight = this.getComputedHeight(this.toolbar.first.nativeElement);
262✔
6983
        }
6984
        return toolbarHeight;
9,683✔
6985
    }
6986

6987
    /**
6988
     * @hidden
6989
     */
6990
    protected getPagingFooterHeight(): number {
6991
        let pagingHeight = 0;
9,683✔
6992
        if (this.footer) {
9,683✔
6993
            const height = this.getComputedHeight(this.footer.nativeElement);
9,683✔
6994
            pagingHeight = this.footer.nativeElement.firstElementChild ?
9,683✔
6995
                height : 0;
6996
        }
6997
        return pagingHeight;
9,683✔
6998
    }
6999

7000
    /**
7001
     * @hidden
7002
     */
7003
    protected getFilterCellHeight(): number {
7004
        const headerGroupNativeEl = (this.headerGroupsList.length !== 0) ?
7,986✔
7005
            this.headerGroupsList[0].nativeElement : null;
7006
        const filterCellNativeEl = (headerGroupNativeEl) ?
7,986✔
7007
            headerGroupNativeEl.querySelector('igx-grid-filtering-cell') as HTMLElement : null;
7008
        return (filterCellNativeEl) ? filterCellNativeEl.offsetHeight : 0;
7,986✔
7009
    }
7010

7011
    /**
7012
     * @hidden
7013
     */
7014
    protected _calculateGridBodyHeight(): number {
7015
        if (!this._height) {
10,482✔
7016
            return null;
799✔
7017
        }
7018
        const actualTheadRow = this.getTheadRowHeight();
9,683✔
7019
        const footerHeight = this.getFooterHeight();
9,683✔
7020
        const toolbarHeight = this.getToolbarHeight();
9,683✔
7021
        const pagingHeight = this.getPagingFooterHeight();
9,683✔
7022
        const groupAreaHeight = this.getGroupAreaHeight();
9,683✔
7023
        const scrHeight = this.getComputedHeight(this.scr.nativeElement);
9,683✔
7024
        const renderedHeight = toolbarHeight + actualTheadRow +
9,683✔
7025
            footerHeight + pagingHeight + groupAreaHeight +
7026
            scrHeight;
7027

7028
        let gridHeight = 0;
9,683✔
7029

7030
        if (this.isPercentHeight) {
9,683✔
7031
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('height');
2,089✔
7032
            const autoSize = this._shouldAutoSize(renderedHeight);
2,089✔
7033
            if (autoSize || computed.indexOf('%') !== -1) {
2,089✔
7034
                const bodyHeight = this.getDataBasedBodyHeight();
858✔
7035
                return bodyHeight > 0 ? bodyHeight : null;
858✔
7036
            }
7037
            gridHeight = parseFloat(computed);
1,231✔
7038
        } else {
7039
            gridHeight = parseInt(this._height, 10);
7,594✔
7040
        }
7041
        const height = Math.abs(gridHeight - renderedHeight);
8,825✔
7042

7043
        if (Math.round(height) === 0 || isNaN(gridHeight)) {
8,825✔
7044
            const bodyHeight = this.defaultTargetBodyHeight;
18✔
7045
            return bodyHeight > 0 ? bodyHeight : null;
18✔
7046
        }
7047
        return height;
8,807✔
7048
    }
7049

7050
    protected checkContainerSizeChange() {
7051
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
99!
7052
        const origHeight = parentElement.offsetHeight;
99✔
7053
        this.nativeElement.style.display = 'none';
99✔
7054
        const height = parentElement.offsetHeight;
99✔
7055
        this.nativeElement.style.display = '';
99✔
7056
        return origHeight !== height;
99✔
7057
    }
7058

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

7075
    /**
7076
     * @hidden
7077
     * Gets calculated width of the unpinned area
7078
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
7079
     */
7080
    protected getUnpinnedWidth(takeHidden = false) {
23,563✔
7081
        let width = this.isPercentWidth ?
23,563✔
7082
            this.calcWidth :
7083
            parseInt(this.width, 10) || parseInt(this.hostWidth, 10) || this.calcWidth;
13,262✔
7084
        if (this.hasVerticalScroll() && !this.isPercentWidth) {
23,563✔
7085
            width -= this.scrollSize;
4,343✔
7086
        }
7087
        if (!this.isPinningToStart) {
23,563✔
7088
            width -= this.featureColumnsWidth();
62✔
7089
        }
7090

7091
        return width - this.getPinnedWidth(takeHidden);
23,563✔
7092
    }
7093

7094
    /**
7095
     * @hidden
7096
     */
7097
    protected _summaries(fieldName: string, hasSummary: boolean, summaryOperand?: any) {
7098
        const column = this.gridAPI.get_column_by_name(fieldName);
27✔
7099
        if (column) {
27✔
7100
            column.hasSummary = hasSummary;
27✔
7101
            if (summaryOperand) {
27✔
7102
                if (this.rootSummariesEnabled) {
2✔
7103
                    this.summaryService.retriggerRootPipe++;
2✔
7104
                }
7105
                column.summaries = summaryOperand;
2✔
7106
            }
7107
        }
7108
    }
7109

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

7128
    /**
7129
     * @hidden
7130
     */
7131
    public resolveDataTypes(rec) {
7132
        if (typeof rec === 'number') {
6,507✔
7133
            return GridColumnDataType.Number;
3,844✔
7134
        } else if (typeof rec === 'boolean') {
2,663✔
7135
            return GridColumnDataType.Boolean;
153✔
7136
        } else if (typeof rec === 'object' && rec instanceof Date) {
2,510✔
7137
            return GridColumnDataType.Date;
144✔
7138
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
2,366✔
7139
            return GridColumnDataType.Image;
1✔
7140
        }
7141
        return GridColumnDataType.String;
2,365✔
7142
    }
7143

7144
    /**
7145
     * @hidden
7146
     */
7147
    protected autogenerateColumns() {
7148
        const data = this.gridAPI.get_data();
664✔
7149
        const fields = this.generateDataFields(data);
664✔
7150
        const columns = [];
664✔
7151

7152
        this._autoGeneratedColsRefs.forEach(ref => ref.destroy());
664✔
7153
        this._autoGeneratedColsRefs = [];
664✔
7154
        fields.forEach((field) => {
664✔
7155
            const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
4,278✔
7156
            ref.instance.field = field;
4,278✔
7157
            ref.instance.dataType = this.resolveDataTypes(data[0][field]);
4,278✔
7158
            ref.changeDetectorRef.detectChanges();
4,278✔
7159
            this._autoGeneratedColsRefs.push(ref);
4,278✔
7160
            columns.push(ref.instance);
4,278✔
7161
        });
7162
        this._autoGeneratedCols = columns;
664✔
7163

7164
        this.updateColumns(columns);
664✔
7165
        this.columnsAutogenerated.emit({ columns: this._autoGeneratedCols });
664✔
7166
    }
7167

7168
    protected generateDataFields(data: any[]): string[] {
7169
        return Object.keys(data && data.length !== 0 ? data[0] : [])
670✔
7170
            .filter(key => !this.autoGenerateExclude.includes(key));
4,554✔
7171
    }
7172

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

7199
            if (cb) {
23,645✔
7200
                cb(column);
23,645✔
7201
            }
7202
        });
7203

7204
        this.updateColumns(collection);
3,650✔
7205

7206
        if (this.hasColumnLayouts) {
3,650✔
7207
            collection.forEach((column: IgxColumnComponent) => {
142✔
7208
                column.populateVisibleIndexes();
1,361✔
7209
            });
7210
        }
7211
    }
7212

7213
    /**
7214
     * @hidden
7215
     */
7216
    protected reinitPinStates() {
7217
        this._pinnedColumns = this._columns
203✔
7218
            .filter((c) => c.pinned).sort((a, b) => this._pinnedColumns.indexOf(a) - this._pinnedColumns.indexOf(b));
2,544✔
7219
        this._unpinnedColumns = this.hasColumnGroups ? this._columns.filter((c) => !c.pinned) :
2,259✔
7220
            this._columns.filter((c) => !c.pinned)
285✔
7221
                .sort((a, b) => this._unpinnedColumns.indexOf(a) - this._unpinnedColumns.indexOf(b));
211✔
7222
    }
7223

7224
    protected extractDataFromSelection(source: any[], formatters = false, headers = false, columnData?: any[]): any[] {
×
7225
        let columnsArray: IgxColumnComponent[];
7226
        let record = {};
246✔
7227
        let selectedData = [];
246✔
7228
        let keys = [];
246✔
7229
        const selectionCollection = new Map();
246✔
7230
        const keysAndData = [];
246✔
7231
        const activeEl = this.selectionService.activeElement;
246✔
7232

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

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

7278
        if (this.cellSelection === GridSelectionMode.single && activeEl) {
246✔
7279
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
10✔
7280
        }
7281

7282
        if (this.cellSelection === GridSelectionMode.none && activeEl) {
246✔
7283
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
5✔
7284
        }
7285

7286
        if (columnData) {
246!
7287
            selectedData = columnData;
×
7288
        }
7289

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

7339
        if (keys.length) {
246!
7340
            keysAndData.push(selectedData);
×
7341
            keysAndData.push(keys);
×
7342
            return keysAndData;
×
7343
        } else {
7344
            return selectedData;
246✔
7345
        }
7346
    }
7347

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

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

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

7412
        if (this.isColumnWidthSum) {
29✔
7413
            this.calcWidth = this.getColumnWidthSum();
2✔
7414
        }
7415
    }
7416

7417
    protected extractDataFromColumnsSelection(source: any[], formatters = false, headers = false): any[] {
×
7418
        let record = {};
20✔
7419
        const selectedData = [];
20✔
7420
        const selectedColumns = this.selectedColumns();
20✔
7421
        if (selectedColumns.length === 0) {
20✔
7422
            return [];
12✔
7423
        }
7424

7425
        for (const data of source) {
8✔
7426
            selectedColumns.forEach((col) => {
71✔
7427
                const key = headers ? col.header || col.field : col.field;
142!
7428
                record[key] = formatters && col.formatter ? col.formatter(data[col.field], data)
142!
7429
                    : data[col.field];
7430
            });
7431

7432
            if (Object.keys(record).length) {
71✔
7433
                selectedData.push(record);
71✔
7434
            }
7435
            record = {};
71✔
7436
        }
7437
        return selectedData;
8✔
7438
    }
7439

7440
    /**
7441
     * @hidden
7442
     */
7443
    protected initPinning() {
7444
        this.calculateGridWidth();
3,941✔
7445
        this.resetCaches();
3,941✔
7446
        this.handleColumnPinningForGroups();
3,941✔
7447
        this.notifyChanges();
3,941✔
7448
    }
7449

7450
    /**
7451
     * @hidden
7452
     */
7453
    protected scrollTo(row: any | number, column: any | number, inCollection = this._filteredSortedUnpinnedData): void {
3✔
7454
        let delayScrolling = false;
106✔
7455

7456
        if (this.paginator && typeof (row) !== 'number') {
106✔
7457
            const rowIndex = inCollection.indexOf(row);
16✔
7458
            const page = Math.floor(rowIndex / this.perPage);
16✔
7459

7460
            if (this.page !== page) {
16✔
7461
                delayScrolling = true;
5✔
7462
                this.page = page;
5✔
7463
            }
7464
        }
7465

7466
        if (delayScrolling) {
106✔
7467
            this.verticalScrollContainer.dataChanged.pipe(first(), takeUntil(this.destroy$)).subscribe(() => {
5✔
7468
                this.scrollDirective(this.verticalScrollContainer,
5✔
7469
                    typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
5!
7470
            });
7471
        } else {
7472
            this.scrollDirective(this.verticalScrollContainer,
101✔
7473
                typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
101✔
7474
        }
7475

7476
        this.scrollToHorizontally(column);
106✔
7477
    }
7478

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

7496
    /**
7497
     * @hidden
7498
     */
7499
    protected scrollDirective(directive: IgxGridForOfDirective<any, any[]>, goal: number): void {
7500
        if (!directive) {
381✔
7501
            return;
1✔
7502
        }
7503
        directive.scrollTo(goal);
380✔
7504
    }
7505

7506

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

7525
    /**
7526
     * Notify changes, reset cache and populateVisibleIndexes.
7527
     *
7528
     * @hidden
7529
     */
7530
    private _columnsReordered(column: IgxColumnComponent) {
7531
        this.notifyChanges();
128✔
7532
        // after reordering is done reset cached column collections.
7533
        this.resetColumnCollections();
128✔
7534
        column.resetCaches();
128✔
7535
    }
7536

7537
    protected buildDataView(_data: any[]) {
7538
        this._dataView = this.isRowPinningToTop ?
13,668✔
7539
            [...this.pinnedDataView, ...this.unpinnedDataView] :
7540
            [...this.unpinnedDataView, ...this.pinnedDataView];
7541
    }
7542

7543
    private _applyWidthHostBinding() {
7544
        let width = this._width;
7,937✔
7545
        if (width === null) {
7,937✔
7546
            let currentWidth = this.calcWidth;
2✔
7547
            if (this.hasVerticalScroll()) {
2!
7548
                currentWidth += this.scrollSize;
×
7549
            }
7550
            width = currentWidth + 'px';
2✔
7551
            this.resetCaches();
2✔
7552
        }
7553
        this._hostWidth = width;
7,937✔
7554
        this.cdr.markForCheck();
7,937✔
7555
    }
7556

7557
    protected verticalScrollHandler(event) {
7558
        this.verticalScrollContainer.onScroll(event);
317✔
7559
        this.disableTransitions = true;
317✔
7560

7561
        this.zone.run(() => {
317✔
7562
            this.zone.onStable.pipe(first()).subscribe(() => {
317✔
7563
                this.verticalScrollContainer.chunkLoad.emit(this.verticalScrollContainer.state);
317✔
7564
                if (this.rowEditable) {
317✔
7565
                    this.changeRowEditingOverlayStateOnScroll(this.crudService.rowInEditMode);
12✔
7566
                }
7567
            });
7568
        });
7569
        this.disableTransitions = false;
317✔
7570

7571
        this.hideOverlays();
317✔
7572
        this.actionStrip?.hide();
317✔
7573
        if (this.actionStrip) {
317✔
7574
            this.actionStrip.context = null;
7✔
7575
        }
7576
        const args: IGridScrollEventArgs = {
317✔
7577
            direction: 'vertical',
7578
            event,
7579
            scrollPosition: this.verticalScrollContainer.scrollPosition
7580
        };
7581
        this.gridScroll.emit(args);
317✔
7582
    }
7583

7584
    protected horizontalScrollHandler(event) {
7585
        const scrollLeft = event.target.scrollLeft;
308✔
7586
        this.headerContainer.onHScroll(scrollLeft);
308✔
7587
        this._horizontalForOfs.forEach(vfor => vfor.onHScroll(scrollLeft));
1,940✔
7588
        this.cdr.markForCheck();
308✔
7589

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

7605
    protected get renderedActualRowHeight() {
7606
        let border = 1;
503✔
7607
        if (this.rowList.toArray().length > 0) {
503✔
7608
            const rowStyles = this.document.defaultView.getComputedStyle(this.rowList.first.nativeElement);
353✔
7609
            border = rowStyles.borderBottomWidth ? Math.ceil(parseFloat(rowStyles.borderBottomWidth)) : border;
353✔
7610
        }
7611
        return this.rowHeight + border;
503✔
7612
    }
7613

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

7634
            return;
8✔
7635
        }
7636
        const args = this.getNavigationArguments(row, visibleColIndex);
542✔
7637
        cb(args);
542✔
7638
    }
7639

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

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

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

7693

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

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

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

7733
    private find(text: string, increment: number, caseSensitive?: boolean, exactMatch?: boolean, scroll?: boolean, endEdit = true) {
195✔
7734
        if (!this.rowList) {
322!
7735
            return 0;
×
7736
        }
7737

7738
        if (endEdit) {
322✔
7739
            this.crudService.endEdit(false);
283✔
7740
        }
7741

7742
        if (!text) {
322!
7743
            this.clearSearch();
×
7744
            return 0;
×
7745
        }
7746

7747
        const caseSensitiveResolved = caseSensitive ? true : false;
322✔
7748
        const exactMatchResolved = exactMatch ? true : false;
322✔
7749
        let rebuildCache = false;
322✔
7750

7751
        if (this._lastSearchInfo.searchText !== text ||
322✔
7752
            this._lastSearchInfo.caseSensitive !== caseSensitiveResolved ||
7753
            this._lastSearchInfo.exactMatch !== exactMatchResolved) {
7754
            this._lastSearchInfo = {
84✔
7755
                searchText: text,
7756
                activeMatchIndex: 0,
7757
                caseSensitive: caseSensitiveResolved,
7758
                exactMatch: exactMatchResolved,
7759
                matchInfoCache: [],
7760
                matchCount: 0,
7761
                content: ''
7762
            };
7763

7764
            rebuildCache = true;
84✔
7765
        } else {
7766
            this._lastSearchInfo.activeMatchIndex += increment;
238✔
7767
        }
7768

7769
        if (rebuildCache) {
322✔
7770
            this.rowList.forEach((row) => {
84✔
7771
                if (row.cells) {
784✔
7772
                    row.cells.forEach((c: IgxGridCellComponent) => {
740✔
7773
                        c.highlightText(text, caseSensitiveResolved, exactMatchResolved);
3,097✔
7774
                    });
7775
                }
7776
            });
7777

7778
            this.rebuildMatchCache();
84✔
7779
        }
7780

7781
        if (this._lastSearchInfo.activeMatchIndex >= this._lastSearchInfo.matchCount) {
322✔
7782
            this._lastSearchInfo.activeMatchIndex = 0;
28✔
7783
        } else if (this._lastSearchInfo.activeMatchIndex < 0) {
294✔
7784
            this._lastSearchInfo.activeMatchIndex = this._lastSearchInfo.matchCount - 1;
8✔
7785
        }
7786

7787
        if (this._lastSearchInfo.matchCount > 0) {
322✔
7788
            const matchInfo = this._lastSearchInfo.matchInfoCache[this._lastSearchInfo.activeMatchIndex];
304✔
7789
            this._lastSearchInfo = { ...this._lastSearchInfo };
304✔
7790

7791
            if (scroll !== false) {
304✔
7792
                this.scrollTo(matchInfo.row, matchInfo.column);
182✔
7793
            }
7794

7795
            this.textHighlightService.setActiveHighlight(this.id, {
304✔
7796
                column: matchInfo.column,
7797
                row: matchInfo.row,
7798
                index: matchInfo.index,
7799
                metadata: matchInfo.metadata,
7800
            });
7801

7802
        } else {
7803
            this.textHighlightService.clearActiveHighlight(this.id);
18✔
7804
        }
7805

7806
        return this._lastSearchInfo.matchCount;
322✔
7807
    }
7808

7809
    private rebuildMatchCache() {
7810
        this._lastSearchInfo.matchInfoCache = [];
211✔
7811

7812
        const caseSensitive = this._lastSearchInfo.caseSensitive;
211✔
7813
        const exactMatch = this._lastSearchInfo.exactMatch;
211✔
7814
        const searchText = caseSensitive ? this._lastSearchInfo.searchText : this._lastSearchInfo.searchText.toLowerCase();
211✔
7815
        const data = this.filteredSortedData;
211✔
7816
        const columnItems = this.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex);
908✔
7817
        const columnsPathParts = columnItems.map(col => columnFieldPath(col.field));
891✔
7818

7819
        data.forEach((dataRow, rowIndex) => {
211✔
7820
            columnItems.forEach((c, cid) => {
3,474✔
7821
                const pipeArgs = this.getColumnByName(c.field).pipeArgs;
14,791✔
7822
                const value = c.formatter ? c.formatter(resolveNestedPath(dataRow, columnsPathParts[cid]), dataRow) :
14,791!
7823
                    c.dataType === 'number' ? formatNumber(resolveNestedPath(dataRow, columnsPathParts[cid]) as number, this.locale, pipeArgs.digitsInfo) :
14,791✔
7824
                        c.dataType === 'date'
11,115✔
7825
                            ? formatDate(resolveNestedPath(dataRow, columnsPathParts[cid]) as string, pipeArgs.format, this.locale, pipeArgs.timezone)
7826
                            : resolveNestedPath(dataRow, columnsPathParts[cid]);
7827
                if (value !== undefined && value !== null && c.searchable) {
14,791✔
7828
                    let searchValue = caseSensitive ? String(value) : String(value).toLowerCase();
14,576✔
7829

7830
                    if (exactMatch) {
14,576✔
7831
                        if (searchValue === searchText) {
536✔
7832
                            const mic: IMatchInfoCache = {
9✔
7833
                                row: dataRow,
7834
                                column: c.field,
7835
                                index: 0,
7836
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7837
                            };
7838

7839
                            this._lastSearchInfo.matchInfoCache.push(mic);
9✔
7840
                        }
7841
                    } else {
7842
                        let occurrenceIndex = 0;
14,040✔
7843
                        let searchIndex = searchValue.indexOf(searchText);
14,040✔
7844

7845
                        while (searchIndex !== -1) {
14,040✔
7846
                            const mic: IMatchInfoCache = {
2,410✔
7847
                                row: dataRow,
7848
                                column: c.field,
7849
                                index: occurrenceIndex++,
7850
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7851
                            };
7852

7853
                            this._lastSearchInfo.matchInfoCache.push(mic);
2,410✔
7854

7855
                            searchValue = searchValue.substring(searchIndex + searchText.length);
2,410✔
7856
                            searchIndex = searchValue.indexOf(searchText);
2,410✔
7857
                        }
7858
                    }
7859
                }
7860
            });
7861
        });
7862

7863
        this._lastSearchInfo.matchCount = this._lastSearchInfo.matchInfoCache.length;
211✔
7864
    }
7865

7866
    protected updateDefaultRowHeight() {
7867
        if (this.dataRowList.length > 0 && this.dataRowList.first.cells && this.dataRowList.first.cells.length > 0) {
3,674✔
7868
            const height = parseFloat(this.document.defaultView.getComputedStyle(this.dataRowList.first.cells.first.nativeElement)?.getPropertyValue('height'));
121✔
7869
            if (height) {
121!
7870
                this._defaultRowHeight = height;
121✔
7871
            } else {
7872
                this._shouldRecalcRowHeight = true;
×
7873
            }
7874
        }
7875
    }
7876

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

7897
    private handleColumnPinningForGroups(): void {
7898
        // When a column is a group or is inside a group, pin all related.
7899
        const pinnedColumns = [];
3,941✔
7900
        const unpinnedColumns = [];
3,941✔
7901

7902
        this._pinnedColumns.forEach(col => {
3,941✔
7903
            if (col.parent) {
492✔
7904
                col.parent.pinned = true;
113✔
7905
            }
7906
            if (col.columnGroup) {
492✔
7907
                col.children.forEach(child => child.pinned = true);
112✔
7908
            }
7909
        });
7910

7911
        // Make sure we don't exceed unpinned area min width and get pinned and unpinned col collections.
7912
        // We take into account top level columns (top level groups and non groups).
7913
        // If top level is unpinned the pinning handles all children to be unpinned as well.
7914
        for (const column of this._columns) {
3,941✔
7915
            if (column.pinned && !column.parent) {
23,293✔
7916
                pinnedColumns.push(column);
380✔
7917
            } else if (column.pinned && column.parent) {
22,913✔
7918
                if (column.topLevelParent.pinned) {
115!
7919
                    pinnedColumns.push(column);
115✔
7920
                } else {
7921
                    column.pinned = false;
×
7922
                    unpinnedColumns.push(column);
×
7923
                }
7924
            } else {
7925
                unpinnedColumns.push(column);
22,798✔
7926
            }
7927
        }
7928
        // Assign the applicable collections.
7929
        this._pinnedColumns = pinnedColumns;
3,941✔
7930
        this._unpinnedColumns = unpinnedColumns;
3,941✔
7931
    }
7932

7933
    protected shouldRecreateColumns(oldData: any[] | null | undefined, newData: any[] | null | undefined): boolean {
7934
        if (!oldData || !oldData.length) return true;
72✔
7935
        if (!newData || !newData.length) return false;
45!
7936
        return Object.keys(oldData[0]).join() !== Object.keys(newData[0]).join();
45✔
7937
    }
7938

7939
    /**
7940
     * Clears the current navigation service active node
7941
     */
7942
    private clearActiveNode() {
7943
        this.navigation.lastActiveNode = this.navigation.activeNode;
76✔
7944
        this.navigation.activeNode = {} as IActiveNode;
76✔
7945
        this.notifyChanges();
76✔
7946
    }
7947

7948
    private getRecreatedTree(value: IFilteringExpressionsTree): IFilteringExpressionsTree {
7949
        if (this._hGridSchema) {
9,486✔
7950
            return recreateTree(value, this._hGridSchema, true) as IFilteringExpressionsTree;
16✔
7951
        } else {
7952
            return recreateTreeFromFields(value, this._columns) as IFilteringExpressionsTree;
9,470✔
7953
        }
7954
    }
7955
}
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