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

IgniteUI / igniteui-angular / 12234153640

09 Dec 2024 10:55AM CUT coverage: 91.618% (-0.004%) from 91.622%
12234153640

Pull #15149

github

web-flow
Merge 4383489f7 into 13eb7ca46
Pull Request #15149: refactor(avatar): remove unused code

12968 of 15202 branches covered (85.3%)

26311 of 28718 relevant lines covered (91.62%)

34016.74 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

298
    public get summaryRowHeight(): number {
299
        if (this.hasSummarizedColumns && this.rootSummariesEnabled) {
264,226✔
300
            return this._summaryRowHeight || this.summaryService.calcMaxSummaryHeight();
182,821✔
301
        }
302
        return 0;
81,405✔
303
    }
304

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

508

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1060

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

1074
    /* End of toolbar related definitions */
1075

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

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

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

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

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

1117

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1215

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

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

1241

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1593

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1794

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

1806
    public get resourceStrings(): IGridResourceStrings {
1807
        return this._resourceStrings;
237,354✔
1808
    }
1809

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

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

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

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

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

1863
            if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
921✔
1864
                this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1865
                this._filteredData = null;
467✔
1866
            }
1867

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

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

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

1897
        this.filtering.emit(filteringEventArgs);
102✔
1898

1899
        if (filteringEventArgs.cancel) {
102✔
1900
            return;
1✔
1901
        }
1902

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

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

1917
        this.selectionService.clearHeaderCBState();
101✔
1918
        this.summaryService.clearSummaryCache();
101✔
1919
        this.notifyChanges();
101✔
1920

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

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

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

1947
    @Input()
1948
    public get pagingMode() {
1949
        return this._pagingMode;
8,419✔
1950
    }
1951

1952
    public set pagingMode(val: GridPagingMode) {
1953
        this._pagingMode = val;
2✔
1954
        this.pipeTrigger++;
2✔
1955
        this.notifyChanges(true);
2✔
1956
    }
1957

1958
    /** @hidden @internal */
1959
    public get page(): number {
1960
        return this.paginator?.page || 0;
2,233,107✔
1961
    }
1962

1963
    public set page(val: number) {
1964
        if (this.paginator) {
713✔
1965
            this.paginator.page = val;
330✔
1966
        }
1967
    }
1968

1969
    /** @hidden @internal */
1970
    public get perPage(): number {
1971
        return this.paginator?.perPage || DEFAULT_ITEMS_PER_PAGE;
2,233,020✔
1972
    }
1973

1974
    public set perPage(val: number) {
1975
        if (this.paginator) {
20✔
1976
            this.paginator.perPage = val;
20✔
1977
        }
1978
    }
1979

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

1992
    public set hideRowSelectors(value: boolean) {
1993
        this._hideRowSelectors = value;
21✔
1994
        this.notifyChanges(true);
21✔
1995
    }
1996

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

2010
    public set rowDraggable(val: boolean) {
2011
        this._rowDrag = val;
47✔
2012
        this.notifyChanges(true);
47✔
2013
    }
2014

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

2026
    /**
2027
     * @hidden
2028
     * @internal
2029
     */
2030
    public rowDragging = false;
4,058✔
2031

2032
    /** @hidden @internal */
2033
    public dragRowID = null;
4,058✔
2034

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

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

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

2074
    public set height(value: string | null) {
2075
        if (this._height !== value) {
3,052✔
2076
            this._height = value;
3,050✔
2077
            this.nativeElement.style.height = value;
3,050✔
2078
            this.notifyChanges(true);
3,050✔
2079
        }
2080
    }
2081

2082
    /**
2083
     * @hidden @internal
2084
     */
2085
    @HostBinding('style.width')
2086
    public get hostWidth() {
2087
        return this._width || this._hostWidth;
32,623✔
2088
    }
2089

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

2104
    public set width(value: string | null) {
2105
        if (this._width !== value) {
2,050✔
2106
            this._width = value;
2,050✔
2107
            this.nativeElement.style.width = value;
2,050✔
2108
            this.notifyChanges(true);
2,050✔
2109
        }
2110
    }
2111

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

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

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

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

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

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

2196
    public get isLoading(): boolean {
2197
        return this._isLoading;
94,027✔
2198
    }
2199

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

2217
    public set shouldGenerate(value: boolean) {
2218
        this.autoGenerate = value;
×
2219
    }
2220

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

2234
    public get emptyFilteredGridMessage(): string {
2235
        return this._emptyFilteredGridMessage || this.resourceStrings.igx_grid_emptyFilteredGrid_message;
224✔
2236
    }
2237

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

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

