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

IgniteUI / igniteui-angular / 14665472814

25 Apr 2025 01:14PM UTC coverage: 91.6%. Remained the same
14665472814

Pull #15749

github

web-flow
Merge ee911cfac into 01141c951
Pull Request #15749: chore(*): expose change events for analyzer

13422 of 15700 branches covered (85.49%)

27011 of 29488 relevant lines covered (91.6%)

34329.17 hits per line

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

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

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

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

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

211
    /**
212
     * Gets/Sets the display time for the row adding snackbar notification.
213
     *
214
     * @remarks
215
     * By default it is 6000ms.
216
     */
217
    @Input()
218
    public snackbarDisplayTime = 6000;
4,113✔
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,113✔
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,113✔
249

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

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

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

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

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

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

309
    /** @hidden @internal */
310
    public get hasColumnsToAutosize() {
311
        return this._columns.some(x => x.width === 'fit-content');
89,459✔
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;
51,887✔
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,113✔
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,113✔
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,113✔
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,113✔
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,113✔
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,326,841✔
448
    }
449

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

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

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

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

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

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

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

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

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

1076

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

1090
    /* End of toolbar related definitions */
1091

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

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

1105
    /**
1106
     * Emitted when the locale of the grid is changed.
1107
     *
1108
     * @example
1109
     * ```html
1110
     * <igx-grid [data]="employeeData" (localeChange)="localeChange($event)" [autoGenerate]="true"></igx-grid>
1111
     * ```
1112
     */
1113
    @Output()
1114
    public localeChange = new EventEmitter<boolean>();
4,113✔
1115

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

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

1138

1139
    /**
1140
     * @hidden @internal
1141
     */
1142
    @ViewChild(IgxSnackbarComponent)
1143
    public addRowSnackbar: IgxSnackbarComponent;
1144

1145
    /**
1146
     * @hidden @internal
1147
     */
1148
    @ViewChild(IgxGridColumnResizerComponent)
1149
    public resizeLine: IgxGridColumnResizerComponent;
1150

1151
    /**
1152
     * @hidden @internal
1153
     */
1154
    @ViewChild('loadingOverlay', { read: IgxToggleDirective, static: true })
1155
    public loadingOverlay: IgxToggleDirective;
1156

1157
    /**
1158
     * @hidden @internal
1159
     */
1160
    @ViewChild('igxLoadingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1161
    public loadingOutlet: IgxOverlayOutletDirective;
1162

1163
    /* reactContentChildren */
1164
    /* blazorInclude */
1165
    /* blazorTreatAsCollection */
1166
    /* blazorCollectionName: ColumnCollection */
1167
    /* ngQueryListName: columnList */
1168
    /**
1169
     * @hidden @internal
1170
     */
1171
    @ContentChildren(IgxColumnComponent, { read: IgxColumnComponent, descendants: true })
1172
    public columnList: QueryList<IgxColumnComponent> = new QueryList<IgxColumnComponent>();
4,113✔
1173

1174
    /* contentChildren */
1175
    /* blazorInclude */
1176
    /* blazorTreatAsCollection */
1177
    /* blazorCollectionName: ActionStripCollection */
1178
    /* blazorCollectionItemName: ActionStrip */
1179
    /* ngQueryListName: actionStripComponents */
1180
    /** @hidden @internal */
1181
    @ContentChildren(IgxActionStripToken)
1182
    protected actionStripComponents: QueryList<IgxActionStripToken>;
1183

1184
    /** @hidden @internal */
1185
    public get actionStrip() {
1186
        return this.actionStripComponents?.first;
4,770✔
1187
    }
1188

1189
    /**
1190
     * @hidden @internal
1191
     */
1192
    @ContentChild(IgxExcelStyleLoadingValuesTemplateDirective, { read: IgxExcelStyleLoadingValuesTemplateDirective, static: true })
1193
    public excelStyleLoadingValuesTemplateDirective: IgxExcelStyleLoadingValuesTemplateDirective;
1194

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

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

1203
    /**
1204
     * @hidden @internal
1205
     */
1206
    @ViewChild('defaultLoadingGrid', { read: TemplateRef, static: true })
1207
    public loadingGridDefaultTemplate: TemplateRef<any>;
1208

1209
    /**
1210
     * @hidden @internal
1211
     */
1212
    @ViewChild('scrollContainer', { read: IgxGridForOfDirective, static: true })
1213
    public parentVirtDir: IgxGridForOfDirective<any, any[]>;
1214

1215
    /**
1216
     * @hidden
1217
     * @internal
1218
     */
1219
    @ContentChildren(IgxHeadSelectorDirective, { read: TemplateRef, descendants: false })
1220
    public headSelectorsTemplates: QueryList<TemplateRef<IgxHeadSelectorTemplateContext>>;
1221

1222
    /**
1223
     * @hidden
1224
     * @internal
1225
     */
1226
    @ContentChildren(IgxRowSelectorDirective, { read: TemplateRef, descendants: false })
1227
    public rowSelectorsTemplates: QueryList<TemplateRef<IgxRowSelectorTemplateContext>>;
1228

1229
    /**
1230
     * @hidden
1231
     * @internal
1232
     */
1233
    @ContentChildren(IgxRowDragGhostDirective, { read: TemplateRef, descendants: false })
1234
    public dragGhostCustomTemplates: QueryList<TemplateRef<IgxGridRowDragGhostContext>>;
1235

1236

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

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

1262

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

1269
    /**
1270
     * @hidden @internal
1271
     */
1272
    @ViewChild('verticalScrollHolder', { read: IgxGridForOfDirective, static: true })
1273
    public verticalScroll: IgxGridForOfDirective<any, any[]>;
1274

1275
    /**
1276
     * @hidden @internal
1277
     */
1278
    @ViewChild('scr', { read: ElementRef, static: true })
1279
    public scr: ElementRef;
1280

1281
    /** @hidden @internal */
1282
    @ViewChild('headSelectorBaseTemplate', { read: TemplateRef, static: true })
1283
    public headerSelectorBaseTemplate: TemplateRef<any>;
1284

1285
    /**
1286
     * @hidden @internal
1287
     */
1288
    @ViewChild('footer', { read: ElementRef })
1289
    public footer: ElementRef;
1290

1291
    /** @hidden @internal */
1292
    public get headerContainer() {
1293
        return this.theadRow?.headerForOf;
14,714✔
1294
    }
1295

1296
    /** @hidden @internal */
1297
    public get headerSelectorContainer() {
1298
        return this.theadRow?.headerSelectorContainer;
30,554✔
1299
    }
1300

1301
    /** @hidden @internal */
1302
    public get headerDragContainer() {
1303
        return this.theadRow?.headerDragContainer;
475✔
1304
    }
1305

1306
    /** @hidden @internal */
1307
    public get headerGroupContainer() {
1308
        return this.theadRow?.headerGroupContainer;
29,806✔
1309
    }
1310

1311
    /** @hidden @internal */
1312
    public get filteringRow(): IgxGridFilteringRowComponent {
1313
        return this.theadRow?.filterRow;
1,324✔
1314
    }
1315

1316
    /** @hidden @internal */
1317
    @ViewChild(IgxGridHeaderRowComponent, { static: true })
1318
    public theadRow: IgxGridHeaderRowComponent;
1319

1320
    /** @hidden @internal */
1321
    @ViewChild(IgxGridGroupByAreaComponent)
1322
    public groupArea: IgxGridGroupByAreaComponent;
1323

1324
    /**
1325
     * @hidden @internal
1326
     */
1327
    @ViewChild('tbody', { static: true })
1328
    public tbody: ElementRef;
1329

1330
    @ViewChild(IgxGridBodyDirective, { static: true, read: ElementRef })
1331
    protected tbodyContainer: ElementRef;
1332

1333
    /**
1334
     * @hidden @internal
1335
     */
1336
    @ViewChild('pinContainer', { read: ElementRef })
1337
    public pinContainer: ElementRef;
1338

1339
    /**
1340
     * @hidden @internal
1341
     */
1342
    @ViewChild('tfoot', { static: true })
1343
    public tfoot: ElementRef<HTMLElement>;
1344

1345
    /**
1346
     * @hidden @internal
1347
     */
1348
    @ViewChild('igxRowEditingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1349
    public rowEditingOutletDirective: IgxOverlayOutletDirective;
1350

1351
    /**
1352
     * @hidden @internal
1353
     */
1354
    @ViewChildren(IgxTemplateOutletDirective, { read: IgxTemplateOutletDirective })
1355
    public tmpOutlets: QueryList<any> = new QueryList<any>();
4,113✔
1356

1357
    /**
1358
     * @hidden
1359
     * @internal
1360
     */
1361
    @ViewChild('dragIndicatorIconBase', { read: TemplateRef, static: true })
1362
    public dragIndicatorIconBase: TemplateRef<any>;
1363

1364
    /**
1365
     * @hidden @internal
1366
     */
1367
    @ContentChildren(IgxRowEditTemplateDirective, { descendants: false, read: TemplateRef })
1368
    public rowEditCustomDirectives: QueryList<TemplateRef<IgxGridRowEditTemplateContext>>;
1369

1370
    /**
1371
     * @hidden @internal
1372
     */
1373
    @ContentChildren(IgxRowEditTextDirective, { descendants: false, read: TemplateRef })
1374
    public rowEditTextDirectives: QueryList<TemplateRef<IgxGridRowEditTextTemplateContext>>;
1375

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

1400
    /**
1401
     * @hidden @internal
1402
     */
1403
    @ContentChild(IgxRowAddTextDirective, { read: TemplateRef })
1404
    public rowAddText: TemplateRef<IgxGridEmptyTemplateContext>;
1405

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

1430
    /**
1431
     * @hidden @internal
1432
     */
1433
    @ContentChildren(IgxRowEditActionsDirective, { descendants: false, read: TemplateRef })
1434
    public rowEditActionsDirectives: QueryList<TemplateRef<IgxGridRowEditActionsTemplateContext>>;
1435

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

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

1467
    /**
1468
     * Gets the row expand indicator template.
1469
    */
1470
    @Input()
1471
    public get rowExpandedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1472
        return this._rowExpandedIndicatorTemplate || this.rowExpandedIndicatorDirectiveTemplate;
12,252✔
1473
    }
1474

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

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

1498
    /**
1499
     * Gets the row collapse indicator template.
1500
    */
1501
    @Input()
1502
    public get rowCollapsedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1503
        return this._rowCollapsedIndicatorTemplate || this.rowCollapsedIndicatorDirectiveTemplate;
51,067✔
1504
    }
1505

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

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

1529
    /**
1530
     * Gets the header expand indicator template.
1531
    */
1532
    @Input()
1533
    public get headerExpandedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
1534
        return this._headerExpandIndicatorTemplate || this.headerExpandedIndicatorDirectiveTemplate;
13,973✔
1535
    }
1536

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

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

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

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

1585
    /** @hidden @internal */
1586
    @ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef })
1587
    public excelStyleHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,113✔
1588

1589
    /**
1590
     * Gets the excel style header icon.
1591
    */
1592
    @Input()
1593
    public get excelStyleHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1594
        return this._excelStyleHeaderIconTemplate || this.excelStyleHeaderIconDirectiveTemplate;
12,387✔
1595
    }
1596

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

1614

1615
    /**
1616
     * @hidden
1617
     * @internal
1618
     */
1619
    @ContentChild(IgxSortAscendingHeaderIconDirective, { read: TemplateRef })
1620
    public sortAscendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,113✔
1621

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

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

1647
    /** @hidden @internal */
1648
    @ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef })
1649
    public sortDescendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,113✔
1650

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

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

