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

IgniteUI / igniteui-angular / 11741984483

08 Nov 2024 12:19PM CUT coverage: 91.57% (-0.04%) from 91.606%
11741984483

Pull #14999

github

web-flow
Merge dba70cfe6 into 7b8563221
Pull Request #14999: fix(grid): add coerceToInt attribute to IPinningConfig and pagingMode props

12947 of 15174 branches covered (85.32%)

26243 of 28659 relevant lines covered (91.57%)

33883.34 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

299
    public get summaryRowHeight(): number {
300
        if (this.hasSummarizedColumns && this.rootSummariesEnabled) {
262,991✔
301
            return this._summaryRowHeight || this.summaryService.calcMaxSummaryHeight();
181,658✔
302
        }
303
        return 0;
81,333✔
304
    }
305

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

509

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1061

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

1075
    /* End of toolbar related definitions */
1076

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

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

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

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

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

1118

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1216

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

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

1242

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1594

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1795

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

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

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

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

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

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

1859
            value.type = FilteringExpressionsTreeType.Regular;
919✔
1860
            this._filteringExpressionsTree = value;
919✔
1861
            this.filteringPipeTrigger++;
919✔
1862
            this.filteringExpressionsTreeChange.emit(this._filteringExpressionsTree);
919✔
1863

1864
            if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
919✔
1865
                this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1866
                this._filteredData = null;
466✔
1867
            }
1868

1869
            this.filteringService.refreshExpressions();
919✔
1870
            this.selectionService.clearHeaderCBState();
919✔
1871
            this.summaryService.clearSummaryCache();
919✔
1872
            this.notifyChanges();
919✔
1873
        }
1874
    }
1875

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

1891
    public set advancedFilteringExpressionsTree(value) {
1892
        const filteringEventArgs: IFilteringEventArgs = {
101✔
1893
            owner: this,
1894
            filteringExpressions: value,
1895
            cancel: false
1896
        };
1897

1898
        this.filtering.emit(filteringEventArgs);
101✔
1899

1900
        if (filteringEventArgs.cancel) {
101✔
1901
            return;
1✔
1902
        }
1903

1904
        if (value && value instanceof FilteringExpressionsTree) {
100✔
1905
            value.type = FilteringExpressionsTreeType.Advanced;
83✔
1906
            this._advancedFilteringExpressionsTree = value;
83✔
1907
            this.filteringPipeTrigger++;
83✔
1908
        } else {
1909
            this._advancedFilteringExpressionsTree = null;
17✔
1910
        }
1911
        this.advancedFilteringExpressionsTreeChange.emit(this._advancedFilteringExpressionsTree);
100✔
1912

1913
        if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
100✔
1914
            this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1915
            this._filteredData = null;
19✔
1916
        }
1917

1918
        this.selectionService.clearHeaderCBState();
100✔
1919
        this.summaryService.clearSummaryCache();
100✔
1920
        this.notifyChanges();
100✔
1921

1922
        // Wait for the change detection to update filtered data through the pipes and then emit the event.
1923
        requestAnimationFrame(() => this.filteringDone.emit(this._advancedFilteringExpressionsTree));
100✔
1924
    }
1925

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

1937
    public set locale(value: string) {
1938
        if (value !== this._locale) {
4,065✔
1939
            this._locale = value;
4,060✔
1940
            this._currencyPositionLeft = undefined;
4,060✔
1941
            this.summaryService.clearSummaryCache();
4,060✔
1942
            this.pipeTrigger++;
4,060✔
1943
            this.notifyChanges();
4,060✔
1944
            this.localeChange.emit();
4,060✔
1945
        }
1946
    }
1947

1948
    /* mustCoerceToInt */
1949
    @Input()
1950
    public get pagingMode() {
1951
        return this._pagingMode;
8,419✔
1952
    }
1953

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

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

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

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

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

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

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

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

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

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

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

2035
    /** @hidden @internal */
2036
    public dragRowID = null;
4,049✔
2037

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2277
    public set allowFiltering(value) {
2278
        if (this._allowFiltering !== value) {
722✔
2279
            this._allowFiltering = value;
711✔
2280
            this.filteringService.registerSVGIcons();
711✔
2281

2282

2283
            this.filteringService.isFilterRowVisible = false;
711✔
2284
            this.filteringService.filteredColumn = null;
711✔
2285

2286
            this.notifyChanges(true);
711✔
2287
        }
2288
    }
2289

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2493

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2652
    /**
2653

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

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

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

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

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

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

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

2716
        if (!activeElem || !Object.keys(activeElem).length) {
133,443✔
2717
            return this.id;
118,079✔
2718
        }
2719

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2989
    /**
2990
     * @hidden
2991
     */
2992
    public _filteredUnpinnedData;
2993
    /**
2994
     * @hidden @internal
2995
     */
2996
    public _destroyed = false;
4,049✔
2997
    /**
2998
     * @hidden @internal
2999
     */
3000
    public _totalRecords = -1;
4,049✔
3001
    /**
3002
     * @hidden @internal
3003
     */
3004
    public columnsWithNoSetWidths = null;
4,049✔
3005
    /**
3006
     * @hidden @internal
3007
     */
3008
    public pipeTrigger = 0;
4,049✔
3009
    /**
3010
     * @hidden @internal
3011
     */
3012
    public filteringPipeTrigger = 0;
4,049✔
3013
    /**
3014
     * @hidden @internal
3015
     */
3016
    public summaryPipeTrigger = 0;
4,049✔
3017
    /**
3018
     * @hidden @internal
3019
     */
3020
    public groupablePipeTrigger = 0;
4,049✔
3021

3022
    /**
3023
    * @hidden @internal
3024
    */
3025
    public EMPTY_DATA = [];
4,049✔
3026

3027
    /** @hidden @internal */
3028
    public get type(): GridType["type"] {
3029
        return 'flat';
24,676✔
3030
    }
3031

3032
    /** @hidden @internal */
3033
    public _baseFontSize: number;
3034

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

3092
    protected _pinnedRecordIDs = [];
4,049✔
3093

3094
    /**
3095
     * @hidden
3096
     */
3097
    protected _hasVisibleColumns;
3098
    protected _allowFiltering = false;
4,049✔
3099
    protected _allowAdvancedFiltering = false;
4,049✔
3100
    protected _filterMode: FilterMode = FilterMode.quickFilter;
4,049✔
3101

3102

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

3129
    /** @hidden @internal */
3130
    public get paginator() {
3131
        return this.paginationComponents?.first;
3,751,799✔
3132
    }
3133

3134
    /**
3135
     * @hidden @internal
3136
     */
3137
    public get scrollSize() {
3138
        return this.verticalScrollContainer.getScrollNativeSize();
186,061✔
3139
    }
3140

3141
    private _primaryKey: string;
3142
    private _rowEditable = false;
4,049✔
3143
    private _currentRowState: any;
3144
    private _filteredSortedData = null;
4,049✔
3145
    private _filteredData = null;
4,049✔
3146

3147
    private _customDragIndicatorIconTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3148
    private _excelStyleHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext>;
3149
    private _rowSelectorTemplate: TemplateRef<IgxRowSelectorTemplateContext>;
3150
    private _headSelectorTemplate: TemplateRef<IgxHeadSelectorTemplateContext>;
3151
    private _rowEditTextTemplate: TemplateRef<IgxGridRowEditTextTemplateContext>;
3152
    private _rowAddTextTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3153
    private _rowEditActionsTemplate: TemplateRef<IgxGridRowEditActionsTemplateContext>;
3154
    private _dragGhostCustomTemplate: TemplateRef<IgxGridRowDragGhostContext>;
3155
    private _rowExpandedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3156
    private _rowCollapsedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3157
    private _headerExpandIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
3158
    private _headerCollapseIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
3159

3160
    private _cdrRequests = false;
4,049✔
3161
    private _resourceStrings = getCurrentResourceStrings(GridResourceStringsEN);
4,049✔
3162
    private _emptyGridMessage = null;
4,049✔
3163
    private _emptyFilteredGridMessage = null;
4,049✔
3164
    private _isLoading = false;
4,049✔
3165
    private _locale: string;
3166
    private overlayIDs = [];
4,049✔
3167
    private _sortingStrategy: IGridSortingStrategy;
3168
    private _pinning: IPinningConfig = { columns: ColumnPinningPosition.Start };
4,049✔
3169
    private _shouldRecalcRowHeight = false;
4,049✔
3170

3171
    private _hostWidth;
3172
    private _advancedFilteringOverlayId: string;
3173
    private _advancedFilteringPositionSettings: PositionSettings = {
4,049✔
3174
        verticalDirection: VerticalAlignment.Middle,
3175
        horizontalDirection: HorizontalAlignment.Center,
3176
        horizontalStartPoint: HorizontalAlignment.Center,
3177
        verticalStartPoint: VerticalAlignment.Middle
3178
    };
3179

3180
    private _advancedFilteringOverlaySettings: OverlaySettings = {
4,049✔
3181
        closeOnOutsideClick: false,
3182
        modal: false,
3183
        positionStrategy: new ConnectedPositioningStrategy(this._advancedFilteringPositionSettings),
3184
    };
3185

3186
    private columnListDiffer;
3187
    private rowListDiffer;
3188
    private _height: string | null = '100%';
4,049✔
3189
    private _width: string | null = '100%';
4,049✔
3190
    private _rowHeight: number | undefined;
3191
    private _horizontalForOfs: Array<IgxGridForOfDirective<any, any[]>> = [];
4,049✔
3192
    private _multiRowLayoutRowSize = 1;
4,049✔
3193
    // Caches
3194
    private _totalWidth = NaN;
4,049✔
3195
    private _pinnedVisible = [];
4,049✔
3196
    private _unpinnedVisible = [];
4,049✔
3197
    private _pinnedWidth = NaN;
4,049✔
3198
    private _unpinnedWidth = NaN;
4,049✔
3199
    private _visibleColumns = [];
4,049✔
3200
    private _columnGroups = false;
4,049✔
3201

3202
    private _columnWidth: string;
3203

3204
    private _summaryPosition: GridSummaryPosition = GridSummaryPosition.bottom;
4,049✔
3205
    private _summaryCalculationMode: GridSummaryCalculationMode = GridSummaryCalculationMode.rootAndChildLevels;
4,049✔
3206
    private _showSummaryOnCollapse = false;
4,049✔
3207
    private _summaryRowHeight = 0;
4,049✔
3208
    private _cellSelectionMode: GridSelectionMode = GridSelectionMode.multiple;
4,049✔
3209
    private _rowSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,049✔
3210
    private _selectRowOnClick = true;
4,049✔
3211
    private _columnSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,049✔
3212

3213
    private lastAddedRowIndex;
3214
    private _currencyPositionLeft: boolean;
3215

3216
    private rowEditPositioningStrategy = new RowEditPositionStrategy({
4,049✔
3217
        horizontalDirection: HorizontalAlignment.Right,
3218
        verticalDirection: VerticalAlignment.Bottom,
3219
        horizontalStartPoint: HorizontalAlignment.Left,
3220
        verticalStartPoint: VerticalAlignment.Bottom,
3221
        closeAnimation: null
3222
    });
3223

3224
    private rowEditSettings: OverlaySettings = {
4,049✔
3225
        scrollStrategy: new AbsoluteScrollStrategy(),
3226
        modal: false,
3227
        closeOnOutsideClick: false,
3228
        outlet: this.rowOutletDirective,
3229
        positionStrategy: this.rowEditPositioningStrategy
3230
    };
3231

3232
    private transactionChange$ = new Subject<void>();
4,049✔
3233
    private _rendered = false;
4,049✔
3234
    private readonly DRAG_SCROLL_DELTA = 10;
4,049✔
3235
    private _dataCloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy();
4,049✔
3236
    private _autoSize = false;
4,049✔
3237
    private _sortHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,049✔
3238
    private _sortAscendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,049✔
3239
    private _sortDescendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,049✔
3240
    private _gridSize: Size = Size.Large;
4,049✔
3241
    private _defaultRowHeight = 50;
4,049✔
3242

3243
    /**
3244
     * @hidden @internal
3245
     */
3246
    protected get minColumnWidth() {
3247
        return MINIMUM_COLUMN_WIDTH;
65,751✔
3248
    }
3249

3250
    protected get isCustomSetRowHeight(): boolean {
3251
        return !isNaN(this._rowHeight);
1,199,235✔
3252
    }
3253

3254
    /**
3255
     * @hidden @internal
3256
     */
3257
    public abstract id: string;
3258
    /* blazorSuppress */
3259
    public abstract data: any[] | null;
3260

3261
    /**
3262
     * Returns an array of objects containing the filtered data.
3263
     *
3264
     * @example
3265
     * ```typescript
3266
     * let filteredData = this.grid.filteredData;
3267
     * ```
3268
     */
3269
    public get filteredData() {
3270
        return this._filteredData;
48,694✔
3271
    }
3272

3273
    /**
3274
     * Returns an array containing the filtered sorted data.
3275
     *
3276
     * @example
3277
     * ```typescript
3278
     * const filteredSortedData = this.grid1.filteredSortedData;
3279
     * ```
3280
     */
3281
    public get filteredSortedData(): any[] {
3282
        return this._filteredSortedData;
128,228✔
3283
    }
3284

3285
    /**
3286
     * @hidden @internal
3287
     */
3288
    public get rowChangesCount() {
3289
        if (!this.crudService.row) {
16,698✔
3290
            return 0;
13,965✔
3291
        }
3292
        const f = (obj: any) => {
2,733✔
3293
            let changes = 0;
470✔
3294
            Object.keys(obj).forEach(key => isObject(obj[key]) ? changes += f(obj[key]) : changes++);
492✔
3295
            return changes;
470✔
3296
        };
3297
        if (this.transactions.getState(this.crudService.row.id)?.type === TransactionType.ADD) {
2,733✔
3298
            return this._columns.filter(c => c.field).length;
77✔
3299
        }
3300
        const rowChanges = this.transactions.getAggregatedValue(this.crudService.row.id, false);
2,714✔
3301
        return rowChanges ? f(rowChanges) : 0;
2,714✔
3302
    }
3303

3304
    /**
3305
     * @hidden @internal
3306
     */
3307
    public get dataWithAddedInTransactionRows() {
3308
        const result = cloneArray(this.gridAPI.get_all_data());
11,387✔
3309
        if (this.transactions.enabled) {
11,387✔
3310
            result.push(...this.transactions.getAggregatedChanges(true)
7,003✔
3311
                .filter(t => t.type === TransactionType.ADD)
1,228✔
3312
                .map(t => t.newValue));
356✔
3313
        }
3314

3315
        if (this.crudService.row && this.crudService.row.getClassName() === IgxAddRow.name) {
11,387✔
3316
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
379✔
3317
        }
3318

3319
        return result;
11,387✔
3320
    }
3321

3322
    /**
3323
     * @hidden @internal
3324
     */
3325
    public get dataLength() {
3326
        return this.transactions.enabled ? this.dataWithAddedInTransactionRows.length : this.gridAPI.get_all_data().length;
45,591✔
3327
    }
3328

3329
    /**
3330
     * @hidden @internal
3331
     */
3332
    public get template(): TemplateRef<IgxGridTemplateContext> {
3333
        if (this.isLoading && (this.hasZeroResultFilter || this.hasNoData)) {
45,197✔
3334
            return this.loadingGridTemplate ? this.loadingGridTemplate : this.loadingGridDefaultTemplate;
81✔
3335
        }
3336

3337
        if (this.hasZeroResultFilter) {
45,116✔
3338
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
218!
3339
        }
3340

3341
        if (this.hasNoData) {
44,898✔
3342
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate;
734✔
3343
        }
3344
    }
3345

3346
    /**
3347
     * @hidden @internal
3348
     */
3349
    private get hasZeroResultFilter(): boolean {
3350
        return this.filteredData && this.filteredData.length === 0;
45,392✔
3351
    }
3352

3353
    /**
3354
     * @hidden @internal
3355
     */
3356
    private get hasNoData(): boolean {
3357
        return !this.data || this.dataLength === 0;
45,243✔
3358
    }
3359

3360
    /**
3361
     * @hidden @internal
3362
     */
3363
    public get shouldOverlayLoading(): boolean {
3364
        return this.isLoading && !this.hasNoData && !this.hasZeroResultFilter;
48,539✔
3365
    }
3366

3367
    /**
3368
     * @hidden @internal
3369
     */
3370
    public get isMultiRowSelectionEnabled(): boolean {
3371
        return this.rowSelection === GridSelectionMode.multiple
3,391✔
3372
            || this.rowSelection === GridSelectionMode.multipleCascade;
3373
    }
3374

3375
    /**
3376
     * @hidden @internal
3377
     */
3378
    public get isRowSelectable(): boolean {
3379
        return this.rowSelection !== GridSelectionMode.none;
288,186✔
3380
    }
3381

3382
    /**
3383
     * @hidden @internal
3384
     */
3385
    public get isCellSelectable() {
3386
        return this.cellSelection !== GridSelectionMode.none;
8,485✔
3387
    }
3388

3389
    /**
3390
     * @hidden @internal
3391
     */
3392
    public get columnInDrag() {
3393
        return this.gridAPI.cms.column;
270,574✔
3394
    }
3395

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

3433
    /**
3434
     * @hidden
3435
     * @internal
3436
     */
3437
    @HostListener('mouseleave')
3438
    public hideActionStrip() {
3439
        this.actionStrip?.hide();
3✔
3440
    }
3441

3442
    /**
3443
     * @hidden
3444
     * @internal
3445
     */
3446
    public get headerFeaturesWidth() {
3447
        return this._headerFeaturesWidth;
1,320✔
3448
    }
3449

3450
    /**
3451
     * @hidden
3452
     * @internal
3453
     */
3454
    public isDetailRecord(_rec) {
3455
        return false;
1,234✔
3456
    }
3457

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

3466
    /**
3467
     * @hidden @internal
3468
     */
3469
    public isGhostRecord(record: any): boolean {
3470
        return record.ghostRecord !== undefined;
535,870✔
3471
    }
3472
    /**
3473
     * @hidden @internal
3474
     */
3475
    public isAddRowRecord(record: any): boolean {
3476
        return record.addRow !== undefined;
×
3477
    }
3478

3479
    /**
3480
     * @hidden
3481
     * Returns the row index of a row that takes into account the full view data like pinning.
3482
     */
3483
    public getDataViewIndex(rowIndex, pinned) {
3484
        if (pinned && !this.isRowPinningToTop) {
252,287✔
3485
            rowIndex = rowIndex + this.unpinnedDataView.length;
198✔
3486
        } else if (!pinned && this.isRowPinningToTop) {
252,089✔
3487
            rowIndex = rowIndex + this.pinnedDataView.length;
250,338✔
3488
        }
3489
        return rowIndex;
252,287✔
3490
    }
3491

3492
    /**
3493
     * @hidden
3494
     * @internal
3495
     */
3496
    public get hasDetails() {
3497
        return false;
370✔
3498
    }
3499

3500
    /**
3501
     * Returns the state of the grid virtualization.
3502
     *
3503
     * @remarks
3504
     * Includes the start index and how many records are rendered.
3505
     * @example
3506
     * ```typescript
3507
     * const gridVirtState = this.grid1.virtualizationState;
3508
     * ```
3509
     */
3510
    public get virtualizationState() {
3511
        return this.verticalScrollContainer.state;
1,238✔
3512
    }
3513

3514
    /**
3515
     * @hidden
3516
     * @internal
3517
     */
3518
    public hideOverlays() {
3519
        this.overlayIDs.forEach(overlayID => {
522✔
3520
            const overlay = this.overlayService.getOverlayById(overlayID);
1✔
3521

3522
            if (overlay?.visible && !overlay.closeAnimationPlayer?.hasStarted()) {
1✔
3523
                this.overlayService.hide(overlayID);
1✔
3524

3525
                this.nativeElement.focus();
1✔
3526
            }
3527
        });
3528
    }
3529

3530
    /**
3531
     * Returns whether the record is pinned or not.
3532
     *
3533
     * @param rowIndex Index of the record in the `dataView` collection.
3534
     *
3535
     * @hidden
3536
     * @internal
3537
     */
3538
    public isRecordPinnedByViewIndex(rowIndex: number) {
3539
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this.pinnedDataView.length) ||
299,098✔
3540
            (!this.isRowPinningToTop && rowIndex >= this.unpinnedDataView.length);
3541
    }
3542

3543
    /**
3544
     * Returns whether the record is pinned or not.
3545
     *
3546
     * @param rowIndex Index of the record in the `filteredSortedData` collection.
3547
     */
3548
    public isRecordPinnedByIndex(rowIndex: number) {
3549
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this._filteredSortedPinnedData.length) ||
2,419!
3550
            (!this.isRowPinningToTop && rowIndex >= this._filteredSortedUnpinnedData.length);
3551
    }
3552

3553
    /**
3554
     * @hidden
3555
     * @internal
3556
     */
3557
    public isRecordPinned(rec) {
3558
        return this.getInitialPinnedIndex(rec) !== -1;
1,090,734✔
3559
    }
3560

3561
    /**
3562
     * @hidden
3563
     * @internal
3564
     * Returns the record index in order of pinning by the user. Does not consider sorting/filtering.
3565
     */
