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

IgniteUI / igniteui-angular / 14129044260

28 Mar 2025 12:34PM CUT coverage: 91.61% (-0.02%) from 91.625%
14129044260

Pull #15517

github

web-flow
Merge 33c29ee74 into 82885eff3
Pull Request #15517: Sizing panes correctly with minSize is used when the browser is shrinked

13320 of 15585 branches covered (85.47%)

3 of 7 new or added lines in 1 file covered. (42.86%)

2 existing lines in 2 files now uncovered.

26860 of 29320 relevant lines covered (91.61%)

33961.07 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 } from '../data-operations/filtering-expression.interface';
41
import { IGroupByRecord } from '../data-operations/groupby-record.interface';
42
import { IForOfDataChangingEventArgs, IgxGridForOfDirective } from '../directives/for-of/for_of.directive';
43
import { IgxTextHighlightService } from '../directives/text-highlight/text-highlight.service';
44
import { ISummaryExpression } from './summaries/grid-summary';
45
import { IgxGridBodyDirective, RowEditPositionStrategy } from './grid.common';
46
import type { IgxGridToolbarComponent } from './toolbar/grid-toolbar.component';
47
import { IgxToolbarToken } from './toolbar/token';
48
import { IgxRowDirective } from './row.directive';
49
import { IgxOverlayOutletDirective, IgxToggleDirective } from '../directives/toggle/toggle.directive';
50
import {
51
    FilteringExpressionsTree, IFilteringExpressionsTree, FilteringExpressionsTreeType
52
} from '../data-operations/filtering-expressions-tree';
53
import { IFilteringOperation } from '../data-operations/filtering-condition';
54
import { Transaction, TransactionType, TransactionService, State } from '../services/public_api';
55
import {
56
    IgxRowAddTextDirective,
57
    IgxRowEditTemplateDirective,
58
    IgxRowEditTabStopDirective,
59
    IgxRowEditTextDirective,
60
    IgxRowEditActionsDirective
61
} from './grid.rowEdit.directive';
62
import { IgxGridNavigationService, IActiveNode } from './grid-navigation.service';
63
import { IgxFilteringService } from './filtering/grid-filtering.service';
64
import { IgxGridFilteringCellComponent } from './filtering/base/grid-filtering-cell.component';
65
import { WatchChanges } from './watch-changes';
66
import { IgxGridHeaderGroupComponent } from './headers/grid-header-group.component';
67
import { GridResourceStringsEN, IGridResourceStrings } from '../core/i18n/grid-resources';
68
import { IgxGridSummaryService } from './summaries/grid-summary.service';
69
import { IgxSummaryRowComponent } from './summaries/summary-row.component';
70
import { IgxGridSelectionService } from './selection/selection.service';
71
import { IgxEditRow, IgxCell, IgxAddRow } from './common/crud.service';
72
import { ICachedViewLoadedEventArgs, IgxTemplateOutletDirective } from '../directives/template-outlet/template_outlet.directive';
73
import { IgxExcelStyleLoadingValuesTemplateDirective } from './filtering/excel-style/excel-style-search.component';
74
import { IgxGridColumnResizerComponent } from './resizing/resizer.component';
75
import { CharSeparatedValueData } from '../services/csv/char-separated-value-data';
76
import { IgxColumnResizingService } from './resizing/resizing.service';
77
import { FilteringStrategy, IFilteringStrategy } from '../data-operations/filtering-strategy';
78
import {
79
    IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandedIndicatorDirective,
80
    IgxHeaderCollapsedIndicatorDirective, IgxExcelStyleHeaderIconDirective, IgxSortAscendingHeaderIconDirective,
81
    IgxSortDescendingHeaderIconDirective, IgxSortHeaderIconDirective
82
} from './grid.directives';
83
import {
84
    GridKeydownTargetType,
85
    GridSelectionMode,
86
    GridSummaryPosition,
87
    GridSummaryCalculationMode,
88
    FilterMode,
89
    ColumnPinningPosition,
90
    RowPinningPosition,
91
    GridPagingMode,
92
    GridValidationTrigger,
93
    Size
94
} from './common/enums';
95
import {
96
    IGridCellEventArgs,
97
    IRowSelectionEventArgs,
98
    IPinColumnEventArgs,
99
    IRowDataEventArgs,
100
    IColumnResizeEventArgs,
101
    IColumnMovingStartEventArgs,
102
    IColumnMovingEventArgs,
103
    IColumnMovingEndEventArgs,
104
    IGridKeydownEventArgs,
105
    IRowDragStartEventArgs,
106
    IRowDragEndEventArgs,
107
    IGridClipboardEvent,
108
    IGridToolbarExportEventArgs,
109
    ISearchInfo,
110
    ICellPosition,
111
    IRowToggleEventArgs,
112
    IColumnSelectionEventArgs,
113
    IPinRowEventArgs,
114
    IGridScrollEventArgs,
115
    IActiveNodeChangeEventArgs,
116
    ISortingEventArgs,
117
    IFilteringEventArgs,
118
    IColumnVisibilityChangedEventArgs,
119
    IColumnVisibilityChangingEventArgs,
120
    IPinColumnCancellableEventArgs,
121
    IGridEditEventArgs,
122
    IRowDataCancelableEventArgs,
123
    IGridEditDoneEventArgs,
124
    IGridRowEventArgs,
125
    IGridContextMenuEventArgs,
126
    IColumnsAutoGeneratedEventArgs
127
} from './common/events';
128
import { IgxAdvancedFilteringDialogComponent } from './filtering/advanced-filtering/advanced-filtering-dialog.component';
129
import {
130
    ColumnType,
131
    GridServiceType,
132
    GridType,
133
    IGridFormGroupCreatedEventArgs,
134
    IGridValidationStatusEventArgs,
135
    IgxGridEmptyTemplateContext,
136
    IgxGridHeaderTemplateContext,
137
    IgxGridRowDragGhostContext,
138
    IgxGridRowEditActionsTemplateContext,
139
    IgxGridRowEditTemplateContext,
140
    IgxGridRowEditTextTemplateContext,
141
    IgxGridRowTemplateContext,
142
    IgxGridTemplateContext,
143
    IgxHeadSelectorTemplateContext,
144
    IgxRowSelectorTemplateContext,
145
    IGX_GRID_SERVICE_BASE,
146
    ISizeInfo,
147
    RowType,
148
    IPinningConfig,
149
    IClipboardOptions
150
} from './common/grid.interface';
151
import { DropPosition } from './moving/moving.service';
152
import { IgxHeadSelectorDirective, IgxRowSelectorDirective } from './selection/row-selectors';
153
import { IgxColumnComponent } from './columns/column.component';
154
import { IgxColumnGroupComponent } from './columns/column-group.component';
155
import { IgxRowDragGhostDirective, IgxDragIndicatorIconDirective } from './row-drag.directive';
156
import { IgxSnackbarComponent } from '../snackbar/snackbar.component';
157
import { IgxActionStripToken } from '../action-strip/token';
158
import { IgxGridRowComponent } from './grid/grid-row.component';
159
import type { IgxPaginatorComponent } from '../paginator/paginator.component';
160
import { IgxPaginatorToken } from '../paginator/token';
161
import { IgxGridHeaderRowComponent } from './headers/grid-header-row.component';
162
import { IgxGridGroupByAreaComponent } from './grouping/grid-group-by-area.component';
163
import { IgxFlatTransactionFactory, TRANSACTION_TYPE } from '../services/transaction/transaction-factory.service';
164
import { ISortingOptions } from './columns/interfaces';
165
import { GridSelectionRange, IgxGridTransaction } from './common/types';
166
import { VerticalAlignment, HorizontalAlignment, PositionSettings, OverlaySettings } from '../services/overlay/utilities';
167
import { IgxOverlayService } from '../services/overlay/overlay';
168
import { ConnectedPositioningStrategy } from '../services/overlay/position/connected-positioning-strategy';
169
import { ContainerPositionStrategy } from '../services/overlay/position/container-position-strategy';
170
import { AbsoluteScrollStrategy } from '../services/overlay/scroll/absolute-scroll-strategy';
171
import { Action, StateUpdateEvent, TransactionEventOrigin } from '../services/transaction/transaction';
172
import { ISortingExpression } from '../data-operations/sorting-strategy';
173
import { IGridSortingStrategy } from './common/strategy';
174
import { IgxGridExcelStyleFilteringComponent } from './filtering/excel-style/excel-style-filtering.component';
175
import { IgxGridHeaderComponent } from './headers/grid-header.component';
176
import { IgxGridFilteringRowComponent } from './filtering/base/grid-filtering-row.component';
177
import { DefaultDataCloneStrategy, IDataCloneStrategy } from '../data-operations/data-clone-strategy';
178
import { IgxGridCellComponent } from './cell.component';
179
import { IgxGridValidationService } from './grid/grid-validation.service';
180
import { getCurrentResourceStrings } from '../core/i18n/resources';
181
import { isTree, recreateTreeFromFields } from '../data-operations/expressions-tree-util';
182

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

473
    /** @hidden @internal */
474
    public get excelStyleFilteringComponent() {
475
        return this.excelStyleFilteringComponents?.first;
269✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
733

734
    /* blazorInclude */
735
    /**
736
     * @hidden @internal
737
     */
738
    @Output()
739
    public columnsAutogenerated = new EventEmitter<IColumnsAutoGeneratedEventArgs>();
4,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
992

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

999
    /* blazorInclude */
1000
    /** @hidden @internal */
1001
    @Output()
1002
    public selectedRowsChange = new EventEmitter<any[]>();
4,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
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,070✔
1089

1090
    /**
1091
     * @hidden @internal
1092
     */
1093
    @Output()
1094
    public localeChange = new EventEmitter<boolean>();
4,070✔
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,070✔
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,070✔
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,070✔
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,749✔
1167
    }
1168

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

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

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

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

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

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

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

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

1216

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

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

1242

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

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

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

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

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

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

1276
    /** @hidden @internal */
1277
    public get headerSelectorContainer() {
1278
        return this.theadRow?.headerSelectorContainer;
30,338✔
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,589✔
1289
    }
1290

1291
    /** @hidden @internal */
1292
    public get filteringRow(): IgxGridFilteringRowComponent {
1293
        return this.theadRow?.filterRow;
1,324✔
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,070✔
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,474✔
1362
    }
1363
    /**
1364
     * Sets the row edit text template.
1365
     *```html
1366
     * <ng-template #template igxRowEditText let-rowChangesCount>
1367
     * Changes: {{rowChangesCount}}
1368
     * </ng-template>
1369
     * ```
1370
     *```typescript
1371
     * @ViewChild('template', {read: TemplateRef })
1372
     * public template: TemplateRef<any>;
1373
     * this.grid.rowEditTextTemplate = this.template;
1374
     * ```
1375
     */
1376
    public set rowEditTextTemplate(template: TemplateRef<IgxGridRowEditTextTemplateContext>) {
1377
        this._rowEditTextTemplate = template;
1✔
1378
    }
1379

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

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

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

1416
    /**
1417
     * Gets the row edit actions template.
1418
     */
1419
    @Input()
1420
    public get rowEditActionsTemplate(): TemplateRef<IgxGridRowEditActionsTemplateContext> {
1421
        return this._rowEditActionsTemplate || this.rowEditActionsDirectives?.first;
5,850✔
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,070✔
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,227✔
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;
895✔
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,070✔
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,775✔
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;
895✔
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,070✔
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,792✔
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;
895✔
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,070✔
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,041✔
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;
895✔
1563
    }
1564

1565
    /** @hidden @internal */
1566
    @ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef })
1567
    public excelStyleHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,070✔
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,290✔
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;
896✔
1592
    }
1593

1594

1595
    /**
1596
     * @hidden
1597
     * @internal
1598
     */
1599
    @ContentChild(IgxSortAscendingHeaderIconDirective, { read: TemplateRef })
1600
    public sortAscendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,070✔
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,051✔
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;
901✔
1625
    }
1626

1627
    /** @hidden @internal */
1628
    @ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef })
1629
    public sortDescendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,070✔
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;
957✔
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;
901✔
1654
    }
1655

1656
    /**
1657
     * @hidden
1658
     * @internal
1659
     */
1660
    @ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef })
1661
    public sortHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,070✔
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,511✔
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;
901✔
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,070✔
1718

1719
    /**
1720
     * @hidden @internal
1721
     */
1722
    @HostBinding('attr.role')
1723
    public hostRole = 'grid';
4,070✔
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,070✔
1794

1795

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

1807
    public get resourceStrings(): IGridResourceStrings {
1808
        return this._resourceStrings;
230,166✔
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;
330✔
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,526✔
1846
    }
1847

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

1858
            value.type = FilteringExpressionsTreeType.Regular;
915✔
1859
            if (value && this._columns?.length > 0) {
915✔
1860
                this._filteringExpressionsTree = recreateTreeFromFields(value, this._columns) as IFilteringExpressionsTree;
768✔
1861
            } else {
1862
                this._filteringExpressionsTree = value;
147✔
1863
            }
1864
            this.filteringPipeTrigger++;
915✔
1865
            this.filteringExpressionsTreeChange.emit(this._filteringExpressionsTree);
915✔
1866

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

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

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

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

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

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

1907
        if (value && isTree(value)) {
69✔
1908
            value.type = FilteringExpressionsTreeType.Advanced;
55✔
1909
            if (this._columns && this._columns.length > 0) {
55✔
1910
                this._advancedFilteringExpressionsTree = recreateTreeFromFields(value, this._columns) as IFilteringExpressionsTree;
50✔
1911
            } else {
1912
                this._advancedFilteringExpressionsTree = value;
5✔
1913
            }
1914
            this.filteringPipeTrigger++;
55✔
1915
        } else {
1916
            this._advancedFilteringExpressionsTree = null;
14✔
1917
        }
1918
        this.advancedFilteringExpressionsTreeChange.emit(this._advancedFilteringExpressionsTree);
69✔
1919

1920
        if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
69✔
1921
            this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1922
            this._filteredData = null;
19✔
1923
        }
1924

1925
        this.selectionService.clearHeaderCBState();
69✔
1926
        this.summaryService.clearSummaryCache();
69✔
1927
        this.notifyChanges();
69✔
1928

1929
        // Wait for the change detection to update filtered data through the pipes and then emit the event.
1930
        requestAnimationFrame(() => this.filteringDone.emit(this._advancedFilteringExpressionsTree));
69✔
1931
    }
1932

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

1944
    public set locale(value: string) {
1945
        if (value !== this._locale) {
4,087✔
1946
            this._locale = value;
4,081✔
1947
            this._currencyPositionLeft = undefined;
4,081✔
1948
            this.summaryService.clearSummaryCache();
4,081✔
1949
            this.pipeTrigger++;
4,081✔
1950
            this.notifyChanges();
4,081✔
1951
            this.localeChange.emit();
4,081✔
1952
        }
1953
    }
1954

1955
    @Input()
1956
    public get pagingMode() {
1957
        return this._pagingMode;
8,419✔
1958
    }
1959

1960
    public set pagingMode(val: GridPagingMode) {
1961
        this._pagingMode = val;
2✔
1962
        this.pipeTrigger++;
2✔
1963
        this.notifyChanges(true);
2✔
1964
    }
1965

1966
    /** @hidden @internal */
1967
    public get page(): number {
1968
        return this.paginator?.page || 0;
2,219,406✔
1969
    }
1970

1971
    public set page(val: number) {
1972
        if (this.paginator) {
713✔
1973
            this.paginator.page = val;
330✔
1974
        }
1975
    }
1976

1977
    /** @hidden @internal */
1978
    public get perPage(): number {
1979
        return this.paginator?.perPage || DEFAULT_ITEMS_PER_PAGE;
2,219,319✔
1980
    }
1981

1982
    public set perPage(val: number) {
1983
        if (this.paginator) {
20✔
1984
            this.paginator.perPage = val;
20✔
1985
        }
1986
    }
1987

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

2000
    public set hideRowSelectors(value: boolean) {
2001
        this._hideRowSelectors = value;
21✔
2002
        this.notifyChanges(true);
21✔
2003
    }
2004

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

2018
    public set rowDraggable(val: boolean) {
2019
        this._rowDrag = val;
47✔
2020
        this.notifyChanges(true);
47✔
2021
    }
2022

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

2034
    /**
2035
     * @hidden
2036
     * @internal
2037
     */
2038
    public rowDragging = false;
4,070✔
2039

2040
    /** @hidden @internal */
2041
    public dragRowID = null;
4,070✔
2042

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

2059
    public set rowEditable(val: boolean) {
2060
        if (!this._init) {
534✔
2061
            this.refreshGridState();
35✔
2062
        }
2063
        this._rowEditable = val;
534✔
2064
        this.notifyChanges();
534✔
2065
    }
2066

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