2274
    public set allowFiltering(value) {
2275
        if (this._allowFiltering !== value) {
725✔
2276
            this._allowFiltering = value;
714✔
2277
            this.filteringService.registerSVGIcons();
714✔
2278

2279

2280
            this.filteringService.isFilterRowVisible = false;
714✔
2281
            this.filteringService.filteredColumn = null;
714✔
2282

2283
            this.notifyChanges(true);
714✔
2284
        }
2285
    }
2286

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

2300
    public set allowAdvancedFiltering(value) {
2301
        if (this._allowAdvancedFiltering !== value) {
85✔
2302
            this._allowAdvancedFiltering = value;
85✔
2303
            this.filteringService.registerSVGIcons();
85✔
2304

2305
            if (!this._init) {
85✔
2306
                this.notifyChanges(true);
5✔
2307
            }
2308
        }
2309
    }
2310

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

2326
    public set filterMode(value: FilterMode) {
2327
        switch (value) {
183!
2328
            case FilterMode.excelStyleFilter:
2329
            case FilterMode.quickFilter:
2330
                this._filterMode = value;
183✔
2331
                break;
183✔
2332
            default:
2333
                break;
×
2334
        }
2335

2336
        if (this.filteringService.isFilterRowVisible) {
183✔
2337
            this.filteringRow.close();
1✔
2338
        }
2339
        this.notifyChanges(true);
183✔
2340
    }
2341

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

2357
    public set summaryPosition(value: GridSummaryPosition) {
2358
        this._summaryPosition = value;
18✔
2359
        this.notifyChanges();
18✔
2360
    }
2361

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

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

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

2402
    public set showSummaryOnCollapse(value: boolean) {
2403
        this._showSummaryOnCollapse = value;
9✔
2404
        this.notifyChanges();
9✔
2405
    }
2406

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

2420
    public set filterStrategy(classRef: IFilteringStrategy) {
2421
        this._filterStrategy = classRef;
37✔
2422
    }
2423

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

2437
    public set sortStrategy(value: IGridSortingStrategy) {
2438
        this._sortingStrategy = value;
14✔
2439
    }
2440

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

2463
    public get sortingOptions() {
2464
        return this._sortingOptions;
28,697✔
2465
    }
2466

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

2486
    public get selectedRows(): any[] {
2487
        return this.selectionService.getSelectedRows();
731✔
2488
    }
2489

2490

2491
    /** @hidden @internal */
2492
    public get headerGroupsList(): IgxGridHeaderGroupComponent[] {
2493
        return this.theadRow.groups;
20,622✔
2494
    }
2495

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

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

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

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

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

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

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

2584
    /**
2585
     * @hidden
2586
     * @internal
2587
     */
2588
    public get isPinningToStart() {
2589
        return this.pinning.columns !== ColumnPinningPosition.End;
2,471,989✔
2590
    }
2591

2592
    /**
2593
     * @hidden
2594
     * @internal
2595
     */
2596
    public get isRowPinningToTop() {
2597
        return this.pinning.rows !== RowPinningPosition.Bottom;
671,821✔
2598
    }
2599

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

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

2625
    /**
2626
     * @hidden @internal
2627
     */
2628
    public get rowOutletDirective() {
2629
        return this.rowEditingOutletDirective;
4,337✔
2630
    }
2631

2632
    /**
2633
     * @hidden @internal
2634
     */
2635
    public get parentRowOutletDirective() {
2636
        return this.outlet;
1✔
2637
    }
2638

2639
    /**
2640
     * @hidden @internal
2641
     */
2642
    public get rowEditCustom(): TemplateRef<IgxGridRowEditTemplateContext> {
2643
        if (this.rowEditCustomDirectives && this.rowEditCustomDirectives.first) {
6,398✔
2644
            return this.rowEditCustomDirectives.first;
62✔
2645
        }
2646
        return null;
6,336✔
2647
    }
2648

2649
    /**
2650

2651
    /**
2652
     * @hidden @internal
2653
     */
2654
    public get rowEditContainer(): TemplateRef<IgxGridRowEditTemplateContext> {
2655
        return this.rowEditCustom ? this.rowEditCustom : this.defaultRowEditTemplate;
5,837✔
2656
    }
2657

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

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

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

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

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

2709
    /** @hidden @internal */
2710
    public get activeDescendant() {
2711
        const activeElem = this.navigation.activeNode;
133,823✔
2712

2713
        if (!activeElem || !Object.keys(activeElem).length) {
133,823✔
2714
            return this.id;
118,462✔
2715
        }
2716

2717
        return activeElem.row < 0 ?
15,361✔
2718
            `${this.id}_${activeElem.row}_${activeElem.mchCache.level}_${activeElem.column}` :
2719
            `${this.id}_${activeElem.row}_${activeElem.column}`;
2720
    }