1676
    /**
1677
     * @hidden
1678
     * @internal
1679
     */
1680
    @ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef })
1681
    public sortHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,113✔
1682

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

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

1708
    /**
1709
     * @hidden
1710
     * @internal
1711
     */
1712
    @ContentChildren(IgxDragIndicatorIconDirective, { read: TemplateRef, descendants: false })
1713
    public dragIndicatorIconTemplates: QueryList<TemplateRef<IgxGridEmptyTemplateContext>>;
1714

1715
    /**
1716
     * @hidden @internal
1717
     */
1718
    @ViewChildren(IgxRowEditTabStopDirective)
1719
    public rowEditTabsDEFAULT: QueryList<IgxRowEditTabStopDirective>;
1720

1721
    /**
1722
     * @hidden @internal
1723
     */
1724
    @ContentChildren(IgxRowEditTabStopDirective, { descendants: true })
1725
    public rowEditTabsCUSTOM: QueryList<IgxRowEditTabStopDirective>;
1726

1727
    /**
1728
     * @hidden @internal
1729
     */
1730
    @ViewChild('rowEditingOverlay', { read: IgxToggleDirective })
1731
    public rowEditingOverlay: IgxToggleDirective;
1732

1733
    /**
1734
     * @hidden @internal
1735
     */
1736
    @HostBinding('attr.tabindex')
1737
    public tabindex = 0;
4,113✔
1738

1739
    /**
1740
     * @hidden @internal
1741
     */
1742
    @HostBinding('attr.role')
1743
    public hostRole = 'grid';
4,113✔
1744

1745
    /* contentChildren */
1746
    /* blazorInclude */
1747
    /* blazorTreatAsCollection */
1748
    /* blazorCollectionName: GridToolbarCollection */
1749
    /* ngQueryListName: toolbar */
1750
    /** @hidden @internal */
1751
    @ContentChildren(IgxToolbarToken)
1752
    public toolbar: QueryList<IgxGridToolbarComponent>;
1753

1754
    /* contentChildren */
1755
    /* blazorInclude */
1756
    /* blazorTreatAsCollection */
1757
    /* blazorCollectionName: PaginatorCollection */
1758
    /* ngQueryListName: paginationComponents */
1759
    /** @hidden @internal */
1760
    @ContentChildren(IgxPaginatorToken)
1761
    protected paginationComponents: QueryList<IgxPaginatorComponent>;
1762

1763
    /**
1764
     * @hidden @internal
1765
     */
1766
    @ViewChild('igxFilteringOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1767
    protected _outletDirective: IgxOverlayOutletDirective;
1768

1769
    /**
1770
     * @hidden @internal
1771
     * @igxElementsAnchor
1772
     */
1773
    @ViewChild('sink', { read: ViewContainerRef, static: true })
1774
    public anchor: ViewContainerRef;
1775

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

1782
    /**
1783
     * @hidden @internal
1784
     */
1785
    @ViewChild('defaultCollapsedTemplate', { read: TemplateRef, static: true })
1786
    protected defaultCollapsedTemplate: TemplateRef<any>;
1787

1788
    /**
1789
     * @hidden @internal
1790
     */
1791
    @ViewChild('defaultESFHeaderIcon', { read: TemplateRef, static: true })
1792
    protected defaultESFHeaderIconTemplate: TemplateRef<any>;
1793

1794
    @ViewChildren('summaryRow', { read: IgxSummaryRowComponent })
1795
    protected _summaryRowList: QueryList<IgxSummaryRowComponent>;
1796

1797
    @ViewChildren('row')
1798
    private _rowList: QueryList<IgxGridRowComponent>;
1799

1800
    @ViewChildren('pinnedRow')
1801
    private _pinnedRowList: QueryList<IgxGridRowComponent>;
1802

1803
    /**
1804
     * @hidden @internal
1805
     */
1806
    @ViewChild('defaultRowEditTemplate', { read: TemplateRef, static: true })
1807
    private defaultRowEditTemplate: TemplateRef<IgxGridRowEditTemplateContext>;
1808

1809
    @ViewChildren(IgxRowDirective, { read: IgxRowDirective })
1810
    private _dataRowList: QueryList<IgxRowDirective>;
1811

1812
    @HostBinding('class.igx-grid')
1813
    protected baseClass = 'igx-grid';
4,113✔
1814

1815

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

1827
    public get resourceStrings(): IGridResourceStrings {
1828
        return this._resourceStrings;
232,072✔
1829
    }
1830

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

1847
    public set filteringLogic(value: FilteringLogic) {
1848
        this._filteringExpressionsTree.operator = value;
3✔
1849
    }
1850

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

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

1878
            value.type = FilteringExpressionsTreeType.Regular;
915✔
1879
            if (value && this._columns?.length > 0) {
915✔
1880
                this._filteringExpressionsTree = this.getRecreatedTree(value);
768✔
1881
            } else {
1882
                this._filteringExpressionsTree = value;
147✔
1883
            }
1884
            this.filteringPipeTrigger++;
915✔
1885
            this.filteringExpressionsTreeChange.emit(this._filteringExpressionsTree);
915✔
1886

1887
            if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
915✔
1888
                this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1889
                this._filteredData = null;
461✔
1890
            }
1891

1892
            this.filteringService.refreshExpressions();
915✔
1893
            this.selectionService.clearHeaderCBState();
915✔
1894
            this.summaryService.clearSummaryCache();
915✔
1895
            this.notifyChanges();
915✔
1896
        }
1897
    }
1898

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

1914
    public set advancedFilteringExpressionsTree(value) {
1915
        const filteringEventArgs: IFilteringEventArgs = {
73✔
1916
            owner: this,
1917
            filteringExpressions: value,
1918
            cancel: false
1919
        };
1920

1921
        this.filtering.emit(filteringEventArgs);
73✔
1922

1923
        if (filteringEventArgs.cancel) {
73✔
1924
            return;
1✔
1925
        }
1926

1927
        if (value && isTree(value)) {
72✔
1928
            value.type = FilteringExpressionsTreeType.Advanced;
58✔
1929
            if (this._columns && this._columns.length > 0) {
58✔
1930
                this._advancedFilteringExpressionsTree = this.getRecreatedTree(value);
53✔
1931
            } else {
1932
                this._advancedFilteringExpressionsTree = value;
5✔
1933
            }
1934
            this.filteringPipeTrigger++;
58✔
1935
        } else {
1936
            this._advancedFilteringExpressionsTree = null;
14✔
1937
        }
1938
        this.advancedFilteringExpressionsTreeChange.emit(this._advancedFilteringExpressionsTree);
72✔
1939

1940
        if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
72✔
1941
            this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1942
            this._filteredData = null;
19✔
1943
        }
1944

1945
        this.selectionService.clearHeaderCBState();
72✔
1946
        this.summaryService.clearSummaryCache();
72✔
1947
        this.notifyChanges();
72✔
1948

1949
        // Wait for the change detection to update filtered data through the pipes and then emit the event.
1950
        requestAnimationFrame(() => this.filteringDone.emit(this._advancedFilteringExpressionsTree));
72✔
1951
    }
1952

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

1964
    public set locale(value: string) {
1965
        if (value !== this._locale) {
4,130✔
1966
            this._locale = value;
4,124✔
1967
            this._currencyPositionLeft = undefined;
4,124✔
1968
            this.summaryService.clearSummaryCache();
4,124✔
1969
            this.pipeTrigger++;
4,124✔
1970
            this.notifyChanges();
4,124✔
1971
            this.localeChange.emit();
4,124✔
1972
        }
1973
    }
1974

1975
    @Input()
1976
    public get pagingMode() {
1977
        return this._pagingMode;
8,419✔
1978
    }
1979

1980
    public set pagingMode(val: GridPagingMode) {
1981
        this._pagingMode = val;
2✔
1982
        this.pipeTrigger++;
2✔
1983
        this.notifyChanges(true);
2✔
1984
    }
1985

1986
    /** @hidden @internal */
1987
    public get page(): number {
1988
        return this.paginator?.page || 0;
2,235,310✔
1989
    }
1990

1991
    public set page(val: number) {
1992
        if (this.paginator) {
713✔
1993
            this.paginator.page = val;
330✔
1994
        }
1995
    }
1996

1997
    /** @hidden @internal */
1998
    public get perPage(): number {
1999
        return this.paginator?.perPage || DEFAULT_ITEMS_PER_PAGE;
2,235,223✔
2000
    }
2001

2002
    public set perPage(val: number) {
2003
        if (this.paginator) {
20✔
2004
            this.paginator.perPage = val;
20✔
2005
        }
2006
    }
2007

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

2020
    public set hideRowSelectors(value: boolean) {
2021
        this._hideRowSelectors = value;
21✔
2022
        this.notifyChanges(true);
21✔
2023
    }
2024

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

2038
    public set rowDraggable(val: boolean) {
2039
        this._rowDrag = val;
47✔
2040
        this.notifyChanges(true);
47✔
2041
    }
2042

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

2054
    /**
2055
     * @hidden
2056
     * @internal
2057
     */
2058
    public rowDragging = false;
4,113✔
2059

2060
    /** @hidden @internal */
2061
    public dragRowID = null;
4,113✔
2062

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

2079
    public set rowEditable(val: boolean) {
2080
        if (!this._init) {
550✔
2081
            this.refreshGridState();
35✔
2082
        }
2083
        this._rowEditable = val;
550✔
2084
        this.notifyChanges();
550✔
2085
    }
2086

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

2102
    public set height(value: string | null) {
2103
        if (this._height !== value) {
3,071✔
2104
            this._height = value;
3,069✔
2105
            this.nativeElement.style.height = value;
3,069✔
2106
            this.notifyChanges(true);
3,069✔
2107
        }
2108
    }
2109

2110
    /**
2111
     * @hidden @internal
2112
     */
2113
    @HostBinding('style.width')
2114
    public get hostWidth() {
2115
        return this._width || this._hostWidth;
33,129✔
2116
    }
2117

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

2132
    public set width(value: string | null) {
2133
        if (this._width !== value) {
2,077✔
2134
            this._width = value;
2,077✔
2135
            this.nativeElement.style.width = value;
2,077✔
2136
            this.notifyChanges(true);
2,077✔
2137
        }
2138
    }
2139

2140
    /** @hidden @internal */
2141
    public get headerWidth() {
2142
        return parseInt(this.width, 10) - 17;
×
2143
    }
2144

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

2159
    public set rowHeight(value: number | string) {
2160
        if (typeof value !== 'number') {
2✔
2161
            value = parseInt(value, 10);
1✔
2162
        }
2163
        this._rowHeight = value;
2✔
2164
    }
2165

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

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

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

2224
    public get isLoading(): boolean {
2225
        return this._isLoading;
94,311✔
2226
    }
2227

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

2245
    public set shouldGenerate(value: boolean) {
2246
        this.autoGenerate = value;
×
2247
    }
2248

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

2262
    public get emptyFilteredGridMessage(): string {
2263
        return this._emptyFilteredGridMessage || this.resourceStrings.igx_grid_emptyFilteredGrid_message;
205✔
2264
    }
2265

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

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

2302
    public set allowFiltering(value) {
2303
        if (this._allowFiltering !== value) {
744✔
2304
            this._allowFiltering = value;
733✔
2305
            this.filteringService.registerSVGIcons();
733✔
2306

2307

2308
            this.filteringService.isFilterRowVisible = false;
733✔
2309
            this.filteringService.filteredColumn = null;
733✔
2310

2311
            this.notifyChanges(true);
733✔
2312
        }
2313
    }
2314

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