3566
    public getInitialPinnedIndex(rec) {
3567
        const id = this.gridAPI.get_row_id(rec);
1,090,842✔
3568
        return this._pinnedRecordIDs.indexOf(id);
1,090,842✔
3569
    }
3570

3571
    /**
3572
     * @hidden
3573
     * @internal
3574
     */
3575
    public get hasPinnedRecords() {
3576
        return this._pinnedRecordIDs.length > 0;
489,291✔
3577
    }
3578

3579
    /**
3580
     * @hidden
3581
     * @internal
3582
     */
3583
    public get pinnedRecordsCount() {
3584
        return this._pinnedRecordIDs.length;
7,348✔
3585
    }
3586

3587
    /**
3588
     * @hidden
3589
     * @internal
3590
     */
3591
    public get crudService() {
3592
        return this.gridAPI.crudService;
3,395,786✔
3593
    }
3594

3595
    /**
3596
     * @hidden
3597
     * @internal
3598
     */
3599
    public _setupServices() {
3600
        this.gridAPI.grid = this as any;
3,540✔
3601
        this.crudService.grid = this as any;
3,540✔
3602
        this.selectionService.grid = this as any;
3,540✔
3603
        this.validation.grid = this as any;
3,540✔
3604
        this.navigation.grid = this as any;
3,540✔
3605
        this.filteringService.grid = this as any;
3,540✔
3606
        this.summaryService.grid = this as any;
3,540✔
3607
    }
3608

3609
    /**
3610
     * @hidden
3611
     * @internal
3612
     */
3613
    public _setupListeners() {
3614
        const destructor = takeUntil<any>(this.destroy$);
3,540✔
3615
        fromEvent(this.nativeElement, 'focusout').pipe(filter(() => !!this.navigation.activeNode), destructor).subscribe((event) => {
3,540✔
3616
            const activeNode = this.navigation.activeNode;
704✔
3617
            if (!this.crudService.cell && !!activeNode &&
704!
3618
                ((event.target === this.tbody.nativeElement && activeNode.row >= 0 &&
3619
                    activeNode.row < this.dataView.length)
3620
                    || (event.target === this.theadRow.nativeElement && activeNode.row === -1)
3621
                    || (event.target === this.tfoot.nativeElement && activeNode.row === this.dataView.length)) &&
3622
                !(this.rowEditable && this.crudService.rowEditingBlocked && this.crudService.rowInEditMode)) {
75!
3623
                this.clearActiveNode();
68✔
3624
            }
3625
        });
3626
        this.rowAddedNotifier.pipe(destructor).subscribe(args => this.refreshGridState(args));
3,540✔
3627
        this.rowDeletedNotifier.pipe(destructor).subscribe(args => {
3,540✔
3628
            this.summaryService.deleteOperation = true;
185✔
3629
            this.summaryService.clearSummaryCache(args);
185✔
3630
        });
3631

3632
        this.subscribeToTransactions();
3,540✔
3633

3634
        this.resizeNotify.pipe(
3,540✔
3635
            filter(() => !this._init),
2,904✔
3636
            throttleTime(40, animationFrameScheduler, { leading: true, trailing: true }),
3637
            destructor
3638
        )
3639
        .subscribe(() => {
3640
            this.zone.run(() => {
2,173✔
3641
                // do not trigger reflow if element is detached.
3642
                if (this.nativeElement.isConnected) {
2,173✔
3643
                    if (this.shouldResize) {
1,725✔
3644
                        // resizing occurs due to the change of --ig-size css var
3645
                        this._gridSize = this.gridSize;
69✔
3646
                        this.updateDefaultRowHeight();
69✔
3647
                        this._autoSize = this.isPercentHeight && this.calcHeight !== this.getDataBasedBodyHeight();
69✔
3648
                        this.crudService.endEdit(false);
69✔
3649
                        if (this._summaryRowHeight === 0) {
69✔
3650
                            this.summaryService.summaryHeight = 0;
69✔
3651
                        }
3652
                    }
3653
                    this.notifyChanges(true);
1,725✔
3654
                }
3655
            });
3656
        });
3657

3658
        this.pipeTriggerNotifier.pipe(takeUntil(this.destroy$)).subscribe(() => this.pipeTrigger++);
3,540✔
3659
        this.columnMovingEnd.pipe(destructor).subscribe(() => this.crudService.endEdit(false));
3,540✔
3660

3661
        this.overlayService.opening.pipe(destructor).subscribe((event) => {
3,540✔
3662
            if (this._advancedFilteringOverlayId === event.id) {
888✔
3663
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
90✔
3664
                if (instance) {
90✔
3665
                    instance.initialize(this as any, this.overlayService, event.id);
90✔
3666
                }
3667
            }
3668
        });
3669

3670
        this.overlayService.opened.pipe(destructor).subscribe((event) => {
3,540✔
3671
            const overlaySettings = this.overlayService.getOverlayById(event.id)?.settings;
623✔
3672

3673
            // do not hide the advanced filtering overlay on scroll
3674
            if (this._advancedFilteringOverlayId === event.id) {
623✔
3675
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
86✔
3676
                if (instance) {
86✔
3677
                    instance.lastActiveNode = this.navigation.activeNode;
86✔
3678
                    instance.queryBuilder.setAddButtonFocus();
86✔
3679
                }
3680
                return;
86✔
3681
            }
3682

3683
            // do not hide the overlay if it's attached to a row
3684
            if (this.rowEditingOverlay?.overlayId === event.id) {
537✔
3685
                return;
21✔
3686
            }
3687

3688
            if (overlaySettings?.outlet === this.outlet && this.overlayIDs.indexOf(event.id) === -1) {
516✔
3689
                this.overlayIDs.push(event.id);
346✔
3690
            }
3691
        });
3692

3693
        this.overlayService.closed.pipe(filter(() => !this._init), destructor).subscribe((event) => {
3,540✔
3694
            if (this._advancedFilteringOverlayId === event.id) {
490✔
3695
                this.overlayService.detach(this._advancedFilteringOverlayId);
32✔
3696
                this._advancedFilteringOverlayId = null;
32✔
3697
                return;
32✔
3698
            }
3699

3700
            const ind = this.overlayIDs.indexOf(event.id);
458✔
3701
            if (ind !== -1) {
458✔
3702
                this.overlayIDs.splice(ind, 1);
196✔
3703
            }
3704
        });
3705

3706
        this.verticalScrollContainer.dataChanging.pipe(filter(() => !this._init), destructor).subscribe(($event) => {
6,736✔
3707
            const shouldRecalcSize = this.isPercentHeight &&
3,280✔
3708
                (!this.calcHeight || this.calcHeight === this.getDataBasedBodyHeight() ||
3709
                    this.calcHeight === this.renderedRowHeight * this._defaultTargetRecordNumber);
3710
            if (shouldRecalcSize) {
3,280✔
3711
                this.calculateGridHeight();
107✔
3712
                $event.containerSize = this.calcHeight;
107✔
3713
            }
3714
            this.evaluateLoadingState();
3,280✔
3715
        });
3716

3717
        this.verticalScrollContainer.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3,540✔
3718
            // called to recalc all widths that may have changes as a result of
3719
            // the vert. scrollbar showing/hiding
3720
            this.notifyChanges(true);
882✔
3721
            this.cdr.detectChanges();
882✔
3722
            Promise.resolve().then(() => this.headerContainer.updateScroll());
882✔
3723
        });
3724

3725

3726
        this.headerContainer?.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3,540✔
3727
            // the horizontal scrollbar showing/hiding
3728
            // update scrollbar visibility and recalc heights
3729
            this.notifyChanges(true);
1,333✔
3730
            this.cdr.detectChanges();
1,333✔
3731
        });
3732

3733
        this.verticalScrollContainer.contentSizeChange.pipe(filter(() => !this._init), throttleTime(30), destructor).subscribe(() => {
3,540✔
3734
            this.notifyChanges(true);
696✔
3735
        });
3736

3737
        // notifier for column autosize requests
3738
        this._autoSizeColumnsNotify.pipe(
3,540✔
3739
            throttleTime(0, animationFrameScheduler, { leading: false, trailing: true }),
3740
            destructor
3741
        )
3742
        .subscribe(() => {
3743
            this.autoSizeColumnsInView();
5✔
3744
            this._firstAutoResize = false;
5✔
3745
        });
3746
    }
3747

3748
    /**
3749
     * @hidden
3750
     */
3751
    public ngOnInit() {
3752
        this._setupServices();
3,540✔
3753
        this._setupListeners();
3,540✔
3754
        this.rowListDiffer = this.differs.find([]).create(null);
3,540✔
3755
        // compare based on field, not on object ref.
3756
        this.columnListDiffer = this.differs.find([]).create((index, col: ColumnType) => col.field);
17,476✔
3757
        this.calcWidth = this.width && this.width.indexOf('%') === -1 ? parseInt(this.width, 10) : 0;
3,540✔
3758
        this.gridComputedStyles = this.document.defaultView.getComputedStyle(this.nativeElement);
3,540✔
3759
    }
3760

3761
    /**
3762
     * @hidden
3763
     * @internal
3764
     */
3765
    public resetColumnsCaches() {
3766
        this._columns.forEach(column => column.resetCaches());
214,628✔
3767
    }
3768

3769
    /**
3770
     * @hidden @internal
3771
     */
3772
    public generateRowID(): string | number {
3773
        const primaryColumn = this._columns.find(col => col.field === this.primaryKey);
60✔
3774
        const idType = this.data.length ?
58✔
3775
            this.resolveDataTypes(this.data[0][this.primaryKey]) : primaryColumn ? primaryColumn.dataType : 'string';
1!
3776
        return idType === 'string' ? uuidv4() : FAKE_ROW_ID--;
58✔
3777
    }
3778

3779
    /**
3780
     * @hidden
3781
     * @internal
3782
     */
3783
    public resetForOfCache() {
3784
        const firstVirtRow = this.dataRowList.first;
33,958✔
3785
        if (firstVirtRow) {
33,958✔
3786
            if (this._cdrRequests) {
16,841✔
3787
                firstVirtRow.virtDirRow.cdr.detectChanges();
6,698✔
3788
            }
3789
            firstVirtRow.virtDirRow.assumeMaster();
16,841✔
3790
        }
3791
    }
3792

3793
    /**
3794
     * @hidden
3795
     * @internal
3796
     */
3797
    public setFilteredData(data, pinned: boolean) {
3798
        if (this.hasPinnedRecords && pinned) {
1,543✔
3799
            this._filteredPinnedData = data || [];
49✔
3800
            const filteredUnpinned = this._filteredUnpinnedData || [];
49✔
3801
            const filteredData = [... this._filteredPinnedData, ...filteredUnpinned];
49✔
3802
            this._filteredData = filteredData.length > 0 ? filteredData : this._filteredUnpinnedData;
49✔
3803
        } else if (this.hasPinnedRecords && !pinned) {
1,494✔
3804
            this._filteredUnpinnedData = data;
47✔
3805
        } else {
3806
            this._filteredData = data;
1,447✔
3807
        }
3808
    }
3809

3810
    /**
3811
     * @hidden
3812
     * @internal
3813
     */
3814
    public resetColumnCollections() {
3815
        if (this.hasColumnLayouts) {
34,099✔
3816
            this._columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
11,902✔
3817
        }
3818
        this._visibleColumns.length = 0;
34,099✔
3819
        this._pinnedVisible.length = 0;
34,099✔
3820
        this._unpinnedVisible.length = 0;
34,099✔
3821
    }
3822

3823
    /**
3824
     * @hidden
3825
     * @internal
3826
     */
3827
    public resetCachedWidths() {
3828
        this._unpinnedWidth = NaN;
45,719✔
3829
        this._pinnedWidth = NaN;
45,719✔
3830
        this._totalWidth = NaN;
45,719✔
3831
    }
3832

3833
    /**
3834
     * @hidden
3835
     * @internal
3836
     */
3837
    public resetCaches(recalcFeatureWidth = true) {
10,441✔
3838
        if (recalcFeatureWidth) {
33,958✔
3839
            this._headerFeaturesWidth = NaN;
33,949✔
3840
            this.summaryService.summaryHeight = 0;
33,949✔
3841
        }
3842
        this.resetColumnsCaches();
33,958✔
3843
        this.resetColumnCollections();
33,958✔
3844
        this.resetForOfCache();
33,958✔
3845
        this.resetCachedWidths();
33,958✔
3846
        this.hasVisibleColumns = undefined;
33,958✔
3847
        this._columnGroups = this._columns.some(col => col.columnGroup);
160,851✔
3848
    }
3849

3850
    /**
3851
     * @hidden
3852
     */
3853
    public ngAfterContentInit() {
3854
        if (this.sortHeaderIconDirectiveTemplate) {
3,413✔
3855
            this.sortHeaderIconTemplate = this.sortHeaderIconDirectiveTemplate;
5✔
3856
        }
3857

3858
        if (this.sortAscendingHeaderIconDirectiveTemplate) {
3,413✔
3859
            this.sortAscendingHeaderIconTemplate = this.sortAscendingHeaderIconDirectiveTemplate;
5✔
3860
        }
3861

3862
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
3,413✔
3863
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
5✔
3864
        }
3865

3866
        this.setupColumns();
3,413✔
3867
        this.toolbar.changes.pipe(filter(() => !this._init), takeUntil(this.destroy$)).subscribe(() => this.notifyChanges(true));
3,413✔
3868
        this.setUpPaginator();
3,413✔
3869
        this.paginationComponents.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,413✔
3870
            this.setUpPaginator();
64✔
3871
        });
3872
        if (this.actionStrip) {
3,413✔
3873
            this.actionStrip.menuOverlaySettings.outlet = this.outlet;
114✔
3874
        }
3875
    }
3876

3877
    /**
3878
     * @hidden @internal
3879
     */
3880
    public dataRebinding(event: IForOfDataChangingEventArgs) {
3881
        if (event.state.chunkSize == 0) {
6,736✔
3882
            this._shouldRecalcRowHeight = true;
3,571✔
3883
        }
3884
        this.dataChanging.emit(event);
6,736✔
3885
    }
3886

3887
    /**
3888
     * @hidden @internal
3889
     */
3890
    public dataRebound(event) {
3891
        this.selectionService.clearHeaderCBState();
6,736✔
3892
        if (this._shouldRecalcRowHeight) {
6,736✔
3893
            this._shouldRecalcRowHeight = false;
3,571✔
3894
            this.updateDefaultRowHeight();
3,571✔
3895
        }
3896
        this.dataChanged.emit(event);
6,736✔
3897
    }
3898

3899
    /** @hidden @internal */
3900
    public createFilterDropdown(column: ColumnType, options: OverlaySettings) {
3901
        options.outlet = this.outlet;
202✔
3902
        if (this.excelStyleFilteringComponent) {
202✔
3903
            this.excelStyleFilteringComponent.initialize(column, this.overlayService);
21✔
3904
            const id = this.overlayService.attach(this.excelStyleFilteringComponent.element, options);
21✔
3905
            this.excelStyleFilteringComponent.overlayComponentId = id;
21✔
3906
            return id;
21✔
3907
        }
3908
        const id = this.overlayService.attach(IgxGridExcelStyleFilteringComponent, this.viewRef, options);
181✔
3909
        return id;
181✔
3910
    }
3911

3912
    /** @hidden @internal */
3913
    public setUpPaginator() {
3914
        if (this.paginator) {
3,480✔
3915
            this.paginator.pageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
220✔
3916
                .subscribe(() => {
3917
                    this.selectionService.clear(true);
114✔
3918
                    this.crudService.endEdit(false);
114✔
3919
                    this.pipeTrigger++;
114✔
3920
                    this.navigateTo(0);
114✔
3921
                    this.notifyChanges();
114✔
3922
                });
3923
            this.paginator.perPageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
220✔
3924
                .subscribe(() => {
3925
                    this.selectionService.clear(true);
70✔
3926
                    this.page = 0;
70✔
3927
                    this.crudService.endEdit(false);
70✔
3928
                    this.notifyChanges();
70✔
3929
                });
3930
        } else {
3931
            this.markForCheck();
3,260✔
3932
        }
3933
    }
3934

3935
    /**
3936
     * @hidden
3937
     * @internal
3938
     */
3939
    public setFilteredSortedData(data, pinned: boolean) {
3940
        data = data || [];
6,491✔
3941
        if (this.pinnedRecordsCount > 0) {
6,491✔
3942
            if (pinned) {
341✔
3943
                this._filteredSortedPinnedData = data;
174✔
3944
                this.pinnedRecords = data;
174✔
3945
                this._filteredSortedData = this.isRowPinningToTop ? [... this._filteredSortedPinnedData, ... this._filteredSortedUnpinnedData] :
174✔
3946
                    [... this._filteredSortedUnpinnedData, ... this._filteredSortedPinnedData];
3947
                this.refreshSearch(true, false);
174✔
3948
            } else {
3949
                this._filteredSortedUnpinnedData = data;
167✔
3950
            }
3951
        } else {
3952
            this._filteredSortedData = data;
6,150✔
3953
            this.refreshSearch(true, false);
6,150✔
3954
        }
3955
        this.buildDataView(data);
6,491✔
3956
    }
3957

3958
    /**
3959
     * @hidden @internal
3960
     */
3961
    public resetHorizontalVirtualization() {
3962
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
49,217✔
3963
        this._horizontalForOfs = [
6,707✔
3964
            ...this._dataRowList.filter(elementFilter).map(item => item.virtDirRow),
46,018✔
3965
            ...this._summaryRowList.filter(elementFilter).map(item => item.virtDirRow)
3,199✔
3966
        ];
3967
    }
3968

3969
    /**
3970
     * @hidden @internal
3971
     */
3972
    public _setupRowObservers() {
3973
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
17,538✔
3974
        const extractForOfs = pipe(map((collection: any[]) => collection.filter(elementFilter).map(item => item.virtDirRow)));
17,538✔
3975
        const rowListObserver = extractForOfs(this._dataRowList.changes);
3,553✔
3976
        const summaryRowObserver = extractForOfs(this._summaryRowList.changes);
3,553✔
3977
        rowListObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,553✔
3978
            this.resetHorizontalVirtualization();
2,741✔
3979
        });
3980
        summaryRowObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,553✔
3981
            this.resetHorizontalVirtualization();
315✔
3982
        });
3983
        this.resetHorizontalVirtualization();
3,553✔
3984
    }
3985

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

4008
    /**
4009
     * @hidden
4010
     */
4011
    public ngAfterViewInit() {
4012
        this.initPinning();
3,553✔
4013
        this.calculateGridSizes();
3,553✔
4014
        this._init = false;
3,553✔
4015
        this.cdr.reattach();
3,553✔
4016
        this._setupRowObservers();
3,553✔
4017
        this._zoneBegoneListeners();
3,553✔
4018

4019
        const vertScrDC = this.verticalScrollContainer.displayContainer;
3,553✔
4020
        vertScrDC.addEventListener('scroll', this.preventContainerScroll.bind(this));
3,553✔
4021

4022
        this._pinnedRowList.changes
3,553✔
4023
            .pipe(takeUntil(this.destroy$))
4024
            .subscribe((change: QueryList<IgxGridRowComponent>) => {
4025
                this.onPinnedRowsChanged(change);
182✔
4026
            });
4027

4028
        this.addRowSnackbar?.clicked.subscribe(() => {
3,553✔
4029
            const rec = this.filteredSortedData[this.lastAddedRowIndex];
4✔
4030
            this.scrollTo(rec, 0);
4✔
4031
            this.addRowSnackbar.close();
4✔
4032
        });
4033

4034
        // Keep the stream open for future subscribers
4035
        this.rendered$.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,553✔
4036
            if (this.paginator) {
1,984✔
4037
                this.paginator.totalRecords = this.totalRecords ? this.totalRecords : this.paginator.totalRecords;
73✔
4038
                this.paginator.overlaySettings = { outlet: this.outlet };
73✔
4039
            }
4040
            if (this.hasColumnsToAutosize) {
1,984✔
4041
                this.autoSizeColumnsInView();
12✔
4042
            }
4043
            this._rendered = true;
1,984✔
4044
        });
4045
        Promise.resolve().then(() => this.rendered.next(true));
3,553✔
4046

4047
    }
4048

4049
    /**
4050
     * @hidden @internal
4051
     */
4052
    public notifyChanges(repaint = false) {
14,882✔
4053
        this._cdrRequests = true;
488,751✔
4054
        this._cdrRequestRepaint = repaint;
488,751✔
4055
        this.cdr.markForCheck();
488,751✔
4056
    }
4057

4058
    /**
4059
     * @hidden @internal
4060
     */
4061
    public ngDoCheck() {
4062
        if (this._init) {
17,604✔
4063
            return;
4,086✔
4064
        }
4065

4066
        if (this._cdrRequestRepaint) {
13,518✔
4067
            this.resetNotifyChanges();
3,947✔
4068
            this.calculateGridSizes();
3,947✔
4069
            this.refreshSearch(true);
3,947✔
4070
            return;
3,947✔
4071
        }
4072

4073
        if (this._cdrRequests) {
9,571✔
4074
            this.resetNotifyChanges();
4,188✔
4075
            this.cdr.detectChanges();
4,188✔
4076
        }
4077
    }