2721

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

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

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

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

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

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

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

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

2807
    /* blazorSuppress */
2808
    /**
2809
     * Get transactions service for the grid.
2810
     */
2811
    public get transactions(): TransactionService<Transaction, State> {
2812
        if (this._diTransactions && !this.batchEditing) {
7,263,859✔
2813
            return this._diTransactions;
3,804✔
2814
        }
2815
        return this._transactions;
7,260,055✔
2816
    }
2817

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

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

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

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

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

2872
    public set rowSelection(selectionMode: GridSelectionMode) {
2873
        this._rowSelectionMode = selectionMode;
523✔
2874
        if (!this._init) {
523✔
2875
            this.selectionService.clearAllSelectedRows();
72✔
2876
            this.notifyChanges(true);
72✔
2877
        }
2878
    }
2879

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

2893
    public set columnSelection(selectionMode: GridSelectionMode) {
2894
        this._columnSelectionMode = selectionMode;
157✔
2895
        // if (this.gridAPI.grid) {
2896
        this.selectionService.clearAllSelectedColumns();
157✔
2897
        this.notifyChanges(true);
157✔
2898
        // }
2899
    }
2900

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

2911
    public get pagingState() {
2912
        return this._pagingState;
174✔
2913
    }
2914

2915
    /**
2916
     * @hidden @internal
2917
     */
2918
    public rowEditMessage;
2919

2920
    /**
2921
     * @hidden @internal
2922
     */
2923
    public calcWidth: number;
2924
    /**
2925
     * @hidden @internal
2926
     */
2927
    public calcHeight = 0;
4,058✔
2928
    /**
2929
     * @hidden @internal
2930
     */
2931
    public tfootHeight: number;
2932

2933
    /**
2934
     * @hidden @internal
2935
     */
2936
    public disableTransitions = false;
4,058✔
2937

2938
    /**
2939
     * Represents the last search information.
2940
     */
2941
    public get lastSearchInfo(): ISearchInfo {
2942
        return this._lastSearchInfo;
1,481,765✔
2943
    }
2944

2945
    /**
2946
     * @hidden @internal
2947
     */
2948
    public columnWidthSetByUser = false;
4,058✔
2949

2950
    /**
2951
     * @hidden @internal
2952
     */
2953
    public pinnedRecords: any[];
2954

2955
    /**
2956
     * @hidden @internal
2957
     */
2958
    public unpinnedRecords: any[];
2959

2960
    /**
2961
     * @hidden @internal
2962
     */
2963
    public rendered$ = this.rendered.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true }));
4,058✔
2964

2965
    /** @hidden @internal */
2966
    public resizeNotify = new Subject<void>();
4,058✔
2967

2968
    /** @hidden @internal */
2969
    public rowAddedNotifier = new Subject<IRowDataEventArgs>();
4,058✔
2970

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

2974
    /** @hidden @internal */
2975
    public pipeTriggerNotifier = new Subject();
4,058✔
2976

2977
    /** @hidden @internal */
2978
    public _filteredSortedPinnedData: any[];
2979

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

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

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

3019
    /**
3020
    * @hidden @internal
3021
    */
3022
    public EMPTY_DATA = [];
4,058✔
3023

3024
    /** @hidden @internal */
3025
    public get type(): GridType["type"] {
3026
        return 'flat';
24,706✔
3027
    }
3028

3029
    /** @hidden @internal */
3030
    public _baseFontSize: number;
3031

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

3089
    protected _pinnedRecordIDs = [];
4,058✔
3090

3091
    /**
3092
     * @hidden
3093
     */
3094
    protected _hasVisibleColumns;
3095
    protected _allowFiltering = false;
4,058✔
3096
    protected _allowAdvancedFiltering = false;
4,058✔
3097
    protected _filterMode: FilterMode = FilterMode.quickFilter;
4,058✔
3098

3099

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

3126
    /** @hidden @internal */
3127
    public get paginator() {
3128
        return this.paginationComponents?.first;
3,768,329✔
3129
    }
3130

3131
    /**
3132
     * @hidden @internal
3133
     */
3134
    public get scrollSize() {
3135
        return this.verticalScrollContainer.getScrollNativeSize();
186,583✔
3136
    }
3137

3138
    private _primaryKey: string;
3139
    private _rowEditable = false;
4,058✔
3140
    private _currentRowState: any;
3141
    private _filteredSortedData = null;
4,058✔
3142
    private _filteredData = null;
4,058✔
3143

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

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

3168
    private _hostWidth;
3169
    private _advancedFilteringOverlayId: string;