2328
    public set allowAdvancedFiltering(value) {
2329
        if (this._allowAdvancedFiltering !== value) {
54✔
2330
            this._allowAdvancedFiltering = value;
53✔
2331
            this.filteringService.registerSVGIcons();
53✔
2332

2333
            if (!this._init) {
53✔
2334
                this.notifyChanges(true);
5✔
2335
            }
2336
        }
2337
    }
2338

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

2354
    public set filterMode(value: FilterMode) {
2355
        switch (value) {
188!
2356
            case FilterMode.excelStyleFilter:
2357
            case FilterMode.quickFilter:
2358
                this._filterMode = value;
188✔
2359
                break;
188✔
2360
            default:
2361
                break;
×
2362
        }
2363

2364
        if (this.filteringService.isFilterRowVisible) {
188✔
2365
            this.filteringRow.close();
1✔
2366
        }
2367
        this.notifyChanges(true);
188✔
2368
    }
2369

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

2385
    public set summaryPosition(value: GridSummaryPosition) {
2386
        this._summaryPosition = value;
18✔
2387
        this.notifyChanges();
18✔
2388
    }
2389

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

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

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

2430
    public set showSummaryOnCollapse(value: boolean) {
2431
        this._showSummaryOnCollapse = value;
9✔
2432
        this.notifyChanges();
9✔
2433
    }
2434

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

2448
    public set filterStrategy(classRef: IFilteringStrategy) {
2449
        this._filterStrategy = classRef;
38✔
2450
    }
2451

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

2465
    public set sortStrategy(value: IGridSortingStrategy) {
2466
        this._sortingStrategy = value;
14✔
2467
    }
2468

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

2491
    public get sortingOptions() {
2492
        return this._sortingOptions;
28,827✔
2493
    }
2494

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

2514
    public get selectedRows(): any[] {
2515
        return this.selectionService.getSelectedRows();
731✔
2516
    }
2517

2518

2519
    /** @hidden @internal */
2520
    public get headerGroupsList(): IgxGridHeaderGroupComponent[] {
2521
        return this.theadRow.groups;
20,603✔
2522
    }
2523

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

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

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

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

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

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

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

2612
    /**
2613
     * @hidden
2614
     * @internal
2615
     */
2616
    public get isPinningToStart() {
2617
        return this.pinning.columns !== ColumnPinningPosition.End;
2,448,192✔
2618
    }
2619

2620
    /**
2621
     * @hidden
2622
     * @internal
2623
     */
2624
    public get isRowPinningToTop() {
2625
        return this.pinning.rows !== RowPinningPosition.Bottom;
676,231✔
2626
    }
2627

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

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

2653
    /**
2654
     * @hidden @internal
2655
     */
2656
    public get rowOutletDirective() {
2657
        return this.rowEditingOutletDirective;
4,389✔
2658
    }
2659

2660
    /**
2661
     * @hidden @internal
2662
     */
2663
    public get parentRowOutletDirective() {
2664
        return this.outlet;
1✔
2665
    }
2666

2667
    /**
2668
     * @hidden @internal
2669
     */
2670
    public get rowEditCustom(): TemplateRef<IgxGridRowEditTemplateContext> {
2671
        if (this.rowEditCustomDirectives && this.rowEditCustomDirectives.first) {
6,537✔
2672
            return this.rowEditCustomDirectives.first;
62✔
2673
        }
2674
        return null;
6,475✔
2675
    }
2676

2677
    /**
2678

2679
    /**
2680
     * @hidden @internal
2681
     */
2682
    public get rowEditContainer(): TemplateRef<IgxGridRowEditTemplateContext> {
2683
        return this.rowEditCustom ? this.rowEditCustom : this.defaultRowEditTemplate;
5,968✔
2684
    }
2685

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

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

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

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

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

2737
    /** @hidden @internal */
2738
    public get activeDescendant() {
2739
        const activeElem = this.navigation.activeNode;
134,283✔
2740

2741
        if (!activeElem || !Object.keys(activeElem).length) {
134,283✔
2742
            return this.id;
118,835✔
2743
        }
2744

2745
        return activeElem.row < 0 ?
15,448✔
2746
            `${this.id}_${activeElem.row}_${activeElem.mchCache.level}_${activeElem.column}` :
2747
            `${this.id}_${activeElem.row}_${activeElem.column}`;
2748
    }
2749

2750
    /** @hidden @internal */
2751
    public get bannerClass(): string {
2752
        const position = this.rowEditPositioningStrategy.isTop ? 'igx-banner__border-top' : 'igx-banner__border-bottom';
5,968✔
2753
        return `igx-banner ${position}`;
5,968✔
2754
    }
2755

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

2773
    public set sortingExpressions(value: ISortingExpression[]) {
2774
        this._sortingExpressions = cloneArray(value);
453✔
2775
        this.sortingExpressionsChange.emit(this._sortingExpressions);
453✔
2776
        this.notifyChanges();
453✔
2777
    }
2778

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

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

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

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

2826
    public set batchEditing(val: boolean) {
2827
        if (val !== this._batchEditing) {
163✔
2828
            delete this._transactions;
152✔
2829
            this._batchEditing = val;
152✔
2830
            this.switchTransactionService(val);
152✔
2831
            this.subscribeToTransactions();
152✔
2832
        }
2833
    }
2834

2835
    /* blazorSuppress */
2836
    /**
2837
     * Get transactions service for the grid.
2838
     */
2839
    public get transactions(): TransactionService<Transaction, State> {
2840
        if (this._diTransactions && !this.batchEditing) {
7,269,543✔
2841
            return this._diTransactions;
3,804✔
2842
        }
2843
        return this._transactions;
7,265,739✔
2844
    }
2845

2846
    /**
2847
     * @hidden @internal
2848
     */
2849
    public get currentRowState(): any {
2850
        return this._currentRowState;
×
2851
    }
2852

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

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

2879
    public set cellSelection(selectionMode: GridSelectionMode) {
2880
        this._cellSelectionMode = selectionMode;
33✔
2881
        // if (this.gridAPI.grid) {
2882
        this.selectionService.clear(true);
33✔
2883
        this.notifyChanges();
33✔
2884
        // }
2885
    }
2886

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

2900
    public set rowSelection(selectionMode: GridSelectionMode) {
2901
        this._rowSelectionMode = selectionMode;
523✔
2902
        if (!this._init) {
523✔
2903
            this.selectionService.clearAllSelectedRows();
72✔
2904
            this.notifyChanges(true);
72✔
2905
        }
2906
    }
2907

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

2921
    public set columnSelection(selectionMode: GridSelectionMode) {
2922
        this._columnSelectionMode = selectionMode;
157✔
2923
        // if (this.gridAPI.grid) {
2924
        this.selectionService.clearAllSelectedColumns();
157✔
2925
        this.notifyChanges(true);
157✔
2926
        // }
2927
    }
2928

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

2939
    public get pagingState() {
2940
        return this._pagingState;
174✔
2941
    }
2942

2943
    /**
2944
     * @hidden @internal
2945
     */
2946
    public rowEditMessage;
2947

2948
    /**
2949
     * @hidden @internal
2950
     */
2951
    public calcWidth: number;
2952
    /**
2953
     * @hidden @internal
2954
     */
2955
    public calcHeight = 0;
4,113✔
2956
    /**
2957
     * @hidden @internal
2958
     */
2959
    public tfootHeight: number;
2960

2961
    /**
2962
     * @hidden @internal
2963
     */
2964
    public disableTransitions = false;
4,113✔
2965

2966
    /**
2967
     * Represents the last search information.
2968
     */
2969
    public get lastSearchInfo(): ISearchInfo {
2970
        return this._lastSearchInfo;
1,484,200✔
2971
    }
2972

2973
    /**
2974
     * @hidden @internal
2975
     */
2976
    public columnWidthSetByUser = false;
4,113✔
2977

2978
    /**
2979
     * @hidden @internal
2980
     */
2981
    public pinnedRecords: any[];
2982

2983
    /**
2984
     * @hidden @internal
2985
     */
2986
    public unpinnedRecords: any[];
2987

2988
    /**
2989
     * @hidden @internal
2990
     */
2991
    public rendered$ = this.rendered.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true }));
4,113✔
2992

2993
    /** @hidden @internal */
2994
    public resizeNotify = new Subject<void>();
4,113✔
2995

2996
    /** @hidden @internal */
2997
    public rowAddedNotifier = new Subject<IRowDataEventArgs>();
4,113✔
2998

2999
    /** @hidden @internal */
3000
    public rowDeletedNotifier = new Subject<IRowDataEventArgs>();
4,113✔
3001

3002
    /** @hidden @internal */
3003
    public pipeTriggerNotifier = new Subject();
4,113✔
3004

3005
    /** @hidden @internal */
3006
    public _filteredSortedPinnedData: any[];
3007

3008
    /** @hidden @internal */
3009
    public _filteredSortedUnpinnedData: any[];
3010

3011
    /** @hidden @internal */
3012
    public _filteredPinnedData: any[];
3013

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

3039
    /**
3040
     * @hidden @internal
3041
     */
3042
    public isColumnWidthSum = false;
4,113✔
3043

3044
    /**
3045
     * @hidden @internal
3046
     */
3047
    public summaryPipeTrigger = 0;
4,113✔
3048
    /**
3049
     * @hidden @internal
3050
     */
3051
    public groupablePipeTrigger = 0;
4,113✔
3052

3053
    /**
3054
    * @hidden @internal
3055
    */
3056
    public EMPTY_DATA = [];
4,113✔
3057

3058
    /** @hidden @internal */
3059
    public get type(): GridType["type"] {
3060
        return 'flat';
24,447✔
3061
    }
3062

3063
    /** @hidden @internal */
3064
    public _baseFontSize: number;
3065

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

3123
    protected _pinnedRecordIDs = [];
4,113✔
3124

3125
    /**
3126
     * @hidden
3127
     */
3128
    protected _hasVisibleColumns;
3129
    protected _allowFiltering = false;
4,113✔
3130
    protected _allowAdvancedFiltering = false;
4,113✔
3131
    protected _filterMode: FilterMode = FilterMode.quickFilter;
4,113✔
3132

3133

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

3162
    /** @hidden @internal */
3163
    public get paginator() {
3164
        return this.paginationComponents?.first;
3,734,533✔
3165
    }
3166

3167
    /**
3168
     * @hidden @internal
3169
     */
3170
    public get scrollSize() {
3171
        return this.verticalScrollContainer.getScrollNativeSize();
187,256✔
3172
    }
3173

3174
    private _primaryKey: string;
3175
    private _rowEditable = false;
4,113✔
3176
    private _currentRowState: any;
3177
    private _filteredSortedData = null;
4,113✔
3178
    private _filteredData = null;
4,113✔
3179

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

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

3204
    private _hostWidth;
3205
    private _advancedFilteringOverlayId: string;
3206
    private _advancedFilteringPositionSettings: PositionSettings = {
4,113✔
3207
        verticalDirection: VerticalAlignment.Middle,
3208
        horizontalDirection: HorizontalAlignment.Center,
3209
        horizontalStartPoint: HorizontalAlignment.Center,
3210
        verticalStartPoint: VerticalAlignment.Middle
3211
    };
3212

3213
    private _advancedFilteringOverlaySettings: OverlaySettings = {
4,113✔
3214
        closeOnOutsideClick: false,
3215
        modal: false,
3216
        positionStrategy: new ConnectedPositioningStrategy(this._advancedFilteringPositionSettings),
3217
    };
3218

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

3235
    private _columnWidth: string;
3236

3237
    private _summaryPosition: GridSummaryPosition = GridSummaryPosition.bottom;