2082
    public set height(value: string | null) {
2083
        if (this._height !== value) {
3,056✔
2084
            this._height = value;
3,054✔
2085
            this.nativeElement.style.height = value;
3,054✔
2086
            this.notifyChanges(true);
3,054✔
2087
        }
2088
    }
2089

2090
    /**
2091
     * @hidden @internal
2092
     */
2093
    @HostBinding('style.width')
2094
    public get hostWidth() {
2095
        return this._width || this._hostWidth;
32,380✔
2096
    }
2097

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

2112
    public set width(value: string | null) {
2113
        if (this._width !== value) {
2,061✔
2114
            this._width = value;
2,061✔
2115
            this.nativeElement.style.width = value;
2,061✔
2116
            this.notifyChanges(true);
2,061✔
2117
        }
2118
    }
2119

2120
    /** @hidden @internal */
2121
    public get headerWidth() {
2122
        return parseInt(this.width, 10) - 17;
×
2123
    }
2124

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

2139
    public set rowHeight(value: number | string) {
2140
        if (typeof value !== 'number') {
2✔
2141
            value = parseInt(value, 10);
1✔
2142
        }
2143
        this._rowHeight = value;
2✔
2144
    }
2145

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

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

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

2204
    public get isLoading(): boolean {
2205
        return this._isLoading;
93,687✔
2206
    }
2207

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

2225
    public set shouldGenerate(value: boolean) {
2226
        this.autoGenerate = value;
×
2227
    }
2228

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

2242
    public get emptyFilteredGridMessage(): string {
2243
        return this._emptyFilteredGridMessage || this.resourceStrings.igx_grid_emptyFilteredGrid_message;
205✔
2244
    }
2245

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

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

2282
    public set allowFiltering(value) {
2283
        if (this._allowFiltering !== value) {
725✔
2284
            this._allowFiltering = value;
714✔
2285
            this.filteringService.registerSVGIcons();
714✔
2286

2287

2288
            this.filteringService.isFilterRowVisible = false;
714✔
2289
            this.filteringService.filteredColumn = null;
714✔
2290

2291
            this.notifyChanges(true);
714✔
2292
        }
2293
    }
2294

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

2308
    public set allowAdvancedFiltering(value) {
2309
        if (this._allowAdvancedFiltering !== value) {
44✔
2310
            this._allowAdvancedFiltering = value;
43✔
2311
            this.filteringService.registerSVGIcons();
43✔
2312

2313
            if (!this._init) {
43✔
2314
                this.notifyChanges(true);
5✔
2315
            }
2316
        }
2317
    }
2318

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

2334
    public set filterMode(value: FilterMode) {
2335
        switch (value) {
185!
2336
            case FilterMode.excelStyleFilter:
2337
            case FilterMode.quickFilter:
2338
                this._filterMode = value;
185✔
2339
                break;
185✔
2340
            default:
2341
                break;
×
2342
        }
2343

2344
        if (this.filteringService.isFilterRowVisible) {
185✔
2345
            this.filteringRow.close();
1✔
2346
        }
2347
        this.notifyChanges(true);
185✔
2348
    }
2349

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

2365
    public set summaryPosition(value: GridSummaryPosition) {
2366
        this._summaryPosition = value;
18✔
2367
        this.notifyChanges();
18✔
2368
    }
2369

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

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

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

2410
    public set showSummaryOnCollapse(value: boolean) {
2411
        this._showSummaryOnCollapse = value;
9✔
2412
        this.notifyChanges();
9✔
2413
    }
2414

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

2428
    public set filterStrategy(classRef: IFilteringStrategy) {
2429
        this._filterStrategy = classRef;
38✔
2430
    }
2431

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

2445
    public set sortStrategy(value: IGridSortingStrategy) {
2446
        this._sortingStrategy = value;
14✔
2447
    }
2448

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

2471
    public get sortingOptions() {
2472
        return this._sortingOptions;
28,711✔
2473
    }
2474

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

2494
    public get selectedRows(): any[] {
2495
        return this.selectionService.getSelectedRows();
731✔
2496
    }
2497

2498

2499
    /** @hidden @internal */
2500
    public get headerGroupsList(): IgxGridHeaderGroupComponent[] {
2501
        return this.theadRow.groups;
20,464✔
2502
    }
2503

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

2509
    /** @hidden @internal */
2510
    public get filterCellList(): IgxGridFilteringCellComponent[] {
2511
        return this.headerGroupsList.map(group => group.filter).filter(cell => cell);
14,499✔
2512
    }
2513

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

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

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

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

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

2592
    /**
2593
     * @hidden
2594
     * @internal
2595
     */
2596
    public get isPinningToStart() {
2597
        return this.pinning.columns !== ColumnPinningPosition.End;
2,439,271✔
2598
    }
2599

2600
    /**
2601
     * @hidden
2602
     * @internal
2603
     */
2604
    public get isRowPinningToTop() {
2605
        return this.pinning.rows !== RowPinningPosition.Bottom;
671,697✔
2606
    }
2607

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

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

2633
    /**
2634
     * @hidden @internal
2635
     */
2636
    public get rowOutletDirective() {
2637
        return this.rowEditingOutletDirective;
4,349✔
2638
    }
2639

2640
    /**
2641
     * @hidden @internal
2642
     */
2643
    public get parentRowOutletDirective() {
2644
        return this.outlet;
1✔
2645
    }
2646

2647
    /**
2648
     * @hidden @internal
2649
     */
2650
    public get rowEditCustom(): TemplateRef<IgxGridRowEditTemplateContext> {
2651
        if (this.rowEditCustomDirectives && this.rowEditCustomDirectives.first) {
6,439✔
2652
            return this.rowEditCustomDirectives.first;
62✔
2653
        }
2654
        return null;
6,377✔
2655
    }
2656

2657
    /**
2658

2659
    /**
2660
     * @hidden @internal
2661
     */
2662
    public get rowEditContainer(): TemplateRef<IgxGridRowEditTemplateContext> {
2663
        return this.rowEditCustom ? this.rowEditCustom : this.defaultRowEditTemplate;
5,870✔
2664
    }
2665

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

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

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

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

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

2717
    /** @hidden @internal */
2718
    public get activeDescendant() {
2719
        const activeElem = this.navigation.activeNode;
133,356✔
2720

2721
        if (!activeElem || !Object.keys(activeElem).length) {
133,356✔
2722
            return this.id;
117,911✔
2723
        }
2724

2725
        return activeElem.row < 0 ?
15,445✔
2726
            `${this.id}_${activeElem.row}_${activeElem.mchCache.level}_${activeElem.column}` :
2727
            `${this.id}_${activeElem.row}_${activeElem.column}`;
2728
    }
2729

2730
    /** @hidden @internal */
2731
    public get bannerClass(): string {
2732
        const position = this.rowEditPositioningStrategy.isTop ? 'igx-banner__border-top' : 'igx-banner__border-bottom';
5,870✔
2733
        return `igx-banner ${position}`;
5,870✔
2734
    }
2735

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

2753
    public set sortingExpressions(value: ISortingExpression[]) {
2754
        this._sortingExpressions = cloneArray(value);
453✔
2755
        this.sortingExpressionsChange.emit(this._sortingExpressions);
453✔
2756
        this.notifyChanges();
453✔
2757
    }
2758

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

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

2783
    /**
2784
     * Gets the number of pinned columns.
2785
     */
2786
    public get pinnedColumnsCount() {
2787
        return this.pinnedColumns.filter(col => !col.columnLayout).length;
101✔
2788
    }
2789

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

2806
    public set batchEditing(val: boolean) {
2807
        if (val !== this._batchEditing) {
163✔
2808
            delete this._transactions;
152✔
2809
            this._batchEditing = val;
152✔
2810
            this.switchTransactionService(val);
152✔
2811
            this.subscribeToTransactions();
152✔
2812
        }
2813
    }
2814

2815
    /* blazorSuppress */
2816
    /**
2817
     * Get transactions service for the grid.
2818
     */
2819
    public get transactions(): TransactionService<Transaction, State> {
2820
        if (this._diTransactions && !this.batchEditing) {
7,212,244✔
2821
            return this._diTransactions;
3,804✔
2822
        }
2823
        return this._transactions;
7,208,440✔
2824
    }
2825

2826
    /**
2827
     * @hidden @internal
2828
     */
2829
    public get currentRowState(): any {
2830
        return this._currentRowState;
×
2831
    }
2832

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

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

2859
    public set cellSelection(selectionMode: GridSelectionMode) {
2860
        this._cellSelectionMode = selectionMode;
33✔
2861
        // if (this.gridAPI.grid) {
2862
        this.selectionService.clear(true);
33✔
2863
        this.notifyChanges();
33✔
2864
        // }
2865
    }
2866

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

2880
    public set rowSelection(selectionMode: GridSelectionMode) {
2881
        this._rowSelectionMode = selectionMode;
523✔
2882
        if (!this._init) {
523✔
2883
            this.selectionService.clearAllSelectedRows();
72✔
2884
            this.notifyChanges(true);
72✔
2885
        }
2886
    }
2887

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

2901
    public set columnSelection(selectionMode: GridSelectionMode) {
2902
        this._columnSelectionMode = selectionMode;
157✔
2903
        // if (this.gridAPI.grid) {
2904
        this.selectionService.clearAllSelectedColumns();
157✔
2905
        this.notifyChanges(true);
157✔
2906
        // }
2907
    }
2908

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

2919
    public get pagingState() {
2920
        return this._pagingState;
174✔
2921
    }
2922

2923
    /**
2924
     * @hidden @internal
2925
     */
2926
    public rowEditMessage;
2927

2928
    /**
2929
     * @hidden @internal
2930
     */
2931
    public calcWidth: number;
2932
    /**
2933
     * @hidden @internal
2934
     */
2935
    public calcHeight = 0;
4,070✔
2936
    /**
2937
     * @hidden @internal
2938
     */
2939
    public tfootHeight: number;
2940

2941
    /**
2942
     * @hidden @internal
2943
     */
2944
    public disableTransitions = false;
4,070✔
2945

2946
    /**
2947
     * Represents the last search information.
2948
     */
2949
    public get lastSearchInfo(): ISearchInfo {
2950
        return this._lastSearchInfo;
1,474,826✔
2951
    }
2952

2953
    /**
2954
     * @hidden @internal
2955
     */
2956
    public columnWidthSetByUser = false;
4,070✔
2957

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

2963
    /**
2964
     * @hidden @internal
2965
     */
2966
    public unpinnedRecords: any[];
2967

2968
    /**
2969
     * @hidden @internal
2970
     */
2971
    public rendered$ = this.rendered.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true }));
4,070✔
2972

2973
    /** @hidden @internal */
2974
    public resizeNotify = new Subject<void>();
4,070✔
2975

2976
    /** @hidden @internal */
2977
    public rowAddedNotifier = new Subject<IRowDataEventArgs>();
4,070✔
2978

2979
    /** @hidden @internal */
2980
    public rowDeletedNotifier = new Subject<IRowDataEventArgs>();
4,070✔
2981

2982
    /** @hidden @internal */
2983
    public pipeTriggerNotifier = new Subject();
4,070✔
2984

2985
    /** @hidden @internal */
2986
    public _filteredSortedPinnedData: any[];
2987

2988
    /** @hidden @internal */
2989
    public _filteredSortedUnpinnedData: any[];
2990

2991
    /** @hidden @internal */
2992
    public _filteredPinnedData: any[];
2993

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

3019
    /**
3020
     * @hidden @internal
3021
     */
3022
    public isColumnWidthSum = false;
4,070✔
3023

3024
    /**
3025
     * @hidden @internal
3026
     */
3027
    public summaryPipeTrigger = 0;
4,070✔
3028
    /**
3029
     * @hidden @internal
3030
     */
3031
    public groupablePipeTrigger = 0;
4,070✔
3032

3033
    /**
3034
    * @hidden @internal
3035
    */
3036
    public EMPTY_DATA = [];
4,070✔
3037

3038
    /** @hidden @internal */
3039
    public get type(): GridType["type"] {
3040
        return 'flat';
24,447✔
3041
    }
3042

3043
    /** @hidden @internal */
3044
    public _baseFontSize: number;
3045

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

3103
    protected _pinnedRecordIDs = [];
4,070✔
3104

3105
    /**
3106
     * @hidden
3107
     */
3108
    protected _hasVisibleColumns;
3109
    protected _allowFiltering = false;
4,070✔
3110
    protected _allowAdvancedFiltering = false;
4,070✔
3111
    protected _filterMode: FilterMode = FilterMode.quickFilter;
4,070✔
3112

3113

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

3140
    /** @hidden @internal */
3141
    public get paginator() {
3142
        return this.paginationComponents?.first;
3,724,329✔
3143
    }
3144

3145
    /**
3146
     * @hidden @internal
3147
     */
3148
    public get scrollSize() {
3149
        return this.verticalScrollContainer.getScrollNativeSize();
185,995✔
3150
    }
3151

3152
    private _primaryKey: string;
3153
    private _rowEditable = false;
4,070✔
3154
    private _currentRowState: any;
3155
    private _filteredSortedData = null;
4,070✔
3156
    private _filteredData = null;
4,070✔
3157

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

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

3182
    private _hostWidth;
3183
    private _advancedFilteringOverlayId: string;
3184
    private _advancedFilteringPositionSettings: PositionSettings = {
4,070✔
3185
        verticalDirection: VerticalAlignment.Middle,
3186
        horizontalDirection: HorizontalAlignment.Center,
3187
        horizontalStartPoint: HorizontalAlignment.Center,
3188
        verticalStartPoint: VerticalAlignment.Middle
3189
    };
3190

3191
    private _advancedFilteringOverlaySettings: OverlaySettings = {
4,070✔
3192
        closeOnOutsideClick: false,
3193
        modal: false,
3194
        positionStrategy: new ConnectedPositioningStrategy(this._advancedFilteringPositionSettings),
3195
    };
3196

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

3213
    private _columnWidth: string;
3214

3215
    private _summaryPosition: GridSummaryPosition = GridSummaryPosition.bottom;
4,070✔
3216
    private _summaryCalculationMode: GridSummaryCalculationMode = GridSummaryCalculationMode.rootAndChildLevels;
4,070✔
3217
    private _showSummaryOnCollapse = false;
4,070✔
3218
    private _summaryRowHeight = 0;
4,070✔
3219
    private _cellSelectionMode: GridSelectionMode = GridSelectionMode.multiple;
4,070✔
3220
    private _rowSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,070✔
3221
    private _selectRowOnClick = true;
4,070✔
3222
    private _columnSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,070✔
3223

3224
    private lastAddedRowIndex;
3225

3226
    private _currencyPositionLeft: boolean;
3227

3228
    private rowEditPositioningStrategy = new RowEditPositionStrategy({
4,070✔
3229
        horizontalDirection: HorizontalAlignment.Right,
3230
        verticalDirection: VerticalAlignment.Bottom,
3231
        horizontalStartPoint: HorizontalAlignment.Left,
3232
        verticalStartPoint: VerticalAlignment.Bottom,
3233
        closeAnimation: null
3234
    });
3235

3236
    private rowEditSettings: OverlaySettings = {
4,070✔
3237
        scrollStrategy: new AbsoluteScrollStrategy(),
3238
        modal: false,
3239
        closeOnOutsideClick: false,
3240
        outlet: this.rowOutletDirective,
3241
        positionStrategy: this.rowEditPositioningStrategy
3242
    };
3243

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

3255
    /**
3256
     * @hidden @internal
3257
     */
3258
    public get minColumnWidth() {
3259
        return MINIMUM_COLUMN_WIDTH;
65,442✔
3260
    }
3261

3262
    protected get isCustomSetRowHeight(): boolean {
3263
        return !isNaN(this._rowHeight);
1,197,216✔
3264
    }
3265

3266
    /**
3267
     * @hidden @internal
3268
     */
3269
    public abstract id: string;
3270
    /* blazorSuppress */
3271
    public abstract data: any[] | null;
3272

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

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

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

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

3327
        if (this.crudService.row && this.crudService.row.getClassName() === IgxAddRow.name) {
11,406✔
3328
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
385✔
3329
        }
3330

3331
        return result;
11,406✔
3332
    }
3333

3334
    /**
3335
     * @hidden @internal
3336
     */
3337
    public get dataLength() {
3338
        return this.transactions.enabled ? this.dataWithAddedInTransactionRows.length : this.gridAPI.get_all_data().length;
45,595✔
3339
    }
3340

3341
    /**
3342
     * @hidden @internal
3343
     */