3170
    private _advancedFilteringPositionSettings: PositionSettings = {
4,058✔
3171
        verticalDirection: VerticalAlignment.Middle,
3172
        horizontalDirection: HorizontalAlignment.Center,
3173
        horizontalStartPoint: HorizontalAlignment.Center,
3174
        verticalStartPoint: VerticalAlignment.Middle
3175
    };
3176

3177
    private _advancedFilteringOverlaySettings: OverlaySettings = {
4,058✔
3178
        closeOnOutsideClick: false,
3179
        modal: false,
3180
        positionStrategy: new ConnectedPositioningStrategy(this._advancedFilteringPositionSettings),
3181
    };
3182

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

3199
    private _columnWidth: string;
3200

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

3210
    private lastAddedRowIndex;
3211
    protected isColumnWidthSum = false;
4,058✔
3212
    private _currencyPositionLeft: boolean;
3213

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

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

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

3241
    /**
3242
     * @hidden @internal
3243
     */
3244
    protected get minColumnWidth() {
3245
        return MINIMUM_COLUMN_WIDTH;
65,803✔
3246
    }
3247

3248
    protected get isCustomSetRowHeight(): boolean {
3249
        return !isNaN(this._rowHeight);
1,203,946✔
3250
    }
3251

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

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

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

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

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

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

3317
        return result;
11,386✔
3318
    }
3319

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

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

3335
        if (this.hasZeroResultFilter) {
45,251✔
3336
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
212!
3337
        }
3338

3339
        if (this.hasNoData) {
45,039✔
3340
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate;
737✔
3341
        }
3342
    }
3343

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

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

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

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

3373
    /**
3374
     * @hidden @internal
3375
     */
3376
    public get isRowSelectable(): boolean {
3377
        return this.rowSelection !== GridSelectionMode.none;
289,011✔
3378
    }
3379

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

3387
    /**
3388
     * @hidden @internal
3389
     */
3390
    public get columnInDrag() {
3391
        return this.gridAPI.cms.column;
271,485✔
3392
    }
3393

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

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

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

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

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

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

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

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

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

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

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

3523
                this.nativeElement.focus();
1✔
3524
            }
3525
        });
3526
    }
3527

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

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

3551
    /**
3552
     * @hidden
3553
     * @internal
3554
     */
3555
    public isRecordPinned(rec) {
3556
        return this.getInitialPinnedIndex(rec) !== -1;
1,094,171✔
3557
    }
3558

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

3569
    /**
3570
     * @hidden
3571
     * @internal
3572
     */
3573
    public get hasPinnedRecords() {
3574
        return this._pinnedRecordIDs.length > 0;
491,141✔
3575
    }
3576

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

3585
    /**
3586
     * @hidden
3587
     * @internal
3588
     */
3589
    public get crudService() {
3590
        return this.gridAPI.crudService;
3,402,458✔
3591
    }
3592

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

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

3630
        this.subscribeToTransactions();
3,549✔
3631

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

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

3659
        this.overlayService.opening.pipe(destructor).subscribe((event) => {
3,549✔
3660
            if (this._advancedFilteringOverlayId === event.id) {
891✔
3661
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
91✔
3662
                if (instance) {
91✔
3663
                    instance.initialize(this as any, this.overlayService, event.id);
91✔
3664
                }
3665
            }
3666
        });
3667

3668
        this.overlayService.opened.pipe(destructor).subscribe((event) => {
3,549✔
3669
            const overlaySettings = this.overlayService.getOverlayById(event.id)?.settings;
626✔
3670

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

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

3686
            if (overlaySettings?.outlet === this.outlet && this.overlayIDs.indexOf(event.id) === -1) {
518✔
3687
                this.overlayIDs.push(event.id);
347✔
3688
            }
3689
        });
3690

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

3698
            const ind = this.overlayIDs.indexOf(event.id);
460✔
3699
            if (ind !== -1) {
460✔
3700
                this.overlayIDs.splice(ind, 1);
197✔
3701
            }
3702
        });
3703

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

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

3723

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

3731
        this.verticalScrollContainer.contentSizeChange.pipe(filter(() => !this._init), throttleTime(30), destructor).subscribe(() => {
3,549✔
3732
            this.notifyChanges(true);
683✔
3733
        });
3734

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

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

3759
    /**
3760
     * @hidden
3761
     * @internal
3762
     */
3763
    public resetColumnsCaches() {
3764
        this._columns.forEach(column => column.resetCaches());
215,310✔
3765
    }
3766

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

3777
    /**
3778
     * @hidden
3779
     * @internal
3780
     */