4,113✔
3238
    private _summaryCalculationMode: GridSummaryCalculationMode = GridSummaryCalculationMode.rootAndChildLevels;
4,113✔
3239
    private _showSummaryOnCollapse = false;
4,113✔
3240
    private _summaryRowHeight = 0;
4,113✔
3241
    private _cellSelectionMode: GridSelectionMode = GridSelectionMode.multiple;
4,113✔
3242
    private _rowSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,113✔
3243
    private _selectRowOnClick = true;
4,113✔
3244
    private _columnSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,113✔
3245

3246
    private lastAddedRowIndex;
3247

3248
    private _currencyPositionLeft: boolean;
3249

3250
    private rowEditPositioningStrategy = new RowEditPositionStrategy({
4,113✔
3251
        horizontalDirection: HorizontalAlignment.Right,
3252
        verticalDirection: VerticalAlignment.Bottom,
3253
        horizontalStartPoint: HorizontalAlignment.Left,
3254
        verticalStartPoint: VerticalAlignment.Bottom,
3255
        closeAnimation: null
3256
    });
3257

3258
    private rowEditSettings: OverlaySettings = {
4,113✔
3259
        scrollStrategy: new AbsoluteScrollStrategy(),
3260
        modal: false,
3261
        closeOnOutsideClick: false,
3262
        outlet: this.rowOutletDirective,
3263
        positionStrategy: this.rowEditPositioningStrategy
3264
    };
3265

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

3277
    /**
3278
     * @hidden @internal
3279
     */
3280
    public get minColumnWidth() {
3281
        return MINIMUM_COLUMN_WIDTH;
65,665✔
3282
    }
3283

3284
    protected get isCustomSetRowHeight(): boolean {
3285
        return !isNaN(this._rowHeight);
1,205,018✔
3286
    }
3287

3288
    /**
3289
     * @hidden @internal
3290
     */
3291
    public abstract id: string;
3292
    /* blazorSuppress */
3293
    public abstract data: any[] | null;
3294

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

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

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

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

3349
        if (this.crudService.row && this.crudService.row.getClassName() === IgxAddRow.name) {
11,401✔
3350
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
385✔
3351
        }
3352

3353
        return result;
11,401✔
3354
    }
3355

3356
    /**
3357
     * @hidden @internal
3358
     */
3359
    public get dataLength() {
3360
        return this.transactions.enabled ? this.dataWithAddedInTransactionRows.length : this.gridAPI.get_all_data().length;
45,948✔
3361
    }
3362

3363
    /**
3364
     * @hidden @internal
3365
     */
3366
    public get template(): TemplateRef<IgxGridTemplateContext> {
3367
        if (this.isLoading && (this.hasZeroResultFilter || this.hasNoData)) {
45,481✔
3368
            return this.loadingGridTemplate ? this.loadingGridTemplate : this.loadingGridDefaultTemplate;
98✔
3369
        }
3370

3371
        if (this.hasZeroResultFilter) {
45,383✔
3372
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
193!
3373
        }
3374

3375
        if (this.hasNoData) {
45,190✔
3376
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate;
737✔
3377
        }
3378
    }
3379

3380
    /**
3381
     * @hidden @internal
3382
     */
3383
    private get hasZeroResultFilter(): boolean {
3384
        return this.filteredData && this.filteredData.length === 0;
45,706✔
3385
    }
3386
    protected get totalCalcWidth() {
3387
        return this.platform.isBrowser ? this.calcWidth : undefined;
43,409!
3388
    }
3389

3390
    protected get renderData() {
3391
        // omit data if not in the browser and size is %
3392
        return !this.platform.isBrowser && this.isPercentHeight ? undefined : this.data;
45,536!
3393
    }
3394

3395
    @HostBinding('style.display')
3396
    protected displayStyle = 'grid';
4,113✔
3397

3398
    @HostBinding('style.grid-template-rows')
3399
    protected templateRows = 'auto auto auto 1fr auto auto';
4,113✔
3400

3401
    /**
3402
     * @hidden @internal
3403
     */
3404
    private get hasNoData(): boolean {
3405
        return !this.data || this.dataLength === 0 || !this.platform.isBrowser;
45,596✔
3406
    }
3407

3408
    /**
3409
     * @hidden @internal
3410
     */
3411
    public get shouldOverlayLoading(): boolean {
3412
        return this.isLoading && !this.hasNoData && !this.hasZeroResultFilter;
48,816✔
3413
    }
3414

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

3423
    /**
3424
     * @hidden @internal
3425
     */
3426
    public get isRowSelectable(): boolean {
3427
        return this.rowSelection !== GridSelectionMode.none;
291,225✔
3428
    }
3429

3430
    /**
3431
     * @hidden @internal
3432
     */
3433
    public get isCellSelectable() {
3434
        return this.cellSelection !== GridSelectionMode.none;
8,466✔
3435
    }
3436

3437
    /**
3438
     * @hidden @internal
3439
     */
3440
    public get columnInDrag() {
3441
        return this.gridAPI.cms.column;
271,070✔
3442
    }
3443

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

3481
    /**
3482
     * @hidden
3483
     * @internal
3484
     */
3485
    @HostListener('mouseleave')
3486
    public hideActionStrip() {
3487
        this.actionStrip?.hide();
3✔
3488
    }
3489

3490
    /**
3491
     * @hidden
3492
     * @internal
3493
     */
3494
    public get headerFeaturesWidth() {
3495
        return this._headerFeaturesWidth;
1,305✔
3496
    }
3497

3498
    /**
3499
     * @hidden
3500
     * @internal
3501
     */
3502
    public isDetailRecord(_rec) {
3503
        return false;
1,237✔
3504
    }
3505

3506
    /**
3507
     * @hidden
3508
     * @internal
3509
     */
3510
    public isGroupByRecord(_rec) {
3511
        return false;
1,237✔
3512
    }
3513

3514
    /**
3515
     * @hidden @internal
3516
     */
3517
    public isGhostRecord(record: any): boolean {
3518
        return record.ghostRecord !== undefined;
542,716✔
3519
    }
3520
    /**
3521
     * @hidden @internal
3522
     */
3523
    public isAddRowRecord(record: any): boolean {
3524
        return record.addRow !== undefined;
×
3525
    }
3526

3527
    /**
3528
     * @hidden
3529
     * Returns the row index of a row that takes into account the full view data like pinning.
3530
     */
3531
    public getDataViewIndex(rowIndex, pinned) {
3532
        if (pinned && !this.isRowPinningToTop) {
254,576✔
3533
            rowIndex = rowIndex + this.unpinnedDataView.length;
198✔
3534
        } else if (!pinned && this.isRowPinningToTop) {
254,378✔
3535
            rowIndex = rowIndex + this.pinnedDataView.length;
252,627✔
3536
        }
3537
        return rowIndex;
254,576✔
3538
    }
3539

3540
    /**
3541
     * @hidden
3542
     * @internal
3543
     */
3544
    public get hasDetails() {
3545
        return false;
376✔
3546
    }
3547

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

3562
    /**
3563
     * @hidden
3564
     * @internal
3565
     */
3566
    public hideOverlays() {
3567
        this.overlayIDs.forEach(overlayID => {
526✔
3568
            const overlay = this.overlayService.getOverlayById(overlayID);
1✔
3569

3570
            if (overlay?.visible && !overlay.closeAnimationPlayer?.hasStarted()) {
1✔
3571
                this.overlayService.hide(overlayID);
1✔
3572

3573
                this.nativeElement.focus();
1✔
3574
            }
3575
        });
3576
    }
3577

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

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

3601
    /**
3602
     * @hidden
3603
     * @internal
3604
     */
3605
    public isRecordPinned(rec) {
3606
        return this.getInitialPinnedIndex(rec) !== -1;
1,099,688✔
3607
    }
3608

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

3619
    /**
3620
     * @hidden
3621
     * @internal
3622
     */
3623
    public get hasPinnedRecords() {
3624
        return this._pinnedRecordIDs.length > 0;
494,320✔
3625
    }
3626

3627
    /**
3628
     * @hidden
3629
     * @internal
3630
     */
3631
    public get pinnedRecordsCount() {
3632
        return this._pinnedRecordIDs.length;
7,354✔
3633
    }
3634

3635
    /**
3636
     * @hidden
3637
     * @internal
3638
     */
3639
    public get crudService() {
3640
        return this.gridAPI.crudService;
3,436,236✔
3641
    }
3642

3643
    /**
3644
     * @hidden
3645
     * @internal
3646
     */
3647
    public _setupServices() {
3648
        this.gridAPI.grid = this as any;
3,569✔
3649
        this.crudService.grid = this as any;
3,569✔
3650
        this.selectionService.grid = this as any;
3,569✔
3651
        this.validation.grid = this as any;
3,569✔
3652
        this.navigation.grid = this as any;
3,569✔
3653
        this.filteringService.grid = this as any;
3,569✔
3654
        this.summaryService.grid = this as any;
3,569✔
3655
    }
3656

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

3680
        this.subscribeToTransactions();
3,569✔
3681

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

3706
        this.pipeTriggerNotifier.pipe(takeUntil(this.destroy$)).subscribe(() => this.pipeTrigger++);
3,569✔
3707
        this.columnMovingEnd.pipe(destructor).subscribe(() => this.crudService.endEdit(false));
3,569✔
3708

3709
        this.overlayService.opening.pipe(destructor).subscribe((event) => {
3,569✔
3710
            if (this._advancedFilteringOverlayId === event.id) {
839✔
3711
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
51✔
3712
                if (instance) {
51✔
3713
                    instance.initialize(this as any, this.overlayService, event.id);
51✔
3714
                }
3715
            }
3716
        });
3717

3718
        this.overlayService.opened.pipe(destructor).subscribe((event) => {
3,569✔
3719
            const overlaySettings = this.overlayService.getOverlayById(event.id)?.settings;
574✔
3720

3721
            // do not hide the advanced filtering overlay on scroll
3722
            if (this._advancedFilteringOverlayId === event.id) {
574✔
3723
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
49✔
3724
                if (instance) {
49✔
3725
                    instance.lastActiveNode = this.navigation.activeNode;
49✔
3726
                    instance.queryBuilder.setAddButtonFocus();
49✔
3727
                }
3728
                return;
49✔
3729
            }
3730

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

3736
            if (overlaySettings?.outlet === this.outlet && this.overlayIDs.indexOf(event.id) === -1) {
504✔
3737
                this.overlayIDs.push(event.id);
354✔
3738
            }
3739
        });
3740

3741
        this.overlayService.closed.pipe(filter(() => !this._init), destructor).subscribe((event) => {
3,569✔
3742
            if (this._advancedFilteringOverlayId === event.id) {
465✔
3743
                this.overlayService.detach(this._advancedFilteringOverlayId);
27✔
3744
                this._advancedFilteringOverlayId = null;
27✔
3745
                return;
27✔
3746
            }
3747

3748
            const ind = this.overlayIDs.indexOf(event.id);
438✔
3749
            if (ind !== -1) {
438✔
3750
                this.overlayIDs.splice(ind, 1);
198✔
3751
            }
3752
        });
3753

3754
        this.verticalScrollContainer.dataChanging.pipe(filter(() => !this._init), destructor).subscribe(($event) => {
6,756✔
3755
            const shouldRecalcSize = this.isPercentHeight &&
3,272✔
3756
                (!this.calcHeight || this.calcHeight === this.getDataBasedBodyHeight() ||
3757
                    this.calcHeight === this.renderedRowHeight * this._defaultTargetRecordNumber);
3758
            if (shouldRecalcSize) {
3,272✔
3759
                this.calculateGridHeight();
110✔
3760
                $event.containerSize = this.calcHeight;
110✔
3761
            }
3762
            this.evaluateLoadingState();
3,272✔
3763
        });