3344
    public get template(): TemplateRef<IgxGridTemplateContext> {
3345
        if (this.isLoading && (this.hasZeroResultFilter || this.hasNoData)) {
45,172✔
3346
            return this.loadingGridTemplate ? this.loadingGridTemplate : this.loadingGridDefaultTemplate;
81✔
3347
        }
3348

3349
        if (this.hasZeroResultFilter) {
45,091✔
3350
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
193!
3351
        }
3352

3353
        if (this.hasNoData) {
44,898✔
3354
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate;
736✔
3355
        }
3356
    }
3357

3358
    /**
3359
     * @hidden @internal
3360
     */
3361
    private get hasZeroResultFilter(): boolean {
3362
        return this.filteredData && this.filteredData.length === 0;
45,367✔
3363
    }
3364

3365
    /**
3366
     * @hidden @internal
3367
     */
3368
    private get hasNoData(): boolean {
3369
        return !this.data || this.dataLength === 0;
45,243✔
3370
    }
3371

3372
    /**
3373
     * @hidden @internal
3374
     */
3375
    public get shouldOverlayLoading(): boolean {
3376
        return this.isLoading && !this.hasNoData && !this.hasZeroResultFilter;
48,502✔
3377
    }
3378

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

3387
    /**
3388
     * @hidden @internal
3389
     */
3390
    public get isRowSelectable(): boolean {
3391
        return this.rowSelection !== GridSelectionMode.none;
288,853✔
3392
    }
3393

3394
    /**
3395
     * @hidden @internal
3396
     */
3397
    public get isCellSelectable() {
3398
        return this.cellSelection !== GridSelectionMode.none;
8,476✔
3399
    }
3400

3401
    /**
3402
     * @hidden @internal
3403
     */
3404
    public get columnInDrag() {
3405
        return this.gridAPI.cms.column;
269,348✔
3406
    }
3407

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

3445
    /**
3446
     * @hidden
3447
     * @internal
3448
     */
3449
    @HostListener('mouseleave')
3450
    public hideActionStrip() {
3451
        this.actionStrip?.hide();
3✔
3452
    }
3453

3454
    /**
3455
     * @hidden
3456
     * @internal
3457
     */
3458
    public get headerFeaturesWidth() {
3459
        return this._headerFeaturesWidth;
1,320✔
3460
    }
3461

3462
    /**
3463
     * @hidden
3464
     * @internal
3465
     */
3466
    public isDetailRecord(_rec) {
3467
        return false;
1,237✔
3468
    }
3469

3470
    /**
3471
     * @hidden
3472
     * @internal
3473
     */
3474
    public isGroupByRecord(_rec) {
3475
        return false;
1,237✔
3476
    }
3477

3478
    /**
3479
     * @hidden @internal
3480
     */
3481
    public isGhostRecord(record: any): boolean {
3482
        return record.ghostRecord !== undefined;
537,583✔
3483
    }
3484
    /**
3485
     * @hidden @internal
3486
     */
3487
    public isAddRowRecord(record: any): boolean {
3488
        return record.addRow !== undefined;
×
3489
    }
3490

3491
    /**
3492
     * @hidden
3493
     * Returns the row index of a row that takes into account the full view data like pinning.
3494
     */
3495
    public getDataViewIndex(rowIndex, pinned) {
3496
        if (pinned && !this.isRowPinningToTop) {
252,679✔
3497
            rowIndex = rowIndex + this.unpinnedDataView.length;
198✔
3498
        } else if (!pinned && this.isRowPinningToTop) {
252,481✔
3499
            rowIndex = rowIndex + this.pinnedDataView.length;
250,728✔
3500
        }
3501
        return rowIndex;
252,679✔
3502
    }
3503

3504
    /**
3505
     * @hidden
3506
     * @internal
3507
     */
3508
    public get hasDetails() {
3509
        return false;
376✔
3510
    }
3511

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

3526
    /**
3527
     * @hidden
3528
     * @internal
3529
     */
3530
    public hideOverlays() {
3531
        this.overlayIDs.forEach(overlayID => {
535✔
3532
            const overlay = this.overlayService.getOverlayById(overlayID);
1✔
3533

3534
            if (overlay?.visible && !overlay.closeAnimationPlayer?.hasStarted()) {
1✔
3535
                this.overlayService.hide(overlayID);
1✔
3536

3537
                this.nativeElement.focus();
1✔
3538
            }
3539
        });
3540
    }
3541

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

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

3565
    /**
3566
     * @hidden
3567
     * @internal
3568
     */
3569
    public isRecordPinned(rec) {
3570
        return this.getInitialPinnedIndex(rec) !== -1;
1,088,061✔
3571
    }
3572

3573
    /**
3574
     * @hidden
3575
     * @internal
3576
     * Returns the record index in order of pinning by the user. Does not consider sorting/filtering.
3577
     */
3578
    public getInitialPinnedIndex(rec) {
3579
        const id = this.gridAPI.get_row_id(rec);
1,088,169✔
3580
        return this._pinnedRecordIDs.indexOf(id);
1,088,169✔
3581
    }
3582

3583
    /**
3584
     * @hidden
3585
     * @internal
3586
     */
3587
    public get hasPinnedRecords() {
3588
        return this._pinnedRecordIDs.length > 0;
491,047✔
3589
    }
3590

3591
    /**
3592
     * @hidden
3593
     * @internal
3594
     */
3595
    public get pinnedRecordsCount() {
3596
        return this._pinnedRecordIDs.length;
7,332✔
3597
    }
3598

3599
    /**
3600
     * @hidden
3601
     * @internal
3602
     */
3603
    public get crudService() {
3604
        return this.gridAPI.crudService;
3,401,169✔
3605
    }
3606

3607
    /**
3608
     * @hidden
3609
     * @internal
3610
     */
3611
    public _setupServices() {
3612
        this.gridAPI.grid = this as any;
3,550✔
3613
        this.crudService.grid = this as any;
3,550✔
3614
        this.selectionService.grid = this as any;
3,550✔
3615
        this.validation.grid = this as any;
3,550✔
3616
        this.navigation.grid = this as any;
3,550✔
3617
        this.filteringService.grid = this as any;
3,550✔
3618
        this.summaryService.grid = this as any;
3,550✔
3619
    }
3620

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

3644
        this.subscribeToTransactions();
3,550✔
3645

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

3670
        this.pipeTriggerNotifier.pipe(takeUntil(this.destroy$)).subscribe(() => this.pipeTrigger++);
3,550✔
3671
        this.columnMovingEnd.pipe(destructor).subscribe(() => this.crudService.endEdit(false));
3,550✔
3672

3673
        this.overlayService.opening.pipe(destructor).subscribe((event) => {
3,550✔
3674
            if (this._advancedFilteringOverlayId === event.id) {
796✔
3675
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
41✔
3676
                if (instance) {
41✔
3677
                    instance.initialize(this as any, this.overlayService, event.id);
41✔
3678
                }
3679
            }
3680
        });
3681

3682
        this.overlayService.opened.pipe(destructor).subscribe((event) => {
3,550✔
3683
            const overlaySettings = this.overlayService.getOverlayById(event.id)?.settings;
531✔
3684

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

3695
            // do not hide the overlay if it's attached to a row
3696
            if (this.rowEditingOverlay?.overlayId === event.id) {
492✔
3697
                return;
21✔
3698
            }
3699

3700
            if (overlaySettings?.outlet === this.outlet && this.overlayIDs.indexOf(event.id) === -1) {
471✔
3701
                this.overlayIDs.push(event.id);
354✔
3702
            }
3703
        });
3704

3705
        this.overlayService.closed.pipe(filter(() => !this._init), destructor).subscribe((event) => {
3,550✔
3706
            if (this._advancedFilteringOverlayId === event.id) {
433✔
3707
                this.overlayService.detach(this._advancedFilteringOverlayId);
21✔
3708
                this._advancedFilteringOverlayId = null;
21✔
3709
                return;
21✔
3710
            }
3711

3712
            const ind = this.overlayIDs.indexOf(event.id);
412✔
3713
            if (ind !== -1) {
412✔
3714
                this.overlayIDs.splice(ind, 1);
198✔
3715
            }
3716
        });
3717

3718
        this.verticalScrollContainer.dataChanging.pipe(filter(() => !this._init), destructor).subscribe(($event) => {
6,734✔
3719
            const shouldRecalcSize = this.isPercentHeight &&
3,268✔
3720
                (!this.calcHeight || this.calcHeight === this.getDataBasedBodyHeight() ||
3721
                    this.calcHeight === this.renderedRowHeight * this._defaultTargetRecordNumber);
3722
            if (shouldRecalcSize) {
3,268✔
3723
                this.calculateGridHeight();
110✔
3724
                $event.containerSize = this.calcHeight;
110✔
3725
            }
3726
            this.evaluateLoadingState();
3,268✔
3727
        });
3728

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

3737

3738
        this.headerContainer?.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3,550✔
3739
            // the horizontal scrollbar showing/hiding
3740
            // update scrollbar visibility and recalc heights
3741
            this.notifyChanges(true);
1,348✔
3742
            this.cdr.detectChanges();
1,348✔
3743
        });
3744

3745
        this.verticalScrollContainer.contentSizeChange.pipe(filter(() => !this._init), throttleTime(30), destructor).subscribe(() => {
3,550✔
3746
            this.notifyChanges(true);
680✔
3747
        });
3748

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

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

3773
    /**
3774
     * @hidden
3775
     * @internal
3776
     */
3777
    public resetColumnsCaches() {
3778
        this._columns.forEach(column => column.resetCaches());
214,431✔
3779
    }
3780

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

3791
    /**
3792
     * @hidden
3793
     * @internal
3794
     */
3795
    public resetForOfCache() {
3796
        const firstVirtRow = this.dataRowList.first;
34,041✔
3797
        if (firstVirtRow) {
34,041✔
3798
            if (this._cdrRequests) {
16,822✔
3799
                firstVirtRow.virtDirRow.cdr.detectChanges();
6,726✔
3800
            }
3801
            firstVirtRow.virtDirRow.assumeMaster();
16,822✔
3802
        }
3803
    }
3804

3805
    /**
3806
     * @hidden
3807
     * @internal
3808
     */
3809
    public setFilteredData(data, pinned: boolean) {
3810
        if (this.hasPinnedRecords && pinned) {
1,525✔
3811
            this._filteredPinnedData = data || [];
49✔
3812
            const filteredUnpinned = this._filteredUnpinnedData || [];
49✔
3813
            const filteredData = [... this._filteredPinnedData, ...filteredUnpinned];
49✔
3814
            this._filteredData = filteredData.length > 0 ? filteredData : this._filteredUnpinnedData;
49✔
3815
        } else if (this.hasPinnedRecords && !pinned) {
1,476✔
3816
            this._filteredUnpinnedData = data;
47✔
3817
        } else {
3818
            this._filteredData = data;
1,429✔
3819
        }
3820
    }
3821

3822
    /**
3823
     * @hidden
3824
     * @internal
3825
     */
3826
    public resetColumnCollections() {
3827
        if (this.hasColumnLayouts) {
34,182✔
3828
            this._columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
12,012✔
3829
        }
3830
        this._visibleColumns.length = 0;
34,182✔
3831
        this._pinnedVisible.length = 0;
34,182✔
3832
        this._unpinnedVisible.length = 0;
34,182✔
3833
    }
3834

3835
    /**
3836
     * @hidden
3837
     * @internal
3838
     */
3839
    public resetCachedWidths() {
3840
        this._unpinnedWidth = NaN;
45,809✔
3841
        this._pinnedWidth = NaN;
45,809✔
3842
        this._totalWidth = NaN;
45,809✔
3843
    }
3844

3845
    /**
3846
     * @hidden
3847
     * @internal
3848
     */
3849
    public resetCaches(recalcFeatureWidth = true) {
10,515✔
3850
        if (recalcFeatureWidth) {
34,041✔
3851
            this._headerFeaturesWidth = NaN;
34,032✔
3852
            this.summaryService.summaryHeight = 0;
34,032✔
3853
        }
3854
        this.resetColumnsCaches();
34,041✔
3855
        this.resetColumnCollections();
34,041✔
3856
        this.resetForOfCache();
34,041✔
3857
        this.resetCachedWidths();
34,041✔
3858
        this.hasVisibleColumns = undefined;
34,041✔
3859
        this._columnGroups = this._columns.some(col => col.columnGroup);
160,535✔
3860
    }
3861

3862
    /**
3863
     * @hidden
3864
     */
3865
    public ngAfterContentInit() {
3866
        if (this.sortHeaderIconDirectiveTemplate) {
3,421✔
3867
            this.sortHeaderIconTemplate = this.sortHeaderIconDirectiveTemplate;
5✔
3868
        }
3869

3870
        if (this.sortAscendingHeaderIconDirectiveTemplate) {
3,421✔
3871
            this.sortAscendingHeaderIconTemplate = this.sortAscendingHeaderIconDirectiveTemplate;
5✔
3872
        }
3873

3874
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
3,421✔
3875
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
5✔
3876
        }
3877

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

3889
    /**
3890
     * @hidden @internal
3891
     */
3892
    public dataRebinding(event: IForOfDataChangingEventArgs) {
3893
        if (event.state.chunkSize == 0) {
6,734✔
3894
            this._shouldRecalcRowHeight = true;
3,580✔
3895
        }
3896
        this.dataChanging.emit(event);
6,734✔
3897
    }
3898

3899
    /**
3900
     * @hidden @internal
3901
     */
3902
    public dataRebound(event) {
3903
        this.selectionService.clearHeaderCBState();
6,734✔
3904
        if (this._shouldRecalcRowHeight) {
6,734✔
3905
            this._shouldRecalcRowHeight = false;
3,580✔
3906
            this.updateDefaultRowHeight();
3,580✔
3907
        }
3908
        this.dataChanged.emit(event);
6,734✔
3909
    }
3910

3911
    /** @hidden @internal */
3912
    public createFilterDropdown(column: ColumnType, options: OverlaySettings) {
3913
        options.outlet = this.outlet;
205✔
3914
        if (this.excelStyleFilteringComponent) {
205✔
3915
            this.excelStyleFilteringComponent.initialize(column, this.overlayService);
21✔
3916
            const id = this.overlayService.attach(this.excelStyleFilteringComponent.element, options);
21✔
3917
            this.excelStyleFilteringComponent.overlayComponentId = id;
21✔
3918
            return id;
21✔
3919
        }
3920
        const id = this.overlayService.attach(IgxGridExcelStyleFilteringComponent, this.viewRef, options);
184✔
3921
        return id;
184✔
3922
    }
3923

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

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

3970
    /**
3971
     * @hidden @internal
3972
     */
3973
    public resetHorizontalVirtualization() {
3974
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
49,255✔
3975
        this._horizontalForOfs = [
6,704✔
3976
            ...this._dataRowList.filter(elementFilter).map(item => item.virtDirRow),
46,122✔
3977
            ...this._summaryRowList.filter(elementFilter).map(item => item.virtDirRow)
3,133✔
3978
        ];
3979
    }
3980

3981
    /**
3982
     * @hidden @internal
3983
     */
3984
    public _setupRowObservers() {
3985
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
17,576✔
3986
        const extractForOfs = pipe(map((collection: any[]) => collection.filter(elementFilter).map(item => item.virtDirRow)));
17,576✔
3987
        const rowListObserver = extractForOfs(this._dataRowList.changes);
3,563✔
3988
        const summaryRowObserver = extractForOfs(this._summaryRowList.changes);
3,563✔
3989
        rowListObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,563✔
3990
            this.resetHorizontalVirtualization();
2,723✔
3991
        });
3992
        summaryRowObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,563✔
3993
            this.resetHorizontalVirtualization();
320✔
3994
        });
3995
        this.resetHorizontalVirtualization();
3,563✔
3996
    }
3997

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

4020
    /**
4021
     * @hidden
4022
     */
4023
    public ngAfterViewInit() {
4024
        this.initPinning();
3,563✔
4025
        this.calculateGridSizes();
3,563✔
4026
        this._init = false;
3,563✔
4027
        this.cdr.reattach();
3,563✔
4028
        this._setupRowObservers();
3,563✔
4029
        this._zoneBegoneListeners();
3,563✔
4030

4031
        const vertScrDC = this.verticalScrollContainer.displayContainer;
3,563✔
4032
        vertScrDC.addEventListener('scroll', this.preventContainerScroll.bind(this));
3,563✔
4033

4034
        this._pinnedRowList.changes
3,563✔
4035
            .pipe(takeUntil(this.destroy$))
4036
            .subscribe((change: QueryList<IgxGridRowComponent>) => {
4037
                this.onPinnedRowsChanged(change);
182✔
4038
            });
4039

4040
        this.addRowSnackbar?.clicked.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,563✔
4041
            const rec = this.filteredSortedData[this.lastAddedRowIndex];
4✔
4042
            this.scrollTo(rec, 0);
4✔
4043
            this.addRowSnackbar.close();
4✔
4044
        });
4045

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

4059
    }
4060