3781
    public resetForOfCache() {
3782
        const firstVirtRow = this.dataRowList.first;
34,058✔
3783
        if (firstVirtRow) {
34,058✔
3784
            if (this._cdrRequests) {
16,904✔
3785
                firstVirtRow.virtDirRow.cdr.detectChanges();
6,718✔
3786
            }
3787
            firstVirtRow.virtDirRow.assumeMaster();
16,904✔
3788
        }
3789
    }
3790

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

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

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

3831
    /**
3832
     * @hidden
3833
     * @internal
3834
     */
3835
    public resetCaches(recalcFeatureWidth = true) {
10,460✔
3836
        if (recalcFeatureWidth) {
34,058✔
3837
            this._headerFeaturesWidth = NaN;
34,049✔
3838
            this.summaryService.summaryHeight = 0;
34,049✔
3839
        }
3840
        this.resetColumnsCaches();
34,058✔
3841
        this.resetColumnCollections();
34,058✔
3842
        this.resetForOfCache();
34,058✔
3843
        this.resetCachedWidths();
34,058✔
3844
        this.hasVisibleColumns = undefined;
34,058✔
3845
        this._columnGroups = this._columns.some(col => col.columnGroup);
161,287✔
3846
    }
3847

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

3856
        if (this.sortAscendingHeaderIconDirectiveTemplate) {
3,420✔
3857
            this.sortAscendingHeaderIconTemplate = this.sortAscendingHeaderIconDirectiveTemplate;
5✔
3858
        }
3859

3860
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
3,420✔
3861
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
5✔
3862
        }
3863

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

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

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

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

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

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

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

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

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

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

4017
        const vertScrDC = this.verticalScrollContainer.displayContainer;
3,562✔
4018
        vertScrDC.addEventListener('scroll', this.preventContainerScroll.bind(this));
3,562✔
4019

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

4026
        this.addRowSnackbar?.clicked.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,562✔
4027
            const rec = this.filteredSortedData[this.lastAddedRowIndex];
4✔
4028
            this.scrollTo(rec, 0);
4✔
4029
            this.addRowSnackbar.close();
4✔
4030
        });
4031

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

4045
    }
4046

4047
    /**
4048
     * @hidden @internal
4049
     */
4050
    public notifyChanges(repaint = false) {
14,906✔
4051
        this._cdrRequests = true;
491,208✔
4052
        this._cdrRequestRepaint = repaint;
491,208✔
4053
        this.cdr.markForCheck();
491,208✔
4054
    }
4055

4056
    /**
4057
     * @hidden @internal
4058
     */
4059
    public ngDoCheck() {
4060
        if (this._init) {
17,643✔
4061
            return;
4,095✔
4062
        }
4063

4064
        if (this._cdrRequestRepaint) {
13,548✔
4065
            this.resetNotifyChanges();
3,963✔
4066
            this.calculateGridSizes();
3,963✔
4067
            this.refreshSearch(true);
3,963✔
4068
            return;
3,963✔
4069
        }
4070

4071
        if (this._cdrRequests) {
9,585✔
4072
            this.resetNotifyChanges();
4,187✔
4073
            this.cdr.detectChanges();
4,187✔
4074
        }
4075
    }
4076

4077
    /**
4078
     * @hidden
4079
     * @internal
4080
     */
4081
    public getDragGhostCustomTemplate() {
4082

4083
        return this.dragGhostCustomTemplate;
1,987✔
4084
    }
4085

4086
    /**
4087
     * @hidden @internal
4088
     */