4078

4079
    /**
4080
     * @hidden
4081
     * @internal
4082
     */
4083
    public getDragGhostCustomTemplate() {
4084

4085
        return this.dragGhostCustomTemplate;
1,987✔
4086
    }
4087

4088
    /**
4089
     * @hidden @internal
4090
     */
4091
    public ngOnDestroy() {
4092
        this.tmpOutlets.forEach((tmplOutlet) => {
3,347✔
4093
            tmplOutlet.cleanCache();
24,661✔
4094
        });
4095

4096
        this.destroy$.next(true);
3,347✔
4097
        this.destroy$.complete();
3,347✔
4098
        this.transactionChange$.next();
3,347✔
4099
        this.transactionChange$.complete();
3,347✔
4100
        this._destroyed = true;
3,347✔
4101

4102
        this.textHighlightService.destroyGroup(this.id);
3,347✔
4103

4104
        if (this._advancedFilteringOverlayId) {
3,347!
4105
            this.overlayService.detach(this._advancedFilteringOverlayId);
×
4106
            delete this._advancedFilteringOverlayId;
×
4107
        }
4108

4109
        this.overlayIDs.forEach(overlayID => {
3,347✔
4110
            const overlay = this.overlayService.getOverlayById(overlayID);
23✔
4111

4112
            if (overlay && !overlay.detached) {
23✔
4113
                this.overlayService.detach(overlayID);
13✔
4114
            }
4115
        });
4116

4117

4118
        this.zone.runOutsideAngular(() => {
3,347✔
4119
            this.verticalScrollContainer?.getScroll()?.removeEventListener('scroll', this.verticalScrollHandler);
3,347✔
4120
            this.headerContainer?.getScroll()?.removeEventListener('scroll', this.horizontalScrollHandler);
3,347✔
4121
            const vertScrDC = this.verticalScrollContainer?.displayContainer;
3,347✔
4122
            vertScrDC?.removeEventListener('scroll', this.preventContainerScroll);
3,347✔
4123
        });
4124
    }
4125

4126
    /**
4127
     * Toggles the specified column's visibility.
4128
     *
4129
     * @example
4130
     * ```typescript
4131
     * this.grid1.toggleColumnVisibility({
4132
     *       column: this.grid1.columns[0],
4133
     *       newValue: true
4134
     * });
4135
     * ```
4136
     */
4137
    public toggleColumnVisibility(args: IColumnVisibilityChangedEventArgs) {
4138
        const col = args.column ? this._columns.find((c) => c === args.column) : undefined;
×
4139

4140
        if (!col) {
×
4141
            return;
×
4142
        }
4143
        col.toggleVisibility(args.newValue);
×
4144
    }
4145

4146
    /* blazorSuppress */
4147
    /**
4148
     * Gets/Sets a list of key-value pairs [row ID, expansion state].
4149
     *
4150
     * @remarks
4151
     * Includes only states that differ from the default one.
4152
     * Supports two-way binding.
4153
     * @example
4154
     * ```html
4155
     * <igx-grid #grid [data]="data" [(expansionStates)]="model.expansionStates">
4156
     * </igx-grid>
4157
     * ```
4158
     */
4159
    @Input()
4160
    public get expansionStates() {
4161
        return this._expansionStates;
223,266✔
4162
    }
4163

4164
    /* blazorSuppress */
4165
    public set expansionStates(value) {
4166
        this._expansionStates = new Map<any, boolean>(value);
676✔
4167
        this.expansionStatesChange.emit(this._expansionStates);
676✔
4168
        this.notifyChanges(true);
676✔
4169
        if (this.gridAPI.grid) {
676✔
4170
            this.cdr.detectChanges();
585✔
4171
        }
4172
    }
4173

4174
    /**
4175
     * Expands all rows.
4176
     *
4177
     * @example
4178
     * ```typescript
4179
     * this.grid.expandAll();
4180
     * ```
4181
     */
4182
    public expandAll() {
4183
        this._defaultExpandState = true;
8✔
4184
        this.expansionStates = new Map<any, boolean>();
8✔
4185
    }
4186

4187
    /**
4188
     * Collapses all rows.
4189
     *
4190
     * @example
4191
     * ```typescript
4192
     * this.grid.collapseAll();
4193
     * ```
4194
     */
4195
    public collapseAll() {
4196
        this._defaultExpandState = false;
2✔
4197
        this.expansionStates = new Map<any, boolean>();
2✔
4198
    }
4199

4200
    /**
4201
     * Expands the row by its id.
4202
     *
4203
     * @remarks
4204
     * ID is either the primaryKey value or the data record instance.
4205
     * @example
4206
     * ```typescript
4207
     * this.grid.expandRow(rowID);
4208
     * ```
4209
     * @param rowID The row id - primaryKey value or the data record instance.
4210
     */
4211
    public expandRow(rowID: any) {
4212
        this.gridAPI.set_row_expansion_state(rowID, true);
54✔
4213
    }
4214

4215
    /**
4216
     * Collapses the row by its id.
4217
     *
4218
     * @remarks
4219
     * ID is either the primaryKey value or the data record instance.
4220
     * @example
4221
     * ```typescript
4222
     * this.grid.collapseRow(rowID);
4223
     * ```
4224
     * @param rowID The row id - primaryKey value or the data record instance.
4225
     */
4226
    public collapseRow(rowID: any) {
4227
        this.gridAPI.set_row_expansion_state(rowID, false);
8✔
4228
    }
4229

4230

4231
    /**
4232
     * Toggles the row by its id.
4233
     *
4234
     * @remarks
4235
     * ID is either the primaryKey value or the data record instance.
4236
     * @example
4237
     * ```typescript
4238
     * this.grid.toggleRow(rowID);
4239
     * ```
4240
     * @param rowID The row id - primaryKey value or the data record instance.
4241
     */
4242
    public toggleRow(rowID: any) {
4243
        const rec = this.gridAPI.get_rec_by_id(rowID);
86✔
4244
        const state = this.gridAPI.get_row_expansion_state(rec);
86✔
4245
        this.gridAPI.set_row_expansion_state(rowID, !state);
86✔
4246
    }
4247

4248
    /**
4249
     * @hidden
4250
     * @internal
4251
     */
4252
    public getDefaultExpandState(_rec: any) {
4253
        return this._defaultExpandState;
9,583✔
4254
    }
4255

4256
    /**
4257
     * Gets the native element.
4258
     *
4259
     * @example
4260
     * ```typescript
4261
     * const nativeEl = this.grid.nativeElement.
4262
     * ```
4263
     */
4264
    public get nativeElement() {
4265
        return this.elementRef.nativeElement;
41,343✔
4266
    }
4267

4268
    /**
4269
     * Gets/Sets the outlet used to attach the grid's overlays to.
4270
     *
4271
     * @remark
4272
     * If set, returns the outlet defined outside the grid. Otherwise returns the grid's internal outlet directive.
4273
     */
4274
    @Input()
4275
    public get outlet() {
4276
        return this.resolveOutlet();
200,999✔
4277
    }
4278

4279
    public set outlet(val: IgxOverlayOutletDirective) {
4280
        this._userOutletDirective = val;
×
4281
    }
4282

4283

4284
    /**
4285
     * Gets the default row height.
4286
     *
4287
     * @example
4288
     * ```typescript
4289
     * const rowHeigh = this.grid.defaultRowHeight;
4290
     * ```
4291
     */
4292
    public get defaultRowHeight(): number {
4293
        return this._defaultRowHeight;
333,679✔
4294
    }
4295

4296
    /**
4297
     * @hidden @internal
4298
     */
4299
    public get defaultSummaryHeight(): number {
4300
        switch (this.gridSize) {
15,735✔
4301
            case Size.Medium:
4302
                return 30;
29✔
4303
            case Size.Small:
4304
                return 24;
123✔
4305
            default:
4306
                return 36;
15,583✔
4307
        }
4308
    }
4309

4310
    /**
4311
     * Returns the `IgxGridHeaderGroupComponent`'s minimum allowed width.
4312
     *
4313
     * @remarks
4314
     * Used internally for restricting header group component width.
4315
     * The values below depend on the header cell default right/left padding values.
4316
     */
4317
    public get defaultHeaderGroupMinWidth(): number {
4318
        switch (this.gridSize) {
472,702✔
4319
            case Size.Medium:
4320
                return 32;
37,692✔
4321
            case Size.Small:
4322
                return 24;
3,193✔
4323
            default:
4324
                return 48;
431,817✔
4325
        }
4326
    }
4327

4328
    /** @hidden @internal */
4329
    public get pinnedWidth() {
4330
        if (!isNaN(this._pinnedWidth)) {
227,285✔
4331
            return this._pinnedWidth;
205,740✔
4332
        }
4333
        this._pinnedWidth = this.getPinnedWidth();
21,545✔
4334
        return this._pinnedWidth;
21,545✔
4335
    }
4336

4337
    /** @hidden @internal */
4338
    public get unpinnedWidth() {
4339
        if (!isNaN(this._unpinnedWidth)) {
367,400✔
4340
            return this._unpinnedWidth;
344,020✔
4341
        }
4342
        this._unpinnedWidth = this.getUnpinnedWidth();
23,380✔
4343
        return this._unpinnedWidth;
23,380✔
4344
    }
4345

4346
    /**
4347
     * @hidden @internal
4348
     */
4349
    public isHorizontalScrollHidden = false;
4,049✔
4350

4351
    /**
4352
     * @hidden @internal
4353
     * Gets the header cell inner width for auto-sizing.
4354
     */
4355
    public getHeaderCellWidth(element: HTMLElement): ISizeInfo {
4356
        const range = this.document.createRange();
35✔
4357
        const headerWidth = this.platform.getNodeSizeViaRange(range,
35✔
4358
            element,
4359
            element.parentElement);
4360

4361
        const headerStyle = this.document.defaultView.getComputedStyle(element);
35✔
4362
        const headerPadding = parseFloat(headerStyle.paddingLeft) + parseFloat(headerStyle.paddingRight) +
35✔
4363
            parseFloat(headerStyle.borderRightWidth);
4364

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

4371
    /**
4372
     * @hidden @internal
4373
     * Gets the combined width of the columns that are specific to the enabled grid features. They are fixed.
4374
     */
4375
    public featureColumnsWidth(expander?: ElementRef) {
4376
        if (Number.isNaN(this._headerFeaturesWidth)) {
129,941✔
4377
            // TODO: platformUtil.isBrowser check
4378
            const rowSelectArea = this.headerSelectorContainer?.nativeElement?.getBoundingClientRect ?
28,548✔
4379
                this.headerSelectorContainer.nativeElement.getBoundingClientRect().width : 0;
4380
            const rowDragArea = this.rowDraggable && this.headerDragContainer?.nativeElement?.getBoundingClientRect ?
28,548✔
4381
                this.headerDragContainer.nativeElement.getBoundingClientRect().width : 0;
4382
            const groupableArea = this.headerGroupContainer?.nativeElement?.getBoundingClientRect ?
28,548✔
4383
                this.headerGroupContainer.nativeElement.getBoundingClientRect().width : 0;
4384
            const expanderWidth = expander?.nativeElement?.getBoundingClientRect ? expander.nativeElement.getBoundingClientRect().width : 0;
28,548✔
4385
            this._headerFeaturesWidth = rowSelectArea + rowDragArea + groupableArea + expanderWidth;
28,548✔
4386
        }
4387
        return this._headerFeaturesWidth;
129,941✔
4388
    }
4389

4390
    /**
4391
     * @hidden @internal
4392
     */
4393
    public get summariesMargin() {
4394
        return this.featureColumnsWidth();
22,273✔
4395
    }
4396

4397
    /**
4398
     * Gets an array of `IgxColumnComponent`s.
4399
     *
4400
     * @example
4401
     * ```typescript
4402
     * const colums = this.grid.columns.
4403
     * ```
4404
     */
4405
    public get columns(): IgxColumnComponent[] {
4406
        return this._columns || [];
38,392!
4407
    }
4408

4409
    /**
4410
     * Gets an array of the pinned `IgxColumnComponent`s.
4411
     *
4412
     * @example
4413
     * ```typescript
4414
     * const pinnedColumns = this.grid.pinnedColumns.
4415
     * ```
4416
     */
4417
    public get pinnedColumns(): IgxColumnComponent[] {
4418
        if (this._pinnedVisible.length) {
4,134,559✔
4419
            return this._pinnedVisible;
339,767✔
4420
        }
4421
        this._pinnedVisible = this._pinnedColumns.filter(col => !col.hidden);
3,794,792✔
4422
        return this._pinnedVisible;
3,794,792✔
4423
    }
4424

4425
    /* csSuppress */
4426
    /**
4427
     * Gets an array of the pinned `IgxRowComponent`s.
4428
     *
4429
     * @example
4430
     * ```typescript
4431
     * const pinnedRow = this.grid.pinnedRows;
4432
     * ```
4433
     */
4434
    public get pinnedRows(): IgxGridRowComponent[] {
4435
        return this._pinnedRowList.toArray().sort((a, b) => a.index - b.index);
146✔
4436
    }
4437

4438
    /**
4439
     * Gets an array of unpinned `IgxColumnComponent`s.
4440
     *
4441
     * @example
4442
     * ```typescript
4443
     * const unpinnedColumns = this.grid.unpinnedColumns.
4444
     * ```
4445
     */
4446
    public get unpinnedColumns(): IgxColumnComponent[] {
4447
        if (this._unpinnedVisible.length) {
765,393✔
4448
            return this._unpinnedVisible;
738,576✔
4449
        }
4450
        this._unpinnedVisible = this._unpinnedColumns.filter((col) => !col.hidden);
155,581✔
4451
        return this._unpinnedVisible;
26,817✔
4452
    }
4453

4454
    /**
4455
     * Gets the `width` to be set on `IgxGridHeaderGroupComponent`.
4456
     */
4457
    public getHeaderGroupWidth(column: IgxColumnComponent): string {
4458
        return this.hasColumnLayouts
×
4459
            ? ''
4460
            : `${Math.max(parseFloat(column.calcWidth), this.defaultHeaderGroupMinWidth)}px`;
4461
    }
4462

4463
    /**
4464
     * Returns the `IgxColumnComponent` by field name.
4465
     *
4466
     * @example
4467
     * ```typescript
4468
     * const myCol = this.grid1.getColumnByName("ID");
4469
     * ```
4470
     * @param name
4471
     */
4472
    public getColumnByName(name: string): IgxColumnComponent {
4473
        return this._columns.find((col) => col.field === name);
126,029✔
4474
    }
4475

4476
    public getColumnByVisibleIndex(index: number): IgxColumnComponent {
4477
        return this.visibleColumns.find((col) =>
1,957✔
4478
            !col.columnGroup && !col.columnLayout &&
11,557✔
4479
            col.visibleIndex === index
4480
        );
4481
    }
4482

4483
    /**
4484
     * Recalculates all widths of columns that have size set to `auto`.
4485
     *
4486
     * @example
4487
     * ```typescript
4488
     * this.grid1.recalculateAutoSizes();
4489
     * ```
4490
     */
4491
    public recalculateAutoSizes() {
4492
        // reset auto-size and calculate it again.
4493
        this._columns.forEach(x => x.autoSize = undefined);
12✔
4494
        this.resetCaches();
2✔
4495
        this.zone.onStable.pipe(first()).subscribe(() => {
2✔
4496
            this.cdr.detectChanges();
2✔
4497
            this.autoSizeColumnsInView();
2✔
4498
        });
4499
    }
4500

4501
    /**
4502
     * Returns an array of visible `IgxColumnComponent`s.
4503
     *
4504
     * @example
4505
     * ```typescript
4506
     * const visibleColumns = this.grid.visibleColumns.
4507
     * ```
4508
     */
4509
    public get visibleColumns(): IgxColumnComponent[] {
4510
        if (this._visibleColumns.length) {
161,776✔
4511
            return this._visibleColumns;
135,358✔
4512
        }
4513
        this._visibleColumns = this._columns.filter(c => !c.hidden);
150,963✔
4514
        return this._visibleColumns;
26,418✔
4515
    }
4516

4517
    /**
4518
     * Returns the total number of records.
4519
     *
4520
     * @remarks
4521
     * Only functions when paging is enabled.
4522
     * @example
4523
     * ```typescript
4524
     * const totalRecords = this.grid.totalRecords;
4525
     * ```
4526
     */
4527
    @Input()
4528
    public get totalRecords(): number {
4529
        return this._totalRecords >= 0 ? this._totalRecords : this.pagingState?.metadata.countRecords;
145✔
4530
    }
4531

4532
    public set totalRecords(total: number) {
4533
        if (total >= 0) {
1✔
4534
            if (this.paginator) {
1✔
4535
                this.paginator.totalRecords = total;
1✔
4536
            }
4537
            this._totalRecords = total;
1✔
4538
            this.pipeTrigger++;
1✔
4539
            this.notifyChanges();
1✔
4540
        }
4541
    }
4542

4543
    /** @hidden @internal */
4544
    public get totalWidth(): number {
4545
        if (!isNaN(this._totalWidth)) {
11,252✔
4546
            return this._totalWidth;
3,199✔
4547
        }
4548
        // Take only top level columns
4549
        const cols = this.visibleColumns.filter(col => col.level === 0 && !col.pinned);
49,966✔
4550
        let totalWidth = 0;
8,053✔
4551
        let i = 0;
8,053✔
4552
        for (i; i < cols.length; i++) {
8,053✔
4553
            totalWidth += parseInt(cols[i].calcWidth, 10) || 0;
40,037✔
4554
        }
4555
        this._totalWidth = totalWidth;
8,053✔
4556
        return totalWidth;
8,053✔
4557
    }
4558

4559
    /**
4560
     * @hidden
4561
     * @internal
4562
     */
4563
    public get showRowSelectors(): boolean {
4564
        return this.isRowSelectable && this.hasVisibleColumns && !this.hideRowSelectors;
277,045✔
4565
    }
4566

4567
    /**
4568
     * @hidden
4569
     * @internal
4570
     */
4571
    public get showAddButton() {
4572
        return this.rowEditable && this.dataView.length === 0 && this._columns.length > 0;
934✔
4573
    }
4574

4575
    /**
4576
     * @hidden
4577
     * @internal
4578
     */
4579
    public get showDragIcons(): boolean {
4580
        return this.rowDraggable && this._columns.length > this.hiddenColumnsCount;
×
4581
    }
4582

4583
    /**
4584
     * @hidden
4585
     * @internal
4586
     */
4587
    protected _getDataViewIndex(index: number): number {
4588
        let newIndex = index;
56,292✔
4589
        if ((index < 0 || index >= this.dataView.length) && this.pagingMode === 1 && this.page !== 0) {
56,292!
4590
            newIndex = index - this.perPage * this.page;
×
4591
        } else if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
56,292!
4592
            newIndex = index - this.gridAPI.grid.virtualizationState.startIndex;
×
4593
        }
4594
        return newIndex;
56,292✔
4595
    }
4596

4597
    /**
4598
     * @hidden
4599
     * @internal
4600
     */
4601
    protected getDataIndex(dataViewIndex: number): number {
4602
        let newIndex = dataViewIndex;
23✔
4603
        if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
23!
4604
            newIndex = dataViewIndex + this.gridAPI.grid.virtualizationState.startIndex;
×
4605
        }
4606
        return newIndex;
23✔
4607
    }
4608

4609
    /**
4610
     * Places a column before or after the specified target column.
4611
     *
4612
     * @example
4613
     * ```typescript
4614
     * grid.moveColumn(column, target);
4615
     * ```
4616
     */
4617
    public moveColumn(column: IgxColumnComponent, target: IgxColumnComponent, pos: DropPosition = DropPosition.AfterDropTarget) {
51✔
4618
        // M.A. May 11th, 2021 #9508 Make the event cancelable
4619
        const eventArgs: IColumnMovingEndEventArgs = { source: column, target, cancel: false };
151✔
4620

4621
        this.columnMovingEnd.emit(eventArgs);
151✔
4622

4623
        if (eventArgs.cancel) {
151✔
4624
            return;
1✔
4625
        }
4626

4627
        if (column === target || (column.level !== target.level) ||
150✔
4628
            (column.topLevelParent !== target.topLevelParent)) {
4629
            return;
22✔
4630
        }
4631

4632
        if (column.level) {
128✔
4633
            this._moveChildColumns(column.parent, column, target, pos);
16✔
4634
        }
4635

4636
        // let columnPinStateChanged;
4637
        // pinning and unpinning will work correctly even without passing index
4638
        // but is easier to calclulate the index here, and later use it in the pinning event args
4639
        if (target.pinned && !column.pinned) {
128✔
4640
            const pinnedIndex = this._pinnedColumns.indexOf(target);
8✔
4641
            const index = pos === DropPosition.AfterDropTarget ? pinnedIndex + 1 : pinnedIndex;
8✔
4642
            column.pin(index);
8✔
4643
        }
4644

4645
        if (!target.pinned && column.pinned) {
128✔
4646
            const unpinnedIndex = this._unpinnedColumns.indexOf(target);
3✔
4647
            const index = pos === DropPosition.AfterDropTarget ? unpinnedIndex + 1 : unpinnedIndex;
3✔
4648
            column.unpin(index);
3✔
4649
        }
4650

4651
        // if (target.pinned && column.pinned && !columnPinStateChanged) {
4652
        //     this._reorderColumns(column, target, pos, this._pinnedColumns);
4653
        // }
4654

4655
        // if (!target.pinned && !column.pinned && !columnPinStateChanged) {
4656
        //     this._reorderColumns(column, target, pos, this._unpinnedColumns);
4657
        // }
4658

4659
        this._moveColumns(column, target, pos);
128✔
4660
        this._columnsReordered(column);
128✔
4661
    }