4061
    /**
4062
     * @hidden @internal
4063
     */
4064
    public notifyChanges(repaint = false) {
14,909✔
4065
        this._cdrRequests = true;
488,811✔
4066
        this._cdrRequestRepaint = repaint;
488,811✔
4067
        this.cdr.markForCheck();
488,811✔
4068
    }
4069

4070
    /**
4071
     * @hidden @internal
4072
     */
4073
    public ngDoCheck() {
4074
        if (this._init) {
17,558✔
4075
            return;
4,110✔
4076
        }
4077

4078
        if (this._cdrRequestRepaint) {
13,448✔
4079
            this.resetNotifyChanges();
3,938✔
4080
            this.calculateGridSizes();
3,938✔
4081
            this.refreshSearch(true);
3,938✔
4082
            return;
3,938✔
4083
        }
4084

4085
        if (this._cdrRequests) {
9,510✔
4086
            this.resetNotifyChanges();
4,191✔
4087
            this.cdr.detectChanges();
4,191✔
4088
        }
4089
    }
4090

4091
    /**
4092
     * @hidden
4093
     * @internal
4094
     */
4095
    public getDragGhostCustomTemplate() {
4096

4097
        return this.dragGhostCustomTemplate;
1,979✔
4098
    }
4099

4100
    /**
4101
     * @hidden @internal
4102
     */
4103
    public ngOnDestroy() {
4104
        this.tmpOutlets.forEach((tmplOutlet) => {
3,390✔
4105
            tmplOutlet.cleanCache();
24,937✔
4106
        });
4107

4108
        this.destroy$.next(true);
3,390✔
4109
        this.destroy$.complete();
3,390✔
4110
        this.transactionChange$.next();
3,390✔
4111
        this.transactionChange$.complete();
3,390✔
4112
        this._destroyed = true;
3,390✔
4113

4114
        this.textHighlightService.destroyGroup(this.id);
3,390✔
4115

4116
        if (this._advancedFilteringOverlayId) {
3,390!
4117
            this.overlayService.detach(this._advancedFilteringOverlayId);
×
4118
            delete this._advancedFilteringOverlayId;
×
4119
        }
4120

4121
        this.overlayIDs.forEach(overlayID => {
3,390✔
4122
            const overlay = this.overlayService.getOverlayById(overlayID);
23✔
4123

4124
            if (overlay && !overlay.detached) {
23✔
4125
                this.overlayService.detach(overlayID);
13✔
4126
            }
4127
        });
4128

4129

4130
        this.zone.runOutsideAngular(() => {
3,390✔
4131
            this.verticalScrollContainer?.getScroll()?.removeEventListener('scroll', this.verticalScrollHandler);
3,390✔
4132
            this.headerContainer?.getScroll()?.removeEventListener('scroll', this.horizontalScrollHandler);
3,390✔
4133
            const vertScrDC = this.verticalScrollContainer?.displayContainer;
3,390✔
4134
            vertScrDC?.removeEventListener('scroll', this.preventContainerScroll);
3,390✔
4135
        });
4136
    }
4137

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

4152
        if (!col) {
×
4153
            return;
×
4154
        }
4155
        col.toggleVisibility(args.newValue);
×
4156
    }
4157

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

4176
    /* blazorSuppress */
4177
    public set expansionStates(value) {
4178
        this._expansionStates = new Map<any, boolean>(value);
690✔
4179
        this.expansionStatesChange.emit(this._expansionStates);
690✔
4180
        this.notifyChanges(true);
690✔
4181
        if (this.gridAPI.grid) {
690✔
4182
            this.cdr.detectChanges();
597✔
4183
        }
4184
    }
4185

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

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

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

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

4242

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

4260
    /**
4261
     * @hidden
4262
     * @internal
4263
     */
4264
    public getDefaultExpandState(_rec: any) {
4265
        return this._defaultExpandState;
9,575✔
4266
    }
4267

4268
    /**
4269
     * Gets the native element.
4270
     *
4271
     * @example
4272
     * ```typescript
4273
     * const nativeEl = this.grid.nativeElement.
4274
     * ```
4275
     */
4276
    public get nativeElement() {
4277
        return this.elementRef.nativeElement;
41,808✔
4278
    }
4279

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

4291
    public set outlet(val: IgxOverlayOutletDirective) {
4292
        this._userOutletDirective = val;
×
4293
    }
4294

4295

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

4308
    /**
4309
     * @hidden @internal
4310
     */
4311
    public get defaultSummaryHeight(): number {
4312
        switch (this.gridSize) {
15,078✔
4313
            case Size.Medium:
4314
                return 30;
29✔
4315
            case Size.Small:
4316
                return 24;
123✔
4317
            default:
4318
                return 36;
14,926✔
4319
        }
4320
    }
4321

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

4340
    /** @hidden @internal */
4341
    public get pinnedWidth() {
4342
        if (!isNaN(this._pinnedWidth)) {
222,483✔
4343
            return this._pinnedWidth;
201,494✔
4344
        }
4345
        this._pinnedWidth = this.getPinnedWidth();
20,989✔
4346
        return this._pinnedWidth;
20,989✔
4347
    }
4348

4349
    /** @hidden @internal */
4350
    public get unpinnedWidth() {
4351
        if (!isNaN(this._unpinnedWidth)) {
367,394✔
4352
            return this._unpinnedWidth;
344,049✔
4353
        }
4354
        this._unpinnedWidth = this.getUnpinnedWidth();
23,345✔
4355
        return this._unpinnedWidth;
23,345✔
4356
    }
4357

4358
    /**
4359
     * @hidden @internal
4360
     */
4361
    public isHorizontalScrollHidden = false;
4,070✔
4362

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

4373
        const headerStyle = this.document.defaultView.getComputedStyle(element);
35✔
4374
        const headerPadding = parseFloat(headerStyle.paddingLeft) + parseFloat(headerStyle.paddingRight) +
35✔
4375
            parseFloat(headerStyle.borderRightWidth);
4376

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

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

4402
    /**
4403
     * @hidden @internal
4404
     */
4405
    public get summariesMargin() {
4406
        return this.featureColumnsWidth();
21,835✔
4407
    }
4408

4409
    /**
4410
     * Gets an array of `IgxColumnComponent`s.
4411
     *
4412
     * @example
4413
     * ```typescript
4414
     * const colums = this.grid.columns.
4415
     * ```
4416
     */
4417
    public get columns(): IgxColumnComponent[] {
4418
        return this._columns || [];
37,949!
4419
    }
4420

4421
    /**
4422
     * Gets an array of the pinned `IgxColumnComponent`s.
4423
     *
4424
     * @example
4425
     * ```typescript
4426
     * const pinnedColumns = this.grid.pinnedColumns.
4427
     * ```
4428
     */
4429
    public get pinnedColumns(): IgxColumnComponent[] {
4430
        if (this._pinnedVisible.length) {
4,093,200✔
4431
            return this._pinnedVisible;
332,957✔
4432
        }
4433
        this._pinnedVisible = this._pinnedColumns.filter(col => !col.hidden);
3,760,243✔
4434
        return this._pinnedVisible;
3,760,243✔
4435
    }
4436

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

4450
    /**
4451
     * Gets an array of unpinned `IgxColumnComponent`s.
4452
     *
4453
     * @example
4454
     * ```typescript
4455
     * const unpinnedColumns = this.grid.unpinnedColumns.
4456
     * ```
4457
     */
4458
    public get unpinnedColumns(): IgxColumnComponent[] {
4459
        if (this._unpinnedVisible.length) {
759,340✔
4460
            return this._unpinnedVisible;
732,502✔
4461
        }
4462
        this._unpinnedVisible = this._unpinnedColumns.filter((col) => !col.hidden);
155,562✔
4463
        return this._unpinnedVisible;
26,838✔
4464
    }
4465

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

4475
    /**
4476
     * Returns the `IgxColumnComponent` by field name.
4477
     *
4478
     * @example
4479
     * ```typescript
4480
     * const myCol = this.grid1.getColumnByName("ID");
4481
     * ```
4482
     * @param name
4483
     */
4484
    public getColumnByName(name: string): IgxColumnComponent {
4485
        return this._columns.find((col) => col.field === name);
133,219✔
4486
    }
4487

4488
    public getColumnByVisibleIndex(index: number): IgxColumnComponent {
4489
        return this.visibleColumns.find((col) =>
1,963✔
4490
            !col.columnGroup && !col.columnLayout &&
11,563✔
4491
            col.visibleIndex === index
4492
        );
4493
    }
4494

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

4513
    /**
4514
     * Returns an array of visible `IgxColumnComponent`s.
4515
     *
4516
     * @example
4517
     * ```typescript
4518
     * const visibleColumns = this.grid.visibleColumns.
4519
     * ```
4520
     */
4521
    public get visibleColumns(): IgxColumnComponent[] {
4522
        if (this._visibleColumns.length) {
161,214✔
4523
            return this._visibleColumns;
134,668✔
4524
        }
4525
        this._visibleColumns = this._columns.filter(c => !c.hidden);
151,295✔
4526
        return this._visibleColumns;
26,546✔
4527
    }
4528

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

4544
    public set totalRecords(total: number) {
4545
        if (total >= 0) {
1✔
4546
            if (this.paginator) {
1✔
4547
                this.paginator.totalRecords = total;
1✔
4548
            }
4549
            this._totalRecords = total;
1✔
4550
            this.pipeTrigger++;
1✔
4551
            this.notifyChanges();
1✔
4552
        }
4553
    }
4554

4555
    /** @hidden @internal */
4556
    public get totalWidth(): number {
4557
        if (!isNaN(this._totalWidth)) {
11,246✔
4558
            return this._totalWidth;
3,187✔
4559
        }
4560
        // Take only top level columns
4561
        const cols = this.visibleColumns.filter(col => col.level === 0 && !col.pinned);
49,743✔
4562
        let totalWidth = 0;
8,059✔
4563
        let i = 0;
8,059✔
4564
        for (i; i < cols.length; i++) {
8,059✔
4565
            totalWidth += parseFloat(cols[i].calcWidth) || 0;
39,818!
4566
        }
4567
        this._totalWidth = totalWidth;
8,059✔
4568
        return totalWidth;
8,059✔
4569
    }
4570

4571
    /**
4572
     * @hidden
4573
     * @internal
4574
     */
4575
    public get showRowSelectors(): boolean {
4576
        return this.isRowSelectable && this.hasVisibleColumns && !this.hideRowSelectors;
277,389✔
4577
    }
4578

4579
    /**
4580
     * @hidden
4581
     * @internal
4582
     */
4583
    public get showAddButton() {
4584
        return this.rowEditable && this.dataView.length === 0 && this._columns.length > 0;
911✔
4585
    }
4586

4587
    /**
4588
     * @hidden
4589
     * @internal
4590
     */
4591
    public get showDragIcons(): boolean {
4592
        return this.rowDraggable && this._columns.length > this.hiddenColumnsCount;
×
4593
    }
4594

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

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

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

4633
        this.columnMovingEnd.emit(eventArgs);
150✔
4634

4635
        if (eventArgs.cancel) {
150!
UNCOV
4636
            return;
×
4637
        }
4638

4639
        if (column === target || (column.level !== target.level) ||
150✔
4640
            (column.topLevelParent !== target.topLevelParent)) {
4641
            return;
22✔
4642
        }
4643

4644
        if (column.level) {
128✔
4645
            this._moveChildColumns(column.parent, column, target, pos);
16✔
4646
        }
4647

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

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

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

4667
        // if (!target.pinned && !column.pinned && !columnPinStateChanged) {
4668
        //     this._reorderColumns(column, target, pos, this._unpinnedColumns);
4669
        // }
4670

4671
        this._moveColumns(column, target, pos);
128✔
4672
        this._columnsReordered(column);
128✔
4673
    }
4674

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

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

4721
        this.pipeTrigger++;
171✔
4722
        this.rowAddedNotifier.next({ data: data, rowData: data, owner: this, primaryKey: data[this.primaryKey], rowKey: data[this.primaryKey] });
171✔
4723
        this.notifyChanges();
171✔
4724
    }
4725

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

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

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

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

4803
                const id = {
18✔
4804
                    rowID: rowSelector,
4805
                    columnID: col.index,
4806
                    rowIndex: index
4807
                };
4808

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

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

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

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

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

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

4902
        const eventArgs: ISortingEventArgs = { owner: this, sortingExpressions: sortingState, cancel: false };
159✔
4903
        this.sorting.emit(eventArgs);
159✔
4904

4905
        if (eventArgs.cancel) {
159!
4906
            return;
×
4907
        }
4908

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

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

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

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

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

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

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

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

5047
    // TODO: We have return values here. Move them to event args ??
5048

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

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

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

5101
        if (eventArgs.cancel) {
133✔
5102
            return;
1✔
5103
        }
5104
        this.crudService.endEdit(false);
132✔
5105

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

5114
        return true;
132✔
5115
    }
5116

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

5135
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, null, row, false);
24✔
5136
        this.rowPinning.emit(eventArgs);
24✔
5137

5138
        if (eventArgs.cancel) {
24✔
5139
            return;
1✔
5140
        }
5141

5142
        this.crudService.endEdit(false);
23✔
5143
        this._pinnedRecordIDs.splice(index, 1);
23✔
5144
        this.pipeTrigger++;
23✔
5145
        if (this.gridAPI.grid) {
23✔
5146
            this.cdr.detectChanges();
23✔
5147
            this.rowPinned.emit(eventArgs);
23✔
5148
        }
5149

5150
        return true;
23✔
5151
    }
5152

5153
    /** @hidden @internal */
5154
    public get pinnedRowHeight() {
5155
        const containerHeight = this.pinContainer ? this.pinContainer.nativeElement.offsetHeight : 0;
81,824✔
5156
        return this.hasPinnedRecords ? containerHeight : 0;
81,824✔
5157
    }
5158

5159
    /** @hidden @internal */
5160
    public get totalHeight() {
5161
        return this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
43,100✔
5162
    }
5163

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

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

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

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

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

5239
            return this.find(this._lastSearchInfo.searchText,
127✔
5240
                0,
5241
                this._lastSearchInfo.caseSensitive,
5242
                this._lastSearchInfo.exactMatch,
5243
                false,
5244
                endEdit);
5245
        } else {
5246
            return 0;
9,824✔
5247
        }
5248
    }
5249

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

5269
        this.rowList.forEach((row) => {
1✔
5270
            if (row.cells) {
10✔
5271
                row.cells.forEach((c: IgxGridCellComponent) => {
10✔
5272
                    c.clearHighlight();
40✔
5273
                });
5274
            }
5275
        });
5276
    }
5277

5278
    /** @hidden @internal */
5279
    public get hasEditableColumns(): boolean {
5280
        return this._columns.some((col) => col.editable);
6✔
5281
    }
5282

5283
    /** @hidden @internal */
5284
    public get hasSummarizedColumns(): boolean {
5285
        const summarizedColumns = this._columns.filter(col => col.hasSummary && !col.hidden);
2,097,208✔
5286
        return summarizedColumns.length > 0;
331,749✔
5287
    }
5288

5289
    /**
5290
     * @hidden @internal
5291
     */
5292
    public get rootSummariesEnabled(): boolean {
5293
        return this.summaryCalculationMode !== GridSummaryCalculationMode.childLevelsOnly;
242,629✔
5294
    }
5295

5296
    /**
5297
     * @hidden @internal
5298
     */
5299
    public get hasVisibleColumns(): boolean {
5300
        if (this._hasVisibleColumns === undefined) {
77,611✔
5301
            return this._columns ? this._columns.some(c => !c.hidden) : false;
78,756!
5302
        }
5303
        return this._hasVisibleColumns;
×
5304
    }
5305

5306
    public set hasVisibleColumns(value) {
5307
        this._hasVisibleColumns = value;
34,041✔
5308
    }
5309

5310
    /** @hidden @internal */
5311
    public get hasMovableColumns(): boolean {
5312
        return this.moving;
×
5313
    }
5314

5315
    /** @hidden @internal */
5316
    public get hasColumnGroups(): boolean {
5317
        return this._columnGroups;
859✔
5318
    }
5319

5320
    /** @hidden @internal */
5321
    public get hasColumnLayouts() {
5322
        return !!this._columns.some(col => col.columnLayout);
21,427,378✔
5323
    }
5324

5325

5326
    /**
5327
     * @hidden @internal
5328
     */
5329
    public get multiRowLayoutRowSize() {
5330
        return this._multiRowLayoutRowSize;
20,000✔
5331
    }
5332

5333
    /**
5334
     * @hidden
5335
     */
5336
    protected get rowBasedHeight() {
5337
        return this.dataLength * this.rowHeight;
×
5338
    }
5339

5340
    /**
5341
     * @hidden
5342
     */