3764

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

3773

3774
        this.headerContainer?.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3,569✔
3775
            // the horizontal scrollbar showing/hiding
3776
            // update scrollbar visibility and recalc heights
3777
            this.notifyChanges(true);
1,335✔
3778
            this.cdr.detectChanges();
1,335✔
3779
        });
3780

3781
        this.verticalScrollContainer.contentSizeChange.pipe(filter(() => !this._init), throttleTime(30), destructor).subscribe(() => {
3,569✔
3782
            this.notifyChanges(true);
689✔
3783
        });
3784

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

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

3809
    /**
3810
     * @hidden
3811
     * @internal
3812
     */
3813
    public resetColumnsCaches() {
3814
        this._columns.forEach(column => column.resetCaches());
215,670✔
3815
    }
3816

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

3827
    /**
3828
     * @hidden
3829
     * @internal
3830
     */
3831
    public resetForOfCache() {
3832
        const firstVirtRow = this.dataRowList.first;
34,316✔
3833
        if (firstVirtRow) {
34,316✔
3834
            if (this._cdrRequests) {
16,957✔
3835
                firstVirtRow.virtDirRow.cdr.detectChanges();
6,732✔
3836
            }
3837
            firstVirtRow.virtDirRow.assumeMaster();
16,957✔
3838
        }
3839
    }
3840

3841
    /**
3842
     * @hidden
3843
     * @internal
3844
     */
3845
    public setFilteredData(data, pinned: boolean) {
3846
        if (this.hasPinnedRecords && pinned) {
1,528✔
3847
            this._filteredPinnedData = data || [];
49✔
3848
            const filteredUnpinned = this._filteredUnpinnedData || [];
49✔
3849
            const filteredData = [... this._filteredPinnedData, ...filteredUnpinned];
49✔
3850
            this._filteredData = filteredData.length > 0 ? filteredData : this._filteredUnpinnedData;
49✔
3851
        } else if (this.hasPinnedRecords && !pinned) {
1,479✔
3852
            this._filteredUnpinnedData = data;
47✔
3853
        } else {
3854
            this._filteredData = data;
1,432✔
3855
        }
3856
    }
3857

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

3871
    /**
3872
     * @hidden
3873
     * @internal
3874
     */
3875
    public resetCachedWidths() {
3876
        this._unpinnedWidth = NaN;
46,167✔
3877
        this._pinnedWidth = NaN;
46,167✔
3878
        this._totalWidth = NaN;
46,167✔
3879
    }
3880

3881
    /**
3882
     * @hidden
3883
     * @internal
3884
     */
3885
    public resetCaches(recalcFeatureWidth = true) {
10,598✔
3886
        if (recalcFeatureWidth) {
34,316✔
3887
            this._headerFeaturesWidth = NaN;
34,307✔
3888
            this.summaryService.summaryHeight = 0;
34,307✔
3889
        }
3890
        this.resetColumnsCaches();
34,316✔
3891
        this.resetColumnCollections();
34,316✔
3892
        this.resetForOfCache();
34,316✔
3893
        this.resetCachedWidths();
34,316✔
3894
        this.hasVisibleColumns = undefined;
34,316✔
3895
        this._columnGroups = this._columns.some(col => col.columnGroup);
161,619✔
3896
    }
3897

3898
    /**
3899
     * @hidden
3900
     */
3901
    public ngAfterContentInit() {
3902
        if (this.sortHeaderIconDirectiveTemplate) {
3,440✔
3903
            this.sortHeaderIconTemplate = this.sortHeaderIconDirectiveTemplate;
5✔
3904
        }
3905

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

3910
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
3,440✔
3911
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
5✔
3912
        }
3913

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

3925
    /**
3926
     * @hidden @internal
3927
     */
3928
    public dataRebinding(event: IForOfDataChangingEventArgs) {
3929
        if (event.state.chunkSize == 0) {
6,756✔
3930
            this._shouldRecalcRowHeight = true;
3,598✔
3931
        }
3932
        this.dataChanging.emit(event);
6,756✔
3933
    }
3934

3935
    /**
3936
     * @hidden @internal
3937
     */
3938
    public dataRebound(event) {
3939
        this.selectionService.clearHeaderCBState();
6,756✔
3940
        if (this._shouldRecalcRowHeight) {
6,756✔
3941
            this._shouldRecalcRowHeight = false;
3,598✔
3942
            this.updateDefaultRowHeight();
3,598✔
3943
        }
3944
        this.dataChanged.emit(event);
6,756✔
3945
    }
3946

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

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

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

4010
    /**
4011
     * @hidden @internal
4012
     */
4013
    public resetHorizontalVirtualization() {
4014
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
49,563✔
4015
        this._horizontalForOfs = [
6,732✔
4016
            ...this._dataRowList.filter(elementFilter).map(item => item.virtDirRow),
46,419✔
4017
            ...this._summaryRowList.filter(elementFilter).map(item => item.virtDirRow)
3,144✔
4018
        ];
4019
    }
4020

4021
    /**
4022
     * @hidden @internal
4023
     */
4024
    public _setupRowObservers() {
4025
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
17,748✔
4026
        const extractForOfs = pipe(map((collection: any[]) => collection.filter(elementFilter).map(item => item.virtDirRow)));
17,748✔
4027
        const rowListObserver = extractForOfs(this._dataRowList.changes);
3,582✔
4028
        const summaryRowObserver = extractForOfs(this._summaryRowList.changes);
3,582✔
4029
        rowListObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,582✔
4030
            this.resetHorizontalVirtualization();
2,732✔
4031
        });
4032
        summaryRowObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,582✔
4033
            this.resetHorizontalVirtualization();
320✔
4034
        });
4035
        this.resetHorizontalVirtualization();
3,582✔
4036
    }
4037

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

4062
    /**
4063
     * @hidden
4064
     */
4065
    public ngAfterViewInit() {
4066
        this.initPinning();
3,582✔
4067
        this.calculateGridSizes();
3,582✔
4068
        this._init = false;
3,582✔
4069
        this.cdr.reattach();
3,582✔
4070
        this._setupRowObservers();
3,582✔
4071
        this._zoneBegoneListeners();
3,582✔
4072

4073
        const vertScrDC = this.verticalScrollContainer.displayContainer;
3,582✔
4074
        vertScrDC.addEventListener('scroll', this.preventContainerScroll);
3,582✔
4075

4076
        this._pinnedRowList.changes
3,582✔
4077
            .pipe(takeUntil(this.destroy$))
4078
            .subscribe((change: QueryList<IgxGridRowComponent>) => {
4079
                this.onPinnedRowsChanged(change);
182✔
4080
            });
4081

4082
        this.addRowSnackbar?.clicked.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,582✔
4083
            const rec = this.filteredSortedData[this.lastAddedRowIndex];
4✔
4084
            this.scrollTo(rec, 0);
4✔
4085
            this.addRowSnackbar.close();
4✔
4086
        });
4087

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

4101
    }
4102

4103
    /**
4104
     * @hidden @internal
4105
     */
4106
    public notifyChanges(repaint = false) {
14,993✔
4107
        this._cdrRequests = true;
495,292✔
4108
        this._cdrRequestRepaint = repaint;
495,292✔
4109
        this.cdr.markForCheck();
495,292✔
4110
    }
4111

4112
    /**
4113
     * @hidden @internal
4114
     */
4115
    public ngDoCheck() {
4116
        if (this._init) {
17,938✔
4117
            return;
4,153✔
4118
        }
4119

4120
        if (this._cdrRequestRepaint) {
13,785✔
4121
            this.resetNotifyChanges();
3,983✔
4122
            this.calculateGridSizes();
3,983✔
4123
            this.refreshSearch(true);
3,983✔
4124
            return;
3,983✔
4125
        }
4126

4127
        if (this._cdrRequests) {
9,802✔
4128
            this.resetNotifyChanges();
4,232✔
4129
            this.cdr.detectChanges();
4,232✔
4130
        }
4131
    }
4132

4133
    /**
4134
     * @hidden
4135
     * @internal
4136
     */
4137
    public getDragGhostCustomTemplate() {
4138

4139
        return this.dragGhostCustomTemplate;
1,979✔
4140
    }
4141

4142
    /**
4143
     * @hidden @internal
4144
     */
4145
    public ngOnDestroy() {
4146
        this.tmpOutlets.forEach((tmplOutlet) => {
3,405✔
4147
            tmplOutlet.cleanCache();
25,049✔
4148
        });
4149
        this._autoGeneratedColsRefs.forEach(ref => ref.destroy());
4,136✔
4150
        this._autoGeneratedColsRefs = [];
3,405✔
4151

4152
        this.destroy$.next(true);
3,405✔
4153
        this.destroy$.complete();
3,405✔
4154
        this.transactionChange$.next();
3,405✔
4155
        this.transactionChange$.complete();
3,405✔
4156
        this._destroyed = true;
3,405✔
4157

4158
        this.textHighlightService.destroyGroup(this.id);
3,405✔
4159

4160
        if (this._advancedFilteringOverlayId) {
3,405!
4161
            this.overlayService.detach(this._advancedFilteringOverlayId);
×
4162
            delete this._advancedFilteringOverlayId;
×
4163
        }
4164

4165
        this.overlayIDs.forEach(overlayID => {
3,405✔
4166
            const overlay = this.overlayService.getOverlayById(overlayID);
23✔
4167

4168
            if (overlay && !overlay.detached) {
23✔
4169
                this.overlayService.detach(overlayID);
13✔
4170
            }
4171
        });
4172

4173

4174
        this.zone.runOutsideAngular(() => {
3,405✔
4175
            this.verticalScrollContainer?.getScroll()?.removeEventListener('scroll', this.verticalScrollHandler);
3,405✔
4176
            this.headerContainer?.getScroll()?.removeEventListener('scroll', this.horizontalScrollHandler);
3,405✔
4177
            const vertScrDC = this.verticalScrollContainer?.displayContainer;
3,405✔
4178
            vertScrDC?.removeEventListener('scroll', this.preventContainerScroll);
3,405✔
4179
        });
4180
    }
4181

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

4196
        if (!col) {
×
4197
            return;
×
4198
        }
4199
        col.toggleVisibility(args.newValue);
×
4200
    }
4201

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

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

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

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

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

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

4286

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

4304
    /**
4305
     * @hidden
4306
     * @internal
4307
     */
4308
    public getDefaultExpandState(_rec: any) {
4309
        return this._defaultExpandState;
9,575✔
4310
    }
4311

4312
    /**
4313
     * Gets the native element.
4314
     *
4315
     * @example
4316
     * ```typescript
4317
     * const nativeEl = this.grid.nativeElement.
4318
     * ```
4319
     */
4320
    public get nativeElement() {
4321
        return this.elementRef.nativeElement;
42,088✔
4322
    }
4323

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

4335
    public set outlet(val: IgxOverlayOutletDirective) {
4336
        this._userOutletDirective = val;
×
4337
    }
4338

4339

4340
    /**
4341
     * Gets the default row height.
4342
     *
4343
     * @example
4344
     * ```typescript
4345
     * const rowHeigh = this.grid.defaultRowHeight;
4346
     * ```
4347
     */
4348
    public get defaultRowHeight(): number {
4349
        return this._defaultRowHeight;
346,613✔
4350
    }
4351

4352
    /**
4353
     * @hidden @internal
4354
     */