4662

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

4694
    /* csSuppress */
4695
    /**
4696
     * Creates a new `IgxGridRowComponent` and adds the data record to the end of the data source.
4697
     *
4698
     * @example
4699
     * ```typescript
4700
     * this.grid1.addRow(record);
4701
     * ```
4702
     * @param data
4703
     */
4704
    public addRow(data: any): void {
4705
        // commit pending states prior to adding a row
4706
        this.crudService.endEdit(true);
171✔
4707
        this.gridAPI.addRowToData(data);
171✔
4708

4709
        this.pipeTrigger++;
171✔
4710
        this.rowAddedNotifier.next({ data: data, rowData: data, owner: this, primaryKey: data[this.primaryKey], rowKey: data[this.primaryKey] });
171✔
4711
        this.notifyChanges();
171✔
4712
    }
4713

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

4733
    /** @hidden */
4734
    public deleteRowById(rowId: any): any {
4735
        const args: IRowDataCancelableEventArgs = {
79✔
4736
            rowID: rowId,
4737
            primaryKey: rowId,
4738
            rowKey: rowId,
4739
            rowData: this.getRowData(rowId),
4740
            data: this.getRowData(rowId),
4741
            oldValue: this.getRowData(rowId),
4742
            owner: this,
4743
            isAddRow: false,
4744
            cancel: false
4745
        };
4746
        this.rowDelete.emit(args);
79✔
4747
        if (args.cancel) {
79!
4748
            return;
×
4749
        }
4750

4751
        const record = this.gridAPI.deleteRowById(rowId);
79✔
4752
        if (record !== null && record !== undefined) {
79✔
4753
            const rowDeletedEventArgs: IRowDataEventArgs = {
78✔
4754
                data: record,
4755
                rowData: record,
4756
                owner: this,
4757
                primaryKey: record[this.primaryKey],
4758
                rowKey: record[this.primaryKey]
4759
            };
4760
            this.rowDeleted.emit(rowDeletedEventArgs);
78✔
4761
        }
4762
        return record;
79✔
4763
    }
4764

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

4791
                const id = {
18✔
4792
                    rowID: rowSelector,
4793
                    columnID: col.index,
4794
                    rowIndex: index
4795
                };
4796

4797
                const cell = new IgxCell(id, index, col, rowData[col.field], value, rowData, this as any);
18✔
4798
                const formControl = this.validation.getFormControl(cell.id.rowID, cell.column.field);
18✔
4799
                formControl.setValue(value);
18✔
4800
                this.gridAPI.update_cell(cell);
18✔
4801
                this.cdr.detectChanges();
18✔
4802
            }
4803
        }
4804
    }
4805

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

4833
            // TODO: fix for #5934 and probably break for #5763
4834
            // consider adding of third optional boolean parameter in updateRow.
4835
            // If developer set this parameter to true we should call notifyChanges(true), and
4836
            // vise-versa if developer set it to false we should call notifyChanges(false).
4837
            // The parameter should default to false
4838
            this.notifyChanges();
32✔
4839
        }
4840
    }
4841

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

4862
    /**
4863
     * Sort a single `IgxColumnComponent`.
4864
     *
4865
     * @remarks
4866
     * Sort the `IgxGridComponent`'s `IgxColumnComponent` based on the provided array of sorting expressions.
4867
     * @example
4868
     * ```typescript
4869
     * this.grid.sort({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false });
4870
     * ```
4871
     */
4872
    public sort(expression: ISortingExpression | Array<ISortingExpression>): void {
4873
        const sortingState = cloneArray(this.sortingExpressions);
159✔
4874

4875
        if (expression instanceof Array) {
159✔
4876
            for (const each of expression) {
3✔
4877
                this.gridAPI.prepare_sorting_expression([sortingState], each);
6✔
4878
            }
4879
        } else {
4880
            if (this._sortingOptions.mode === 'single') {
156✔
4881
                this._columns.forEach((col) => {
4✔
4882
                    if (!(col.field === expression.fieldName)) {
12✔
4883
                        this.clearSort(col.field);
8✔
4884
                    }
4885
                });
4886
            }
4887
            this.gridAPI.prepare_sorting_expression([sortingState], expression);
156✔
4888
        }
4889

4890
        const eventArgs: ISortingEventArgs = { owner: this, sortingExpressions: sortingState, cancel: false };
159✔
4891
        this.sorting.emit(eventArgs);
159✔
4892

4893
        if (eventArgs.cancel) {
159!
4894
            return;
×
4895
        }
4896

4897
        this.crudService.endEdit(false);
159✔
4898
        if (expression instanceof Array) {
159✔
4899
            this.gridAPI.sort_multiple(expression);
3✔
4900
        } else {
4901
            this.gridAPI.sort(expression);
156✔
4902
        }
4903
        requestAnimationFrame(() => this.sortingDone.emit(expression));
159✔
4904
    }
4905

4906
    /**
4907
     * Filters a single `IgxColumnComponent`.
4908
     *
4909
     * @example
4910
     * ```typescript
4911
     * public filter(term) {
4912
     *      this.grid.filter("ProductName", term, IgxStringFilteringOperand.instance().condition("contains"));
4913
     * }
4914
     * ```
4915
     * @param name
4916
     * @param value
4917
     * @param conditionOrExpressionTree
4918
     * @param ignoreCase
4919
     */
4920
    public filter(name: string, value: any, conditionOrExpressionTree?: IFilteringOperation | IFilteringExpressionsTree,
4921
        ignoreCase?: boolean) {
4922
        this.filteringService.filter(name, value, conditionOrExpressionTree, ignoreCase);
275✔
4923
    }
4924

4925
    /**
4926
     * Filters all the `IgxColumnComponent` in the `IgxGridComponent` with the same condition.
4927
     *
4928
     * @example
4929
     * ```typescript
4930
     * grid.filterGlobal('some', IgxStringFilteringOperand.instance().condition('contains'));
4931
     * ```
4932
     * @param value
4933
     * @param condition
4934
     * @param ignoreCase
4935
     */
4936
    public filterGlobal(value: any, condition, ignoreCase?) {
4937
        this.filteringService.filterGlobal(value, condition, ignoreCase);
3✔
4938
    }
4939

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

4964
    /**
4965
     * Disable summaries for the specified column.
4966
     *
4967
     * @example
4968
     * ```typescript
4969
     * grid.disableSummaries('ProductName');
4970
     * ```
4971
     * @remarks
4972
     * Disable summaries for the listed columns.
4973
     * @example
4974
     * ```typescript
4975
     * grid.disableSummaries([{ fieldName: 'ProductName' }]);
4976
     * ```
4977
     */
4978
    public disableSummaries(...rest) {
4979
        if (rest.length === 1 && Array.isArray(rest[0])) {
7✔
4980
            this._disableMultipleSummaries(rest[0]);
5✔
4981
        } else {
4982
            this._summaries(rest[0], false);
2✔
4983
        }
4984
    }
4985

4986
    /**
4987
     * If name is provided, clears the filtering state of the corresponding `IgxColumnComponent`.
4988
     *
4989
     * @remarks
4990
     * Otherwise clears the filtering state of all `IgxColumnComponent`s.
4991
     * @example
4992
     * ```typescript
4993
     * this.grid.clearFilter();
4994
     * ```
4995
     * @param name
4996
     */
4997
    public clearFilter(name?: string) {
4998
        this.filteringService.clearFilter(name);
147✔
4999
    }
5000

5001
    /**
5002
     * If name is provided, clears the sorting state of the corresponding `IgxColumnComponent`.
5003
     *
5004
     * @remarks
5005
     * otherwise clears the sorting state of all `IgxColumnComponent`.
5006
     * @example
5007
     * ```typescript
5008
     * this.grid.clearSort();
5009
     * ```
5010
     * @param name
5011
     */
5012
    public clearSort(name?: string) {
5013
        if (!name) {
35✔
5014
            this.sortingExpressions = [];
22✔
5015
            return;
22✔
5016
        }
5017
        if (!this.gridAPI.get_column_by_name(name)) {
13!
5018
            return;
×
5019
        }
5020
        this.gridAPI.clear_sort(name);
13✔
5021
    }
5022

5023
    /**
5024
     * @hidden @internal
5025
     */
5026
    public refreshGridState(_args?) {
5027
        this.crudService.endEdit(true);
285✔
5028
        this.selectionService.clearHeaderCBState();
285✔
5029
        this.summaryService.clearSummaryCache();
285✔
5030
        this.summaryPipeTrigger++;
285✔
5031
        this.cdr.detectChanges();
285✔
5032
    }
5033

5034
    // TODO: We have return values here. Move them to event args ??
5035

5036
    /**
5037
     * Pins a column by field name.
5038
     *
5039
     * @remarks
5040
     * Returns whether the operation is successful.
5041
     * @example
5042
     * ```typescript
5043
     * this.grid.pinColumn("ID");
5044
     * ```
5045
     * @param columnName
5046
     * @param index
5047
     */
5048
    public pinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
5049
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
37✔
5050
        return col.pin(index);
37✔
5051
    }
5052

5053
    /**
5054
     * Unpins a column by field name. Returns whether the operation is successful.
5055
     *
5056
     * @example
5057
     * ```typescript
5058
     * this.grid.pinColumn("ID");
5059
     * ```
5060
     * @param columnName
5061
     * @param index
5062
     */
5063
    public unpinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
5064
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
19✔
5065
        return col.unpin(index);
19✔
5066
    }
5067

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

5088
        if (eventArgs.cancel) {
133✔
5089
            return;
1✔
5090
        }
5091
        this.crudService.endEdit(false);
132✔
5092

5093
        const insertIndex = typeof eventArgs.insertAtIndex === 'number' ? eventArgs.insertAtIndex : this._pinnedRecordIDs.length;
132✔
5094
        this._pinnedRecordIDs.splice(insertIndex, 0, rowID);
132✔
5095
        this.pipeTrigger++;
132✔
5096
        if (this.gridAPI.grid) {
132✔
5097
            this.cdr.detectChanges();
130✔
5098
            this.rowPinned.emit(eventArgs);
130✔
5099
        }
5100

5101
        return true;
132✔
5102
    }
5103

5104
    /* csSuppress */
5105
    /**
5106
     * Unpin the row by its id.
5107
     *
5108
     * @remarks
5109
     * ID is either the primaryKey value or the data record instance.
5110
     * @example
5111
     * ```typescript
5112
     * this.grid.unpinRow(rowID);
5113
     * ```
5114
     * @param rowID The row id - primaryKey value or the data record instance.
5115
     */
5116
    public unpinRow(rowID: any, row?: RowType): boolean {
5117
        const index = this._pinnedRecordIDs.indexOf(rowID);
24✔
5118
        if (index === -1) {
24!
5119
            return false;
×
5120
        }
5121

5122
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, null, row, false);
24✔
5123
        this.rowPinning.emit(eventArgs);
24✔
5124

5125
        if (eventArgs.cancel) {
24✔
5126
            return;
1✔
5127
        }
5128

5129
        this.crudService.endEdit(false);
23✔
5130
        this._pinnedRecordIDs.splice(index, 1);
23✔
5131
        this.pipeTrigger++;
23✔
5132
        if (this.gridAPI.grid) {
23✔
5133
            this.cdr.detectChanges();
23✔
5134
            this.rowPinned.emit(eventArgs);
23✔
5135
        }
5136

5137
        return true;
23✔
5138
    }
5139

5140
    /** @hidden @internal */
5141
    public get pinnedRowHeight() {
5142
        const containerHeight = this.pinContainer ? this.pinContainer.nativeElement.offsetHeight : 0;
81,969✔
5143
        return this.hasPinnedRecords ? containerHeight : 0;
81,969✔
5144
    }
5145

5146
    /** @hidden @internal */
5147
    public get totalHeight() {
5148
        return this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
43,137✔
5149
    }
5150

5151
    /**
5152
     * Recalculates grid width/height dimensions.
5153
     *
5154
     * @remarks
5155
     * Should be run when changing DOM elements dimentions manually that affect the grid's size.
5156
     * @example
5157
     * ```typescript
5158
     * this.grid.reflow();
5159
     * ```
5160
     */
5161
    public reflow() {
5162
        this.calculateGridSizes();
318✔
5163
    }
5164

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

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

5199
    /**
5200
     * Reapplies the existing search.
5201
     *
5202
     * @remarks
5203
     * Returns how many times the grid contains the last search.
5204
     * @example
5205
     * ```typescript
5206
     * this.grid.refreshSearch();
5207
     * ```
5208
     * @param updateActiveInfo
5209
     */
5210
    public refreshSearch(updateActiveInfo?: boolean, endEdit = true): number {
4,034✔
5211
        if (this._lastSearchInfo.searchText) {
9,974✔
5212
            this.rebuildMatchCache();
127✔
5213

5214
            if (updateActiveInfo) {
127✔
5215
                const activeInfo = this.textHighlightService.highlightGroupsMap.get(this.id);
126✔
5216
                this._lastSearchInfo.matchInfoCache.forEach((match, i) => {
126✔
5217
                    if (match.column === activeInfo.column &&
1,552✔
5218
                        match.row === activeInfo.row &&
5219
                        match.index === activeInfo.index &&
5220
                        compareMaps(match.metadata, activeInfo.metadata)) {
5221
                        this._lastSearchInfo.activeMatchIndex = i;
111✔
5222
                    }
5223
                });
5224
            }
5225

5226
            return this.find(this._lastSearchInfo.searchText,
127✔
5227
                0,
5228
                this._lastSearchInfo.caseSensitive,
5229
                this._lastSearchInfo.exactMatch,
5230
                false,
5231
                endEdit);
5232
        } else {
5233
            return 0;
9,847✔
5234
        }
5235
    }
5236

5237
    /**
5238
     * Removes all the highlights in the cell.
5239
     *
5240
     * @example
5241
     * ```typescript
5242
     * this.grid.clearSearch();
5243
     * ```
5244
     */
5245
    public clearSearch() {
5246
        this._lastSearchInfo = {
1✔
5247
            searchText: '',
5248
            caseSensitive: false,
5249
            exactMatch: false,
5250
            activeMatchIndex: 0,
5251
            matchInfoCache: [],
5252
            matchCount: 0,
5253
            content: ''
5254
        };
5255

5256
        this.rowList.forEach((row) => {
1✔
5257
            if (row.cells) {
10✔
5258
                row.cells.forEach((c: IgxGridCellComponent) => {
10✔
5259
                    c.clearHighlight();
40✔
5260
                });
5261
            }
5262
        });
5263
    }
5264

5265
    /** @hidden @internal */
5266
    public get hasEditableColumns(): boolean {
5267
        return this._columns.some((col) => col.editable);
6✔
5268
    }
5269

5270
    /** @hidden @internal */
5271
    public get hasSummarizedColumns(): boolean {
5272
        const summarizedColumns = this._columns.filter(col => col.hasSummary && !col.hidden);
2,152,184✔
5273
        return summarizedColumns.length > 0;
338,803✔
5274
    }
5275

5276
    /**
5277
     * @hidden @internal
5278
     */
5279
    public get rootSummariesEnabled(): boolean {
5280
        return this.summaryCalculationMode !== GridSummaryCalculationMode.childLevelsOnly;
250,711✔
5281
    }
5282

5283
    /**
5284
     * @hidden @internal
5285
     */
5286
    public get hasVisibleColumns(): boolean {
5287
        if (this._hasVisibleColumns === undefined) {
77,524✔
5288
            return this._columns ? this._columns.some(c => !c.hidden) : false;
78,681!
5289
        }
5290
        return this._hasVisibleColumns;
×
5291
    }
5292

5293
    public set hasVisibleColumns(value) {
5294
        this._hasVisibleColumns = value;
33,958✔
5295
    }
5296

5297
    /** @hidden @internal */
5298
    public get hasMovableColumns(): boolean {
5299
        return this.moving;
×
5300
    }
5301

5302
    /** @hidden @internal */
5303
    public get hasColumnGroups(): boolean {
5304
        return this._columnGroups;
848✔
5305
    }
5306

5307
    /** @hidden @internal */
5308
    public get hasColumnLayouts() {
5309
        return !!this._columns.some(col => col.columnLayout);
21,523,816✔
5310
    }
5311

5312

5313
    /**
5314
     * @hidden @internal
5315
     */
5316
    public get multiRowLayoutRowSize() {
5317
        return this._multiRowLayoutRowSize;
20,345✔
5318
    }
5319

5320
    /**
5321
     * @hidden
5322
     */
5323
    protected get rowBasedHeight() {
5324
        return this.dataLength * this.rowHeight;
×
5325
    }
5326

5327
    /**
5328
     * @hidden
5329
     */
5330
    protected get isPercentWidth() {
5331
        return this.width && this.width.indexOf('%') !== -1;
41,784✔
5332
    }
5333

5334
    protected get shouldResize(): boolean {
5335
        return this._gridSize !== this.gridSize;
1,725✔
5336
    }
5337

5338
    /**
5339
     * @hidden @internal
5340
     */
5341
    public get isPercentHeight() {
5342
        return this._height && this._height.indexOf('%') !== -1;
15,788✔
5343
    }
5344

5345
    /**
5346
     * @hidden
5347
     */
5348
    protected get defaultTargetBodyHeight(): number {
5349
        const allItems = this.dataLength;
319✔
5350
        return this.renderedActualRowHeight * Math.min(this._defaultTargetRecordNumber,
319✔
5351
            this.paginator ? Math.min(allItems, this.paginator.perPage) : allItems);
319✔
5352
    }
5353

5354
    /**
5355
     * @hidden @internal
5356
     * The rowHeight input is bound to min-height css prop of rows that adds a 1px border in all cases
5357
     */
5358
    public get renderedRowHeight(): number {
5359
        return this.rowHeight + 1;
74,454✔
5360
    }
5361

5362
    /**
5363
     * @hidden @internal
5364
     */
5365
    public get outerWidth() {
5366
        return this.hasVerticalScroll() ? this.calcWidth + this.scrollSize : this.calcWidth;
3,871✔
5367
    }
5368

5369
    /**
5370
     * @hidden @internal
5371
     * Gets the size of the grid
5372
     */
5373
    public get gridSize(): Size {
5374
        return this.gridComputedStyles?.getPropertyValue('--component-size') || Size.Large;
801,708✔
5375
    }
5376

5377
    /**
5378
     * @hidden @internal
5379
     * Gets the visible content height that includes header + tbody + footer.
5380
     */
5381
    public getVisibleContentHeight() {
5382
        let height = this.theadRow.nativeElement.clientHeight + this.tbody.nativeElement.clientHeight;
50✔
5383
        if (this.hasSummarizedColumns) {
50✔
5384
            height += this.tfoot.nativeElement.clientHeight;
5✔
5385
        }
5386
        return height;
50✔
5387
    }
5388

5389
    /**
5390
     * @hidden @internal
5391
     */