5343
    protected get isPercentWidth() {
5344
        return this.width && this.width.indexOf('%') !== -1;
41,809✔
5345
    }
5346

5347
    protected get shouldResize(): boolean {
5348
        return this._gridSize !== this.gridSize;
1,749✔
5349
    }
5350

5351
    /**
5352
     * @hidden @internal
5353
     */
5354
    public get isPercentHeight() {
5355
        return this._height && this._height.indexOf('%') !== -1;
15,727✔
5356
    }
5357

5358
    /**
5359
     * @hidden
5360
     */
5361
    protected get defaultTargetBodyHeight(): number {
5362
        const allItems = this.dataLength;
325✔
5363
        return this.renderedActualRowHeight * Math.min(this._defaultTargetRecordNumber,
325✔
5364
            this.paginator ? Math.min(allItems, this.paginator.perPage) : allItems);
325✔
5365
    }
5366

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

5375
    /**
5376
     * @hidden @internal
5377
     */
5378
    public get outerWidth() {
5379
        return this.hasVerticalScroll() ? this.calcWidth + this.scrollSize : this.calcWidth;
3,858✔
5380
    }
5381

5382
    /**
5383
     * @hidden @internal
5384
     * Gets the size of the grid
5385
     */
5386
    public get gridSize(): Size {
5387
        return this.gridComputedStyles?.getPropertyValue('--component-size') || Size.Large;
796,762✔
5388
    }
5389

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

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

5414
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
681,495✔
5415

5416

5417
        // Column layouts related
5418
        let visibleCols = [];
72,474✔
5419
        const columnBlocks = this.visibleColumns.filter(c => c.columnGroup);
681,495✔
5420
        const colsPerBlock = columnBlocks.map(block => block.getInitialChildColumnSizes(block.children));
122,272✔
5421
        const combinedBlocksSize = colsPerBlock.reduce((acc, item) => acc + item.length, 0);
122,272✔
5422
        colsPerBlock.forEach(blockCols => visibleCols = visibleCols.concat(blockCols));
122,272✔
5423
        //
5424

5425
        const columnsWithSetWidths = this.hasColumnLayouts ?
72,474✔
5426
            visibleCols.filter(c => c.widthSetByUser) :
164,920✔
5427
            visibleChildColumns.filter(c => c.widthSetByUser && c.width !== 'fit-content');
296,214✔
5428

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

5445
        // When all columns are hidden, return 0px width
5446
        if (!sumExistingWidths && !columnsToSize) {
72,474✔
5447
            return '0px';
1,474✔
5448
        }
5449
        computedWidth -= this.featureColumnsWidth();
71,000✔
5450

5451
        const columnWidth = !Number.isFinite(sumExistingWidths) ?
71,000!
5452
            Math.max(computedWidth / columnsToSize, this.minColumnWidth) :
5453
            Math.max((computedWidth - sumExistingWidths) / columnsToSize, this.minColumnWidth);
5454

5455
        return columnWidth + 'px';
71,000✔
5456
    }
5457

5458
    /**
5459
     * @hidden @internal
5460
     */
5461
    public hasVerticalScroll() {
5462
        if (this._init) {
187,142✔
5463
            return false;
82,665✔
5464
        }
5465
        const isScrollable = this.verticalScrollContainer ? this.verticalScrollContainer.isScrollable() : false;
104,477✔
5466
        return !!(this.calcWidth && this.dataView && this.dataView.length > 0 && isScrollable);
104,477✔
5467
    }
5468

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

5490
        return sum;
44,334✔
5491
    }
5492

5493
    /**
5494
     * @hidden @internal
5495
     */
5496
    public isColumnGrouped(_fieldName: string): boolean {
5497
        return false;
×
5498
    }
5499

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

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

5523
        return this.selectionService.areAllRowSelected() ? 'Deselect all' : 'Select all';
3,058✔
5524
    }
5525

5526
    /**
5527
     * @hidden
5528
     * @internal
5529
     */
5530
    public get totalRowsCountAfterFilter() {
5531
        if (this.data) {
3,335✔
5532
            return this.selectionService.allData.length;
3,335✔
5533
        }
5534

5535
        return 0;
×
5536
    }
5537

5538
    /** @hidden @internal */
5539
    public get pinnedDataView(): any[] {
5540
        return this.pinnedRecords ? this.pinnedRecords : [];
261,941✔
5541
    }
5542

5543
    /** @hidden @internal */
5544
    public get unpinnedDataView(): any[] {
5545
        return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer?.igxForOf || [];
15,605✔
5546
    }
5547

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

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

5573
    public set selectRowOnClick(enabled: boolean) {
5574
        this._selectRowOnClick = enabled;
13✔
5575
    }
5576

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

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

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

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

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

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

5666
        horizontal.scrollLeft += left * this.DRAG_SCROLL_DELTA;
1✔
5667
        vertical.scrollTop += top * this.DRAG_SCROLL_DELTA;
1✔
5668
    }
5669

5670
    /**
5671
     * @hidden @internal
5672
     */
5673
    public isDefined(arg: any): boolean {
5674
        return arg !== undefined && arg !== null;
67,052✔
5675
    }
5676

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

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

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

5711
        this.selectionService.pointerState.node = startNode;
166✔
5712
        this.selectionService.selectRange(endNode, this.selectionService.pointerState);
166✔
5713
        this.selectionService.addRangeMeta(endNode, this.selectionService.pointerState);
166✔
5714
        this.selectionService.initPointerState();
166✔
5715
    }
5716

5717
    /**
5718
     * Get the currently selected ranges in the grid.
5719
     */
5720
    public getSelectedRanges(): GridSelectionRange[] {
5721
        return this.selectionService.ranges;
348✔
5722
    }
5723

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

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

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

5776
        this.selectionService.selectColumnsWithNoEvent(fieldToSelect, clearCurrentSelection);
12✔
5777
        this.notifyChanges();
12✔
5778
    }
5779

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

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

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

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

5845

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

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

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

5877
        const selectedColumns = this.gridAPI.grid.selectedColumns();
12✔
5878
        const columnData = this.getSelectedColumnsData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
5879
        let selectedData;
5880
        if (event.type === 'copy') {
12✔
5881
            selectedData = this.getSelectedData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
5882
        }
5883

5884
        let data = [];
12✔
5885
        let result;
5886

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

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

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

5924
        if (ev.cancel) {
10✔
5925
            return;
1✔
5926
        }
5927

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

5931
        if (!this.clipboardOptions.copyHeaders) {
9✔
5932
            result = result.substring(result.indexOf('\n') + 1);
2✔
5933
        }
5934

5935
        if (data && data.length > 0 && Object.values(data[0]).length === 1) {
9✔
5936
            result = result.slice(0, -2);
4✔
5937
        }
5938

5939
        event.preventDefault();
9✔
5940

5941
        /* Necessary for the hiearachical case but will probably have to
5942
           change how getSelectedData is propagated in the hiearachical grid
5943
        */
5944
        event.stopPropagation();
9✔
5945

5946
        return result;
9✔
5947
    }
5948

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

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

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

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

6077
    /**
6078
     * @hidden
6079
     * @internal
6080
     */
6081
    public endRowEditTabStop(commit = true, event?: Event) {
×
6082
        const canceled = this.crudService.endEdit(commit, event);
15✔
6083

6084
        if (canceled) {
15✔
6085
            return true;
3✔
6086
        }
6087

6088
        this.navigation.restoreActiveNodeFocus();
12✔
6089
    }
6090

6091
    /**
6092
     * @hidden @internal
6093
     */
6094
    public trackColumnChanges(index, col) {
6095
        return col.field + col._calcWidth;
1,695,440✔
6096
    }
6097

6098
    /**
6099
     * @hidden
6100
     */
6101
    public isExpandedGroup(_group: IGroupByRecord): boolean {
6102
        return undefined;
×
6103
    }
6104

6105
    /**
6106
     * @hidden @internal
6107
     * TODO: MOVE to CRUD
6108
     */
6109
    public openRowOverlay(id) {
6110
        this.configureRowEditingOverlay(id, this.rowList.length <= MIN_ROW_EDITING_COUNT_THRESHOLD);
208✔
6111

6112
        this.rowEditingOverlay.open(this.rowEditSettings);
208✔
6113
        this.rowEditingOverlay.element.addEventListener('wheel', this.rowEditingWheelHandler.bind(this));
208✔
6114
    }
6115

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

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

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

6154
    /**
6155
     * @hidden @internal
6156
     */
6157
    public cachedViewLoaded(args: ICachedViewLoadedEventArgs) {
6158
        if (this.hasHorizontalScroll()) {
632✔
6159
            const tmplId = args.context.templateID.type;
447✔
6160
            const index = args.context.index;
447✔
6161
            args.view.detectChanges();
447✔
6162
            this.zone.onStable.pipe(first()).subscribe(() => {
447✔
6163
                const row = tmplId === 'dataRow' ? this.gridAPI.get_row_by_index(index) : null;
447✔
6164
                const summaryRow = tmplId === 'summaryRow' ? this.summariesRowList.find((sr) => sr.dataRowIndex === index) : null;
447✔
6165
                if (row && row instanceof IgxRowDirective) {
447✔
6166
                    this._restoreVirtState(row);
268✔
6167
                } else if (summaryRow) {
179✔
6168
                    this._restoreVirtState(summaryRow);
44✔
6169
                }
6170
            });
6171
        }
6172
    }
6173

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

6184
            this._advancedFilteringOverlayId = this.overlayService.attach(
41✔
6185
                IgxAdvancedFilteringDialogComponent,
6186
                this.viewRef,
6187
                settings);
6188
            this.overlayService.show(this._advancedFilteringOverlayId);
41✔
6189
        }
6190
    }
6191

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

6202
            if (applyChanges) {
3✔
6203
                advancedFilteringDialog.applyChanges();
1✔
6204
            }
6205
            advancedFilteringDialog.closeDialog();
3✔
6206
        }
6207
    }
6208

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

6220
    /**
6221
     * @hidden @internal
6222
     */
6223
    public hasHorizontalScroll() {
6224
        return this.totalWidth - this.unpinnedWidth > 0 && this.width !== null;
11,246✔
6225
    }
6226

6227
    /**
6228
     * @hidden @internal
6229
     */
6230
    public isSummaryRow(rowData): boolean {
6231
        return rowData && rowData.summaries && (rowData.summaries instanceof Map);
435,002✔
6232
    }
6233

6234
    /**
6235
     * @hidden @internal
6236
     */
6237
    public triggerPipes() {
6238
        this.pipeTrigger++;
129✔
6239
        this.cdr.detectChanges();
129✔
6240
    }
6241

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

6253
    /**
6254
     * @hidden
6255
     */
6256
    public getUnpinnedIndexById(id) {
6257
        return this.unpinnedRecords.findIndex(x => x[this.primaryKey] === id);
102✔
6258
    }
6259

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

6278
        const success = this.crudService.endEdit(commit, event);
6✔
6279

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

6288
        return success;
6✔
6289
    }
6290

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

6324
        this._addRowForIndex(index, asChild);
4✔
6325
    }
6326

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

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

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

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

6393
    protected switchTransactionService(val: boolean) {
6394
        if (val) {
244✔
6395
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);
243✔
6396
        } else {
6397
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
1✔
6398
        }
6399

6400
        if (this.dataCloneStrategy) {
244✔
6401
            this._transactions.cloneStrategy = this.dataCloneStrategy;
244✔
6402
        }
6403
    }
6404

6405
    protected subscribeToTransactions(): void {
6406
        this.transactionChange$.next();
3,794✔
6407
        this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$)))
3,794✔
6408
            .subscribe(this.transactionStatusUpdate.bind(this));
6409
    }
6410

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

6441
            });
6442
        }
6443

6444
        this.selectionService.clearHeaderCBState();
324✔
6445
        this.summaryService.clearSummaryCache();
324✔
6446
        this.pipeTrigger++;
324✔
6447
        this.notifyChanges();
324✔
6448
    }
6449

6450
    protected writeToData(rowIndex: number, value: any) {
6451
        mergeObjects(this.gridAPI.get_all_data()[rowIndex], value);
×
6452
    }
6453

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

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

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

6492
    /**
6493
     * @hidden
6494
     * Sets grid width i.e. this.calcWidth
6495
     */
6496
    protected calculateGridWidth() {
6497
        let width;
6498

6499
        if (this.isPercentWidth) {
11,750✔
6500
            /* width in %*/
6501
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width');
5,400✔
6502
            width = computed.indexOf('%') === -1 ? parseFloat(computed) : null;
5,400✔
6503
        } else {
6504
            width = parseInt(this.width, 10);
6,350✔
6505
        }
6506

6507
        if (!width && this.nativeElement) {
11,750✔
6508
            width = this.nativeElement.offsetWidth;
41✔
6509
        }
6510

6511

6512
        if (this.width === null || !width) {
11,750✔
6513
            this.isColumnWidthSum = true;
41✔
6514
            width = this.getColumnWidthSum();
41✔
6515
        } else {
6516
            this.isColumnWidthSum = false;
11,709✔
6517
        }
6518

6519
        if (this.hasVerticalScroll() && this.width !== null) {
11,750✔
6520
            width -= this.scrollSize;
2,395✔
6521
        }
6522
        if ((Number.isFinite(width) || width === null) && width !== this.calcWidth) {
11,750!
6523
            this.calcWidth = width;
3,789✔
6524
        }
6525
        this._derivePossibleWidth();
11,750✔
6526
    }
6527

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

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

6564
        if (minWidth > parseFloat(width)) {
68,996!
6565
            width = String(column.minWidth);
×
6566
        } else if (maxWidth < parseFloat(width)) {
68,996✔
6567
            width = String(column.maxWidth);
1,114✔
6568
        }
6569

6570
        // if no px or % are defined in maxWidth/minWidth consider it px
6571
        if (width.indexOf('%') === -1 && width.indexOf('px') === -1) {
68,996✔
6572
            width += 'px';
276✔
6573
        }
6574
        return width;
68,996✔
6575
    }
6576

6577
    protected resetNotifyChanges() {
6578
        this._cdrRequestRepaint = false;
8,129✔
6579
        this._cdrRequests = false;
8,129✔
6580
    }
6581

6582
    /** @hidden @internal */
6583
    public resolveOutlet() {
6584
        return this._userOutletDirective ? this._userOutletDirective : this._outletDirective;
237,439!
6585
    }
6586

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

6600

6601
    /**
6602
     * Update internal column's collection.
6603
     * @hidden
6604
     */
6605
    public updateColumns(newColumns: IgxColumnComponent[]) {
6606
        // update internal collections to retain order.
6607
        this._pinnedColumns = newColumns
5,707✔
6608
            .filter((c) => c.pinned);
34,424✔
6609
        this._unpinnedColumns = newColumns.filter((c) => !c.pinned);
34,424✔
6610
        this._columns = newColumns;
5,707✔
6611
        if (this._columns && this._columns.length && this._filteringExpressionsTree) {
5,707✔
6612
            this._filteringExpressionsTree = recreateTreeFromFields(this._filteringExpressionsTree, this.columns) as IFilteringExpressionsTree;
5,276✔
6613
        }
6614
        if (this._columns && this._columns.length && this._advancedFilteringExpressionsTree) {
5,707✔
6615
            this._advancedFilteringExpressionsTree = recreateTreeFromFields(this._advancedFilteringExpressionsTree, this.columns) as IFilteringExpressionsTree;
6✔
6616
        }
6617
        this.resetCaches();
5,707✔
6618
    }
6619

6620
    /**
6621
     * @hidden
6622
     */
6623
    protected _resetColumnList(list?) {
6624
        if (!list) {
138!
6625
            list = this._columns;
×
6626
        }
6627
        let newList = [];
138✔
6628
        list.filter(c => c.level === 0).forEach(p => {
1,189✔
6629
            newList.push(p);
814✔
6630
            if (p.columnGroup) {
814✔
6631
                newList = newList.concat(p.allChildren);
138✔
6632
            }
6633
        });
6634
        return newList;
138✔
6635
    }
6636

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

6659
    /**
6660
     * Reorder column group collection.
6661
     *
6662
     * @hidden
6663
     */
6664
    protected _moveChildColumns(parent: IgxColumnComponent, from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6665
        const buffer = parent.children.toArray();
16✔
6666
        this._reorderColumns(from, to, pos, buffer, true);
16✔
6667
        parent.children.reset(buffer);
16✔
6668
    }
6669

6670
    /**
6671
     * @hidden @internal
6672
     */