4089
    public ngOnDestroy() {
4090
        this.tmpOutlets.forEach((tmplOutlet) => {
3,355✔
4091
            tmplOutlet.cleanCache();
24,721✔
4092
        });
4093

4094
        this.destroy$.next(true);
3,355✔
4095
        this.destroy$.complete();
3,355✔
4096
        this.transactionChange$.next();
3,355✔
4097
        this.transactionChange$.complete();
3,355✔
4098
        this._destroyed = true;
3,355✔
4099

4100
        this.textHighlightService.destroyGroup(this.id);
3,355✔
4101

4102
        if (this._advancedFilteringOverlayId) {
3,355!
4103
            this.overlayService.detach(this._advancedFilteringOverlayId);
×
4104
            delete this._advancedFilteringOverlayId;
×
4105
        }
4106

4107
        this.overlayIDs.forEach(overlayID => {
3,355✔
4108
            const overlay = this.overlayService.getOverlayById(overlayID);
23✔
4109

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

4115

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

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

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

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

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

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

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

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

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

4228

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

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

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

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

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

4281

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

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

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

4326
    /** @hidden @internal */
4327
    public get pinnedWidth() {
4328
        if (!isNaN(this._pinnedWidth)) {
223,285✔
4329
            return this._pinnedWidth;
202,223✔
4330
        }
4331
        this._pinnedWidth = this.getPinnedWidth();
21,062✔
4332
        return this._pinnedWidth;
21,062✔
4333
    }
4334

4335
    /** @hidden @internal */
4336
    public get unpinnedWidth() {
4337
        if (!isNaN(this._unpinnedWidth)) {
368,697✔
4338
            return this._unpinnedWidth;
345,308✔
4339
        }
4340
        this._unpinnedWidth = this.getUnpinnedWidth();
23,389✔
4341
        return this._unpinnedWidth;
23,389✔
4342
    }
4343

4344
    /**
4345
     * @hidden @internal
4346
     */
4347
    public isHorizontalScrollHidden = false;
4,058✔
4348

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

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

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

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

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

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

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

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

4436
    /**
4437
     * Gets an array of unpinned `IgxColumnComponent`s.
4438
     *
4439
     * @example
4440
     * ```typescript
4441
     * const unpinnedColumns = this.grid.unpinnedColumns.
4442
     * ```
4443
     */
4444
    public get unpinnedColumns(): IgxColumnComponent[] {
4445
        if (this._unpinnedVisible.length) {
767,539✔
4446
            return this._unpinnedVisible;
740,652✔
4447
        }
4448
        this._unpinnedVisible = this._unpinnedColumns.filter((col) => !col.hidden);
156,074✔
4449
        return this._unpinnedVisible;
26,887✔
4450
    }
4451

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

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

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

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

4499
    /**
4500
     * Returns an array of visible `IgxColumnComponent`s.
4501
     *
4502
     * @example
4503
     * ```typescript
4504
     * const visibleColumns = this.grid.visibleColumns.
4505
     * ```
4506
     */
4507
    public get visibleColumns(): IgxColumnComponent[] {
4508
        if (this._visibleColumns.length) {
162,003✔
4509
            return this._visibleColumns;
135,512✔
4510
        }
4511
        this._visibleColumns = this._columns.filter(c => !c.hidden);
151,469✔
4512
        return this._visibleColumns;
26,491✔
4513
    }
4514

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

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

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

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

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

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

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

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

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

4619
        this.columnMovingEnd.emit(eventArgs);
150✔
4620

4621
        if (eventArgs.cancel) {
150!
4622
            return;
×
4623
        }
4624

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4891
        if (eventArgs.cancel) {
159!
4892
            return;
×
4893
        }
4894

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5100
        return true;
132✔
5101
    }
5102

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

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

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

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

5136
        return true;
23✔
5137
    }
5138

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

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

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

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

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

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

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

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

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

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

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

5269
    /** @hidden @internal */
5270
    public get hasSummarizedColumns(): boolean {
5271
        const summarizedColumns = this._columns.filter(col => col.hasSummary && !col.hidden);
2,161,110✔
5272
        return summarizedColumns.length > 0;
340,256✔
5273
    }
5274

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

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

5292
    public set hasVisibleColumns(value) {
5293
        this._hasVisibleColumns = value;
34,058✔
5294
    }
5295

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

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

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

5311

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

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

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

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

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

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

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

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

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

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

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

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

5402

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

5411
        const columnsWithSetWidths = this.hasColumnLayouts ?
72,857✔
5412
            visibleCols.filter(c => c.widthSetByUser) :
168,621✔
5413
            visibleChildColumns.filter(c => c.widthSetByUser && c.width !== 'fit-content');
293,265✔
5414

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

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

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

5441
        return columnWidth + 'px';
71,399✔
5442
    }
5443

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

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

5476
        return sum;
44,451✔
5477
    }
5478

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

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

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

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

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

5521
        return 0;
×
5522
    }
5523

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5831

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

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

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

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

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

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

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

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

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

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

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

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

5925
        event.preventDefault();
9✔
5926

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

5932
        return result;
9✔
5933
    }
5934

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

6274
        return success;
6✔
6275
    }
6276

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

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

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

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

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

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

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

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

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

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

6427
            });
6428
        }
6429

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

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

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

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

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

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

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

6493
        if (!width && this.nativeElement) {
11,782✔
6494
            width = this.nativeElement.offsetWidth;
39✔
6495
        }
6496

6497

6498
        if (this.width === null || !width) {
11,782✔
6499
            width = this.getColumnWidthSum();
39✔
6500
            this.isColumnWidthSum = true;
39✔
6501
        }
6502

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

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

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

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

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

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

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

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

6584

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

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

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

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

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

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

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

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

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

6691

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

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

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

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

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

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

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

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

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

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

6766
            this.resetCaches();
67✔
6767

6768
            if (added || removed) {
67✔
6769
                this.onColumnsAddedOrRemoved();
63✔
6770
            }
6771
        }
6772
    }
6773