5392
    public getPossibleColumnWidth(baseWidth: number = null) {
72,761✔
5393
        let computedWidth;
5394
        if (baseWidth !== null) {
72,761!
5395
            computedWidth = baseWidth;
×
5396
        } else {
5397
            computedWidth = this.calcWidth ||
72,761✔
5398
                parseInt(this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width'), 10);
5399
        }
5400

5401
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
685,029✔
5402

5403

5404
        // Column layouts related
5405
        let visibleCols = [];
72,761✔
5406
        const columnBlocks = this.visibleColumns.filter(c => c.columnGroup);
685,029✔
5407
        const colsPerBlock = columnBlocks.map(block => block.getInitialChildColumnSizes(block.children));
123,581✔
5408
        const combinedBlocksSize = colsPerBlock.reduce((acc, item) => acc + item.length, 0);
123,581✔
5409
        colsPerBlock.forEach(blockCols => visibleCols = visibleCols.concat(blockCols));
123,581✔
5410
        //
5411

5412
        const columnsWithSetWidths = this.hasColumnLayouts ?
72,761✔
5413
            visibleCols.filter(c => c.widthSetByUser) :
168,616✔
5414
            visibleChildColumns.filter(c => c.widthSetByUser && c.width !== 'fit-content');
292,675✔
5415

5416
        const columnsToSize = this.hasColumnLayouts ?
72,761✔
5417
            combinedBlocksSize - columnsWithSetWidths.length :
5418
            visibleChildColumns.length - columnsWithSetWidths.length;
5419
        const sumExistingWidths = columnsWithSetWidths
72,761✔
5420
            .reduce((prev, curr) => {
5421
                const colWidth = curr.width;
38,385✔
5422
                let widthValue = parseInt(colWidth, 10);
38,385✔
5423
                if (isNaN(widthValue)) {
38,385!
5424
                    widthValue = MINIMUM_COLUMN_WIDTH;
×
5425
                }
5426
                const currWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1 ?
38,385✔
5427
                    widthValue / 100 * computedWidth :
5428
                    widthValue;
5429
                return prev + currWidth;
38,385✔
5430
            }, 0);
5431

5432
        // When all columns are hidden, return 0px width
5433
        if (!sumExistingWidths && !columnsToSize) {
72,761✔
5434
            return '0px';
1,458✔
5435
        }
5436
        computedWidth -= this.featureColumnsWidth();
71,303✔
5437

5438
        const columnWidth = Math.floor(!Number.isFinite(sumExistingWidths) ?
71,303!
5439
            Math.max(computedWidth / columnsToSize, this.minColumnWidth) :
5440
            Math.max((computedWidth - sumExistingWidths) / columnsToSize, this.minColumnWidth));
5441

5442
        return columnWidth + 'px';
71,303✔
5443
    }
5444

5445
    /**
5446
     * @hidden @internal
5447
     */
5448
    public hasVerticalScroll() {
5449
        if (this._init) {
187,242✔
5450
            return false;
82,597✔
5451
        }
5452
        const isScrollable = this.verticalScrollContainer ? this.verticalScrollContainer.isScrollable() : false;
104,645✔
5453
        return !!(this.calcWidth && this.dataView && this.dataView.length > 0 && isScrollable);
104,645✔
5454
    }
5455

5456
    /**
5457
     * Gets calculated width of the pinned area.
5458
     *
5459
     * @example
5460
     * ```typescript
5461
     * const pinnedWidth = this.grid.getPinnedWidth();
5462
     * ```
5463
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
5464
     */
5465
    public getPinnedWidth(takeHidden = false) {
20,412✔
5466
        const fc = takeHidden ? this._pinnedColumns : this.pinnedColumns;
44,925!
5467
        let sum = 0;
44,925✔
5468
        for (const col of fc) {
44,925✔
5469
            if (col.level === 0) {
6,590✔
5470
                sum += parseInt(col.calcWidth, 10);
3,870✔
5471
            }
5472
        }
5473
        if (this.isPinningToStart) {
44,925✔
5474
            sum += this.featureColumnsWidth();
44,801✔
5475
        }
5476

5477
        return sum;
44,925✔
5478
    }
5479

5480
    /**
5481
     * @hidden @internal
5482
     */
5483
    public isColumnGrouped(_fieldName: string): boolean {
5484
        return false;
×
5485
    }
5486

5487
    /**
5488
     * @hidden @internal
5489
     * TODO: REMOVE
5490
     */
5491
    public onHeaderSelectorClick(event) {
5492
        if (!this.isMultiRowSelectionEnabled) {
7!
5493
            return;
×
5494
        }
5495
        if (this.selectionService.areAllRowSelected()) {
7✔
5496
            this.selectionService.clearRowSelection(event);
1✔
5497
        } else {
5498
            this.selectionService.selectAllRows(event);
6✔
5499
        }
5500
    }
5501

5502
    /**
5503
     * @hidden @internal
5504
     */
5505
    public get headSelectorBaseAriaLabel() {
5506
        if (this._filteringExpressionsTree.filteringOperands.length > 0) {
3,167✔
5507
            return this.selectionService.areAllRowSelected() ? 'Deselect all filtered' : 'Select all filtered';
112✔
5508
        }
5509

5510
        return this.selectionService.areAllRowSelected() ? 'Deselect all' : 'Select all';
3,055✔
5511
    }
5512

5513
    /**
5514
     * @hidden
5515
     * @internal
5516
     */
5517
    public get totalRowsCountAfterFilter() {
5518
        if (this.data) {
3,332✔
5519
            return this.selectionService.allData.length;
3,332✔
5520
        }
5521

5522
        return 0;
×
5523
    }
5524

5525
    /** @hidden @internal */
5526
    public get pinnedDataView(): any[] {
5527
        return this.pinnedRecords ? this.pinnedRecords : [];
261,609✔
5528
    }
5529

5530
    /** @hidden @internal */
5531
    public get unpinnedDataView(): any[] {
5532
        return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer?.igxForOf || [];
15,602✔
5533
    }
5534

5535
    /**
5536
     * Returns the currently transformed paged/filtered/sorted/grouped/pinned/unpinned row data, displayed in the grid.
5537
     *
5538
     * @example
5539
     * ```typescript
5540
     *      const dataView = this.grid.dataView;
5541
     * ```
5542
     */
5543
    public get dataView() {
5544
        return this._dataView;
400,732✔
5545
    }
5546

5547
    /**
5548
     * Gets/Sets whether clicking over a row should select/deselect it
5549
     *
5550
     * @remarks
5551
     * By default it is set to true
5552
     * @param enabled: boolean
5553
     */
5554
    @WatchChanges()
5555
    @Input({ transform: booleanAttribute })
5556
    public get selectRowOnClick() {
5557
        return this._selectRowOnClick;
110✔
5558
    }
5559

5560
    public set selectRowOnClick(enabled: boolean) {
5561
        this._selectRowOnClick = enabled;
13✔
5562
    }
5563

5564
    /**
5565
     * Select specified rows by ID.
5566
     *
5567
     * @example
5568
     * ```typescript
5569
     * this.grid.selectRows([1,2,5], true);
5570
     * ```
5571
     * @param rowIDs
5572
     * @param clearCurrentSelection if true clears the current selection
5573
     */
5574
    public selectRows(rowIDs: any[], clearCurrentSelection?: boolean) {
5575
        this.selectionService.selectRowsWithNoEvent(rowIDs, clearCurrentSelection);
264✔
5576
        this.notifyChanges();
264✔
5577
    }
5578

5579
    /**
5580
     * Deselect specified rows by ID.
5581
     *
5582
     * @example
5583
     * ```typescript
5584
     * this.grid.deselectRows([1,2,5]);
5585
     * ```
5586
     * @param rowIDs
5587
     */
5588
    public deselectRows(rowIDs: any[]) {
5589
        this.selectionService.deselectRowsWithNoEvent(rowIDs);
19✔
5590
        this.notifyChanges();
19✔
5591
    }
5592

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

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

5633
    /**
5634
     * Deselect selected cells.
5635
     * @example
5636
     * ```typescript
5637
     * this.grid.clearCellSelection();
5638
     * ```
5639
     */
5640
    public clearCellSelection(): void {
5641
        this.selectionService.clear(true);
8✔
5642
        this.notifyChanges();
8✔
5643
    }
5644

5645
    /**
5646
     * @hidden @internal
5647
     */
5648
    public dragScroll(delta: { left: number; top: number }): void {
5649
        const horizontal = this.headerContainer.getScroll();
1✔
5650
        const vertical = this.verticalScrollContainer.getScroll();
1✔
5651
        const { left, top } = delta;
1✔
5652

5653
        horizontal.scrollLeft += left * this.DRAG_SCROLL_DELTA;
1✔
5654
        vertical.scrollTop += top * this.DRAG_SCROLL_DELTA;
1✔
5655
    }
5656

5657
    /**
5658
     * @hidden @internal
5659
     */
5660
    public isDefined(arg: any): boolean {
5661
        return arg !== undefined && arg !== null;
66,974✔
5662
    }
5663

5664
    /**
5665
     * Select range(s) of cells between certain rows and columns of the grid.
5666
     */
5667
    public selectRange(arg: GridSelectionRange | GridSelectionRange[] | null | undefined): void {
5668
        if (!this.isDefined(arg)) {
168✔
5669
            this.clearCellSelection();
6✔
5670
            return;
6✔
5671
        }
5672
        if (arg instanceof Array) {
162✔
5673
            arg.forEach(range => this.setSelection(range));
6✔
5674
        } else {
5675
            this.setSelection(arg);
159✔
5676
        }
5677
        this.notifyChanges();
159✔
5678
    }
5679

5680
    /**
5681
     * @hidden @internal
5682
     */
5683
    public columnToVisibleIndex(field: string | number): number {
5684
        const visibleColumns = this.visibleColumns;
337✔
5685
        if (typeof field === 'number') {
337✔
5686
            return field;
204✔
5687
        }
5688
        return visibleColumns.find(column => column.field === field).visibleIndex;
322✔
5689
    }
5690

5691
    /**
5692
     * @hidden @internal
5693
     */
5694
    public setSelection(range: GridSelectionRange): void {
5695
        const startNode = { row: range.rowStart, column: this.columnToVisibleIndex(range.columnStart) };
167✔
5696
        const endNode = { row: range.rowEnd, column: this.columnToVisibleIndex(range.columnEnd) };
165✔
5697

5698
        this.selectionService.pointerState.node = startNode;
164✔
5699
        this.selectionService.selectRange(endNode, this.selectionService.pointerState);
164✔
5700
        this.selectionService.addRangeMeta(endNode, this.selectionService.pointerState);
164✔
5701
        this.selectionService.initPointerState();
164✔
5702
    }
5703

5704
    /**
5705
     * Get the currently selected ranges in the grid.
5706
     */
5707
    public getSelectedRanges(): GridSelectionRange[] {
5708
        return this.selectionService.ranges;
348✔
5709
    }
5710

5711
    /**
5712
     *
5713
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
5714
     *
5715
     * @remarks
5716
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5717
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5718
     */
5719
    public getSelectedData(formatters = false, headers = false) {
4✔
5720
        const source = this.filteredSortedData;
167✔
5721
        return this.extractDataFromSelection(source, formatters, headers);
167✔
5722
    }
5723

5724
    /**
5725
     * Get current selected columns.
5726
     *
5727
     * @example
5728
     * Returns an array with selected columns
5729
     * ```typescript
5730
     * const selectedColumns = this.grid.selectedColumns();
5731
     * ```
5732
     */
5733
    public selectedColumns(): ColumnType[] {
5734
        const fields = this.selectionService.getSelectedColumns();
67✔
5735
        return fields.map(field => this.getColumnByName(field)).filter(field => field);
67✔
5736
    }
5737

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

5763
        this.selectionService.selectColumnsWithNoEvent(fieldToSelect, clearCurrentSelection);
12✔
5764
        this.notifyChanges();
12✔
5765
    }
5766

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

5794
    /**
5795
     * Deselects all columns
5796
     *
5797
     * @example
5798
     * ```typescript
5799
     * this.grid.deselectAllColumns();
5800
     * ```
5801
     */
5802
    public deselectAllColumns() {
5803
        this.selectionService.clearAllSelectedColumns();
3✔
5804
        this.notifyChanges();
3✔
5805
    }
5806

5807
    /**
5808
     * Selects all columns
5809
     *
5810
     * @example
5811
     * ```typescript
5812
     * this.grid.deselectAllColumns();
5813
     * ```
5814
     */
5815
    public selectAllColumns() {
5816
        this.selectColumns(this._columns.filter(c => !c.columnGroup));
15✔
5817
    }
5818

5819
    /**
5820
     *
5821
     * Returns an array of the current columns selection in the form of `[{ column.field: cell.value }, ...]`.
5822
     *
5823
     * @remarks
5824
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5825
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5826
     */
5827
    public getSelectedColumnsData(formatters = false, headers = false) {
16✔
5828
        const source = this.filteredSortedData ? this.filteredSortedData : this.data;
20!
5829
        return this.extractDataFromColumnsSelection(source, formatters, headers);
20✔
5830
    }
5831

5832

5833
    /** @hidden @internal **/
5834
    public combineSelectedCellAndColumnData(columnData: any[], formatters = false, headers = false) {
×
5835
        const source = this.filteredSortedData;
×
5836
        return this.extractDataFromSelection(source, formatters, headers, columnData);
×
5837
    }
5838

5839
    /**
5840
     * @hidden @internal
5841
     */
5842
    public preventContainerScroll = (evt) => {
4,049✔
5843
        if (evt.target.scrollTop !== 0) {
3!
5844
            this.verticalScrollContainer.addScroll(evt.target.scrollTop);
×
5845
            evt.target.scrollTop = 0;
×
5846
        }
5847
        if (evt.target.scrollLeft !== 0) {
3!
5848
            this.headerContainer.scrollPosition += evt.target.scrollLeft;
×
5849
            evt.target.scrollLeft = 0;
×
5850
        }
5851
    };
5852

5853
    /**
5854
     * @hidden
5855
     * @internal
5856
     */
5857
    public copyHandler(event) {
5858
        const eventPathElements = event.composedPath().map(el => el.tagName?.toLowerCase());
98✔
5859
        if (eventPathElements.includes('igx-grid-filtering-row') ||
13✔
5860
            eventPathElements.includes('igx-grid-filtering-cell')) {
5861
            return;
1✔
5862
        }
5863

5864
        const selectedColumns = this.gridAPI.grid.selectedColumns();
12✔
5865
        const columnData = this.getSelectedColumnsData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
5866
        let selectedData;
5867
        if (event.type === 'copy') {
12✔
5868
            selectedData = this.getSelectedData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
5869
        }
5870

5871
        let data = [];
12✔
5872
        let result;
5873

5874
        if (event.code === 'KeyC' && (event.ctrlKey || event.metaKey) && event.currentTarget.className === 'igx-grid-thead__wrapper') {
12!
5875
            if (selectedData.length) {
×
5876
                if (columnData.length === 0) {
×
5877
                    result = this.prepareCopyData(event, selectedData);
×
5878
                } else {
5879
                    data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
×
5880
                        this.clipboardOptions.copyHeaders);
5881
                    result = this.prepareCopyData(event, data[0], data[1]);
×
5882
                }
5883
            } else {
5884
                data = columnData;
×
5885
                result = this.prepareCopyData(event, data);
×
5886
            }
5887

5888
            navigator.clipboard.writeText(result).then().catch(e => console.error(e));
×
5889
        } else if (!this.clipboardOptions.enabled || this.crudService.cellInEditMode || event.type === 'keydown') {
12✔
5890
            return;
2✔
5891
        } else {
5892
            if (selectedColumns.length) {
10!
5893
                data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
×
5894
                    this.clipboardOptions.copyHeaders);
5895
                result = this.prepareCopyData(event, data[0], data[1]);
×
5896
            } else {
5897
                data = selectedData;
10✔
5898
                result = this.prepareCopyData(event, data);
10✔
5899
            }
5900
            event.clipboardData.setData('text/plain', result);
10✔
5901
        }
5902
    }
5903

5904
    /**
5905
     * @hidden @internal
5906
     */
5907
    public prepareCopyData(event, data, keys?) {
5908
        const ev = { data, cancel: false } as IGridClipboardEvent;
10✔
5909
        this.gridCopy.emit(ev);
10✔
5910

5911
        if (ev.cancel) {
10✔
5912
            return;
1✔
5913
        }
5914

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

5918
        if (!this.clipboardOptions.copyHeaders) {
9✔
5919
            result = result.substring(result.indexOf('\n') + 1);
2✔
5920
        }
5921

5922
        if (data && data.length > 0 && Object.values(data[0]).length === 1) {
9✔
5923
            result = result.slice(0, -2);
4✔
5924
        }
5925

5926
        event.preventDefault();
9✔
5927

5928
        /* Necessary for the hiearachical case but will probably have to
5929
           change how getSelectedData is propagated in the hiearachical grid
5930
        */
5931
        event.stopPropagation();
9✔
5932

5933
        return result;
9✔
5934
    }
5935

5936
    /**
5937
     * @hidden @internal
5938
     */
5939
    public showSnackbarFor(index: number) {
5940
        this.addRowSnackbar.actionText = index === -1 ? '' : this.resourceStrings.igx_grid_snackbar_addrow_actiontext;
28✔
5941
        this.lastAddedRowIndex = index;
28✔
5942
        this.addRowSnackbar.open();
28✔
5943
    }
5944

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

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

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

6064
    /**
6065
     * @hidden
6066
     * @internal
6067
     */
6068
    public endRowEditTabStop(commit = true, event?: Event) {
×
6069
        const canceled = this.crudService.endEdit(commit, event);
15✔
6070

6071
        if (canceled) {
15✔
6072
            return true;
3✔
6073
        }
6074

6075
        this.navigation.restoreActiveNodeFocus();
12✔
6076
    }
6077

6078
    /**
6079
     * @hidden @internal
6080
     */
6081
    public trackColumnChanges(index, col) {
6082
        return col.field + col._calcWidth;
1,700,536✔
6083
    }
6084

6085
    /**
6086
     * @hidden
6087
     */
6088
    public isExpandedGroup(_group: IGroupByRecord): boolean {
6089
        return undefined;
×
6090
    }
6091

6092
    /**
6093
     * @hidden @internal
6094
     * TODO: MOVE to CRUD
6095
     */
6096
    public openRowOverlay(id) {
6097
        this.configureRowEditingOverlay(id, this.rowList.length <= MIN_ROW_EDITING_COUNT_THRESHOLD);
207✔
6098

6099
        this.rowEditingOverlay.open(this.rowEditSettings);
207✔
6100
        this.rowEditingOverlay.element.addEventListener('wheel', this.rowEditingWheelHandler.bind(this));
207✔
6101
    }
6102

6103
    /**
6104
     * @hidden @internal
6105
     */
6106
    public closeRowEditingOverlay() {
6107
        this.rowEditingOverlay.element.removeEventListener('wheel', this.rowEditingWheelHandler);
129✔
6108
        this.rowEditPositioningStrategy.isTopInitialPosition = null;
129✔
6109
        this.rowEditingOverlay.close();
129✔
6110
        this.rowEditingOverlay.element.parentElement.style.display = '';
129✔
6111
    }
6112

6113
    /**
6114
     * @hidden @internal
6115
     */
6116
    public toggleRowEditingOverlay(show) {
6117
        const rowStyle = this.rowEditingOverlay.element.style;
279✔
6118
        if (show) {
279!
6119
            rowStyle.display = 'block';
279✔
6120
        } else {
6121
            rowStyle.display = 'none';
×
6122
        }
6123
    }
6124

6125
    /**
6126
     * @hidden @internal
6127
     */
6128
    public repositionRowEditingOverlay(row: RowType) {
6129
        if (row && !this.rowEditingOverlay.collapsed) {
863✔
6130
            const rowStyle = this.rowEditingOverlay.element.parentElement.style;
72✔
6131
            if (row) {
72!
6132
                rowStyle.display = '';
72✔
6133
                this.configureRowEditingOverlay(row.key);
72✔
6134
                this.rowEditingOverlay.reposition();
72✔
6135
            } else {
6136
                rowStyle.display = 'none';
×
6137
            }
6138
        }
6139
    }
6140

6141
    /**
6142
     * @hidden @internal
6143
     */
6144
    public cachedViewLoaded(args: ICachedViewLoadedEventArgs) {
6145
        if (this.hasHorizontalScroll()) {
627✔
6146
            const tmplId = args.context.templateID.type;
442✔
6147
            const index = args.context.index;
442✔
6148
            args.view.detectChanges();
442✔
6149
            this.zone.onStable.pipe(first()).subscribe(() => {
442✔
6150
                const row = tmplId === 'dataRow' ? this.gridAPI.get_row_by_index(index) : null;
442✔
6151
                const summaryRow = tmplId === 'summaryRow' ? this.summariesRowList.find((sr) => sr.dataRowIndex === index) : null;
442✔
6152
                if (row && row instanceof IgxRowDirective) {
442✔
6153
                    this._restoreVirtState(row);
265✔
6154
                } else if (summaryRow) {
177✔
6155
                    this._restoreVirtState(summaryRow);
43✔
6156
                }
6157
            });
6158
        }
6159
    }
6160

6161
    /**
6162
     * Opens the advanced filtering dialog.
6163
     */
6164
    public openAdvancedFilteringDialog(overlaySettings?: OverlaySettings) {
6165
        const settings = overlaySettings ? overlaySettings : this._advancedFilteringOverlaySettings;
93!
6166
        if (!this._advancedFilteringOverlayId) {
93✔
6167
            this._advancedFilteringOverlaySettings.target =
90✔
6168
                (this as any).rootGrid ? (this as any).rootGrid.nativeElement : this.nativeElement;
90!
6169
            this._advancedFilteringOverlaySettings.outlet = this.outlet;
90✔
6170

6171
            this._advancedFilteringOverlayId = this.overlayService.attach(
90✔
6172
                IgxAdvancedFilteringDialogComponent,
6173
                this.viewRef,
6174
                settings);
6175
            this.overlayService.show(this._advancedFilteringOverlayId);
90✔
6176
        }
6177
    }
6178

6179
    /**
6180
     * Closes the advanced filtering dialog.
6181
     *
6182
     * @param applyChanges indicates whether the changes should be applied
6183
     */
6184
    public closeAdvancedFilteringDialog(applyChanges: boolean) {
6185
        if (this._advancedFilteringOverlayId) {
4✔
6186
            const advancedFilteringOverlay = this.overlayService.getOverlayById(this._advancedFilteringOverlayId);
4✔
6187
            const advancedFilteringDialog = advancedFilteringOverlay.componentRef.instance as IgxAdvancedFilteringDialogComponent;
4✔
6188

6189
            if (applyChanges) {
4✔
6190
                advancedFilteringDialog.applyChanges();
2✔
6191
            }
6192
            advancedFilteringDialog.closeDialog();
4✔
6193
        }
6194
    }
6195