6673
    protected setupColumns() {
6674
        if (this.autoGenerate) {
3,353✔
6675
            this.autogenerateColumns();
907✔
6676
        } else {
6677
            this._columns = this.getColumnList();
2,446✔
6678
        }
6679
        if (this._columns && this._columns.length && this._filteringExpressionsTree) {
3,353✔
6680
            this._filteringExpressionsTree = recreateTreeFromFields(this._filteringExpressionsTree, this._columns) as IFilteringExpressionsTree;
3,297✔
6681
        }
6682
        if (this._columns && this._columns.length && this._advancedFilteringExpressionsTree) {
3,353✔
6683
            this._advancedFilteringExpressionsTree = recreateTreeFromFields(this._advancedFilteringExpressionsTree, this._columns) as IFilteringExpressionsTree;
5✔
6684
        }
6685

6686
        this.initColumns(this._columns, (col: IgxColumnComponent) => this.columnInit.emit(col));
21,594✔
6687
        this.columnListDiffer.diff(this.columnList);
3,353✔
6688

6689
        this.columnList.changes
3,353✔
6690
            .pipe(takeUntil(this.destroy$))
6691
            .subscribe((change: QueryList<IgxColumnComponent>) => {
6692
                this.onColumnsChanged(change);
74✔
6693
            });
6694
    }
6695

6696
    protected getColumnList() {
6697
        return this.columnList.toArray();
2,257✔
6698
    }
6699

6700
    /**
6701
     * @hidden
6702
     */
6703
    protected deleteRowFromData(rowID: any, index: number) {
6704
        //  if there is a row (index !== 0) delete it
6705
        //  if there is a row in ADD or UPDATE state change it's state to DELETE
6706
        if (index !== -1) {
×
6707
            if (this.transactions.enabled) {
×
6708
                const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null };
×
6709
                this.transactions.add(transaction, this.data[index]);
×
6710
            } else {
6711
                this.data.splice(index, 1);
×
6712
            }
6713
        } else {
6714
            const state: State = this.transactions.getState(rowID);
×
6715
            this.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef);
×
6716
        }
6717
    }
6718

6719

6720
    /**
6721
     * @hidden @internal
6722
     */
6723
    protected getDataBasedBodyHeight(): number {
6724
        return !this.data || (this.data.length < this._defaultTargetRecordNumber) ?
1,057✔
6725
            0 : this.defaultTargetBodyHeight;
6726
    }
6727

6728
    /**
6729
     * @hidden @internal
6730
     */
6731
    protected onPinnedRowsChanged(change: QueryList<IgxGridRowComponent>) {
6732
        const diff = this.rowListDiffer.diff(change);
182✔
6733
        if (diff) {
182✔
6734
            this.notifyChanges(true);
182✔
6735
        }
6736
    }
6737

6738
    /**
6739
     * @hidden
6740
     */
6741
    protected onColumnsChanged(change: QueryList<IgxColumnComponent>) {
6742
        const diff = this.columnListDiffer.diff(change);
63✔
6743

6744
        if (this.autoGenerate && this._columns.length === 0 && this._autoGeneratedCols.length > 0) {
63!
6745
            // In Ivy if there are nested conditional templates the content children are re-evaluated
6746
            // hence autogenerated columns are cleared and need to be reset.
6747
            this.updateColumns(this._autoGeneratedCols);
×
6748
            return;
×
6749
        }
6750
        if (diff) {
63✔
6751
            let added = false;
63✔
6752
            let removed = false;
63✔
6753
            let pinning = false;
63✔
6754
            diff.forEachAddedItem((record: IterableChangeRecord<IgxColumnComponent>) => {
63✔
6755
                added = true;
1,031✔
6756
                if (record.item.pinned) {
1,031✔
6757
                    this._pinnedColumns.push(record.item);
1✔
6758
                    pinning = true;
1✔
6759
                } else {
6760
                    this._unpinnedColumns.push(record.item);
1,030✔
6761
                }
6762
            });
6763

6764
            this.initColumns(this.columnList.toArray(), (col: IgxColumnComponent) => this.columnInit.emit(col));
1,277✔
6765
            if (pinning) {
63✔
6766
                this.initPinning();
1✔
6767
            }
6768

6769
            diff.forEachRemovedItem((record: IterableChangeRecord<IgxColumnComponent | IgxColumnGroupComponent>) => {
63✔
6770
                const isColumnGroup = record.item instanceof IgxColumnGroupComponent;
130✔
6771
                if (!isColumnGroup) {
130✔
6772
                    // Clear Grouping
6773
                    this.gridAPI.clear_groupby(record.item.field);
127✔
6774

6775
                    // Clear Filtering
6776
                    this.filteringService.clear_filter(record.item.field);
127✔
6777

6778
                    // Close filter row
6779
                    if (this.filteringService.isFilterRowVisible
127!
6780
                        && this.filteringService.filteredColumn
6781
                        && this.filteringService.filteredColumn.field === record.item.field) {
6782
                        this.filteringRow.close();
×
6783
                    }
6784

6785
                    // Clear Sorting
6786
                    this.gridAPI.clear_sort(record.item.field);
127✔
6787

6788
                    // Remove column selection
6789
                    this.selectionService.deselectColumnsWithNoEvent([record.item.field]);
127✔
6790
                }
6791
                removed = true;
130✔
6792
            });
6793

6794
            this.resetCaches();
63✔
6795

6796
            if (added || removed) {
63✔
6797
                this.onColumnsAddedOrRemoved();
62✔
6798
            }
6799
        }
6800
    }
6801

6802
    protected checkPrimaryKeyField() {
6803
        if (this.primaryKey && this.data?.length && !(this.primaryKey in this.data[0])) {
4,644✔
6804
            console.warn(`Field "${this.primaryKey}" is not defined in the data. Set \`primaryKey\` to a valid field.`);
9✔
6805
        }
6806
    }
6807

6808
    /**
6809
     * @hidden @internal
6810
     */
6811
    protected onColumnsAddedOrRemoved() {
6812
        this.summaryService.clearSummaryCache();
62✔
6813
        Promise.resolve().then(() => {
62✔
6814
            // `onColumnsChanged` can be executed midway a current detectChange cycle and markForCheck will be ignored then.
6815
            // This ensures that we will wait for the current cycle to end so we can trigger a new one and ngDoCheck to fire.
6816
            this.notifyChanges(true);
62✔
6817
        });
6818
    }
6819

6820
    /**
6821
     * @hidden
6822
     */
6823
    protected calculateGridSizes(recalcFeatureWidth = true) {
7,349✔
6824
        /*
6825
            TODO: (R.K.) This layered lasagne should be refactored
6826
            ASAP. The reason I have to reset the caches so many times is because
6827
            after teach `detectChanges` call they are filled with invalid
6828
            state. Of course all of this happens midway through the grid
6829
            sizing process which of course, uses values from the caches, thus resulting
6830
            in a broken layout.
6831
        */
6832
        this.cdr.detectChanges();
7,842✔
6833
        this.resetCaches(recalcFeatureWidth);
7,842✔
6834
        const hasScroll = this.hasVerticalScroll();
7,842✔
6835
        const hasHScroll = !this.isHorizontalScrollHidden;
7,842✔
6836
        this.calculateGridWidth();
7,842✔
6837
        this.resetCaches(recalcFeatureWidth);
7,842✔
6838
        this.cdr.detectChanges();
7,842✔
6839
        this.calculateGridHeight();
7,842✔
6840

6841
        if (this.rowEditable) {
7,842✔
6842
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
860✔
6843
        }
6844

6845
        if (this.filteringService.isFilterRowVisible) {
7,842✔
6846
            this.filteringRow.resetChipsArea();
153✔
6847
        }
6848

6849
        this.cdr.detectChanges();
7,842✔
6850
        // in case scrollbar has appeared recalc to size correctly.
6851
        if (hasScroll !== this.hasVerticalScroll()) {
7,842✔
6852
            this.calculateGridWidth();
167✔
6853
            this.cdr.detectChanges();
167✔
6854
        }
6855

6856
        // in case horizontal scrollbar has appeared recalc to size correctly.
6857
        if (hasHScroll !== this.hasHorizontalScroll()) {
7,842✔
6858
            this.isHorizontalScrollHidden = !this.hasHorizontalScroll();
2,763✔
6859
            this.cdr.detectChanges();
2,763✔
6860
            this.calculateGridHeight();
2,763✔
6861
            this.cdr.detectChanges();
2,763✔
6862
        }
6863
        if (this.zone.isStable) {
7,842✔
6864
            this.zone.run(() => {
225✔
6865
                this._applyWidthHostBinding();
225✔
6866
                this.cdr.detectChanges();
225✔
6867
            });
6868
        } else {
6869
            this.zone.onStable.pipe(first()).subscribe(() => {
7,617✔
6870
                this.zone.run(() => {
7,617✔
6871
                    this._applyWidthHostBinding();
7,617✔
6872
                });
6873
            });
6874
        }
6875
        this.resetCaches(recalcFeatureWidth);
7,842✔
6876
        if (this.hasColumnsToAutosize) {
7,842✔
6877
            this.cdr.detectChanges();
18✔
6878
            this.zone.onStable.pipe(first()).subscribe(() => {
18✔
6879
                this._autoSizeColumnsNotify.next();
18✔
6880
            });
6881
        }
6882
    }
6883

6884
    /**
6885
     * @hidden
6886
     * Sets TBODY height i.e. this.calcHeight
6887
     */
6888
    protected calculateGridHeight() {
6889

6890
        this.calcHeight = this._calculateGridBodyHeight();
10,359✔
6891
        if (this.pinnedRowHeight && this.calcHeight) {
10,359✔
6892
            this.calcHeight -= this.pinnedRowHeight;
108✔
6893
        }
6894
    }
6895

6896
    /**
6897
     * @hidden
6898
     */
6899
    protected getGroupAreaHeight(): number {
6900
        return 0;
2,725✔
6901
    }
6902

6903
    /**
6904
     * @hidden
6905
     */
6906
    protected getComputedHeight(elem) {
6907
        return elem.offsetHeight ? parseFloat(this.document.defaultView.getComputedStyle(elem).getPropertyValue('height')) : 0;
36,806✔
6908
    }
6909
    /**
6910
     * @hidden
6911
     */
6912
    protected getFooterHeight(): number {
6913
        return this.summaryRowHeight || this.getComputedHeight(this.tfoot.nativeElement);
9,561✔
6914
    }
6915
    /**
6916
     * @hidden
6917
     */
6918
    protected getTheadRowHeight(): number {
6919
        // D.P.: Before CSS loads,theadRow computed height will be 'auto'->NaN, so use 0 fallback
6920
        const height = this.getComputedHeight(this.theadRow.nativeElement) || 0;
9,561✔
6921
        return (!this.allowFiltering || (this.allowFiltering && this.filterMode !== FilterMode.quickFilter)) ?
9,561✔
6922
            height - this.getFilterCellHeight() :
6923
            height;
6924
    }
6925

6926
    /**
6927
     * @hidden
6928
     */
6929
    protected getToolbarHeight(): number {
6930
        let toolbarHeight = 0;
9,561✔
6931
        if (this.toolbar.first) {
9,561✔
6932
            toolbarHeight = this.getComputedHeight(this.toolbar.first.nativeElement);
258✔
6933
        }
6934
        return toolbarHeight;
9,561✔
6935
    }
6936

6937
    /**
6938
     * @hidden
6939
     */
6940
    protected getPagingFooterHeight(): number {
6941
        let pagingHeight = 0;
9,561✔
6942
        if (this.footer) {
9,561✔
6943
            const height = this.getComputedHeight(this.footer.nativeElement);
9,561✔
6944
            pagingHeight = this.footer.nativeElement.firstElementChild ?
9,561✔
6945
                height : 0;
6946
        }
6947
        return pagingHeight;
9,561✔
6948
    }
6949

6950
    /**
6951
     * @hidden
6952
     */
6953
    protected getFilterCellHeight(): number {
6954
        const headerGroupNativeEl = (this.headerGroupsList.length !== 0) ?
7,898✔
6955
            this.headerGroupsList[0].nativeElement : null;
6956
        const filterCellNativeEl = (headerGroupNativeEl) ?
7,898✔
6957
            headerGroupNativeEl.querySelector('igx-grid-filtering-cell') as HTMLElement : null;
6958
        return (filterCellNativeEl) ? filterCellNativeEl.offsetHeight : 0;
7,898✔
6959
    }
6960

6961
    /**
6962
     * @hidden
6963
     */
6964
    protected _calculateGridBodyHeight(): number {
6965
        if (!this._height) {
10,359✔
6966
            return null;
798✔
6967
        }
6968
        const actualTheadRow = this.getTheadRowHeight();
9,561✔
6969
        const footerHeight = this.getFooterHeight();
9,561✔
6970
        const toolbarHeight = this.getToolbarHeight();
9,561✔
6971
        const pagingHeight = this.getPagingFooterHeight();
9,561✔
6972
        const groupAreaHeight = this.getGroupAreaHeight();
9,561✔
6973
        const scrHeight = this.getComputedHeight(this.scr.nativeElement);
9,561✔
6974
        const renderedHeight = toolbarHeight + actualTheadRow +
9,561✔
6975
            footerHeight + pagingHeight + groupAreaHeight +
6976
            scrHeight;
6977

6978
        let gridHeight = 0;
9,561✔
6979

6980
        if (this.isPercentHeight) {
9,561✔
6981
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('height');
2,036✔
6982
            const autoSize = this._shouldAutoSize(renderedHeight);
2,036✔
6983
            if (autoSize || computed.indexOf('%') !== -1) {
2,036✔
6984
                const bodyHeight = this.getDataBasedBodyHeight();
857✔
6985
                return bodyHeight > 0 ? bodyHeight : null;
857✔
6986
            }
6987
            gridHeight = parseFloat(computed);
1,179✔
6988
        } else {
6989
            gridHeight = parseInt(this._height, 10);
7,525✔
6990
        }
6991
        const height = Math.abs(gridHeight - renderedHeight);
8,704✔
6992

6993
        if (Math.round(height) === 0 || isNaN(gridHeight)) {
8,704✔
6994
            const bodyHeight = this.defaultTargetBodyHeight;
18✔
6995
            return bodyHeight > 0 ? bodyHeight : null;
18✔
6996
        }
6997
        return height;
8,686✔
6998
    }
6999

7000
    protected checkContainerSizeChange() {
7001
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
98!
7002
        const origHeight = parentElement.offsetHeight;
98✔
7003
        this.nativeElement.style.display = 'none';
98✔
7004
        const height = parentElement.offsetHeight;
98✔
7005
        this.nativeElement.style.display = '';
98✔
7006
        return origHeight !== height;
98✔
7007
    }
7008

7009
    protected _shouldAutoSize(renderedHeight) {
7010
        this.tbody.nativeElement.style.display = 'none';
1,294✔
7011
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
1,294✔
7012
        let res = !parentElement ||
1,294✔
7013
            parentElement.clientHeight === 0 ||
7014
            parentElement.clientHeight === renderedHeight;
7015
        if (parentElement && (res || this._autoSize)) {
1,294✔
7016
            // If grid causes the parent container to extend (for example when container is flex)
7017
            // we should always auto-size since the actual size of the container will continuously change as the grid renders elements.
7018
            this._autoSize = false;
98✔
7019
            res = this.checkContainerSizeChange();
98✔
7020
        }
7021
        this.tbody.nativeElement.style.display = '';
1,294✔
7022
        return res;
1,294✔
7023
    }
7024

7025
    /**
7026
     * @hidden
7027
     * Gets calculated width of the unpinned area
7028
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
7029
     */
7030
    protected getUnpinnedWidth(takeHidden = false) {
23,345✔
7031
        let width = this.isPercentWidth ?
23,345✔
7032
            this.calcWidth :
7033
            parseInt(this.width, 10) || parseInt(this.hostWidth, 10) || this.calcWidth;
13,120✔
7034
        if (this.hasVerticalScroll() && !this.isPercentWidth) {
23,345✔
7035
            width -= this.scrollSize;
4,305✔
7036
        }
7037
        if (!this.isPinningToStart) {
23,345✔
7038
            width -= this.featureColumnsWidth();
62✔
7039
        }
7040

7041
        return width - this.getPinnedWidth(takeHidden);
23,345✔
7042
    }
7043

7044
    /**
7045
     * @hidden
7046
     */
7047
    protected _summaries(fieldName: string, hasSummary: boolean, summaryOperand?: any) {
7048
        const column = this.gridAPI.get_column_by_name(fieldName);
27✔
7049
        if (column) {
27✔
7050
            column.hasSummary = hasSummary;
27✔
7051
            if (summaryOperand) {
27✔
7052
                if (this.rootSummariesEnabled) {
2✔
7053
                    this.summaryService.retriggerRootPipe++;
2✔
7054
                }
7055
                column.summaries = summaryOperand;
2✔
7056
            }
7057
        }
7058
    }
7059

7060
    /**
7061
     * @hidden
7062
     */