4355
    public get defaultSummaryHeight(): number {
4356
        switch (this.gridSize) {
15,135✔
4357
            case Size.Medium:
4358
                return 30;
29✔
4359
            case Size.Small:
4360
                return 24;
123✔
4361
            default:
4362
                return 36;
14,983✔
4363
        }
4364
    }
4365

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

4384
    /** @hidden @internal */
4385
    public get pinnedWidth() {
4386
        if (!isNaN(this._pinnedWidth)) {
224,011✔
4387
            return this._pinnedWidth;
202,877✔
4388
        }
4389
        this._pinnedWidth = this.getPinnedWidth();
21,134✔
4390
        return this._pinnedWidth;
21,134✔
4391
    }
4392

4393
    /** @hidden @internal */
4394
    public get unpinnedWidth() {
4395
        if (!isNaN(this._unpinnedWidth)) {
370,082✔
4396
            return this._unpinnedWidth;
346,584✔
4397
        }
4398
        this._unpinnedWidth = this.getUnpinnedWidth();
23,498✔
4399
        return this._unpinnedWidth;
23,498✔
4400
    }
4401

4402
    /**
4403
     * @hidden @internal
4404
     */
4405
    public isHorizontalScrollHidden = false;
4,113✔
4406

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

4417
        const headerStyle = this.document.defaultView.getComputedStyle(element);
35✔
4418
        const headerPadding = parseFloat(headerStyle.paddingLeft) + parseFloat(headerStyle.paddingRight) +
35✔
4419
            parseFloat(headerStyle.borderRightWidth);
4420

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

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

4446
    /**
4447
     * @hidden @internal
4448
     */
4449
    public get summariesMargin() {
4450
        return this.featureColumnsWidth();
22,116✔
4451
    }
4452

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

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

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

4494
    /**
4495
     * Gets an array of unpinned `IgxColumnComponent`s.
4496
     *
4497
     * @example
4498
     * ```typescript
4499
     * const unpinnedColumns = this.grid.unpinnedColumns.
4500
     * ```
4501
     */
4502
    public get unpinnedColumns(): IgxColumnComponent[] {
4503
        if (this._unpinnedVisible.length) {
762,120✔
4504
            return this._unpinnedVisible;
735,142✔
4505
        }
4506
        this._unpinnedVisible = this._unpinnedColumns.filter((col) => !col.hidden);
156,147✔
4507
        return this._unpinnedVisible;
26,978✔
4508
    }
4509

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

4519
    /**
4520
     * Returns the `IgxColumnComponent` by field name.
4521
     *
4522
     * @example
4523
     * ```typescript
4524
     * const myCol = this.grid1.getColumnByName("ID");
4525
     * ```
4526
     * @param name
4527
     */
4528
    public getColumnByName(name: string): IgxColumnComponent {
4529
        return this._columns.find((col) => col.field === name);
135,562✔
4530
    }
4531

4532
    public getColumnByVisibleIndex(index: number): IgxColumnComponent {
4533
        return this.visibleColumns.find((col) =>
1,962✔
4534
            !col.columnGroup && !col.columnLayout &&
11,552✔
4535
            col.visibleIndex === index
4536
        );
4537
    }
4538

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

4557
    /**
4558
     * Returns an array of visible `IgxColumnComponent`s.
4559
     *
4560
     * @example
4561
     * ```typescript
4562
     * const visibleColumns = this.grid.visibleColumns.
4563
     * ```
4564
     */
4565
    public get visibleColumns(): IgxColumnComponent[] {
4566
        if (this._visibleColumns.length) {
161,929✔
4567
            return this._visibleColumns;
135,002✔
4568
        }
4569
        this._visibleColumns = this._columns.filter(c => !c.hidden);
151,773✔
4570
        return this._visibleColumns;
26,927✔
4571
    }
4572

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

4588
    public set totalRecords(total: number) {
4589
        if (total >= 0) {
1✔
4590
            if (this.paginator) {
1✔
4591
                this.paginator.totalRecords = total;
1✔
4592
            }
4593
            this._totalRecords = total;
1✔
4594
            this.pipeTrigger++;
1✔
4595
            this.notifyChanges();
1✔
4596
        }
4597
    }
4598

4599
    /** @hidden @internal */
4600
    public get totalWidth(): number {
4601
        if (!isNaN(this._totalWidth)) {
11,340✔
4602
            return this._totalWidth;
3,217✔
4603
        }
4604
        // Take only top level columns
4605
        const cols = this.visibleColumns.filter(col => col.level === 0 && !col.pinned);
50,054✔
4606
        let totalWidth = 0;
8,123✔
4607
        let i = 0;
8,123✔
4608
        for (i; i < cols.length; i++) {
8,123✔
4609
            totalWidth += parseFloat(cols[i].calcWidth) || 0;
40,105!
4610
        }
4611
        this._totalWidth = totalWidth;
8,123✔
4612
        return totalWidth;
8,123✔
4613
    }
4614

4615
    /**
4616
     * @hidden
4617
     * @internal
4618
     */
4619
    public get showRowSelectors(): boolean {
4620
        return this.isRowSelectable && this.hasVisibleColumns && !this.hideRowSelectors;
279,593✔
4621
    }
4622

4623
    /**
4624
     * @hidden
4625
     * @internal
4626
     */
4627
    public get showAddButton() {
4628
        return this.rowEditable && this.dataView.length === 0 && this._columns.length > 0;
912✔
4629
    }
4630

4631
    /**
4632
     * @hidden
4633
     * @internal
4634
     */
4635
    public get showDragIcons(): boolean {
4636
        return this.rowDraggable && this._columns.length > this.hiddenColumnsCount;
×
4637
    }
4638

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

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

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

4677
        this.columnMovingEnd.emit(eventArgs);
151✔
4678

4679
        if (eventArgs.cancel) {
151✔
4680
            return;
1✔
4681
        }
4682

4683
        if (column === target || (column.level !== target.level) ||
150✔
4684
            (column.topLevelParent !== target.topLevelParent)) {
4685
            return;
22✔
4686
        }
4687

4688
        if (column.level) {
128✔
4689
            this._moveChildColumns(column.parent, column, target, pos);
16✔
4690
        }
4691

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

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

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

4711
        // if (!target.pinned && !column.pinned && !columnPinStateChanged) {
4712
        //     this._reorderColumns(column, target, pos, this._unpinnedColumns);
4713
        // }
4714

4715
        this._moveColumns(column, target, pos);
128✔
4716
        this._columnsReordered(column);
128✔
4717
    }
4718

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

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

4765
        this.pipeTrigger++;
171✔
4766
        this.rowAddedNotifier.next({ data: data, rowData: data, owner: this, primaryKey: data[this.primaryKey], rowKey: data[this.primaryKey] });
171✔
4767
        this.notifyChanges();
171✔
4768
    }
4769

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

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

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

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

4847
                const id = {
18✔
4848
                    rowID: rowSelector,
4849
                    columnID: col.index,
4850
                    rowIndex: index
4851
                };
4852

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

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

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

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

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

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

4946
        const eventArgs: ISortingEventArgs = { owner: this, sortingExpressions: sortingState, cancel: false };
159✔
4947
        this.sorting.emit(eventArgs);
159✔
4948

4949
        if (eventArgs.cancel) {
159!
4950
            return;
×
4951
        }
4952

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

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

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

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

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

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

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

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

5091
    // TODO: We have return values here. Move them to event args ??
5092

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

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

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

5145
        if (eventArgs.cancel) {
133✔
5146
            return;
1✔
5147
        }
5148
        this.crudService.endEdit(false);
132✔
5149

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

5158
        return true;
132✔
5159
    }
5160

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

5179
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, null, row, false);
24✔
5180
        this.rowPinning.emit(eventArgs);
24✔
5181

5182
        if (eventArgs.cancel) {
24✔
5183
            return;
1✔
5184
        }
5185

5186
        this.crudService.endEdit(false);
23✔
5187
        this._pinnedRecordIDs.splice(index, 1);
23✔
5188
        this.pipeTrigger++;
23✔
5189
        if (this.gridAPI.grid) {
23✔
5190
            this.cdr.detectChanges();
23✔
5191
            this.rowPinned.emit(eventArgs);
23✔
5192
        }
5193

5194
        return true;
23✔
5195
    }
5196

5197
    /** @hidden @internal */
5198
    public get pinnedRowHeight() {
5199
        const containerHeight = this.pinContainer ? this.pinContainer.nativeElement.offsetHeight : 0;
82,442✔
5200
        return this.hasPinnedRecords ? containerHeight : 0;
82,442✔
5201
    }
5202

5203
    /** @hidden @internal */
5204
    public get totalHeight() {
5205
        const height = this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
43,409✔
5206
        return this.platform.isBrowser ? height : undefined;
43,409!
5207
    }
5208

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

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

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

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

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

5284
            return this.find(this._lastSearchInfo.searchText,
127✔
5285
                0,
5286
                this._lastSearchInfo.caseSensitive,
5287
                this._lastSearchInfo.exactMatch,
5288
                false,
5289
                endEdit);
5290
        } else {
5291
            return 0;
9,891✔
5292
        }
5293
    }
5294

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

5314
        this.rowList.forEach((row) => {
1✔
5315
            if (row.cells) {
10✔
5316
                row.cells.forEach((c: IgxGridCellComponent) => {
10✔
5317
                    c.clearHighlight();
40✔
5318
                });
5319
            }
5320
        });
5321
    }
5322

5323
    /** @hidden @internal */
5324
    public get hasEditableColumns(): boolean {
5325
        return this._columns.some((col) => col.editable);
6✔
5326
    }
5327

5328
    /** @hidden @internal */
5329
    public get hasSummarizedColumns(): boolean {
5330
        const summarizedColumns = this._columns.filter(col => col.hasSummary && !col.hidden);
2,105,533✔
5331
        return summarizedColumns.length > 0;
333,592✔
5332
    }
5333

5334
    /**
5335
     * @hidden @internal
5336
     */
5337
    public get rootSummariesEnabled(): boolean {
5338
        return this.summaryCalculationMode !== GridSummaryCalculationMode.childLevelsOnly;
243,935✔
5339
    }
5340

5341
    /**
5342
     * @hidden @internal
5343
     */
5344
    public get hasVisibleColumns(): boolean {
5345
        if (this._hasVisibleColumns === undefined) {
78,049✔
5346
            return this._columns ? this._columns.some(c => !c.hidden) : false;
79,171!
5347
        }
5348
        return this._hasVisibleColumns;
×
5349
    }
5350

5351
    public set hasVisibleColumns(value) {
5352
        this._hasVisibleColumns = value;
34,316✔
5353
    }
5354

5355
    /** @hidden @internal */
5356
    public get hasMovableColumns(): boolean {
5357
        return this.moving;
×
5358
    }
5359

5360
    /** @hidden @internal */
5361
    public get hasColumnGroups(): boolean {
5362
        return this._columnGroups;
859✔
5363
    }
5364

5365
    /** @hidden @internal */
5366
    public get hasColumnLayouts() {
5367
        return !!this._columns.some(col => col.columnLayout);
22,351,166✔
5368
    }
5369

5370

5371
    /**
5372
     * @hidden @internal
5373
     */
5374
    public get multiRowLayoutRowSize() {
5375
        return this._multiRowLayoutRowSize;
20,000✔
5376
    }
5377

5378
    /**
5379
     * @hidden
5380
     */
5381
    protected get rowBasedHeight() {
5382
        return this.dataLength * this.rowHeight;
×
5383
    }
5384

5385
    /**
5386
     * @hidden
5387
     */
5388
    protected get isPercentWidth() {
5389
        return this.width && this.width.indexOf('%') !== -1;
42,084✔
5390
    }