6196
    /**
6197
     * @hidden @internal
6198
     */
6199
    public getEmptyRecordObjectFor(inRow: RowType) {
6200
        const row = { ...inRow?.data };
49✔
6201
        Object.keys(row).forEach(key => row[key] = undefined);
213✔
6202
        const id = this.generateRowID();
49✔
6203
        row[this.primaryKey] = id;
49✔
6204
        return { rowID: id, data: row, recordRef: row };
49✔
6205
    }
6206

6207
    /**
6208
     * @hidden @internal
6209
     */
6210
    public hasHorizontalScroll() {
6211
        return this.totalWidth - this.unpinnedWidth > 0 && this.width !== null;
11,252✔
6212
    }
6213

6214
    /**
6215
     * @hidden @internal
6216
     */
6217
    public isSummaryRow(rowData): boolean {
6218
        return rowData && rowData.summaries && (rowData.summaries instanceof Map);
436,259✔
6219
    }
6220

6221
    /**
6222
     * @hidden @internal
6223
     */
6224
    public triggerPipes() {
6225
        this.pipeTrigger++;
125✔
6226
        this.cdr.detectChanges();
125✔
6227
    }
6228

6229
    /**
6230
     * @hidden
6231
     */
6232
    public rowEditingWheelHandler(event: WheelEvent) {
6233
        if (event.deltaY > 0) {
×
6234
            this.verticalScrollContainer.scrollNext();
×
6235
        } else {
6236
            this.verticalScrollContainer.scrollPrev();
×
6237
        }
6238
    }
6239

6240
    /**
6241
     * @hidden
6242
     */
6243
    public getUnpinnedIndexById(id) {
6244
        return this.unpinnedRecords.findIndex(x => x[this.primaryKey] === id);
102✔
6245
    }
6246

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

6265
        const success = this.crudService.endEdit(commit, event);
6✔
6266

6267
        if (focusWithin) {
6✔
6268
            // restore focus for navigation
6269
            this.navigation.restoreActiveNodeFocus();
2✔
6270
        } else if (this.navigation.activeNode) {
4✔
6271
            // grid already lost focus, clear active node
6272
            this.clearActiveNode();
4✔
6273
        }
6274

6275
        return success;
6✔
6276
    }
6277

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

6311
        this._addRowForIndex(index, asChild);
4✔
6312
    }
6313

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

6340
    /* csSuppress */
6341
    /**
6342
     * Enters add mode by spawning the UI at the specified index.
6343
     *
6344
     * @remarks
6345
     * Accepted values for index are integers from 0 to this.grid.dataView.length
6346
     * @example
6347
     * ```typescript
6348
     * this.grid.beginAddRowByIndex(0);
6349
     * ```
6350
     * @param index - The index to spawn the UI at. Accepts integers from 0 to this.grid.dataView.length
6351
     */
6352
    public beginAddRowByIndex(index: number): void {
6353
        if (index === 0) {
1✔
6354
            return this.beginAddRowById(null);
1✔
6355
        }
6356
        return this._addRowForIndex(index - 1);
×
6357
    }
6358

6359
    /**
6360
     * @hidden
6361
     */
6362
    public preventHeaderScroll(args) {
6363
        if (args.target.scrollLeft !== 0) {
×
6364
            (this.navigation as any).forOfDir().getScroll().scrollLeft = args.target.scrollLeft;
×
6365
            args.target.scrollLeft = 0;
×
6366
        }
6367
    }
6368

6369
    protected beginAddRowForIndex(index: number, asChild = false) {
3✔
6370
        // TODO is row from rowList suitable for enterAddRowMode
6371
        const row = index == null ?
4✔
6372
            null : this.rowList.find(r => r.index === index);
2✔
6373
        if (row !== undefined) {
4!
6374
            this.crudService.enterAddRowMode(row, asChild);
4✔
6375
        } else {
6376
            console.warn('No row with the specified PK or index was found.');
×
6377
        }
6378
    }
6379

6380
    protected switchTransactionService(val: boolean) {
6381
        if (val) {
244✔
6382
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);
243✔
6383
        } else {
6384
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
1✔
6385
        }
6386

6387
        if (this.dataCloneStrategy) {
244✔
6388
            this._transactions.cloneStrategy = this.dataCloneStrategy;
244✔
6389
        }
6390
    }
6391

6392
    protected subscribeToTransactions(): void {
6393
        this.transactionChange$.next();
3,784✔
6394
        this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$)))
3,784✔
6395
            .subscribe(this.transactionStatusUpdate.bind(this));
6396
    }
6397

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

6428
            });
6429
        }
6430

6431
        this.selectionService.clearHeaderCBState();
324✔
6432
        this.summaryService.clearSummaryCache();
324✔
6433
        this.pipeTrigger++;
324✔
6434
        this.notifyChanges();
324✔
6435
    }
6436

6437
    protected writeToData(rowIndex: number, value: any) {
6438
        mergeObjects(this.gridAPI.get_all_data()[rowIndex], value);
×
6439
    }
6440

6441
    protected _restoreVirtState(row) {
6442
        // check virtualization state of data record added from cache
6443
        // in case state is no longer valid - update it.
6444
        const rowForOf = row.virtDirRow;
308✔
6445
        const gridScrLeft = rowForOf.getScroll().scrollLeft;
308✔
6446
        rowForOf.onHScroll(gridScrLeft);
308✔
6447
        rowForOf.cdr.detectChanges();
308✔
6448
    }
6449

6450
    protected changeRowEditingOverlayStateOnScroll(row: RowType) {
6451
        if (!this.rowEditable || !this.rowEditingOverlay || this.rowEditingOverlay.collapsed) {
14✔
6452
            return;
12✔
6453
        }
6454
        if (!row) {
2!
6455
            this.toggleRowEditingOverlay(false);
×
6456
        } else {
6457
            this.repositionRowEditingOverlay(row);
2✔
6458
        }
6459
    }
6460

6461
    /**
6462
     * Should be called when data and/or isLoading input changes so that the overlay can be
6463
     * hidden/shown based on the current value of shouldOverlayLoading
6464
     */
6465
    protected evaluateLoadingState() {
6466
        if (this.shouldOverlayLoading) {
3,287✔
6467
            // a new overlay should be shown
6468
            const overlaySettings: OverlaySettings = {
15✔
6469
                outlet: this.loadingOutlet,
6470
                closeOnOutsideClick: false,
6471
                positionStrategy: new ContainerPositionStrategy()
6472
            };
6473
            this.loadingOverlay.open(overlaySettings);
15✔
6474
        } else {
6475
            this.loadingOverlay.close();
3,272✔
6476
        }
6477
    }
6478

6479
    /**
6480
     * @hidden
6481
     * Sets grid width i.e. this.calcWidth
6482
     */
6483
    protected calculateGridWidth() {
6484
        let width;
6485

6486
        if (this.isPercentWidth) {
11,743✔
6487
            /* width in %*/
6488
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width');
5,431✔
6489
            width = computed.indexOf('%') === -1 ? parseFloat(computed) : null;
5,431✔
6490
        } else {
6491
            width = parseInt(this.width, 10);
6,312✔
6492
        }
6493

6494
        if (!width && this.nativeElement) {
11,743✔
6495
            width = this.nativeElement.offsetWidth;
38✔
6496
        }
6497

6498

6499
        if (this.width === null || !width) {
11,743✔
6500
            width = this.getColumnWidthSum();
38✔
6501
        }
6502

6503
        if (this.hasVerticalScroll() && this.width !== null) {
11,743✔
6504
            width -= this.scrollSize;
2,382✔
6505
        }
6506
        if ((Number.isFinite(width) || width === null) && width !== this.calcWidth) {
11,743!
6507
            this.calcWidth = width;
3,790✔
6508
        }
6509
        this._derivePossibleWidth();
11,743✔
6510
    }
6511

6512
    /**
6513
     * @hidden
6514
     * Sets columns defaultWidth property
6515
     */
6516
    protected _derivePossibleWidth() {
6517
        if (!this.columnWidthSetByUser) {
11,743✔
6518
            this._columnWidth = this.width !== null ? this.getPossibleColumnWidth() : this.minColumnWidth + 'px';
10,984✔
6519
        }
6520
        this._columns.forEach((column: IgxColumnComponent) => {
11,743✔
6521
            if (this.hasColumnLayouts && parseInt(this._columnWidth, 10)) {
74,332✔
6522
                const columnWidthCombined = parseInt(this._columnWidth, 10) * (column.colEnd ? column.colEnd - column.colStart : 1);
3,546✔
6523
                column.defaultWidth = columnWidthCombined + 'px';
3,546✔
6524
            } else {
6525
                // D.K. March 29th, 2021 #9145 Consider min/max width when setting defaultWidth property
6526
                column.defaultWidth = this.getExtremumBasedColWidth(column);
70,786✔
6527
                column.resetCaches();
70,786✔
6528
            }
6529
        });
6530
        this.resetCachedWidths();
11,743✔
6531
    }
6532

6533
    /**
6534
     * @hidden
6535
     * @internal
6536
     */
6537
    protected getExtremumBasedColWidth(column: IgxColumnComponent): string {
6538
        let width = this._columnWidth;
70,786✔
6539
        if (width && typeof width !== 'string') {
70,786!
6540
            width = String(width);
×
6541
        }
6542
        const minWidth = width.indexOf('%') === -1 ? column.minWidthPx : column.minWidthPercent;
70,786✔
6543
        const maxWidth = width.indexOf('%') === -1 ? column.maxWidthPx : column.maxWidthPercent;
70,786✔
6544
        if (column.hidden) {
70,786✔
6545
            return width;
1,326✔
6546
        }
6547

6548
        if (minWidth > parseFloat(width)) {
69,460!
6549
            width = String(column.minWidth);
×
6550
        } else if (maxWidth < parseFloat(width)) {
69,460✔
6551
            width = String(column.maxWidth);
1,110✔
6552
        }
6553

6554
        // if no px or % are defined in maxWidth/minWidth consider it px
6555
        if (width.indexOf('%') === -1 && width.indexOf('px') === -1) {
69,460✔
6556
            width += 'px';
276✔
6557
        }
6558
        return width;
69,460✔
6559
    }
6560

6561
    protected resetNotifyChanges() {
6562
        this._cdrRequestRepaint = false;
8,135✔
6563
        this._cdrRequests = false;
8,135✔
6564
    }
6565

6566
    /** @hidden @internal */
6567
    public resolveOutlet() {
6568
        return this._userOutletDirective ? this._userOutletDirective : this._outletDirective;
238,824!
6569
    }
6570

6571
    /**
6572
     * Reorder columns in the main columnList and _columns collections.
6573
     *
6574
     * @hidden
6575
     */
6576
    protected _moveColumns(from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6577
        const orderedList = this._pinnedColumns.concat(this._unpinnedColumns);
138✔
6578
        const list = orderedList;
138✔
6579
        this._reorderColumns(from, to, pos, list);
138✔
6580
        const newList = this._resetColumnList(list);
138✔
6581
        this.updateColumns(newList);
138✔
6582
    }
6583

6584

6585
    /**
6586
     * Update internal column's collection.
6587
     * @hidden
6588
     */
6589
    public updateColumns(newColumns: IgxColumnComponent[]) {
6590
        // update internal collections to retain order.
6591
        this._pinnedColumns = newColumns
5,657✔
6592
            .filter((c) => c.pinned);
34,134✔
6593
        this._unpinnedColumns = newColumns.filter((c) => !c.pinned);
34,134✔
6594
        this._columns = newColumns;
5,657✔
6595
        this.resetCaches();
5,657✔
6596
    }
6597

6598
    /**
6599
     * @hidden
6600
     */
6601
    protected _resetColumnList(list?) {
6602
        if (!list) {
138!
6603
            list = this._columns;
×
6604
        }
6605
        let newList = [];
138✔
6606
        list.filter(c => c.level === 0).forEach(p => {
1,189✔
6607
            newList.push(p);
814✔
6608
            if (p.columnGroup) {
814✔
6609
                newList = newList.concat(p.allChildren);
138✔
6610
            }
6611
        });
6612
        return newList;
138✔
6613
    }
6614

6615
    /**
6616
     * Reorders columns inside the passed column collection.
6617
     * When reordering column group collection, the collection is not flattened.
6618
     * In all other cases, the columns collection is flattened, this is why adittional calculations on the dropIndex are done.
6619
     *
6620
     * @hidden
6621
     */
6622
    protected _reorderColumns(from: IgxColumnComponent, to: IgxColumnComponent, position: DropPosition, columnCollection: any[],
6623
        inGroup = false) {
138✔
6624
        const fromIndex = columnCollection.indexOf(from);
154✔
6625
        const childColumnsCount = inGroup ? 1 : from.allChildren.length + 1;
154✔
6626
        columnCollection.splice(fromIndex, childColumnsCount);
154✔
6627
        let dropIndex = columnCollection.indexOf(to);
154✔
6628
        if (position === DropPosition.AfterDropTarget) {
154✔
6629
            dropIndex++;
87✔
6630
            if (!inGroup && to.columnGroup) {
87✔
6631
                dropIndex += to.allChildren.length;
21✔
6632
            }
6633
        }
6634
        columnCollection.splice(dropIndex, 0, from);
154✔
6635
    }
6636

6637
    /**
6638
     * Reorder column group collection.
6639
     *
6640
     * @hidden
6641
     */
6642
    protected _moveChildColumns(parent: IgxColumnComponent, from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6643
        const buffer = parent.children.toArray();
16✔
6644
        this._reorderColumns(from, to, pos, buffer, true);
16✔
6645
        parent.children.reset(buffer);
16✔
6646
    }
6647

6648
    /**
6649
     * @hidden @internal
6650
     */
6651
    protected setupColumns() {
6652
        if (this.autoGenerate) {
3,348✔
6653
            this.autogenerateColumns();
877✔
6654
        } else {
6655
            this._columns = this.getColumnList();
2,471✔
6656
        }
6657

6658
        this.initColumns(this._columns, (col: IgxColumnComponent) => this.columnInit.emit(col));
21,543✔
6659
        this.columnListDiffer.diff(this.columnList);
3,348✔
6660
        this.checkPrimaryKeyColumn();
3,348✔
6661

6662
        this.columnList.changes
3,348✔
6663
            .pipe(takeUntil(this.destroy$))
6664
            .subscribe((change: QueryList<IgxColumnComponent>) => {
6665
                this.onColumnsChanged(change);
80✔
6666
            });
6667
    }
6668

6669
    protected getColumnList() {
6670
        return this.columnList.toArray();
2,285✔
6671
    }
6672

6673
    /**
6674
     * @hidden
6675
     */
6676
    protected deleteRowFromData(rowID: any, index: number) {
6677
        //  if there is a row (index !== 0) delete it
6678
        //  if there is a row in ADD or UPDATE state change it's state to DELETE
6679
        if (index !== -1) {
×
6680
            if (this.transactions.enabled) {
×
6681
                const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null };
×
6682
                this.transactions.add(transaction, this.data[index]);
×
6683
            } else {
6684
                this.data.splice(index, 1);
×
6685
            }
6686
        } else {
6687
            const state: State = this.transactions.getState(rowID);
×
6688
            this.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef);
×
6689
        }
6690
    }
6691

6692

6693
    /**
6694
     * @hidden @internal
6695
     */
6696
    protected getDataBasedBodyHeight(): number {
6697
        return !this.data || (this.data.length < this._defaultTargetRecordNumber) ?
1,048✔
6698
            0 : this.defaultTargetBodyHeight;
6699
    }
6700

6701
    /**
6702
     * @hidden @internal
6703
     */
6704
    protected onPinnedRowsChanged(change: QueryList<IgxGridRowComponent>) {
6705
        const diff = this.rowListDiffer.diff(change);
182✔
6706
        if (diff) {
182✔
6707
            this.notifyChanges(true);
182✔
6708
        }
6709
    }
6710

6711
    /**
6712
     * @hidden
6713
     */
6714
    protected onColumnsChanged(change: QueryList<IgxColumnComponent>) {
6715
        const diff = this.columnListDiffer.diff(change);
69✔
6716

6717
        if (this.autoGenerate && this._columns.length === 0 && this._autoGeneratedCols.length > 0) {
69!
6718
            // In Ivy if there are nested conditional templates the content children are re-evaluated
6719
            // hence autogenerated columns are cleared and need to be reset.
6720
            this.updateColumns(this._autoGeneratedCols);
×
6721
            return;
×
6722
        }
6723
        if (diff) {
69✔
6724
            let added = false;
69✔
6725
            let removed = false;
69✔
6726
            let pinning = false;
69✔
6727
            diff.forEachAddedItem((record: IterableChangeRecord<IgxColumnComponent>) => {
69✔
6728
                added = true;
1,036✔
6729
                if (record.item.pinned) {
1,036✔
6730
                    this._pinnedColumns.push(record.item);
1✔
6731
                    pinning = true;
1✔
6732
                } else {
6733
                    this._unpinnedColumns.push(record.item);
1,035✔
6734
                }
6735
            });
6736

6737
            this.initColumns(this.columnList.toArray(), (col: IgxColumnComponent) => this.columnInit.emit(col));
1,302✔
6738
            if (pinning) {
69✔
6739
                this.initPinning();
1✔
6740
            }
6741

6742
            diff.forEachRemovedItem((record: IterableChangeRecord<IgxColumnComponent | IgxColumnGroupComponent>) => {
69✔
6743
                const isColumnGroup = record.item instanceof IgxColumnGroupComponent;
135✔
6744
                if (!isColumnGroup) {
135✔
6745
                    // Clear Grouping
6746
                    this.gridAPI.clear_groupby(record.item.field);
132✔
6747

6748
                    // Clear Filtering
6749
                    this.filteringService.clear_filter(record.item.field);
132✔
6750

6751
                    // Close filter row
6752
                    if (this.filteringService.isFilterRowVisible
132!
6753
                        && this.filteringService.filteredColumn
6754
                        && this.filteringService.filteredColumn.field === record.item.field) {
6755
                        this.filteringRow.close();
×
6756
                    }
6757

6758
                    // Clear Sorting
6759
                    this.gridAPI.clear_sort(record.item.field);
132✔
6760

6761
                    // Remove column selection
6762
                    this.selectionService.deselectColumnsWithNoEvent([record.item.field]);
132✔
6763
                }
6764
                removed = true;
135✔
6765
            });
6766

6767
            this.resetCaches();
69✔
6768

6769
            if (added || removed) {
69✔
6770
                this.onColumnsAddedOrRemoved();
65✔
6771
            }
6772
            this.checkPrimaryKeyColumn();
69✔
6773
        }
6774
    }
6775

6776
    /**
6777
     * @hidden @internal
6778
     */
6779
    protected checkPrimaryKeyColumn() {
6780
        if (this.primaryKey && this.columns.length > 0 && !this.columns.find(c => c.field === this.primaryKey)) {
5,460✔
6781
            console.warn(`Primary key column "${this.primaryKey}" is not defined. Set \`primaryKey\` to a valid column.`);
92✔
6782
        }
6783
    }
6784

6785
    /**
6786
     * @hidden @internal
6787
     */
6788
    protected onColumnsAddedOrRemoved() {
6789
        this.summaryService.clearSummaryCache();
65✔
6790
        Promise.resolve().then(() => {
65✔
6791
            // `onColumnsChanged` can be executed midway a current detectChange cycle and markForCheck will be ignored then.
6792
            // This ensures that we will wait for the current cycle to end so we can trigger a new one and ngDoCheck to fire.
6793
            this.notifyChanges(true);
65✔
6794
        });
6795
    }
6796

6797
    /**
6798
     * @hidden
6799
     */
6800
    protected calculateGridSizes(recalcFeatureWidth = true) {
7,346✔
6801
        /*
6802
            TODO: (R.K.) This layered lasagne should be refactored
6803
            ASAP. The reason I have to reset the caches so many times is because
6804
            after teach `detectChanges` call they are filled with invalid
6805
            state. Of course all of this happens midway through the grid
6806
            sizing process which of course, uses values from the caches, thus resulting
6807
            in a broken layout.
6808
        */
6809
        this.cdr.detectChanges();
7,839✔
6810
        this.resetCaches(recalcFeatureWidth);
7,839✔
6811
        const hasScroll = this.hasVerticalScroll();
7,839✔
6812
        const hasHScroll = !this.isHorizontalScrollHidden;
7,839✔
6813
        this.calculateGridWidth();
7,839✔
6814
        this.resetCaches(recalcFeatureWidth);
7,839✔
6815
        this.cdr.detectChanges();
7,839✔
6816
        this.calculateGridHeight();
7,839✔
6817

6818
        if (this.rowEditable) {
7,839✔
6819
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
854✔
6820
        }
6821

6822
        if (this.filteringService.isFilterRowVisible) {
7,839✔
6823
            this.filteringRow.resetChipsArea();
155✔
6824
        }
6825

6826
        this.cdr.detectChanges();
7,839✔
6827
        // in case scrollbar has appeared recalc to size correctly.
6828
        if (hasScroll !== this.hasVerticalScroll()) {
7,839✔
6829
            this.calculateGridWidth();
169✔
6830
            this.cdr.detectChanges();
169✔
6831
        }
6832

6833
        // in case horizontal scrollbar has appeared recalc to size correctly.
6834
        if (hasHScroll !== this.hasHorizontalScroll()) {
7,839✔
6835
            this.isHorizontalScrollHidden = !this.hasHorizontalScroll();
2,777✔
6836
            this.cdr.detectChanges();
2,777✔
6837
            this.calculateGridHeight();
2,777✔
6838
            this.cdr.detectChanges();
2,777✔
6839
        }
6840
        if (this.zone.isStable) {
7,839✔
6841
            this.zone.run(() => {
225✔
6842
                this._applyWidthHostBinding();
225✔
6843
                this.cdr.detectChanges();
225✔
6844
            });
6845
        } else {
6846
            this.zone.onStable.pipe(first()).subscribe(() => {
7,614✔
6847
                this.zone.run(() => {
7,614✔
6848
                    this._applyWidthHostBinding();
7,614✔
6849
                });
6850
            });
6851
        }
6852
        this.resetCaches(recalcFeatureWidth);
7,839✔
6853
        if (this.hasColumnsToAutosize) {
7,839✔
6854
            this.cdr.detectChanges();
18✔
6855
            this.zone.onStable.pipe(first()).subscribe(() => {
18✔
6856
                this._autoSizeColumnsNotify.next();
18✔
6857
            });
6858
        }
6859
    }