7063
    protected _multipleSummaries(expressions: ISummaryExpression[], hasSummary: boolean) {
7064
        expressions.forEach((element) => {
7✔
7065
            this._summaries(element.fieldName, hasSummary, element.customSummary);
9✔
7066
        });
7067
    }
7068
    /**
7069
     * @hidden
7070
     */
7071
    protected _disableMultipleSummaries(expressions) {
7072
        expressions.forEach((column) => {
5✔
7073
            const columnName = column && column.fieldName ? column.fieldName : column;
13✔
7074
            this._summaries(columnName, false);
13✔
7075
        });
7076
    }
7077

7078
    /**
7079
     * @hidden
7080
     */
7081
    public resolveDataTypes(rec) {
7082
        if (typeof rec === 'number') {
6,445✔
7083
            return GridColumnDataType.Number;
3,802✔
7084
        } else if (typeof rec === 'boolean') {
2,643✔
7085
            return GridColumnDataType.Boolean;
152✔
7086
        } else if (typeof rec === 'object' && rec instanceof Date) {
2,491✔
7087
            return GridColumnDataType.Date;
143✔
7088
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
2,348✔
7089
            return GridColumnDataType.Image;
1✔
7090
        }
7091
        return GridColumnDataType.String;
2,347✔
7092
    }
7093

7094
    /**
7095
     * @hidden
7096
     */
7097
    protected autogenerateColumns() {
7098
        const data = this.gridAPI.get_data();
663✔
7099
        const fields = this.generateDataFields(data);
663✔
7100
        const columns = [];
663✔
7101

7102
        fields.forEach((field) => {
663✔
7103
            const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
4,280✔
7104
            ref.instance.field = field;
4,280✔
7105
            ref.instance.dataType = this.resolveDataTypes(data[0][field]);
4,280✔
7106
            ref.changeDetectorRef.detectChanges();
4,280✔
7107
            columns.push(ref.instance);
4,280✔
7108
        });
7109
        this._autoGeneratedCols = columns;
663✔
7110

7111
        this.updateColumns(columns);
663✔
7112
        this.columnsAutogenerated.emit({ columns: this._autoGeneratedCols });
663✔
7113
    }
7114

7115
    protected generateDataFields(data: any[]): string[] {
7116
        return Object.keys(data && data.length !== 0 ? data[0] : [])
669✔
7117
            .filter(key => !this.autoGenerateExclude.includes(key));
4,557✔
7118
    }
7119

7120
    /**
7121
     * @hidden
7122
     */
7123
    protected initColumns(collection: IgxColumnComponent[], cb: (args: any) => void = null) {
×
7124
        this._columnGroups = collection.some(col => col.columnGroup);
17,527✔
7125
        if (this.hasColumnLayouts) {
3,615✔
7126
            // Set overall row layout size
7127
            collection.forEach((col) => {
142✔
7128
                if (col.columnLayout) {
1,371✔
7129
                    const layoutSize = col.children ?
285!
7130
                        col.children.reduce((acc, val) => Math.max(val.rowStart + val.gridRowSpan - 1, acc), 1) :
1,076✔
7131
                        1;
7132
                    this._multiRowLayoutRowSize = Math.max(layoutSize, this._multiRowLayoutRowSize);
285✔
7133
                }
7134
            });
7135
        }
7136
        if (this.hasColumnLayouts && this.hasColumnGroups) {
3,615✔
7137
            // invalid configuration - multi-row and column groups
7138
            // remove column groups
7139
            const columnLayoutColumns = collection.filter((col) => col.columnLayout || col.columnLayoutChild);
1,371✔
7140
            collection = columnLayoutColumns;
142✔
7141
        }
7142
        this._maxLevelHeaderDepth = null;
3,615✔
7143
        collection.forEach((column: IgxColumnComponent) => {
3,615✔
7144
            column.defaultWidth = this.columnWidthSetByUser ? this._columnWidth : column.defaultWidth ? column.defaultWidth : '';
23,526✔
7145

7146
            if (cb) {
23,526✔
7147
                cb(column);
23,526✔
7148
            }
7149
        });
7150

7151
        this.updateColumns(collection);
3,615✔
7152

7153
        if (this.hasColumnLayouts) {
3,615✔
7154
            collection.forEach((column: IgxColumnComponent) => {
142✔
7155
                column.populateVisibleIndexes();
1,361✔
7156
            });
7157
        }
7158
    }
7159

7160
    /**
7161
     * @hidden
7162
     */
7163
    protected reinitPinStates() {
7164
        this._pinnedColumns = this._columns
206✔
7165
            .filter((c) => c.pinned).sort((a, b) => this._pinnedColumns.indexOf(a) - this._pinnedColumns.indexOf(b));
2,559✔
7166
        this._unpinnedColumns = this.hasColumnGroups ? this._columns.filter((c) => !c.pinned) :
2,259✔
7167
            this._columns.filter((c) => !c.pinned)
300✔
7168
                .sort((a, b) => this._unpinnedColumns.indexOf(a) - this._unpinnedColumns.indexOf(b));
214✔
7169
    }
7170

7171
    protected extractDataFromSelection(source: any[], formatters = false, headers = false, columnData?: any[]): any[] {
×
7172
        let columnsArray: IgxColumnComponent[];
7173
        let record = {};
246✔
7174
        let selectedData = [];
246✔
7175
        let keys = [];
246✔
7176
        const selectionCollection = new Map();
246✔
7177
        const keysAndData = [];
246✔
7178
        const activeEl = this.selectionService.activeElement;
246✔
7179

7180
        if (this.type === 'hierarchical') {
246✔
7181
            const expansionRowIndexes = [];
2✔
7182
            for (const [key, value] of this.expansionStates.entries()) {
2✔
7183
                if (value) {
×
7184
                    const rowIndex = this.gridAPI.get_rec_index_by_id(key, this.dataView);
×
7185
                    expansionRowIndexes.push(rowIndex);
×
7186
                }
7187
            }
7188
            if (this.selectionService.selection.size > 0) {
2!
7189
                if (expansionRowIndexes.length > 0) {
2!
7190
                    for (const [key, value] of this.selectionService.selection.entries()) {
×
7191
                        const updatedKey = key;
×
7192
                        let subtract = 0;
×
7193
                        expansionRowIndexes.forEach((row) => {
×
7194
                            if (updatedKey > Number(row)) {
×
7195
                                subtract++;
×
7196
                            }
7197
                        });
7198
                        selectionCollection.set(updatedKey - subtract, value);
×
7199
                    }
7200
                }
7201
            } else if (activeEl) {
×
7202
                let subtract = 0;
×
7203
                if (expansionRowIndexes.length > 0) {
×
7204
                    expansionRowIndexes.forEach(row => {
×
7205
                        if (activeEl.row > Number(row)) {
×
7206
                            subtract++;
×
7207
                        }
7208
                    });
7209
                    activeEl.row -= subtract;
×
7210
                }
7211
            }
7212
        }
7213

7214
        const totalItems = (this as any).totalItemCount ?? 0;
246✔
7215
        const isRemote = totalItems && totalItems > this.dataView.length;
246!
7216
        let selectionMap;
7217
        if (this.type === 'hierarchical' && selectionCollection.size > 0) {
246!
7218
            selectionMap = isRemote ? Array.from(selectionCollection) :
×
7219
                Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length);
×
7220
        } else {
7221
            selectionMap = isRemote ? Array.from(this.selectionService.selection) :
246!
7222
                Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length);
883✔
7223
        }
7224

7225
        if (this.cellSelection === GridSelectionMode.single && activeEl) {
246✔
7226
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
10✔
7227
        }
7228

7229
        if (this.cellSelection === GridSelectionMode.none && activeEl) {
246✔
7230
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
5✔
7231
        }
7232

7233
        if (columnData) {
246!
7234
            selectedData = columnData;
×
7235
        }
7236

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

7286
        if (keys.length) {
246!
7287
            keysAndData.push(selectedData);
×
7288
            keysAndData.push(keys);
×
7289
            return keysAndData;
×
7290
        } else {
7291
            return selectedData;
246✔
7292
        }
7293
    }
7294

7295
    protected getSelectableColumnsAt(index) {
7296
        if (this.hasColumnLayouts) {
2,512✔
7297
            const visibleLayoutColumns = this.visibleColumns
2✔
7298
                .filter(col => col.columnLayout)
20✔
7299
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
2✔
7300
            const colLayout = visibleLayoutColumns[index];
2✔
7301
            return colLayout ? colLayout.children.toArray() : [];
2!
7302
        } else {
7303
            const visibleColumns = this.visibleColumns
2,510✔
7304
                .filter(col => !col.columnGroup)
14,120✔
7305
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
11,790✔
7306
            return [visibleColumns[index]];
2,510✔
7307
        }
7308
    }
7309

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

7339
                if (max === 0 || (maxForCells <= emptyCellWithPaddingOnly && this._firstAutoResize)) {
76!
7340
                    // cells not in DOM yet or content not fully initialized.
7341
                    continue;
6✔
7342
                }
7343
                let maxSize = Math.ceil(Math.max(...cellsContentWidths)) + 1;
70✔
7344
                if (col.maxWidth && maxSize > col.maxWidthPx) {
70✔
7345
                    maxSize = col.maxWidthPx;
3✔
7346
                } else if (maxSize < col.minWidthPx) {
67✔
7347
                    maxSize = col.minWidthPx;
6✔
7348
                }
7349
                col.autoSize = maxSize;
70✔
7350
                col.resetCaches();
70✔
7351
                colResized = true;
70✔
7352
            }
7353
        }
7354
        if (colResized) {
26✔
7355
            this.resetCachedWidths();
18✔
7356
            this.cdr.detectChanges();
18✔
7357
        }
7358

7359
        if (this.isColumnWidthSum) {
26✔
7360
            this.calcWidth = this.getColumnWidthSum();
2✔
7361
        }
7362
    }
7363

7364
    protected extractDataFromColumnsSelection(source: any[], formatters = false, headers = false): any[] {
×
7365
        let record = {};
20✔
7366
        const selectedData = [];
20✔
7367
        const selectedColumns = this.selectedColumns();
20✔
7368
        if (selectedColumns.length === 0) {
20✔
7369
            return [];
12✔
7370
        }
7371

7372
        for (const data of source) {
8✔
7373
            selectedColumns.forEach((col) => {
71✔
7374
                const key = headers ? col.header || col.field : col.field;
142!
7375
                record[key] = formatters && col.formatter ? col.formatter(data[col.field], data)
142!
7376
                    : data[col.field];
7377
            });
7378

7379
            if (Object.keys(record).length) {
71✔
7380
                selectedData.push(record);
71✔
7381
            }
7382
            record = {};
71✔
7383
        }
7384
        return selectedData;
8✔
7385
    }
7386

7387
    /**
7388
     * @hidden
7389
     */
7390
    protected initPinning() {
7391
        this.calculateGridWidth();
3,923✔
7392
        this.resetCaches();
3,923✔
7393
        this.handleColumnPinningForGroups();
3,923✔
7394
        this.notifyChanges();
3,923✔
7395
    }
7396

7397
    /**
7398
     * @hidden
7399
     */
7400
    protected scrollTo(row: any | number, column: any | number, inCollection = this._filteredSortedUnpinnedData): void {
3✔
7401
        let delayScrolling = false;
106✔
7402

7403
        if (this.paginator && typeof (row) !== 'number') {
106✔
7404
            const rowIndex = inCollection.indexOf(row);
16✔
7405
            const page = Math.floor(rowIndex / this.perPage);
16✔
7406

7407
            if (this.page !== page) {
16✔
7408
                delayScrolling = true;
5✔
7409
                this.page = page;
5✔
7410
            }
7411
        }
7412

7413
        if (delayScrolling) {
106✔
7414
            this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
5✔
7415
                this.scrollDirective(this.verticalScrollContainer,
5✔
7416
                    typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
5!
7417
            });
7418
        } else {
7419
            this.scrollDirective(this.verticalScrollContainer,
101✔
7420
                typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
101✔
7421
        }
7422

7423
        this.scrollToHorizontally(column);
106✔
7424
    }
7425

7426
    /**
7427
     * @hidden
7428
     */
7429
    protected scrollToHorizontally(column: any | number) {
7430
        let columnIndex = typeof column === 'number' ? column : this.getColumnByName(column).visibleIndex;
191✔
7431
        const scrollRow = this.rowList.find(r => !!r.virtDirRow);
233✔
7432
        const virtDir = scrollRow ? scrollRow.virtDirRow : null;
191✔
7433
        if (this.isPinningToStart && this.pinnedColumns.length) {
191✔
7434
            if (columnIndex >= this.pinnedColumns.length) {
1!
7435
                columnIndex -= this.pinnedColumns.length;
×
7436
                this.scrollDirective(virtDir, columnIndex);
×
7437
            }
7438
        } else {
7439
            this.scrollDirective(virtDir, columnIndex);
190✔
7440
        }
7441
    }
7442

7443
    /**
7444
     * @hidden
7445
     */
7446
    protected scrollDirective(directive: IgxGridForOfDirective<any, any[]>, goal: number): void {
7447
        if (!directive) {
381✔
7448
            return;
1✔
7449
        }
7450
        directive.scrollTo(goal);
380✔
7451
    }
7452

7453

7454
    /**
7455
     * @hidden
7456
     */
7457
    protected getColumnWidthSum(): number {
7458
        let colSum = 0;
43✔
7459
        const cols = this.hasColumnLayouts ?
43!
7460
            this.visibleColumns.filter(x => x.columnLayout) : this.visibleColumns.filter(x => !x.columnGroup);
271✔
7461
        cols.forEach((item) => {
43✔
7462
            colSum += parseInt((item.calcWidth || item.defaultWidth), 10) || this.minColumnWidth;
238!
7463
        });
7464
        if (!colSum) {
43!
7465
            return null;
×
7466
        }
7467
        this.cdr.detectChanges();
43✔
7468
        colSum += this.featureColumnsWidth();
43✔
7469
        return colSum;
43✔
7470
    }
7471

7472
    /**
7473
     * Notify changes, reset cache and populateVisibleIndexes.
7474
     *
7475
     * @hidden
7476
     */
7477
    private _columnsReordered(column: IgxColumnComponent) {
7478
        this.notifyChanges();
128✔
7479
        // after reordering is done reset cached column collections.
7480
        this.resetColumnCollections();
128✔
7481
        column.resetCaches();
128✔
7482
    }
7483

7484
    protected buildDataView(_data: any[]) {
7485
        this._dataView = this.isRowPinningToTop ?
13,613✔
7486
            [...this.pinnedDataView, ...this.unpinnedDataView] :
7487
            [...this.unpinnedDataView, ...this.pinnedDataView];
7488
    }
7489

7490
    private _applyWidthHostBinding() {
7491
        let width = this._width;
7,842✔
7492
        if (width === null) {
7,842✔
7493
            let currentWidth = this.calcWidth;
2✔
7494
            if (this.hasVerticalScroll()) {
2!
7495
                currentWidth += this.scrollSize;
×
7496
            }
7497
            width = currentWidth + 'px';
2✔
7498
            this.resetCaches();
2✔
7499
        }
7500
        this._hostWidth = width;
7,842✔
7501
        this.cdr.markForCheck();
7,842✔
7502
    }
7503

7504
    protected verticalScrollHandler(event) {
7505
        this.verticalScrollContainer.onScroll(event);
330✔
7506
        this.disableTransitions = true;
330✔
7507

7508
        this.zone.run(() => {
330✔
7509
            this.zone.onStable.pipe(first()).subscribe(() => {
330✔
7510
                this.verticalScrollContainer.chunkLoad.emit(this.verticalScrollContainer.state);
330✔
7511
                if (this.rowEditable) {
330✔
7512
                    this.changeRowEditingOverlayStateOnScroll(this.crudService.rowInEditMode);
16✔
7513
                }
7514
            });
7515
        });
7516
        this.disableTransitions = false;
330✔
7517

7518
        this.hideOverlays();
330✔
7519
        this.actionStrip?.hide();
330✔
7520
        if (this.actionStrip) {
330✔
7521
            this.actionStrip.context = null;
11✔
7522
        }
7523
        const args: IGridScrollEventArgs = {
330✔
7524
            direction: 'vertical',
7525
            event,
7526
            scrollPosition: this.verticalScrollContainer.scrollPosition
7527
        };
7528
        this.gridScroll.emit(args);
330✔
7529
    }
7530