6774
    protected checkPrimaryKeyField() {
6775
        if (this.primaryKey && this.data?.length && !(this.primaryKey in this.data[0])) {
4,662✔
6776
            console.warn(`Field "${this.primaryKey}" is not defined in the data. Set \`primaryKey\` to a valid field.`);
9✔
6777
        }
6778
    }
6779

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

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

6813
        if (this.rowEditable) {
7,866✔
6814
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
855✔
6815
        }
6816

6817
        if (this.filteringService.isFilterRowVisible) {
7,866✔
6818
            this.filteringRow.resetChipsArea();
155✔
6819
        }
6820

6821
        this.cdr.detectChanges();
7,866✔
6822
        // in case scrollbar has appeared recalc to size correctly.
6823
        if (hasScroll !== this.hasVerticalScroll()) {
7,866✔
6824
            this.calculateGridWidth();
172✔
6825
            this.cdr.detectChanges();
172✔
6826
        }
6827

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

6856
    /**
6857
     * @hidden
6858
     * Sets TBODY height i.e. this.calcHeight
6859
     */
6860
    protected calculateGridHeight() {
6861

6862
        this.calcHeight = this._calculateGridBodyHeight();
10,424✔
6863
        if (this.pinnedRowHeight && this.calcHeight) {
10,424✔
6864
            this.calcHeight -= this.pinnedRowHeight;
108✔
6865
        }
6866
    }
6867

6868
    /**
6869
     * @hidden
6870
     */
6871
    protected getGroupAreaHeight(): number {
6872
        return 0;
2,665✔
6873
    }
6874

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

6898
    /**
6899
     * @hidden
6900
     */
6901
    protected getToolbarHeight(): number {
6902
        let toolbarHeight = 0;
9,626✔
6903
        if (this.toolbar.first) {
9,626✔
6904
            toolbarHeight = this.getComputedHeight(this.toolbar.first.nativeElement);
398✔
6905
        }
6906
        return toolbarHeight;
9,626✔
6907
    }
6908

6909
    /**
6910
     * @hidden
6911
     */
6912
    protected getPagingFooterHeight(): number {
6913
        let pagingHeight = 0;
9,626✔
6914
        if (this.footer) {
9,626✔
6915
            const height = this.getComputedHeight(this.footer.nativeElement);
9,626✔
6916
            pagingHeight = this.footer.nativeElement.firstElementChild ?
9,626✔
6917
                height : 0;
6918
        }
6919
        return pagingHeight;
9,626✔
6920
    }
6921

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

6933
    /**
6934
     * @hidden
6935
     */
6936
    protected _calculateGridBodyHeight(): number {
6937
        if (!this._height) {
10,424✔
6938
            return null;
798✔
6939
        }
6940
        const actualTheadRow = this.getTheadRowHeight();
9,626✔
6941
        const footerHeight = this.getFooterHeight();
9,626✔
6942
        const toolbarHeight = this.getToolbarHeight();
9,626✔
6943
        const pagingHeight = this.getPagingFooterHeight();
9,626✔
6944
        const groupAreaHeight = this.getGroupAreaHeight();
9,626✔
6945
        const scrHeight = this.getComputedHeight(this.scr.nativeElement);
9,626✔
6946
        const renderedHeight = toolbarHeight + actualTheadRow +
9,626✔
6947
            footerHeight + pagingHeight + groupAreaHeight +
6948
            scrHeight;
6949

6950
        let gridHeight = 0;
9,626✔
6951

6952
        if (this.isPercentHeight) {
9,626✔
6953
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('height');
2,035✔
6954
            const autoSize = this._shouldAutoSize(renderedHeight);
2,035✔
6955
            if (autoSize || computed.indexOf('%') !== -1) {
2,035✔
6956
                const bodyHeight = this.getDataBasedBodyHeight();
850✔
6957
                return bodyHeight > 0 ? bodyHeight : null;
850✔
6958
            }
6959
            gridHeight = parseFloat(computed);
1,185✔
6960
        } else {
6961
            gridHeight = parseInt(this._height, 10);
7,591✔
6962
        }
6963
        const height = Math.abs(gridHeight - renderedHeight);
8,776✔
6964

6965
        if (Math.round(height) === 0 || isNaN(gridHeight)) {
8,776✔
6966
            const bodyHeight = this.defaultTargetBodyHeight;
18✔
6967
            return bodyHeight > 0 ? bodyHeight : null;
18✔
6968
        }
6969
        return height;
8,758✔
6970
    }
6971

6972
    protected checkContainerSizeChange() {
6973
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
98!
6974
        const origHeight = parentElement.offsetHeight;
98✔
6975
        this.nativeElement.style.display = 'none';
98✔
6976
        const height = parentElement.offsetHeight;
98✔
6977
        this.nativeElement.style.display = '';
98✔
6978
        return origHeight !== height;
98✔
6979
    }