6860

6861
    /**
6862
     * @hidden
6863
     * Sets TBODY height i.e. this.calcHeight
6864
     */
6865
    protected calculateGridHeight() {
6866

6867
        this.calcHeight = this._calculateGridBodyHeight();
10,385✔
6868
        if (this.pinnedRowHeight && this.calcHeight) {
10,385✔
6869
            this.calcHeight -= this.pinnedRowHeight;
107✔
6870
        }
6871
    }
6872

6873
    /**
6874
     * @hidden
6875
     */
6876
    protected getGroupAreaHeight(): number {
6877
        return 0;
2,658✔
6878
    }
6879

6880
    /**
6881
     * @hidden
6882
     */
6883
    protected getComputedHeight(elem) {
6884
        return elem.offsetHeight ? parseFloat(this.document.defaultView.getComputedStyle(elem).getPropertyValue('height')) : 0;
36,938✔
6885
    }
6886
    /**
6887
     * @hidden
6888
     */
6889
    protected getFooterHeight(): number {
6890
        return this.summaryRowHeight || this.getComputedHeight(this.tfoot.nativeElement);
9,587✔
6891
    }
6892
    /**
6893
     * @hidden
6894
     */
6895
    protected getTheadRowHeight(): number {
6896
        // D.P.: Before CSS loads,theadRow computed height will be 'auto'->NaN, so use 0 fallback
6897
        const height = this.getComputedHeight(this.theadRow.nativeElement) || 0;
9,587✔
6898
        return (!this.allowFiltering || (this.allowFiltering && this.filterMode !== FilterMode.quickFilter)) ?
9,587✔
6899
            height - this.getFilterCellHeight() :
6900
            height;
6901
    }
6902

6903
    /**
6904
     * @hidden
6905
     */
6906
    protected getToolbarHeight(): number {
6907
        let toolbarHeight = 0;
9,587✔
6908
        if (this.toolbar.first) {
9,587✔
6909
            toolbarHeight = this.getComputedHeight(this.toolbar.first.nativeElement);
395✔
6910
        }
6911
        return toolbarHeight;
9,587✔
6912
    }
6913

6914
    /**
6915
     * @hidden
6916
     */
6917
    protected getPagingFooterHeight(): number {
6918
        let pagingHeight = 0;
9,587✔
6919
        if (this.footer) {
9,587✔
6920
            const height = this.getComputedHeight(this.footer.nativeElement);
9,587✔
6921
            pagingHeight = this.footer.nativeElement.firstElementChild ?
9,587✔
6922
                height : 0;
6923
        }
6924
        return pagingHeight;
9,587✔
6925
    }
6926

6927
    /**
6928
     * @hidden
6929
     */
6930
    protected getFilterCellHeight(): number {
6931
        const headerGroupNativeEl = (this.headerGroupsList.length !== 0) ?
7,934✔
6932
            this.headerGroupsList[0].nativeElement : null;
6933
        const filterCellNativeEl = (headerGroupNativeEl) ?
7,934✔
6934
            headerGroupNativeEl.querySelector('igx-grid-filtering-cell') as HTMLElement : null;
6935
        return (filterCellNativeEl) ? filterCellNativeEl.offsetHeight : 0;
7,934✔
6936
    }
6937

6938
    /**
6939
     * @hidden
6940
     */
6941
    protected _calculateGridBodyHeight(): number {
6942
        if (!this._height) {
10,385✔
6943
            return null;
798✔
6944
        }
6945
        const actualTheadRow = this.getTheadRowHeight();
9,587✔
6946
        const footerHeight = this.getFooterHeight();
9,587✔
6947
        const toolbarHeight = this.getToolbarHeight();
9,587✔
6948
        const pagingHeight = this.getPagingFooterHeight();
9,587✔
6949
        const groupAreaHeight = this.getGroupAreaHeight();
9,587✔
6950
        const scrHeight = this.getComputedHeight(this.scr.nativeElement);
9,587✔
6951
        const renderedHeight = toolbarHeight + actualTheadRow +
9,587✔
6952
            footerHeight + pagingHeight + groupAreaHeight +
6953
            scrHeight;
6954

6955
        let gridHeight = 0;
9,587✔
6956

6957
        if (this.isPercentHeight) {
9,587✔
6958
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('height');
2,008✔
6959
            const autoSize = this._shouldAutoSize(renderedHeight);
2,008✔
6960
            if (autoSize || computed.indexOf('%') !== -1) {
2,008✔
6961
                const bodyHeight = this.getDataBasedBodyHeight();
847✔
6962
                return bodyHeight > 0 ? bodyHeight : null;
847✔
6963
            }
6964
            gridHeight = parseFloat(computed);
1,161✔
6965
        } else {
6966
            gridHeight = parseInt(this._height, 10);
7,579✔
6967
        }
6968
        const height = Math.abs(gridHeight - renderedHeight);
8,740✔
6969

6970
        if (Math.round(height) === 0 || isNaN(gridHeight)) {
8,740✔
6971
            const bodyHeight = this.defaultTargetBodyHeight;
18✔
6972
            return bodyHeight > 0 ? bodyHeight : null;
18✔
6973
        }
6974
        return height;
8,722✔
6975
    }
6976

6977
    protected checkContainerSizeChange() {
6978
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
95!
6979
        const origHeight = parentElement.offsetHeight;
95✔
6980
        this.nativeElement.style.display = 'none';
95✔
6981
        const height = parentElement.offsetHeight;
95✔
6982
        this.nativeElement.style.display = '';
95✔
6983
        return origHeight !== height;
95✔
6984
    }
6985

6986
    protected _shouldAutoSize(renderedHeight) {
6987
        this.tbody.nativeElement.style.display = 'none';
1,271✔
6988
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
1,271✔
6989
        let res = !parentElement ||
1,271✔
6990
            parentElement.clientHeight === 0 ||
6991
            parentElement.clientHeight === renderedHeight;
6992
        if (parentElement && (res || this._autoSize)) {
1,271✔
6993
            // If grid causes the parent container to extend (for example when container is flex)
6994
            // we should always auto-size since the actual size of the container will continuously change as the grid renders elements.
6995
            this._autoSize = false;
95✔
6996
            res = this.checkContainerSizeChange();
95✔
6997
        }
6998
        this.tbody.nativeElement.style.display = '';
1,271✔
6999
        return res;
1,271✔
7000
    }
7001

7002
    /**
7003
     * @hidden
7004
     * Gets calculated width of the unpinned area
7005
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
7006
     */
7007
    protected getUnpinnedWidth(takeHidden = false) {
23,380✔
7008
        let width = this.isPercentWidth ?
23,380✔
7009
            this.calcWidth :
7010
            parseInt(this.width, 10) || parseInt(this.hostWidth, 10) || this.calcWidth;
13,102✔
7011
        if (this.hasVerticalScroll() && !this.isPercentWidth) {
23,380✔
7012
            width -= this.scrollSize;
4,279✔
7013
        }
7014
        if (!this.isPinningToStart) {
23,380✔
7015
            width -= this.featureColumnsWidth();
62✔
7016
        }
7017

7018
        return width - this.getPinnedWidth(takeHidden);
23,380✔
7019
    }
7020

7021
    /**
7022
     * @hidden
7023
     */
7024
    protected _summaries(fieldName: string, hasSummary: boolean, summaryOperand?: any) {
7025
        const column = this.gridAPI.get_column_by_name(fieldName);
24✔
7026
        if (column) {
24✔
7027
            column.hasSummary = hasSummary;
24✔
7028
            if (summaryOperand) {
24✔
7029
                if (this.rootSummariesEnabled) {
2✔
7030
                    this.summaryService.retriggerRootPipe++;
2✔
7031
                }
7032
                column.summaries = summaryOperand;
2✔
7033
            }
7034
        }
7035
    }
7036

7037
    /**
7038
     * @hidden
7039
     */
7040
    protected _multipleSummaries(expressions: ISummaryExpression[], hasSummary: boolean) {
7041
        expressions.forEach((element) => {
4✔
7042
            this._summaries(element.fieldName, hasSummary, element.customSummary);
6✔
7043
        });
7044
    }
7045
    /**
7046
     * @hidden
7047
     */
7048
    protected _disableMultipleSummaries(expressions) {
7049
        expressions.forEach((column) => {
5✔
7050
            const columnName = column && column.fieldName ? column.fieldName : column;
13✔
7051
            this._summaries(columnName, false);
13✔
7052
        });
7053
    }
7054

7055
    /**
7056
     * @hidden
7057
     */
7058
    public resolveDataTypes(rec) {
7059
        if (typeof rec === 'number') {
6,225✔
7060
            return GridColumnDataType.Number;
3,650✔
7061
        } else if (typeof rec === 'boolean') {
2,575✔
7062
            return GridColumnDataType.Boolean;
152✔
7063
        } else if (typeof rec === 'object' && rec instanceof Date) {
2,423✔
7064
            return GridColumnDataType.Date;
143✔
7065
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
2,280✔
7066
            return GridColumnDataType.Image;
1✔
7067
        }
7068
        return GridColumnDataType.String;
2,279✔
7069
    }
7070

7071
    /**
7072
     * @hidden
7073
     */
7074
    protected autogenerateColumns() {
7075
        const data = this.gridAPI.get_data();
635✔
7076
        const fields = this.generateDataFields(data);
635✔
7077
        const columns = [];
635✔
7078

7079
        fields.forEach((field) => {
635✔
7080
            const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
4,073✔
7081
            ref.instance.field = field;
4,073✔
7082
            ref.instance.dataType = this.resolveDataTypes(data[0][field]);
4,073✔
7083
            ref.changeDetectorRef.detectChanges();
4,073✔
7084
            columns.push(ref.instance);
4,073✔
7085
        });
7086
        this._autoGeneratedCols = columns;
635✔
7087

7088
        this.updateColumns(columns);
635✔
7089
        this.columnsAutogenerated.emit({ columns: this._autoGeneratedCols });
635✔
7090
    }
7091

7092
    protected generateDataFields(data: any[]): string[] {
7093
        return Object.keys(data && data.length !== 0 ? data[0] : [])
641✔
7094
            .filter(key => !this.autoGenerateExclude.includes(key));
4,333✔
7095
    }
7096

7097
    /**
7098
     * @hidden
7099
     */
7100
    protected initColumns(collection: IgxColumnComponent[], cb: (args: any) => void = null) {
×
7101
        this._columnGroups = collection.some(col => col.columnGroup);
17,494✔
7102
        if (this.hasColumnLayouts) {
3,613✔
7103
            // Set overall row layout size
7104
            collection.forEach((col) => {
146✔
7105
                if (col.columnLayout) {
1,391✔
7106
                    const layoutSize = col.children ?
289!
7107
                        col.children.reduce((acc, val) => Math.max(val.rowStart + val.gridRowSpan - 1, acc), 1) :
1,092✔
7108
                        1;
7109
                    this._multiRowLayoutRowSize = Math.max(layoutSize, this._multiRowLayoutRowSize);
289✔
7110
                }
7111
            });
7112
        }
7113
        if (this.hasColumnLayouts && this.hasColumnGroups) {
3,613✔
7114
            // invalid configuration - multi-row and column groups
7115
            // remove column groups
7116
            const columnLayoutColumns = collection.filter((col) => col.columnLayout || col.columnLayoutChild);
1,391✔
7117
            collection = columnLayoutColumns;
146✔
7118
        }
7119
        this._maxLevelHeaderDepth = null;
3,613✔
7120
        collection.forEach((column: IgxColumnComponent) => {
3,613✔
7121
            column.defaultWidth = this.columnWidthSetByUser ? this._columnWidth : column.defaultWidth ? column.defaultWidth : '';
23,491✔
7122

7123
            if (cb) {
23,491✔
7124
                cb(column);
23,491✔
7125
            }
7126
        });
7127

7128
        this.updateColumns(collection);
3,613✔
7129

7130
        if (this.hasColumnLayouts) {
3,613✔
7131
            collection.forEach((column: IgxColumnComponent) => {
146✔
7132
                column.populateVisibleIndexes();
1,381✔
7133
            });
7134
        }
7135
    }
7136

7137
    /**
7138
     * @hidden
7139
     */
7140
    protected reinitPinStates() {
7141
        this._pinnedColumns = this._columns
191✔
7142
            .filter((c) => c.pinned).sort((a, b) => this._pinnedColumns.indexOf(a) - this._pinnedColumns.indexOf(b));
2,409✔
7143
        this._unpinnedColumns = this.hasColumnGroups ? this._columns.filter((c) => !c.pinned) :
2,109✔
7144
            this._columns.filter((c) => !c.pinned)
300✔
7145
                .sort((a, b) => this._unpinnedColumns.indexOf(a) - this._unpinnedColumns.indexOf(b));
214✔
7146
    }
7147

7148
    protected extractDataFromSelection(source: any[], formatters = false, headers = false, columnData?: any[]): any[] {
×
7149
        let columnsArray: IgxColumnComponent[];
7150
        let record = {};
246✔
7151
        let selectedData = [];
246✔
7152
        let keys = [];
246✔
7153
        const selectionCollection = new Map();
246✔
7154
        const keysAndData = [];
246✔
7155
        const activeEl = this.selectionService.activeElement;
246✔
7156

7157
        if (this.type === 'hierarchical') {
246✔
7158
            const expansionRowIndexes = [];
2✔
7159
            for (const [key, value] of this.expansionStates.entries()) {
2✔
7160
                if (value) {
×
7161
                    const rowIndex = this.gridAPI.get_rec_index_by_id(key, this.dataView);
×
7162
                    expansionRowIndexes.push(rowIndex);
×
7163
                }
7164
            }
7165
            if (this.selectionService.selection.size > 0) {
2!
7166
                if (expansionRowIndexes.length > 0) {
2!
7167
                    for (const [key, value] of this.selectionService.selection.entries()) {
×
7168
                        const updatedKey = key;
×
7169
                        let subtract = 0;
×
7170
                        expansionRowIndexes.forEach((row) => {
×
7171
                            if (updatedKey > Number(row)) {
×
7172
                                subtract++;
×
7173
                            }
7174
                        });
7175
                        selectionCollection.set(updatedKey - subtract, value);
×
7176
                    }
7177
                }
7178
            } else if (activeEl) {
×
7179
                let subtract = 0;
×
7180
                if (expansionRowIndexes.length > 0) {
×
7181
                    expansionRowIndexes.forEach(row => {
×
7182
                        if (activeEl.row > Number(row)) {
×
7183
                            subtract++;
×
7184
                        }
7185
                    });
7186
                    activeEl.row -= subtract;
×
7187
                }
7188
            }
7189
        }
7190

7191
        const totalItems = (this as any).totalItemCount ?? 0;
246✔
7192
        const isRemote = totalItems && totalItems > this.dataView.length;
246!
7193
        let selectionMap;
7194
        if (this.type === 'hierarchical' && selectionCollection.size > 0) {
246!
7195
            selectionMap = isRemote ? Array.from(selectionCollection) :
×
7196
                Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length);
×
7197
        } else {
7198
            selectionMap = isRemote ? Array.from(this.selectionService.selection) :
246!
7199
                Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length);
883✔
7200
        }
7201

7202
        if (this.cellSelection === GridSelectionMode.single && activeEl) {
246✔
7203
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
10✔
7204
        }
7205

7206
        if (this.cellSelection === GridSelectionMode.none && activeEl) {
246✔
7207
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
5✔
7208
        }
7209

7210
        if (columnData) {
246!
7211
            selectedData = columnData;
×
7212
        }
7213

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

7263
        if (keys.length) {
246!
7264
            keysAndData.push(selectedData);
×
7265
            keysAndData.push(keys);
×
7266
            return keysAndData;
×
7267
        } else {
7268
            return selectedData;
246✔
7269
        }
7270
    }
7271

7272
    protected getSelectableColumnsAt(index) {
7273
        if (this.hasColumnLayouts) {
2,512✔
7274
            const visibleLayoutColumns = this.visibleColumns
2✔
7275
                .filter(col => col.columnLayout)
20✔
7276
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
2✔
7277
            const colLayout = visibleLayoutColumns[index];
2✔
7278
            return colLayout ? colLayout.children.toArray() : [];
2!
7279
        } else {
7280
            const visibleColumns = this.visibleColumns
2,510✔
7281
                .filter(col => !col.columnGroup)
14,120✔
7282
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
11,790✔
7283
            return [visibleColumns[index]];
2,510✔
7284
        }
7285
    }
7286

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

7316
                if (max === 0 || (maxForCells <= emptyCellWithPaddingOnly && this._firstAutoResize)) {
76!
7317
                    // cells not in DOM yet or content not fully initialized.
7318
                    continue;
6✔
7319
                }
7320
                let maxSize = Math.ceil(Math.max(...cellsContentWidths)) + 1;
70✔
7321
                if (col.maxWidth && maxSize > col.maxWidthPx) {
70✔
7322
                    maxSize = col.maxWidthPx;
3✔
7323
                } else if (maxSize < col.minWidthPx) {
67✔
7324
                    maxSize = col.minWidthPx;
6✔
7325
                }
7326
                col.autoSize = maxSize;
70✔
7327
                col.resetCaches();
70✔
7328
                colResized = true;
70✔
7329
            }
7330
        }
7331
        if (colResized) {
26✔
7332
            this.resetCachedWidths();
18✔
7333
            this.cdr.detectChanges();
18✔
7334
        }
7335
    }
7336

7337
    protected extractDataFromColumnsSelection(source: any[], formatters = false, headers = false): any[] {
×
7338
        let record = {};
20✔
7339
        const selectedData = [];
20✔
7340
        const selectedColumns = this.selectedColumns();
20✔
7341
        if (selectedColumns.length === 0) {
20✔
7342
            return [];
12✔
7343
        }
7344

7345
        for (const data of source) {
8✔
7346
            selectedColumns.forEach((col) => {
71✔
7347
                const key = headers ? col.header || col.field : col.field;
142!
7348
                record[key] = formatters && col.formatter ? col.formatter(data[col.field], data)
142!
7349
                    : data[col.field];
7350
            });
7351

7352
            if (Object.keys(record).length) {
71✔
7353
                selectedData.push(record);
71✔
7354
            }
7355
            record = {};
71✔
7356
        }
7357
        return selectedData;
8✔
7358
    }
7359

7360
    /**
7361
     * @hidden
7362
     */
7363
    protected initPinning() {
7364
        this.calculateGridWidth();
3,908✔
7365
        this.resetCaches();
3,908✔
7366
        this.handleColumnPinningForGroups();
3,908✔
7367
        this.notifyChanges();
3,908✔
7368
    }
7369

7370
    /**
7371
     * @hidden
7372
     */
7373
    protected scrollTo(row: any | number, column: any | number, inCollection = this._filteredSortedUnpinnedData): void {
3✔
7374
        let delayScrolling = false;
106✔
7375

7376
        if (this.paginator && typeof (row) !== 'number') {
106✔
7377
            const rowIndex = inCollection.indexOf(row);
16✔
7378
            const page = Math.floor(rowIndex / this.perPage);
16✔
7379

7380
            if (this.page !== page) {
16✔
7381
                delayScrolling = true;
5✔
7382
                this.page = page;
5✔
7383
            }
7384
        }
7385

7386
        if (delayScrolling) {
106✔
7387
            this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
5✔
7388
                this.scrollDirective(this.verticalScrollContainer,
5✔
7389
                    typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
5!
7390
            });
7391
        } else {
7392
            this.scrollDirective(this.verticalScrollContainer,
101✔
7393
                typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
101✔
7394
        }
7395

7396
        this.scrollToHorizontally(column);
106✔
7397
    }
7398

7399
    /**
7400
     * @hidden
7401
     */