7531
    protected horizontalScrollHandler(event) {
7532
        const scrollLeft = event.target.scrollLeft;
312✔
7533
        this.headerContainer.onHScroll(scrollLeft);
312✔
7534
        this._horizontalForOfs.forEach(vfor => vfor.onHScroll(scrollLeft));
1,970✔
7535
        this.cdr.markForCheck();
312✔
7536

7537
        this.zone.run(() => {
312✔
7538
            this.zone.onStable.pipe(first()).subscribe(() => {
312✔
7539
                this.parentVirtDir.chunkLoad.emit(this.headerContainer.state);
310✔
7540
                requestAnimationFrame(() => {
310✔
7541
                    this.autoSizeColumnsInView();
310✔
7542
                });
7543
            });
7544
        });
7545
        if (!this.navigation.isColumnFullyVisible(this.navigation.lastColumnIndex)) {
312✔
7546
            this.hideOverlays();
205✔
7547
        }
7548
        const args: IGridScrollEventArgs = { direction: 'horizontal', event, scrollPosition: this.headerContainer.scrollPosition };
312✔
7549
        this.gridScroll.emit(args);
312✔
7550
    }
7551

7552
    protected get renderedActualRowHeight() {
7553
        let border = 1;
503✔
7554
        if (this.rowList.toArray().length > 0) {
503✔
7555
            const rowStyles = this.document.defaultView.getComputedStyle(this.rowList.first.nativeElement);
353✔
7556
            border = rowStyles.borderBottomWidth ? Math.ceil(parseFloat(rowStyles.borderBottomWidth)) : border;
353✔
7557
        }
7558
        return this.rowHeight + border;
503✔
7559
    }
7560

7561
    private executeCallback(rowIndex, visibleColIndex = -1, cb: (args: any) => void = null) {
×
7562
        if (!cb) {
832✔
7563
            return;
281✔
7564
        }
7565
        let row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
1,622✔
7566
        if (!row) {
551✔
7567
            if ((this as any).totalItemCount) {
8!
7568
                this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
×
7569
                    this.cdr.detectChanges();
×
7570
                    row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
×
7571
                    const cbArgs = this.getNavigationArguments(row, visibleColIndex);
×
7572
                    cb(cbArgs);
×
7573
                });
7574
            }
7575
            const dataViewIndex = this._getDataViewIndex(rowIndex);
8✔
7576
            if (this.dataView[dataViewIndex].detailsData) {
8✔
7577
                this.navigation.setActiveNode({ row: rowIndex });
8✔
7578
                this.cdr.detectChanges();
8✔
7579
            }
7580

7581
            return;
8✔
7582
        }
7583
        const args = this.getNavigationArguments(row, visibleColIndex);
543✔
7584
        cb(args);
543✔
7585
    }
7586

7587
    private getNavigationArguments(row, visibleColIndex) {
7588
        let targetType: GridKeydownTargetType; let target;
7589
        switch (row.nativeElement.tagName.toLowerCase()) {
543!
7590
            case 'igx-grid-groupby-row':
7591
                targetType = 'groupRow';
30✔
7592
                target = row;
30✔
7593
                break;
30✔
7594
            case 'igx-grid-summary-row':
7595
                targetType = 'summaryCell';
50✔
7596
                target = visibleColIndex !== -1 ?
50!
7597
                    row.summaryCells.find(c => c.visibleColumnIndex === visibleColIndex) : row.summaryCells.first;
139✔
7598
                break;
50✔
7599
            case 'igx-child-grid-row':
7600
                targetType = 'hierarchicalRow';
×
7601
                target = row;
×
7602
                break;
×
7603
            default:
7604
                targetType = 'dataCell';
463✔
7605
                target = visibleColIndex !== -1 ? row.cells.find(c => c.visibleColumnIndex === visibleColIndex) : row.cells.first;
1,286!
7606
                break;
463✔
7607
        }
7608
        return { targetType, target };
543✔
7609
    }
7610

7611
    private getNextDataRowIndex(currentRowIndex, previous = false): number {
15✔
7612
        const resolvedIndex = this._getDataViewIndex(currentRowIndex);
30✔
7613
        if (currentRowIndex < 0 || (currentRowIndex === 0 && previous) || (resolvedIndex >= this.dataView.length - 1 && !previous)) {
30!
7614
            return currentRowIndex;
7✔
7615
        }
7616
        // find next/prev record that is editable.
7617
        const nextRowIndex = previous ? this.findPrevEditableDataRowIndex(currentRowIndex) :
23✔
7618
            this.dataView.findIndex((rec, index) =>
7619
                index > resolvedIndex && this.isEditableDataRecordAtIndex(index));
56✔
7620
        const nextDataIndex = this.getDataIndex(nextRowIndex);
23✔
7621
        return nextDataIndex !== -1 ? nextDataIndex : currentRowIndex;
23!
7622
    }
7623

7624
    /**
7625
     * Returns the previous editable row index or -1 if no such row is found.
7626
     *
7627
     * @param currentIndex The index of the current editable record.
7628
     */
7629
    private findPrevEditableDataRowIndex(currentIndex): number {
7630
        let i = this.dataView.length;
8✔
7631
        const resolvedIndex = this._getDataViewIndex(currentIndex);
8✔
7632
        while (i--) {
8✔
7633
            if (i < resolvedIndex && this.isEditableDataRecordAtIndex(i)) {
178✔
7634
                return i;
8✔
7635
            }
7636
        }
7637
        return -1;
×
7638
    }
7639

7640

7641
    /**
7642
     * Returns if the record at the specified data view index is a an editable data record.
7643
     * If record is group rec, summary rec, child rec, ghost rec. etc. it is not editable.
7644
     *
7645
     * @param dataViewIndex The index of that record in the data view.
7646
     *
7647
     */
7648
    // TODO: Consider moving it into CRUD
7649
    private isEditableDataRecordAtIndex(dataViewIndex) {
7650
        const rec = this.dataView[dataViewIndex];
29✔
7651
        return !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData &&
29✔
7652
            !this.isGhostRecordAtIndex(dataViewIndex);
7653
    }
7654

7655
    /**
7656
     * Returns if the record at the specified data view index is a ghost.
7657
     * If record is pinned but is not in pinned area then it is a ghost record.
7658
     *
7659
     * @param dataViewIndex The index of that record in the data view.
7660
     */
7661
    private isGhostRecordAtIndex(dataViewIndex) {
7662
        const isPinned = this.isRecordPinned(this.dataView[dataViewIndex]);
27✔
7663
        const isInPinnedArea = this.isRecordPinnedByViewIndex(dataViewIndex);
27✔
7664
        return isPinned && !isInPinnedArea;
27✔
7665
    }
7666

7667
    private isValidPosition(rowIndex, colIndex): boolean {
7668
        const rows = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).length;
97✔
7669
        const cols = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0 && !col.hidden).length;
777✔
7670
        if (rows < 1 || cols < 1) {
97✔
7671
            return false;
2✔
7672
        }
7673
        if (rowIndex > -1 && rowIndex < this.dataView.length &&
95✔
7674
            colIndex > - 1 && colIndex <= Math.max(...this.visibleColumns.map(c => c.visibleIndex))) {
694✔
7675
            return true;
93✔
7676
        }
7677
        return false;
2✔
7678
    }
7679

7680
    private find(text: string, increment: number, caseSensitive?: boolean, exactMatch?: boolean, scroll?: boolean, endEdit = true) {
195✔
7681
        if (!this.rowList) {
322!
7682
            return 0;
×
7683
        }
7684

7685
        if (endEdit) {
322✔
7686
            this.crudService.endEdit(false);
283✔
7687
        }
7688

7689
        if (!text) {
322!
7690
            this.clearSearch();
×
7691
            return 0;
×
7692
        }
7693

7694
        const caseSensitiveResolved = caseSensitive ? true : false;
322✔
7695
        const exactMatchResolved = exactMatch ? true : false;
322✔
7696
        let rebuildCache = false;
322✔
7697

7698
        if (this._lastSearchInfo.searchText !== text ||
322✔
7699
            this._lastSearchInfo.caseSensitive !== caseSensitiveResolved ||
7700
            this._lastSearchInfo.exactMatch !== exactMatchResolved) {
7701
            this._lastSearchInfo = {
84✔
7702
                searchText: text,
7703
                activeMatchIndex: 0,
7704
                caseSensitive: caseSensitiveResolved,
7705
                exactMatch: exactMatchResolved,
7706
                matchInfoCache: [],
7707
                matchCount: 0,
7708
                content: ''
7709
            };
7710

7711
            rebuildCache = true;
84✔
7712
        } else {
7713
            this._lastSearchInfo.activeMatchIndex += increment;
238✔
7714
        }
7715

7716
        if (rebuildCache) {
322✔
7717
            this.rowList.forEach((row) => {
84✔
7718
                if (row.cells) {
784✔
7719
                    row.cells.forEach((c: IgxGridCellComponent) => {
740✔
7720
                        c.highlightText(text, caseSensitiveResolved, exactMatchResolved);
3,097✔
7721
                    });
7722
                }
7723
            });
7724

7725
            this.rebuildMatchCache();
84✔
7726
        }
7727

7728
        if (this._lastSearchInfo.activeMatchIndex >= this._lastSearchInfo.matchCount) {
322✔
7729
            this._lastSearchInfo.activeMatchIndex = 0;
28✔
7730
        } else if (this._lastSearchInfo.activeMatchIndex < 0) {
294✔
7731
            this._lastSearchInfo.activeMatchIndex = this._lastSearchInfo.matchCount - 1;
8✔
7732
        }
7733

7734
        if (this._lastSearchInfo.matchCount > 0) {
322✔
7735
            const matchInfo = this._lastSearchInfo.matchInfoCache[this._lastSearchInfo.activeMatchIndex];
304✔
7736
            this._lastSearchInfo = { ...this._lastSearchInfo };
304✔
7737

7738
            if (scroll !== false) {
304✔
7739
                this.scrollTo(matchInfo.row, matchInfo.column);
182✔
7740
            }
7741

7742
            this.textHighlightService.setActiveHighlight(this.id, {
304✔
7743
                column: matchInfo.column,
7744
                row: matchInfo.row,
7745
                index: matchInfo.index,
7746
                metadata: matchInfo.metadata,
7747
            });
7748

7749
        } else {
7750
            this.textHighlightService.clearActiveHighlight(this.id);
18✔
7751
        }
7752

7753
        return this._lastSearchInfo.matchCount;
322✔
7754
    }
7755

7756
    private rebuildMatchCache() {
7757
        this._lastSearchInfo.matchInfoCache = [];
211✔
7758

7759
        const caseSensitive = this._lastSearchInfo.caseSensitive;
211✔
7760
        const exactMatch = this._lastSearchInfo.exactMatch;
211✔
7761
        const searchText = caseSensitive ? this._lastSearchInfo.searchText : this._lastSearchInfo.searchText.toLowerCase();
211✔
7762
        const data = this.filteredSortedData;
211✔
7763
        const columnItems = this.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex);
908✔
7764

7765
        data.forEach((dataRow, rowIndex) => {
211✔
7766
            columnItems.forEach((c) => {
3,474✔
7767
                const pipeArgs = this.getColumnByName(c.field).pipeArgs;
14,791✔
7768
                const value = c.formatter ? c.formatter(resolveNestedPath(dataRow, c.field), dataRow) :
14,791!
7769
                    c.dataType === 'number' ? formatNumber(resolveNestedPath(dataRow, c.field), this.locale, pipeArgs.digitsInfo) :
14,791✔
7770
                        c.dataType === 'date'
11,115✔
7771
                            ? formatDate(resolveNestedPath(dataRow, c.field), pipeArgs.format, this.locale, pipeArgs.timezone)
7772
                            : resolveNestedPath(dataRow, c.field);
7773
                if (value !== undefined && value !== null && c.searchable) {
14,791✔
7774
                    let searchValue = caseSensitive ? String(value) : String(value).toLowerCase();
14,576✔
7775

7776
                    if (exactMatch) {
14,576✔
7777
                        if (searchValue === searchText) {
536✔
7778
                            const mic: IMatchInfoCache = {
9✔
7779
                                row: dataRow,
7780
                                column: c.field,
7781
                                index: 0,
7782
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7783
                            };
7784

7785
                            this._lastSearchInfo.matchInfoCache.push(mic);
9✔
7786
                        }
7787
                    } else {
7788
                        let occurrenceIndex = 0;
14,040✔
7789
                        let searchIndex = searchValue.indexOf(searchText);
14,040✔
7790

7791
                        while (searchIndex !== -1) {
14,040✔
7792
                            const mic: IMatchInfoCache = {
2,410✔
7793
                                row: dataRow,
7794
                                column: c.field,
7795
                                index: occurrenceIndex++,
7796
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7797
                            };
7798

7799
                            this._lastSearchInfo.matchInfoCache.push(mic);
2,410✔
7800

7801
                            searchValue = searchValue.substring(searchIndex + searchText.length);
2,410✔
7802
                            searchIndex = searchValue.indexOf(searchText);
2,410✔
7803
                        }
7804
                    }
7805
                }
7806
            });
7807
        });
7808

7809
        this._lastSearchInfo.matchCount = this._lastSearchInfo.matchInfoCache.length;
211✔
7810
    }
7811

7812
    protected updateDefaultRowHeight() {
7813
        if (this.dataRowList.length > 0 && this.dataRowList.first.cells && this.dataRowList.first.cells.length > 0) {
3,650✔
7814
            const height = parseFloat(this.document.defaultView.getComputedStyle(this.dataRowList.first.cells.first.nativeElement)?.getPropertyValue('height'));
112✔
7815
            if (height) {
112!
7816
                this._defaultRowHeight = height;
112✔
7817
            } else {
7818
                this._shouldRecalcRowHeight = true;
×
7819
            }
7820
        }
7821
    }
7822

7823
    // TODO: About to Move to CRUD
7824
    private configureRowEditingOverlay(rowID: any, useOuter = false) {
72✔
7825
        let settings = this.rowEditSettings;
280✔
7826
        const overlay = this.overlayService.getOverlayById(this.rowEditingOverlay.overlayId);
280✔
7827
        if (overlay) {
280✔
7828
            settings = overlay.settings;
73✔
7829
        }
7830
        settings.outlet = useOuter ? this.parentRowOutletDirective : this.rowOutletDirective;
280✔
7831
        this.rowEditPositioningStrategy.settings.container = this.tbody.nativeElement;
280✔
7832
        const pinned = this._pinnedRecordIDs.indexOf(rowID) !== -1;
280✔
7833
        const targetRow = !pinned ?
280✔
7834
            this.gridAPI.get_row_by_key(rowID) as IgxRowDirective
7835
            : this.pinnedRows.find(x => x.key === rowID) as IgxRowDirective;
8✔
7836
        if (!targetRow) {
280!
7837
            return;
×
7838
        }
7839
        settings.target = targetRow.element.nativeElement;
280✔
7840
        this.toggleRowEditingOverlay(true);
280✔
7841
    }
7842

7843
    private handleColumnPinningForGroups(): void {
7844
        // When a column is a group or is inside a group, pin all related.
7845
        const pinnedColumns = [];
3,923✔
7846
        const unpinnedColumns = [];
3,923✔
7847

7848
        this._pinnedColumns.forEach(col => {
3,923✔
7849
            if (col.parent) {
492✔
7850
                col.parent.pinned = true;
113✔
7851
            }
7852
            if (col.columnGroup) {
492✔
7853
                col.children.forEach(child => child.pinned = true);
112✔
7854
            }
7855
        });
7856

7857
        // Make sure we don't exceed unpinned area min width and get pinned and unpinned col collections.
7858
        // We take into account top level columns (top level groups and non groups).
7859
        // If top level is unpinned the pinning handles all children to be unpinned as well.
7860
        for (const column of this._columns) {
3,923✔
7861
            if (column.pinned && !column.parent) {
23,245✔
7862
                pinnedColumns.push(column);
380✔
7863
            } else if (column.pinned && column.parent) {
22,865✔
7864
                if (column.topLevelParent.pinned) {
115!
7865
                    pinnedColumns.push(column);
115✔
7866
                } else {
7867
                    column.pinned = false;
×
7868
                    unpinnedColumns.push(column);
×
7869
                }
7870
            } else {
7871
                unpinnedColumns.push(column);
22,750✔
7872
            }
7873
        }
7874
        // Assign the applicable collections.
7875
        this._pinnedColumns = pinnedColumns;
3,923✔
7876
        this._unpinnedColumns = unpinnedColumns;
3,923✔
7877
    }
7878

7879
    protected shouldRecreateColumns(oldData: any[] | null | undefined, newData: any[] | null | undefined): boolean {
7880
        if (!oldData || !oldData.length) return true;
73✔
7881
        if (!newData || !newData.length) return false;
46!
7882
        return Object.keys(oldData[0]).join() !== Object.keys(newData[0]).join();
46✔
7883
    }
7884

7885
    /**
7886
     * Clears the current navigation service active node
7887
     */
7888
    private clearActiveNode() {
7889
        this.navigation.lastActiveNode = this.navigation.activeNode;
76✔
7890
        this.navigation.activeNode = {} as IActiveNode;
76✔
7891
        this.notifyChanges();
76✔
7892
    }
7893
}
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