5391

5392
    protected get shouldResize(): boolean {
5393
        return this._gridSize !== this.gridSize;
1,765✔
5394
    }
5395

5396
    /**
5397
     * @hidden @internal
5398
     */
5399
    public get isPercentHeight() {
5400
        return this._height && this._height.indexOf('%') !== -1;
15,829✔
5401
    }
5402

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

5412
    /**
5413
     * @hidden @internal
5414
     * The rowHeight input is bound to min-height css prop of rows that adds a 1px border in all cases
5415
     */
5416
    public get renderedRowHeight(): number {
5417
        return this.rowHeight + 1;
84,949✔
5418
    }
5419

5420
    /**
5421
     * @hidden @internal
5422
     */
5423
    public get outerWidth() {
5424
        return this.hasVerticalScroll() ? this.calcWidth + this.scrollSize : this.calcWidth;
3,858✔
5425
    }
5426

5427
    /**
5428
     * @hidden @internal
5429
     * Gets the size of the grid
5430
     */
5431
    public get gridSize(): Size {
5432
        return this.gridComputedStyles?.getPropertyValue('--component-size') || Size.Large;
886,953✔
5433
    }
5434

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

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

5459
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
682,326✔
5460

5461

5462
        // Column layouts related
5463
        let visibleCols = [];
72,805✔
5464
        const columnBlocks = this.visibleColumns.filter(c => c.columnGroup);
682,326✔
5465
        const colsPerBlock = columnBlocks.map(block => block.getInitialChildColumnSizes(block.children));
122,357✔
5466
        const combinedBlocksSize = colsPerBlock.reduce((acc, item) => acc + item.length, 0);
122,357✔
5467
        colsPerBlock.forEach(blockCols => visibleCols = visibleCols.concat(blockCols));
122,357✔
5468
        //
5469

5470
        const columnsWithSetWidths = this.hasColumnLayouts ?
72,805✔
5471
            visibleCols.filter(c => c.widthSetByUser) :
164,920✔
5472
            visibleChildColumns.filter(c => (c.widthSetByUser || c.widthConstrained) && c.width !== 'fit-content');
296,960✔
5473

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

5493
        // When all columns are hidden, return 0px width
5494
        if (!sumExistingWidths && !columnsToSize) {
72,805✔
5495
            return '0px';
1,582✔
5496
        }
5497
        computedWidth -= this.featureColumnsWidth();
71,223✔
5498

5499
        const columnWidth = !Number.isFinite(sumExistingWidths) ?
71,223!
5500
            Math.max(computedWidth / columnsToSize, this.minColumnWidth) :
5501
            Math.max((computedWidth - sumExistingWidths) / columnsToSize, this.minColumnWidth);
5502

5503
        return columnWidth + 'px';
71,223✔
5504
    }
5505

5506
    /**
5507
     * @hidden @internal
5508
     */
5509
    public hasVerticalScroll() {
5510
        if (this._init) {
188,423✔
5511
            return false;
83,138✔
5512
        }
5513
        const isScrollable = this.verticalScrollContainer ? this.verticalScrollContainer.isScrollable() : false;
105,285✔
5514
        return !!(this.calcWidth && this.dataView && this.dataView.length > 0 && isScrollable);
105,285✔
5515
    }
5516

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

5538
        return sum;
44,632✔
5539
    }
5540

5541
    /**
5542
     * @hidden @internal
5543
     */
5544
    public isColumnGrouped(_fieldName: string): boolean {
5545
        return false;
×
5546
    }
5547

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

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

5571
        return this.selectionService.areAllRowSelected() ? 'Deselect all' : 'Select all';
3,057✔
5572
    }
5573

5574
    /**
5575
     * @hidden
5576
     * @internal
5577
     */
5578
    public get totalRowsCountAfterFilter() {
5579
        if (this.data) {
3,334✔
5580
            return this.selectionService.allData.length;
3,334✔
5581
        }
5582

5583
        return 0;
×
5584
    }
5585

5586
    /** @hidden @internal */
5587
    public get pinnedDataView(): any[] {
5588
        return this.pinnedRecords ? this.pinnedRecords : [];
263,885✔
5589
    }
5590

5591
    /** @hidden @internal */
5592
    public get unpinnedDataView(): any[] {
5593
        return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer?.igxForOf || [];
15,650✔
5594
    }
5595

5596
    /**
5597
     * Returns the currently transformed paged/filtered/sorted/grouped/pinned/unpinned row data, displayed in the grid.
5598
     *
5599
     * @example
5600
     * ```typescript
5601
     *      const dataView = this.grid.dataView;
5602
     * ```
5603
     */
5604
    public get dataView() {
5605
        return this._dataView;
401,969✔
5606
    }
5607

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

5621
    public set selectRowOnClick(enabled: boolean) {
5622
        this._selectRowOnClick = enabled;
13✔
5623
    }
5624

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

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

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

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

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

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

5714
        horizontal.scrollLeft += left * this.DRAG_SCROLL_DELTA;
1✔
5715
        vertical.scrollTop += top * this.DRAG_SCROLL_DELTA;
1✔
5716
    }
5717

5718
    /**
5719
     * @hidden @internal
5720
     */
5721
    public isDefined(arg: any): boolean {
5722
        return arg !== undefined && arg !== null;
67,532✔
5723
    }
5724

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

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

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

5759
        this.selectionService.pointerState.node = startNode;
166✔
5760
        this.selectionService.selectRange(endNode, this.selectionService.pointerState);
166✔
5761
        this.selectionService.addRangeMeta(endNode, this.selectionService.pointerState);
166✔
5762
        this.selectionService.initPointerState();
166✔
5763
    }
5764

5765
    /**
5766
     * Get the currently selected ranges in the grid.
5767
     */
5768
    public getSelectedRanges(): GridSelectionRange[] {
5769
        return this.selectionService.ranges;
348✔
5770
    }
5771

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

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

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

5824
        this.selectionService.selectColumnsWithNoEvent(fieldToSelect, clearCurrentSelection);
12✔
5825
        this.notifyChanges();
12✔
5826
    }
5827

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

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

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

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

5893

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

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

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

5925
        const selectedColumns = this.gridAPI.grid.selectedColumns();
12✔
5926
        const columnData = this.getSelectedColumnsData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
5927
        let selectedData;
5928
        if (event.type === 'copy') {
12✔
5929
            selectedData = this.getSelectedData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
5930
        }
5931

5932
        let data = [];
12✔
5933
        let result;
5934

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

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

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

5972
        if (ev.cancel) {
10✔
5973
            return;
1✔
5974
        }
5975

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

5979
        if (!this.clipboardOptions.copyHeaders) {
9✔
5980
            result = result.substring(result.indexOf('\n') + 1);
2✔
5981
        }
5982

5983
        if (data && data.length > 0 && Object.values(data[0]).length === 1) {
9✔
5984
            result = result.slice(0, -2);
4✔
5985
        }
5986

5987
        event.preventDefault();
9✔
5988

5989
        /* Necessary for the hiearachical case but will probably have to
5990
           change how getSelectedData is propagated in the hiearachical grid
5991
        */
5992
        event.stopPropagation();
9✔
5993

5994
        return result;
9✔
5995
    }
5996

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

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

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

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

6125
    /**
6126
     * @hidden
6127
     * @internal
6128
     */
6129
    public endRowEditTabStop(commit = true, event?: Event) {
×
6130
        const canceled = this.crudService.endEdit(commit, event);
15✔
6131

6132
        if (canceled) {
15✔
6133
            return true;
3✔
6134
        }
6135

6136
        this.navigation.restoreActiveNodeFocus();
12✔
6137
    }
6138

6139
    /**
6140
     * @hidden @internal
6141
     */
6142
    public trackColumnChanges(_index, col) {
6143
        return col.field + col._calcWidth;
1,706,649✔
6144
    }
6145

6146
    /**
6147
     * @hidden
6148
     */
6149
    public isExpandedGroup(_group: IGroupByRecord): boolean {
6150
        return undefined;
×
6151
    }
6152

6153
    /**
6154
     * @hidden @internal
6155
     * TODO: MOVE to CRUD
6156
     */
6157
    public openRowOverlay(id) {
6158
        this.configureRowEditingOverlay(id, this.rowList.length <= MIN_ROW_EDITING_COUNT_THRESHOLD);
208✔
6159

6160
        this.rowEditingOverlay.open(this.rowEditSettings);
208✔
6161
        this.rowEditingOverlay.element.addEventListener('wheel', this.rowEditingWheelHandler);
208✔
6162
    }
6163

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

6174
    /**
6175
     * @hidden @internal
6176
     */
6177
    public toggleRowEditingOverlay(show) {
6178
        const rowStyle = this.rowEditingOverlay.element.style;
277✔
6179
        if (show) {
277!
6180
            rowStyle.display = 'block';
277✔
6181
        } else {
6182
            rowStyle.display = 'none';
×
6183
        }
6184
    }
6185

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

6202
    /**
6203
     * @hidden @internal
6204
     */
6205
    public cachedViewLoaded(args: ICachedViewLoadedEventArgs) {
6206
        if (this.hasHorizontalScroll()) {
632✔
6207
            const tmplId = args.context.templateID.type;
447✔
6208
            const index = args.context.index;
447✔
6209
            args.view.detectChanges();
447✔
6210
            this.zone.onStable.pipe(first()).subscribe(() => {
447✔
6211
                const row = tmplId === 'dataRow' ? this.gridAPI.get_row_by_index(index) : null;
447✔
6212
                const summaryRow = tmplId === 'summaryRow' ? this.summariesRowList.find((sr) => sr.dataRowIndex === index) : null;
447✔
6213
                if (row && row instanceof IgxRowDirective) {
447✔
6214
                    this._restoreVirtState(row);
268✔
6215
                } else if (summaryRow) {
179✔
6216
                    this._restoreVirtState(summaryRow);
44✔
6217
                }
6218
            });
6219
        }
6220
    }
6221

6222
    /**
6223
     * Opens the advanced filtering dialog.
6224
     */
6225
    public openAdvancedFilteringDialog(overlaySettings?: OverlaySettings) {
6226
        const settings = overlaySettings ? overlaySettings : this._advancedFilteringOverlaySettings;
54!
6227
        if (!this._advancedFilteringOverlayId) {
54✔
6228
            this._advancedFilteringOverlaySettings.target =
51✔
6229
                (this as any).rootGrid ? (this as any).rootGrid.nativeElement : this.nativeElement;
51✔
6230
            this._advancedFilteringOverlaySettings.outlet = this.outlet;
51✔
6231

6232
            this._advancedFilteringOverlayId = this.overlayService.attach(
51✔
6233
                IgxAdvancedFilteringDialogComponent,
6234
                this.viewRef,
6235
                settings);
6236
            this.overlayService.show(this._advancedFilteringOverlayId);
51✔
6237
        }
6238
    }
6239

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

6250
            if (applyChanges) {
9✔
6251
                advancedFilteringDialog.applyChanges();
2✔
6252
            }
6253
            advancedFilteringDialog.closeDialog();
9✔
6254
        }
6255
    }
6256

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

6268
    /**
6269
     * @hidden @internal
6270
     */
6271
    public hasHorizontalScroll() {
6272
        return this.totalWidth - this.unpinnedWidth > 0 && this.width !== null;
11,340✔
6273
    }
6274

6275
    /**
6276
     * @hidden @internal
6277
     */
6278
    public isSummaryRow(rowData): boolean {
6279
        return rowData && rowData.summaries && (rowData.summaries instanceof Map);
436,129✔
6280
    }
6281