7402
    protected scrollToHorizontally(column: any | number) {
7403
        let columnIndex = typeof column === 'number' ? column : this.getColumnByName(column).visibleIndex;
191✔
7404
        const scrollRow = this.rowList.find(r => !!r.virtDirRow);
233✔
7405
        const virtDir = scrollRow ? scrollRow.virtDirRow : null;
191✔
7406
        if (this.isPinningToStart && this.pinnedColumns.length) {
191✔
7407
            if (columnIndex >= this.pinnedColumns.length) {
1!
7408
                columnIndex -= this.pinnedColumns.length;
×
7409
                this.scrollDirective(virtDir, columnIndex);
×
7410
            }
7411
        } else {
7412
            this.scrollDirective(virtDir, columnIndex);
190✔
7413
        }
7414
    }
7415

7416
    /**
7417
     * @hidden
7418
     */
7419
    protected scrollDirective(directive: IgxGridForOfDirective<any, any[]>, goal: number): void {
7420
        if (!directive) {
381✔
7421
            return;
1✔
7422
        }
7423
        directive.scrollTo(goal);
380✔
7424
    }
7425

7426

7427
    /**
7428
     * @hidden
7429
     */
7430
    protected getColumnWidthSum(): number {
7431
        let colSum = 0;
38✔
7432
        const cols = this.hasColumnLayouts ?
38!
7433
            this.visibleColumns.filter(x => x.columnLayout) : this.visibleColumns.filter(x => !x.columnGroup);
248✔
7434
        cols.forEach((item) => {
38✔
7435
            colSum += parseInt((item.calcWidth || item.defaultWidth), 10) || this.minColumnWidth;
218!
7436
        });
7437
        if (!colSum) {
38!
7438
            return null;
×
7439
        }
7440
        this.cdr.detectChanges();
38✔
7441
        colSum += this.featureColumnsWidth();
38✔
7442
        return colSum;
38✔
7443
    }
7444

7445
    /**
7446
     * Notify changes, reset cache and populateVisibleIndexes.
7447
     *
7448
     * @hidden
7449
     */
7450
    private _columnsReordered(column: IgxColumnComponent) {
7451
        this.notifyChanges();
128✔
7452
        // after reordering is done reset cached column collections.
7453
        this.resetColumnCollections();
128✔
7454
        column.resetCaches();
128✔
7455
    }
7456

7457
    protected buildDataView(_data: any[]) {
7458
        this._dataView = this.isRowPinningToTop ?
13,610✔
7459
            [...this.pinnedDataView, ...this.unpinnedDataView] :
7460
            [...this.unpinnedDataView, ...this.pinnedDataView];
7461
    }
7462

7463
    private _applyWidthHostBinding() {
7464
        let width = this._width;
7,839✔
7465
        if (width === null) {
7,839✔
7466
            let currentWidth = this.calcWidth;
2✔
7467
            if (this.hasVerticalScroll()) {
2!
7468
                currentWidth += this.scrollSize;
×
7469
            }
7470
            width = currentWidth + 'px';
2✔
7471
            this.resetCaches();
2✔
7472
        }
7473
        this._hostWidth = width;
7,839✔
7474
        this.cdr.markForCheck();
7,839✔
7475
    }
7476

7477
    protected verticalScrollHandler(event) {
7478
        this.verticalScrollContainer.onScroll(event);
317✔
7479
        this.disableTransitions = true;
317✔
7480

7481
        this.zone.run(() => {
317✔
7482
            this.zone.onStable.pipe(first()).subscribe(() => {
317✔
7483
                this.verticalScrollContainer.chunkLoad.emit(this.verticalScrollContainer.state);
317✔
7484
                if (this.rowEditable) {
317✔
7485
                    this.changeRowEditingOverlayStateOnScroll(this.crudService.rowInEditMode);
14✔
7486
                }
7487
            });
7488
        });
7489
        this.disableTransitions = false;
317✔
7490

7491
        this.hideOverlays();
317✔
7492
        this.actionStrip?.hide();
317✔
7493
        if (this.actionStrip) {
317✔
7494
            this.actionStrip.context = null;
9✔
7495
        }
7496
        const args: IGridScrollEventArgs = {
317✔
7497
            direction: 'vertical',
7498
            event,
7499
            scrollPosition: this.verticalScrollContainer.scrollPosition
7500
        };
7501
        this.gridScroll.emit(args);
317✔
7502
    }
7503

7504
    protected horizontalScrollHandler(event) {
7505
        const scrollLeft = event.target.scrollLeft;
312✔
7506
        this.headerContainer.onHScroll(scrollLeft);
312✔
7507
        this._horizontalForOfs.forEach(vfor => vfor.onHScroll(scrollLeft));
1,970✔
7508
        this.cdr.markForCheck();
312✔
7509

7510
        this.zone.run(() => {
312✔
7511
            this.zone.onStable.pipe(first()).subscribe(() => {
312✔
7512
                this.parentVirtDir.chunkLoad.emit(this.headerContainer.state);
310✔
7513
                requestAnimationFrame(() => {
310✔
7514
                    this.autoSizeColumnsInView();
310✔
7515
                });
7516
            });
7517
        });
7518
        if (!this.navigation.isColumnFullyVisible(this.navigation.lastColumnIndex)) {
312✔
7519
            this.hideOverlays();
205✔
7520
        }
7521
        const args: IGridScrollEventArgs = { direction: 'horizontal', event, scrollPosition: this.headerContainer.scrollPosition };
312✔
7522
        this.gridScroll.emit(args);
312✔
7523
    }
7524

7525
    protected get renderedActualRowHeight() {
7526
        let border = 1;
497✔
7527
        if (this.rowList.toArray().length > 0) {
497✔
7528
            const rowStyles = document.defaultView.getComputedStyle(this.rowList.first.nativeElement);
348✔
7529
            border = rowStyles.borderBottomWidth ? Math.ceil(parseFloat(rowStyles.borderBottomWidth)) : border;
348✔
7530
        }
7531
        return this.rowHeight + border;
497✔
7532
    }
7533

7534
    private executeCallback(rowIndex, visibleColIndex = -1, cb: (args: any) => void = null) {
×
7535
        if (!cb) {
831✔
7536
            return;
280✔
7537
        }
7538
        let row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
1,622✔
7539
        if (!row) {
551✔
7540
            if ((this as any).totalItemCount) {
8!
7541
                this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
×
7542
                    this.cdr.detectChanges();
×
7543
                    row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
×
7544
                    const cbArgs = this.getNavigationArguments(row, visibleColIndex);
×
7545
                    cb(cbArgs);
×
7546
                });
7547
            }
7548
            const dataViewIndex = this._getDataViewIndex(rowIndex);
8✔
7549
            if (this.dataView[dataViewIndex].detailsData) {
8✔
7550
                this.navigation.setActiveNode({ row: rowIndex });
8✔
7551
                this.cdr.detectChanges();
8✔
7552
            }
7553

7554
            return;
8✔
7555
        }
7556
        const args = this.getNavigationArguments(row, visibleColIndex);
543✔
7557
        cb(args);
543✔
7558
    }
7559

7560
    private getNavigationArguments(row, visibleColIndex) {
7561
        let targetType: GridKeydownTargetType; let target;
7562
        switch (row.nativeElement.tagName.toLowerCase()) {
543!
7563
            case 'igx-grid-groupby-row':
7564
                targetType = 'groupRow';
30✔
7565
                target = row;
30✔
7566
                break;
30✔
7567
            case 'igx-grid-summary-row':
7568
                targetType = 'summaryCell';
50✔
7569
                target = visibleColIndex !== -1 ?
50!
7570
                    row.summaryCells.find(c => c.visibleColumnIndex === visibleColIndex) : row.summaryCells.first;
139✔
7571
                break;
50✔
7572
            case 'igx-child-grid-row':
7573
                targetType = 'hierarchicalRow';
×
7574
                target = row;
×
7575
                break;
×
7576
            default:
7577
                targetType = 'dataCell';
463✔
7578
                target = visibleColIndex !== -1 ? row.cells.find(c => c.visibleColumnIndex === visibleColIndex) : row.cells.first;
1,286!
7579
                break;
463✔
7580
        }
7581
        return { targetType, target };
543✔
7582
    }
7583

7584
    private getNextDataRowIndex(currentRowIndex, previous = false): number {
15✔
7585
        const resolvedIndex = this._getDataViewIndex(currentRowIndex);
30✔
7586
        if (currentRowIndex < 0 || (currentRowIndex === 0 && previous) || (resolvedIndex >= this.dataView.length - 1 && !previous)) {
30!
7587
            return currentRowIndex;
7✔
7588
        }
7589
        // find next/prev record that is editable.
7590
        const nextRowIndex = previous ? this.findPrevEditableDataRowIndex(currentRowIndex) :
23✔
7591
            this.dataView.findIndex((rec, index) =>
7592
                index > resolvedIndex && this.isEditableDataRecordAtIndex(index));
56✔
7593
        const nextDataIndex = this.getDataIndex(nextRowIndex);
23✔
7594
        return nextDataIndex !== -1 ? nextDataIndex : currentRowIndex;
23!
7595
    }
7596

7597
    /**
7598
     * Returns the previous editable row index or -1 if no such row is found.
7599
     *
7600
     * @param currentIndex The index of the current editable record.
7601
     */
7602
    private findPrevEditableDataRowIndex(currentIndex): number {
7603
        let i = this.dataView.length;
8✔
7604
        const resolvedIndex = this._getDataViewIndex(currentIndex);
8✔
7605
        while (i--) {
8✔
7606
            if (i < resolvedIndex && this.isEditableDataRecordAtIndex(i)) {
178✔
7607
                return i;
8✔
7608
            }
7609
        }
7610
        return -1;
×
7611
    }
7612

7613

7614
    /**
7615
     * Returns if the record at the specified data view index is a an editable data record.
7616
     * If record is group rec, summary rec, child rec, ghost rec. etc. it is not editable.
7617
     *
7618
     * @param dataViewIndex The index of that record in the data view.
7619
     *
7620
     */
7621
    // TODO: Consider moving it into CRUD
7622
    private isEditableDataRecordAtIndex(dataViewIndex) {
7623
        const rec = this.dataView[dataViewIndex];
29✔
7624
        return !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData &&
29✔
7625
            !this.isGhostRecordAtIndex(dataViewIndex);
7626
    }
7627

7628
    /**
7629
     * Returns if the record at the specified data view index is a ghost.
7630
     * If record is pinned but is not in pinned area then it is a ghost record.
7631
     *
7632
     * @param dataViewIndex The index of that record in the data view.
7633
     */
7634
    private isGhostRecordAtIndex(dataViewIndex) {
7635
        const isPinned = this.isRecordPinned(this.dataView[dataViewIndex]);
27✔
7636
        const isInPinnedArea = this.isRecordPinnedByViewIndex(dataViewIndex);
27✔
7637
        return isPinned && !isInPinnedArea;
27✔
7638
    }
7639

7640
    private isValidPosition(rowIndex, colIndex): boolean {
7641
        const rows = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).length;
97✔
7642
        const cols = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0 && !col.hidden).length;
777✔
7643
        if (rows < 1 || cols < 1) {
97✔
7644
            return false;
2✔
7645
        }
7646
        if (rowIndex > -1 && rowIndex < this.dataView.length &&
95✔
7647
            colIndex > - 1 && colIndex <= Math.max(...this.visibleColumns.map(c => c.visibleIndex))) {
694✔
7648
            return true;
93✔
7649
        }
7650
        return false;
2✔
7651
    }
7652

7653
    private find(text: string, increment: number, caseSensitive?: boolean, exactMatch?: boolean, scroll?: boolean, endEdit = true) {
195✔
7654
        if (!this.rowList) {
322!
7655
            return 0;
×
7656
        }
7657

7658
        if (endEdit) {
322✔
7659
            this.crudService.endEdit(false);
283✔
7660
        }
7661

7662
        if (!text) {
322!
7663
            this.clearSearch();
×
7664
            return 0;
×
7665
        }
7666

7667
        const caseSensitiveResolved = caseSensitive ? true : false;
322✔
7668
        const exactMatchResolved = exactMatch ? true : false;
322✔
7669
        let rebuildCache = false;
322✔
7670

7671
        if (this._lastSearchInfo.searchText !== text ||
322✔
7672
            this._lastSearchInfo.caseSensitive !== caseSensitiveResolved ||
7673
            this._lastSearchInfo.exactMatch !== exactMatchResolved) {
7674
            this._lastSearchInfo = {
84✔
7675
                searchText: text,
7676
                activeMatchIndex: 0,
7677
                caseSensitive: caseSensitiveResolved,
7678
                exactMatch: exactMatchResolved,
7679
                matchInfoCache: [],
7680
                matchCount: 0,
7681
                content: ''
7682
            };
7683

7684
            rebuildCache = true;
84✔
7685
        } else {
7686
            this._lastSearchInfo.activeMatchIndex += increment;
238✔
7687
        }
7688

7689
        if (rebuildCache) {
322✔
7690
            this.rowList.forEach((row) => {
84✔
7691
                if (row.cells) {
784✔
7692
                    row.cells.forEach((c: IgxGridCellComponent) => {
740✔
7693
                        c.highlightText(text, caseSensitiveResolved, exactMatchResolved);
3,097✔
7694
                    });
7695
                }
7696
            });
7697

7698
            this.rebuildMatchCache();
84✔
7699
        }
7700

7701
        if (this._lastSearchInfo.activeMatchIndex >= this._lastSearchInfo.matchCount) {
322✔
7702
            this._lastSearchInfo.activeMatchIndex = 0;
28✔
7703
        } else if (this._lastSearchInfo.activeMatchIndex < 0) {
294✔
7704
            this._lastSearchInfo.activeMatchIndex = this._lastSearchInfo.matchCount - 1;
8✔
7705
        }
7706

7707
        if (this._lastSearchInfo.matchCount > 0) {
322✔
7708
            const matchInfo = this._lastSearchInfo.matchInfoCache[this._lastSearchInfo.activeMatchIndex];
304✔
7709
            this._lastSearchInfo = { ...this._lastSearchInfo };
304✔
7710

7711
            if (scroll !== false) {
304✔
7712
                this.scrollTo(matchInfo.row, matchInfo.column);
182✔
7713
            }
7714

7715
            this.textHighlightService.setActiveHighlight(this.id, {
304✔
7716
                column: matchInfo.column,
7717
                row: matchInfo.row,
7718
                index: matchInfo.index,
7719
                metadata: matchInfo.metadata,
7720
            });
7721

7722
        } else {
7723
            this.textHighlightService.clearActiveHighlight(this.id);
18✔
7724
        }
7725

7726
        return this._lastSearchInfo.matchCount;
322✔
7727
    }
7728

7729
    private rebuildMatchCache() {
7730
        this._lastSearchInfo.matchInfoCache = [];
211✔
7731

7732
        const caseSensitive = this._lastSearchInfo.caseSensitive;
211✔
7733
        const exactMatch = this._lastSearchInfo.exactMatch;
211✔
7734
        const searchText = caseSensitive ? this._lastSearchInfo.searchText : this._lastSearchInfo.searchText.toLowerCase();
211✔
7735
        const data = this.filteredSortedData;
211✔
7736
        const columnItems = this.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex);
908✔
7737

7738
        data.forEach((dataRow, rowIndex) => {
211✔
7739
            columnItems.forEach((c) => {
3,474✔
7740
                const pipeArgs = this.getColumnByName(c.field).pipeArgs;
14,791✔
7741
                const value = c.formatter ? c.formatter(resolveNestedPath(dataRow, c.field), dataRow) :
14,791!
7742
                    c.dataType === 'number' ? formatNumber(resolveNestedPath(dataRow, c.field), this.locale, pipeArgs.digitsInfo) :
14,791✔
7743
                        c.dataType === 'date'
11,115✔
7744
                            ? formatDate(resolveNestedPath(dataRow, c.field), pipeArgs.format, this.locale, pipeArgs.timezone)
7745
                            : resolveNestedPath(dataRow, c.field);
7746
                if (value !== undefined && value !== null && c.searchable) {
14,791✔
7747
                    let searchValue = caseSensitive ? String(value) : String(value).toLowerCase();
14,576✔
7748

7749
                    if (exactMatch) {
14,576✔
7750
                        if (searchValue === searchText) {
536✔
7751
                            const mic: IMatchInfoCache = {
9✔
7752
                                row: dataRow,
7753
                                column: c.field,
7754
                                index: 0,
7755
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7756
                            };
7757

7758
                            this._lastSearchInfo.matchInfoCache.push(mic);
9✔
7759
                        }
7760
                    } else {
7761
                        let occurrenceIndex = 0;
14,040✔
7762
                        let searchIndex = searchValue.indexOf(searchText);
14,040✔
7763

7764
                        while (searchIndex !== -1) {
14,040✔
7765
                            const mic: IMatchInfoCache = {
2,410✔
7766
                                row: dataRow,
7767
                                column: c.field,
7768
                                index: occurrenceIndex++,
7769
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7770
                            };
7771

7772
                            this._lastSearchInfo.matchInfoCache.push(mic);
2,410✔
7773

7774
                            searchValue = searchValue.substring(searchIndex + searchText.length);
2,410✔
7775
                            searchIndex = searchValue.indexOf(searchText);
2,410✔
7776
                        }
7777
                    }
7778
                }
7779
            });
7780
        });
7781

7782
        this._lastSearchInfo.matchCount = this._lastSearchInfo.matchInfoCache.length;
211✔
7783
    }
7784

7785
    protected updateDefaultRowHeight() {
7786
        if (this.dataRowList.length > 0 && this.dataRowList.first.cells && this.dataRowList.first.cells.length > 0) {
3,640✔
7787
            const height = parseFloat(this.document.defaultView.getComputedStyle(this.dataRowList.first.cells.first.nativeElement)?.getPropertyValue('height'));
111✔
7788
            if (height) {
111!
7789
                this._defaultRowHeight = height;
111✔
7790
            } else {
7791
                this._shouldRecalcRowHeight = true;
×
7792
            }
7793
        } 
7794
    }
7795

7796
    // TODO: About to Move to CRUD
7797
    private configureRowEditingOverlay(rowID: any, useOuter = false) {
72✔
7798
        let settings = this.rowEditSettings;
279✔
7799
        const overlay = this.overlayService.getOverlayById(this.rowEditingOverlay.overlayId);
279✔
7800
        if (overlay) {
279✔
7801
            settings = overlay.settings;
73✔
7802
        }
7803
        settings.outlet = useOuter ? this.parentRowOutletDirective : this.rowOutletDirective;
279✔
7804
        this.rowEditPositioningStrategy.settings.container = this.tbody.nativeElement;
279✔
7805
        const pinned = this._pinnedRecordIDs.indexOf(rowID) !== -1;
279✔
7806
        const targetRow = !pinned ?
279✔
7807
            this.gridAPI.get_row_by_key(rowID) as IgxRowDirective
7808
            : this.pinnedRows.find(x => x.key === rowID) as IgxRowDirective;
8✔
7809
        if (!targetRow) {
279!
7810
            return;
×
7811
        }
7812
        settings.target = targetRow.element.nativeElement;
279✔
7813
        this.toggleRowEditingOverlay(true);
279✔
7814
    }
7815

7816
    private handleColumnPinningForGroups(): void {
7817
        // When a column is a group or is inside a group, pin all related.
7818
        const pinnedColumns = [];
3,908✔
7819
        const unpinnedColumns = [];
3,908✔
7820

7821
        this._pinnedColumns.forEach(col => {
3,908✔
7822
            if (col.parent) {
492✔
7823
                col.parent.pinned = true;
113✔
7824
            }
7825
            if (col.columnGroup) {
492✔
7826
                col.children.forEach(child => child.pinned = true);
112✔
7827
            }
7828
        });
7829

7830
        // Make sure we don't exceed unpinned area min width and get pinned and unpinned col collections.
7831
        // We take into account top level columns (top level groups and non groups).
7832
        // If top level is unpinned the pinning handles all children to be unpinned as well.
7833
        for (const column of this._columns) {
3,908✔
7834
            if (column.pinned && !column.parent) {
23,164✔
7835
                pinnedColumns.push(column);
380✔
7836
            } else if (column.pinned && column.parent) {
22,784✔
7837
                if (column.topLevelParent.pinned) {
115!
7838
                    pinnedColumns.push(column);
115✔
7839
                } else {
7840
                    column.pinned = false;
×
7841
                    unpinnedColumns.push(column);
×
7842
                }
7843
            } else {
7844
                unpinnedColumns.push(column);
22,669✔
7845
            }
7846
        }
7847
        // Assign the applicable collections.
7848
        this._pinnedColumns = pinnedColumns;
3,908✔
7849
        this._unpinnedColumns = unpinnedColumns;
3,908✔
7850
    }
7851

7852
    protected shouldRecreateColumns(oldData: any[] | null | undefined, newData: any[] | null | undefined): boolean {
7853
        if (!oldData || !oldData.length) return true;
72✔
7854
        if (!newData || !newData.length) return false;
45!
7855
        return Object.keys(oldData[0]).join() !== Object.keys(newData[0]).join();
45✔
7856
    }
7857

7858
    /**
7859
     * Clears the current navigation service active node
7860
     */
7861
    private clearActiveNode() {
7862
        this.navigation.lastActiveNode = this.navigation.activeNode;
72✔
7863
        this.navigation.activeNode = {} as IActiveNode;
72✔
7864
        this.notifyChanges();
72✔
7865
    }
7866
}
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