6980

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

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

7013
        return width - this.getPinnedWidth(takeHidden);
23,389✔
7014
    }
7015

7016
    /**
7017
     * @hidden
7018
     */
7019
    protected _summaries(fieldName: string, hasSummary: boolean, summaryOperand?: any) {
7020
        const column = this.gridAPI.get_column_by_name(fieldName);
27✔
7021
        if (column) {
27✔
7022
            column.hasSummary = hasSummary;
27✔
7023
            if (summaryOperand) {
27✔
7024
                if (this.rootSummariesEnabled) {
2✔
7025
                    this.summaryService.retriggerRootPipe++;
2✔
7026
                }
7027
                column.summaries = summaryOperand;
2✔
7028
            }
7029
        }
7030
    }
7031

7032
    /**
7033
     * @hidden
7034
     */
7035
    protected _multipleSummaries(expressions: ISummaryExpression[], hasSummary: boolean) {
7036
        expressions.forEach((element) => {
7✔
7037
            this._summaries(element.fieldName, hasSummary, element.customSummary);
9✔
7038
        });
7039
    }
7040
    /**
7041
     * @hidden
7042
     */
7043
    protected _disableMultipleSummaries(expressions) {
7044
        expressions.forEach((column) => {
5✔
7045
            const columnName = column && column.fieldName ? column.fieldName : column;
13✔
7046
            this._summaries(columnName, false);
13✔
7047
        });
7048
    }
7049

7050
    /**
7051
     * @hidden
7052
     */
7053
    public resolveDataTypes(rec) {
7054
        if (typeof rec === 'number') {
6,244✔
7055
            return GridColumnDataType.Number;
3,666✔
7056
        } else if (typeof rec === 'boolean') {
2,578✔
7057
            return GridColumnDataType.Boolean;
152✔
7058
        } else if (typeof rec === 'object' && rec instanceof Date) {
2,426✔
7059
            return GridColumnDataType.Date;
143✔
7060
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
2,283✔
7061
            return GridColumnDataType.Image;
1✔
7062
        }
7063
        return GridColumnDataType.String;
2,282✔
7064
    }
7065

7066
    /**
7067
     * @hidden
7068
     */
7069
    protected autogenerateColumns() {
7070
        const data = this.gridAPI.get_data();
636✔
7071
        const fields = this.generateDataFields(data);
636✔
7072
        const columns = [];
636✔
7073

7074
        fields.forEach((field) => {
636✔
7075
            const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
4,080✔
7076
            ref.instance.field = field;
4,080✔
7077
            ref.instance.dataType = this.resolveDataTypes(data[0][field]);
4,080✔
7078
            ref.changeDetectorRef.detectChanges();
4,080✔
7079
            columns.push(ref.instance);
4,080✔
7080
        });
7081
        this._autoGeneratedCols = columns;
636✔
7082

7083
        this.updateColumns(columns);
636✔
7084
        this.columnsAutogenerated.emit({ columns: this._autoGeneratedCols });
636✔
7085
    }
7086

7087
    protected generateDataFields(data: any[]): string[] {
7088
        return Object.keys(data && data.length !== 0 ? data[0] : [])
642✔
7089
            .filter(key => !this.autoGenerateExclude.includes(key));
4,341✔
7090
    }
7091

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

7118
            if (cb) {
23,545✔
7119
                cb(column);
23,545✔
7120
            }
7121
        });
7122

7123
        this.updateColumns(collection);
3,620✔
7124

7125
        if (this.hasColumnLayouts) {
3,620✔
7126
            collection.forEach((column: IgxColumnComponent) => {
146✔
7127
                column.populateVisibleIndexes();
1,381✔
7128
            });
7129
        }
7130
    }
7131

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

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

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

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

7197
        if (this.cellSelection === GridSelectionMode.single && activeEl) {
246✔
7198
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
10✔
7199
        }
7200

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

7205
        if (columnData) {
246!
7206
            selectedData = columnData;
×
7207
        }
7208

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

7258
        if (keys.length) {
246!
7259
            keysAndData.push(selectedData);
×
7260
            keysAndData.push(keys);
×
7261
            return keysAndData;
×
7262
        } else {
7263
            return selectedData;
246✔
7264
        }
7265
    }
7266

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

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

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

7331
        if (this.isColumnWidthSum) {
26✔
7332
            this.calcWidth = this.getColumnWidthSum();
3✔
7333
        }
7334
    }
7335

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

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

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

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

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

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

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

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

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

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

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

7425

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

7612

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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