6282
    /**
6283
     * @hidden @internal
6284
     */
6285
    public triggerPipes() {
6286
        this.pipeTrigger++;
129✔
6287
        this.cdr.detectChanges();
129✔
6288
    }
6289

6290
    /**
6291
     * @hidden
6292
     */
6293
    public rowEditingWheelHandler = (event: WheelEvent) => {
4,113✔
6294
        if (event.deltaY > 0) {
×
6295
            this.verticalScrollContainer.scrollNext();
×
6296
        } else {
6297
            this.verticalScrollContainer.scrollPrev();
×
6298
        }
6299
    }
6300

6301
    /**
6302
     * @hidden
6303
     */
6304
    public getUnpinnedIndexById(id) {
6305
        return this.unpinnedRecords.findIndex(x => x[this.primaryKey] === id);
102✔
6306
    }
6307

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

6326
        const success = this.crudService.endEdit(commit, event);
6✔
6327

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

6336
        return success;
6✔
6337
    }
6338

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

6372
        this._addRowForIndex(index, asChild);
4✔
6373
    }
6374

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

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

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

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

6441
    protected switchTransactionService(val: boolean) {
6442
        if (val) {
244✔
6443
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);
243✔
6444
        } else {
6445
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
1✔
6446
        }
6447

6448
        if (this.dataCloneStrategy) {
244✔
6449
            this._transactions.cloneStrategy = this.dataCloneStrategy;
244✔
6450
        }
6451
    }
6452

6453
    protected subscribeToTransactions(): void {
6454
        this.transactionChange$.next();
3,813✔
6455
        this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$)))
3,813✔
6456
            .subscribe(this.transactionStatusUpdate.bind(this));
6457
    }
6458

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

6489
            });
6490
        }
6491

6492
        this.selectionService.clearHeaderCBState();
324✔
6493
        this.summaryService.clearSummaryCache();
324✔
6494
        this.pipeTrigger++;
324✔
6495
        this.notifyChanges();
324✔
6496
    }
6497

6498
    protected writeToData(rowIndex: number, value: any) {
6499
        mergeObjects(this.gridAPI.get_all_data()[rowIndex], value);
×
6500
    }
6501

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

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

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

6540
    /**
6541
     * @hidden
6542
     * Sets grid width i.e. this.calcWidth
6543
     */
6544
    protected calculateGridWidth() {
6545
        let width;
6546

6547
        if (this.isPercentWidth) {
11,831✔
6548
            /* width in %*/
6549
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width');
5,429✔
6550
            width = computed.indexOf('%') === -1 ? parseFloat(computed) : null;
5,429✔
6551
        } else {
6552
            width = parseInt(this.width, 10);
6,402✔
6553
        }
6554

6555
        if (!width && this.nativeElement) {
11,831✔
6556
            width = this.nativeElement.offsetWidth;
41✔
6557
        }
6558

6559

6560
        if (this.width === null || !width) {
11,831✔
6561
            this.isColumnWidthSum = true;
41✔
6562
            width = this.getColumnWidthSum();
41✔
6563
        } else {
6564
            this.isColumnWidthSum = false;
11,790✔
6565
        }
6566

6567
        if (this.hasVerticalScroll() && this.width !== null) {
11,831✔
6568
            width -= this.scrollSize;
2,413✔
6569
        }
6570
        if ((Number.isFinite(width) || width === null) && width !== this.calcWidth) {
11,831!
6571
            this.calcWidth = width;
3,831✔
6572
        }
6573
        this._derivePossibleWidth();
11,831✔
6574
    }
6575

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

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

6612
        if (minWidth > parseFloat(width)) {
69,379✔
6613
            width = String(column.minWidth);
6✔
6614
        } else if (maxWidth < parseFloat(width)) {
69,373✔
6615
            width = String(column.maxWidth);
1,120✔
6616
        }
6617

6618
        // if no px or % are defined in maxWidth/minWidth consider it px
6619
        if (width.indexOf('%') === -1 && width.indexOf('px') === -1) {
69,379✔
6620
            width += 'px';
276✔
6621
        }
6622
        return width;
69,379✔
6623
    }
6624

6625
    protected resetNotifyChanges() {
6626
        this._cdrRequestRepaint = false;
8,215✔
6627
        this._cdrRequests = false;
8,215✔
6628
    }
6629

6630
    /** @hidden @internal */
6631
    public resolveOutlet() {
6632
        return this._userOutletDirective ? this._userOutletDirective : this._outletDirective;
238,536!
6633
    }
6634

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

6648

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

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

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

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

6718
    /**
6719
     * @hidden @internal
6720
     */
6721
    protected setupColumns() {
6722
        if (this.autoGenerate) {
3,370✔
6723
            this.autogenerateColumns();
908✔
6724
        } else {
6725
            this._columns = this.getColumnList();
2,462✔
6726
        }
6727
        if (this._columns && this._columns.length && this._filteringExpressionsTree) {
3,370✔
6728
            this._filteringExpressionsTree = this.getRecreatedTree(this._filteringExpressionsTree);
3,313✔
6729
        }
6730
        if (this._columns && this._columns.length && this._advancedFilteringExpressionsTree) {
3,370✔
6731
            this._advancedFilteringExpressionsTree = this.getRecreatedTree(this._advancedFilteringExpressionsTree);
5✔
6732
        }
6733

6734
        this.initColumns(this._columns, (col: IgxColumnComponent) => this.columnInit.emit(col));
21,656✔
6735
        this.columnListDiffer.diff(this.columnList);
3,370✔
6736

6737
        this.columnList.changes
3,370✔
6738
            .pipe(takeUntil(this.destroy$))
6739
            .subscribe((change: QueryList<IgxColumnComponent>) => {
6740
                this.onColumnsChanged(change);
74✔
6741
            });
6742
    }
6743

6744
    protected getColumnList() {
6745
        return this.columnList.toArray();
2,263✔
6746
    }
6747

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

6767

6768
    /**
6769
     * @hidden @internal
6770
     */
6771
    protected getDataBasedBodyHeight(): number {
6772
        return !this.data || (this.data.length < this._defaultTargetRecordNumber) ?
1,064✔
6773
            0 : this.defaultTargetBodyHeight;
6774
    }
6775

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

6786
    /**
6787
     * @hidden
6788
     */
6789
    protected onColumnsChanged(change: QueryList<IgxColumnComponent>) {
6790
        const diff = this.columnListDiffer.diff(change);
63✔
6791

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

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

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

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

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

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

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

6842
            this.resetCaches();
63✔
6843

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

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

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

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

6889
        if (this.rowEditable) {
7,906✔
6890
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
876✔
6891
        }
6892

6893
        if (this.filteringService.isFilterRowVisible) {
7,906✔
6894
            this.filteringRow.resetChipsArea();
153✔
6895
        }
6896

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

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

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

6938
        this.calcHeight = this._calculateGridBodyHeight();
10,445✔
6939
        if (this.pinnedRowHeight && this.calcHeight) {
10,445✔
6940
            this.calcHeight -= this.pinnedRowHeight;
107✔
6941
        }
6942
    }
6943

6944
    /**
6945
     * @hidden
6946
     */
6947
    protected getGroupAreaHeight(): number {
6948
        return 0;
2,766✔
6949
    }
6950

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

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

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

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

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

7026
        let gridHeight = 0;
9,646✔
7027

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

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

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

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

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

7089
        return width - this.getPinnedWidth(takeHidden);
23,498✔
7090
    }
7091

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

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

7126
    /**
7127
     * @hidden
7128
     */
7129
    public resolveDataTypes(rec) {
7130
        if (typeof rec === 'number') {
6,518✔
7131
            return GridColumnDataType.Number;
3,851✔
7132
        } else if (typeof rec === 'boolean') {
2,667✔
7133
            return GridColumnDataType.Boolean;
152✔
7134
        } else if (typeof rec === 'object' && rec instanceof Date) {
2,515✔
7135
            return GridColumnDataType.Date;
143✔
7136
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
2,372✔
7137
            return GridColumnDataType.Image;
1✔
7138
        }
7139
        return GridColumnDataType.String;
2,371✔
7140
    }
7141

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

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

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

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

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

7197
            if (cb) {
23,626✔
7198
                cb(column);
23,626✔
7199
            }
7200
        });
7201

7202
        this.updateColumns(collection);
3,642✔
7203

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

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

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

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

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

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

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

7284
        if (columnData) {
246!
7285
            selectedData = columnData;
×
7286
        }
7287

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

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

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

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

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

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

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

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

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

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

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

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

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

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

7474
        this.scrollToHorizontally(column);
106✔
7475
    }
7476

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

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

7504

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

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

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

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

7555
    protected verticalScrollHandler(event) {
7556
        this.verticalScrollContainer.onScroll(event);
321✔
7557
        this.disableTransitions = true;
321✔
7558

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

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

7582
    protected horizontalScrollHandler(event) {
7583
        const scrollLeft = event.target.scrollLeft;
306✔
7584
        this.headerContainer.onHScroll(scrollLeft);
306✔
7585
        this._horizontalForOfs.forEach(vfor => vfor.onHScroll(scrollLeft));
1,928✔
7586
        this.cdr.markForCheck();
306✔
7587

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

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

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

7632
            return;
8✔
7633
        }
7634
        const args = this.getNavigationArguments(row, visibleColIndex);
543✔
7635
        cb(args);
543✔
7636
    }
7637

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

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

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

7691

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

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

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

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

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

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

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

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

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

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

7776
            this.rebuildMatchCache();
84✔
7777
        }
7778

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

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

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

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

7800
        } else {
7801
            this.textHighlightService.clearActiveHighlight(this.id);
18✔
7802
        }
7803

7804
        return this._lastSearchInfo.matchCount;
322✔
7805
    }
7806

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

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

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

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

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

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

7850
                            this._lastSearchInfo.matchInfoCache.push(mic);
2,410✔
7851

7852
                            searchValue = searchValue.substring(searchIndex + searchText.length);
2,410✔
7853
                            searchIndex = searchValue.indexOf(searchText);
2,410✔
7854
                        }
7855
                    }
7856
                }
7857
            });
7858
        });
7859

7860
        this._lastSearchInfo.matchCount = this._lastSearchInfo.matchInfoCache.length;
211✔
7861
    }
7862

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

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

7894
    private handleColumnPinningForGroups(): void {
7895
        // When a column is a group or is inside a group, pin all related.
7896
        const pinnedColumns = [];
3,944✔
7897
        const unpinnedColumns = [];
3,944✔
7898

7899
        this._pinnedColumns.forEach(col => {
3,944✔
7900
            if (col.parent) {
492✔
7901
                col.parent.pinned = true;
113✔
7902
            }
7903
            if (col.columnGroup) {
492✔
7904
                col.children.forEach(child => child.pinned = true);
112✔
7905
            }
7906
        });
7907

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

7930
    protected shouldRecreateColumns(oldData: any[] | null | undefined, newData: any[] | null | undefined): boolean {
7931
        if (!oldData || !oldData.length) return true;
73✔
7932
        if (!newData || !newData.length) return false;
46!
7933
        return Object.keys(oldData[0]).join() !== Object.keys(newData[0]).join();
46✔
7934
    }
7935

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

7945
    private getRecreatedTree(value: IFilteringExpressionsTree): IFilteringExpressionsTree {
7946
        if (this._hGridSchema) {
9,472✔
7947
            return recreateTree(value, this._hGridSchema, true) as IFilteringExpressionsTree;
15✔
7948
        } else {
7949
            return recreateTreeFromFields(value, this._columns) as IFilteringExpressionsTree;
9,457✔
7950
        }
7951
    }
7952
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc