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

IgniteUI / igniteui-angular / 11630388350

01 Nov 2024 01:47PM CUT coverage: 91.602% (-0.004%) from 91.606%
11630388350

Pull #14999

github

web-flow
Merge 0633c06bf into 59b4b5fb5
Pull Request #14999: fix(grid): add coerceToInt attribute to IPinningConfig and pagingMode props

12962 of 15185 branches covered (85.36%)

26267 of 28675 relevant lines covered (91.6%)

34037.8 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

509

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1061

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

1075
    /* End of toolbar related definitions */
1076

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

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

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

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

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

1118

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1216

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

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

1242

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1594

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1795

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2033
    /** @hidden @internal */
2034
    public dragRowID = null;
4,048✔
2035

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

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

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

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

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

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

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

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

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

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

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

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

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

2197
    public get isLoading(): boolean {
2198
        return this._isLoading;
93,755✔
2199
    }
2200

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

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

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

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

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

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

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

2280
            if (!this._init) {
711✔
2281
                this.calcGridHeadRow();
18✔
2282
            }
2283

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2494

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2653
    /**
2654

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

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

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

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

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

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

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

2717
        if (!activeElem || !Object.keys(activeElem).length) {
133,452✔
2718
            return this.id;
118,094✔
2719
        }
2720

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

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

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

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

2755
    /**
2756
     * @hidden @internal
2757
     */
2758
    public get maxLevelHeaderDepth() {
2759
        if (this._maxLevelHeaderDepth === null) {
32,786✔
2760
            this._maxLevelHeaderDepth = this.hasColumnLayouts ?
2,634✔
2761
                this._columns.reduce((acc, col) => Math.max(acc, col.rowStart), 0) :
1,381✔
2762
                this._columns.reduce((acc, col) => Math.max(acc, col.level), 0);
15,867✔
2763
        }
2764
        return this._maxLevelHeaderDepth;
32,786✔
2765
    }
2766

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3093
    protected _pinnedRecordIDs = [];
4,048✔
3094

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

3103

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

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

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

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

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

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

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

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

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

3203
    private _columnWidth: string;
3204

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

3214
    private lastAddedRowIndex;
3215
    private _currencyPositionLeft: boolean;
3216

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

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

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

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

3251
    protected get isCustomSetRowHeight(): boolean {
3252
        return !isNaN(this._rowHeight);
1,197,321✔
3253
    }
3254

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

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

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

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

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

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

3320
        return result;
11,384✔
3321
    }
3322

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3633
        this.subscribeToTransactions();
3,539✔
3634

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

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

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

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

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

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

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

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

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

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

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

3726

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3959
    /**
3960
     * @hidden @internal
3961
     */
3962
    public resetHorizontalVirtualization() {
3963
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
48,918✔
3964
        this._horizontalForOfs = [
6,675✔
3965
            ...this._dataRowList.filter(elementFilter).map(item => item.virtDirRow),
45,731✔
3966
            ...this._summaryRowList.filter(elementFilter).map(item => item.virtDirRow)
3,187✔
3967
        ];
3968
    }
3969

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

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

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

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

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

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

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

4048
    }
4049

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

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

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

4074
        if (this._cdrRequests) {
9,570✔
4075
            this.resetNotifyChanges();
4,187✔
4076
            this.cdr.detectChanges();
4,187✔
4077
        }
4078
    }
4079

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

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

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

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

4103
        this.textHighlightService.destroyGroup(this.id);
3,346✔
4104

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

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

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

4118

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

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

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

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

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

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

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

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

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

4231

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

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

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

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

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

4284

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4622
        this.columnMovingEnd.emit(eventArgs);
150✔
4623

4624
        if (eventArgs.cancel) {
150!
4625
            return;
×
4626
        }
4627

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5102
        return true;
132✔
5103
    }
5104

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

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

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

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

5138
        return true;
23✔
5139
    }
5140

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5313

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

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

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

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

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

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

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

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

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

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

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

5402
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
683,755✔
5403

5404

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

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

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

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

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

5443
        return columnWidth + 'px';
71,227✔
5444
    }
5445

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

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

5478
        return sum;
44,924✔
5479
    }
5480

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

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

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

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

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

5523
        return 0;
×
5524
    }
5525

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5833

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

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

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

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

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

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

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

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

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

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

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

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

5927
        event.preventDefault();
9✔
5928

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

5934
        return result;
9✔
5935
    }
5936

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

6276
        return success;
6✔
6277
    }
6278

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

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

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

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

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

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

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

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

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

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

6429
            });
6430
        }
6431

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

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

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

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

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

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

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

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

6499

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

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

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

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

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

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

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

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

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

6585

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

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

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

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

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

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

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

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

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

6693

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

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

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

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

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

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

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

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

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

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

6768
            this.resetCaches();
69✔
6769

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

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

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

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

6819
        if (this.rowEditable) {
7,837✔
6820
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
855✔
6821
        }
6822

6823
        if (this.filteringService.isFilterRowVisible) {
7,837✔
6824
            this.filteringRow.resetChipsArea();
157✔
6825
        }
6826

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

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

6862
    /**
6863
     * @hidden
6864
     * @internal
6865
     */
6866
    protected calcGridHeadRow() {
6867
        if (this.maxLevelHeaderDepth) {
9,811✔
6868
            this._baseFontSize = parseFloat(getComputedStyle(this.document.documentElement).getPropertyValue('font-size'));
798✔
6869
            const hasFilterRow = this._allowFiltering && this._filterMode === FilterMode.quickFilter;
798✔
6870
            const minSize = (this.maxLevelHeaderDepth + 1 + (hasFilterRow ? 1 : 0)) * this.defaultRowHeight / this._baseFontSize;
798✔
6871
            this.theadRow.nativeElement.style.minHeight = `${minSize}rem`;
798✔
6872
        }
6873
    }
6874

6875
    /**
6876
     * @hidden
6877
     * Sets TBODY height i.e. this.calcHeight
6878
     */
6879
    protected calculateGridHeight() {
6880
        this.calcGridHeadRow();
10,383✔
6881

6882
        this.calcHeight = this._calculateGridBodyHeight();
10,383✔
6883
        if (this.pinnedRowHeight && this.calcHeight) {
10,383✔
6884
            this.calcHeight -= this.pinnedRowHeight;
108✔
6885
        }
6886
    }
6887

6888
    /**
6889
     * @hidden
6890
     */
6891
    protected getGroupAreaHeight(): number {
6892
        return 0;
2,656✔
6893
    }
6894

6895
    /**
6896
     * @hidden
6897
     */
6898
    protected getComputedHeight(elem) {
6899
        return elem.offsetHeight ? parseFloat(this.document.defaultView.getComputedStyle(elem).getPropertyValue('height')) : 0;
36,930✔
6900
    }
6901
    /**
6902
     * @hidden
6903
     */
6904
    protected getFooterHeight(): number {
6905
        return this.summaryRowHeight || this.getComputedHeight(this.tfoot.nativeElement);
9,585✔
6906
    }
6907
    /**
6908
     * @hidden
6909
     */
6910
    protected getTheadRowHeight(): number {
6911
        // D.P.: Before CSS loads,theadRow computed height will be 'auto'->NaN, so use 0 fallback
6912
        const height = this.getComputedHeight(this.theadRow.nativeElement) || 0;
9,585✔
6913
        return (!this.allowFiltering || (this.allowFiltering && this.filterMode !== FilterMode.quickFilter)) ?
9,585✔
6914
            height - this.getFilterCellHeight() :
6915
            height;
6916
    }
6917

6918
    /**
6919
     * @hidden
6920
     */
6921
    protected getToolbarHeight(): number {
6922
        let toolbarHeight = 0;
9,585✔
6923
        if (this.toolbar.first) {
9,585✔
6924
            toolbarHeight = this.getComputedHeight(this.toolbar.first.nativeElement);
395✔
6925
        }
6926
        return toolbarHeight;
9,585✔
6927
    }
6928

6929
    /**
6930
     * @hidden
6931
     */
6932
    protected getPagingFooterHeight(): number {
6933
        let pagingHeight = 0;
9,585✔
6934
        if (this.footer) {
9,585✔
6935
            const height = this.getComputedHeight(this.footer.nativeElement);
9,585✔
6936
            pagingHeight = this.footer.nativeElement.firstElementChild ?
9,585✔
6937
                height : 0;
6938
        }
6939
        return pagingHeight;
9,585✔
6940
    }
6941

6942
    /**
6943
     * @hidden
6944
     */
6945
    protected getFilterCellHeight(): number {
6946
        const headerGroupNativeEl = (this.headerGroupsList.length !== 0) ?
7,930✔
6947
            this.headerGroupsList[0].nativeElement : null;
6948
        const filterCellNativeEl = (headerGroupNativeEl) ?
7,930✔
6949
            headerGroupNativeEl.querySelector('igx-grid-filtering-cell') as HTMLElement : null;
6950
        return (filterCellNativeEl) ? filterCellNativeEl.offsetHeight : 0;
7,930✔
6951
    }
6952

6953
    /**
6954
     * @hidden
6955
     */
6956
    protected _calculateGridBodyHeight(): number {
6957
        if (!this._height) {
10,383✔
6958
            return null;
798✔
6959
        }
6960
        const actualTheadRow = this.getTheadRowHeight();
9,585✔
6961
        const footerHeight = this.getFooterHeight();
9,585✔
6962
        const toolbarHeight = this.getToolbarHeight();
9,585✔
6963
        const pagingHeight = this.getPagingFooterHeight();
9,585✔
6964
        const groupAreaHeight = this.getGroupAreaHeight();
9,585✔
6965
        const scrHeight = this.getComputedHeight(this.scr.nativeElement);
9,585✔
6966
        const renderedHeight = toolbarHeight + actualTheadRow +
9,585✔
6967
            footerHeight + pagingHeight + groupAreaHeight +
6968
            scrHeight;
6969

6970
        let gridHeight = 0;
9,585✔
6971

6972
        if (this.isPercentHeight) {
9,585✔
6973
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('height');
2,009✔
6974
            const autoSize = this._shouldAutoSize(renderedHeight);
2,009✔
6975
            if (autoSize || computed.indexOf('%') !== -1) {
2,009✔
6976
                const bodyHeight = this.getDataBasedBodyHeight();
847✔
6977
                return bodyHeight > 0 ? bodyHeight : null;
847✔
6978
            }
6979
            gridHeight = parseFloat(computed);
1,162✔
6980
        } else {
6981
            gridHeight = parseInt(this._height, 10);
7,576✔
6982
        }
6983
        const height = Math.abs(gridHeight - renderedHeight);
8,738✔
6984

6985
        if (Math.round(height) === 0 || isNaN(gridHeight)) {
8,738✔
6986
            const bodyHeight = this.defaultTargetBodyHeight;
16✔
6987
            return bodyHeight > 0 ? bodyHeight : null;
16✔
6988
        }
6989
        return height;
8,722✔
6990
    }
6991

6992
    protected checkContainerSizeChange() {
6993
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
95!
6994
        const origHeight = parentElement.offsetHeight;
95✔
6995
        this.nativeElement.style.display = 'none';
95✔
6996
        const height = parentElement.offsetHeight;
95✔
6997
        this.nativeElement.style.display = '';
95✔
6998
        return origHeight !== height;
95✔
6999
    }
7000

7001
    protected _shouldAutoSize(renderedHeight) {
7002
        this.tbody.nativeElement.style.display = 'none';
1,272✔
7003
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
1,272✔
7004
        let res = !parentElement ||
1,272✔
7005
            parentElement.clientHeight === 0 ||
7006
            parentElement.clientHeight === renderedHeight;
7007
        if (parentElement && (res || this._autoSize)) {
1,272✔
7008
            // If grid causes the parent container to extend (for example when container is flex)
7009
            // we should always auto-size since the actual size of the container will continuously change as the grid renders elements.
7010
            this._autoSize = false;
95✔
7011
            res = this.checkContainerSizeChange();
95✔
7012
        }
7013
        this.tbody.nativeElement.style.display = '';
1,272✔
7014
        return res;
1,272✔
7015
    }
7016

7017
    /**
7018
     * @hidden
7019
     * Gets calculated width of the unpinned area
7020
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
7021
     */
7022
    protected getUnpinnedWidth(takeHidden = false) {
23,380✔
7023
        let width = this.isPercentWidth ?
23,380✔
7024
            this.calcWidth :
7025
            parseInt(this.width, 10) || parseInt(this.hostWidth, 10) || this.calcWidth;
13,102✔
7026
        if (this.hasVerticalScroll() && !this.isPercentWidth) {
23,380✔
7027
            width -= this.scrollSize;
4,279✔
7028
        }
7029
        if (!this.isPinningToStart) {
23,380✔
7030
            width -= this.featureColumnsWidth();
62✔
7031
        }
7032

7033
        return width - this.getPinnedWidth(takeHidden);
23,380✔
7034
    }
7035

7036
    /**
7037
     * @hidden
7038
     */
7039
    protected _summaries(fieldName: string, hasSummary: boolean, summaryOperand?: any) {
7040
        const column = this.gridAPI.get_column_by_name(fieldName);
24✔
7041
        if (column) {
24✔
7042
            column.hasSummary = hasSummary;
24✔
7043
            if (summaryOperand) {
24✔
7044
                if (this.rootSummariesEnabled) {
2✔
7045
                    this.summaryService.retriggerRootPipe++;
2✔
7046
                }
7047
                column.summaries = summaryOperand;
2✔
7048
            }
7049
        }
7050
    }
7051

7052
    /**
7053
     * @hidden
7054
     */
7055
    protected _multipleSummaries(expressions: ISummaryExpression[], hasSummary: boolean) {
7056
        expressions.forEach((element) => {
4✔
7057
            this._summaries(element.fieldName, hasSummary, element.customSummary);
6✔
7058
        });
7059
    }
7060
    /**
7061
     * @hidden
7062
     */
7063
    protected _disableMultipleSummaries(expressions) {
7064
        expressions.forEach((column) => {
5✔
7065
            const columnName = column && column.fieldName ? column.fieldName : column;
13✔
7066
            this._summaries(columnName, false);
13✔
7067
        });
7068
    }
7069

7070
    /**
7071
     * @hidden
7072
     */
7073
    public resolveDataTypes(rec) {
7074
        if (typeof rec === 'number') {
6,225✔
7075
            return GridColumnDataType.Number;
3,650✔
7076
        } else if (typeof rec === 'boolean') {
2,575✔
7077
            return GridColumnDataType.Boolean;
152✔
7078
        } else if (typeof rec === 'object' && rec instanceof Date) {
2,423✔
7079
            return GridColumnDataType.Date;
143✔
7080
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
2,280✔
7081
            return GridColumnDataType.Image;
1✔
7082
        }
7083
        return GridColumnDataType.String;
2,279✔
7084
    }
7085

7086
    /**
7087
     * @hidden
7088
     */
7089
    protected autogenerateColumns() {
7090
        const data = this.gridAPI.get_data();
635✔
7091
        const fields = this.generateDataFields(data);
635✔
7092
        const columns = [];
635✔
7093

7094
        fields.forEach((field) => {
635✔
7095
            const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
4,073✔
7096
            ref.instance.field = field;
4,073✔
7097
            ref.instance.dataType = this.resolveDataTypes(data[0][field]);
4,073✔
7098
            ref.changeDetectorRef.detectChanges();
4,073✔
7099
            columns.push(ref.instance);
4,073✔
7100
        });
7101
        this._autoGeneratedCols = columns;
635✔
7102

7103
        this.updateColumns(columns);
635✔
7104
        this.columnsAutogenerated.emit({ columns: this._autoGeneratedCols });
635✔
7105
    }
7106

7107
    protected generateDataFields(data: any[]): string[] {
7108
        return Object.keys(data && data.length !== 0 ? data[0] : [])
641✔
7109
            .filter(key => !this.autoGenerateExclude.includes(key));
4,333✔
7110
    }
7111

7112
    /**
7113
     * @hidden
7114
     */
7115
    protected initColumns(collection: IgxColumnComponent[], cb: (args: any) => void = null) {
×
7116
        this._columnGroups = collection.some(col => col.columnGroup);
17,492✔
7117
        if (this.hasColumnLayouts) {
3,612✔
7118
            // Set overall row layout size
7119
            collection.forEach((col) => {
146✔
7120
                if (col.columnLayout) {
1,391✔
7121
                    const layoutSize = col.children ?
289!
7122
                        col.children.reduce((acc, val) => Math.max(val.rowStart + val.gridRowSpan - 1, acc), 1) :
1,092✔
7123
                        1;
7124
                    this._multiRowLayoutRowSize = Math.max(layoutSize, this._multiRowLayoutRowSize);
289✔
7125
                }
7126
            });
7127
        }
7128
        if (this.hasColumnLayouts && this.hasColumnGroups) {
3,612✔
7129
            // invalid configuration - multi-row and column groups
7130
            // remove column groups
7131
            const columnLayoutColumns = collection.filter((col) => col.columnLayout || col.columnLayoutChild);
1,391✔
7132
            collection = columnLayoutColumns;
146✔
7133
        }
7134
        this._maxLevelHeaderDepth = null;
3,612✔
7135
        collection.forEach((column: IgxColumnComponent) => {
3,612✔
7136
            column.defaultWidth = this.columnWidthSetByUser ? this._columnWidth : column.defaultWidth ? column.defaultWidth : '';
23,474✔
7137

7138
            if (cb) {
23,474✔
7139
                cb(column);
23,474✔
7140
            }
7141
        });
7142

7143
        this.updateColumns(collection);
3,612✔
7144

7145
        if (this.hasColumnLayouts) {
3,612✔
7146
            collection.forEach((column: IgxColumnComponent) => {
146✔
7147
                column.populateVisibleIndexes();
1,381✔
7148
            });
7149
        }
7150
    }
7151

7152
    /**
7153
     * @hidden
7154
     */
7155
    protected reinitPinStates() {
7156
        this._pinnedColumns = this._columns
191✔
7157
            .filter((c) => c.pinned).sort((a, b) => this._pinnedColumns.indexOf(a) - this._pinnedColumns.indexOf(b));
2,409✔
7158
        this._unpinnedColumns = this.hasColumnGroups ? this._columns.filter((c) => !c.pinned) :
2,109✔
7159
            this._columns.filter((c) => !c.pinned)
300✔
7160
                .sort((a, b) => this._unpinnedColumns.indexOf(a) - this._unpinnedColumns.indexOf(b));
214✔
7161
    }
7162

7163
    protected extractDataFromSelection(source: any[], formatters = false, headers = false, columnData?: any[]): any[] {
×
7164
        let columnsArray: IgxColumnComponent[];
7165
        let record = {};
246✔
7166
        let selectedData = [];
246✔
7167
        let keys = [];
246✔
7168
        const selectionCollection = new Map();
246✔
7169
        const keysAndData = [];
246✔
7170
        const activeEl = this.selectionService.activeElement;
246✔
7171

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

7206
        const totalItems = (this as any).totalItemCount ?? 0;
246✔
7207
        const isRemote = totalItems && totalItems > this.dataView.length;
246!
7208
        let selectionMap;
7209
        if (this.type === 'hierarchical' && selectionCollection.size > 0) {
246!
7210
            selectionMap = isRemote ? Array.from(selectionCollection) :
×
7211
                Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length);
×
7212
        } else {
7213
            selectionMap = isRemote ? Array.from(this.selectionService.selection) :
246!
7214
                Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length);
883✔
7215
        }
7216

7217
        if (this.cellSelection === GridSelectionMode.single && activeEl) {
246✔
7218
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
10✔
7219
        }
7220

7221
        if (this.cellSelection === GridSelectionMode.none && activeEl) {
246✔
7222
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
5✔
7223
        }
7224

7225
        if (columnData) {
246!
7226
            selectedData = columnData;
×
7227
        }
7228

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

7278
        if (keys.length) {
246!
7279
            keysAndData.push(selectedData);
×
7280
            keysAndData.push(keys);
×
7281
            return keysAndData;
×
7282
        } else {
7283
            return selectedData;
246✔
7284
        }
7285
    }
7286

7287
    protected getSelectableColumnsAt(index) {
7288
        if (this.hasColumnLayouts) {
2,512✔
7289
            const visibleLayoutColumns = this.visibleColumns
2✔
7290
                .filter(col => col.columnLayout)
20✔
7291
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
2✔
7292
            const colLayout = visibleLayoutColumns[index];
2✔
7293
            return colLayout ? colLayout.children.toArray() : [];
2!
7294
        } else {
7295
            const visibleColumns = this.visibleColumns
2,510✔
7296
                .filter(col => !col.columnGroup)
14,120✔
7297
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
11,790✔
7298
            return [visibleColumns[index]];
2,510✔
7299
        }
7300
    }
7301

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

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

7352
    protected extractDataFromColumnsSelection(source: any[], formatters = false, headers = false): any[] {
×
7353
        let record = {};
20✔
7354
        const selectedData = [];
20✔
7355
        const selectedColumns = this.selectedColumns();
20✔
7356
        if (selectedColumns.length === 0) {
20✔
7357
            return [];
12✔
7358
        }
7359

7360
        for (const data of source) {
8✔
7361
            selectedColumns.forEach((col) => {
71✔
7362
                const key = headers ? col.header || col.field : col.field;
142!
7363
                record[key] = formatters && col.formatter ? col.formatter(data[col.field], data)
142!
7364
                    : data[col.field];
7365
            });
7366

7367
            if (Object.keys(record).length) {
71✔
7368
                selectedData.push(record);
71✔
7369
            }
7370
            record = {};
71✔
7371
        }
7372
        return selectedData;
8✔
7373
    }
7374

7375
    /**
7376
     * @hidden
7377
     */
7378
    protected initPinning() {
7379
        this.calculateGridWidth();
3,907✔
7380
        this.resetCaches();
3,907✔
7381
        this.handleColumnPinningForGroups();
3,907✔
7382
        this.notifyChanges();
3,907✔
7383
    }
7384

7385
    /**
7386
     * @hidden
7387
     */
7388
    protected scrollTo(row: any | number, column: any | number, inCollection = this._filteredSortedUnpinnedData): void {
3✔
7389
        let delayScrolling = false;
106✔
7390

7391
        if (this.paginator && typeof (row) !== 'number') {
106✔
7392
            const rowIndex = inCollection.indexOf(row);
16✔
7393
            const page = Math.floor(rowIndex / this.perPage);
16✔
7394

7395
            if (this.page !== page) {
16✔
7396
                delayScrolling = true;
5✔
7397
                this.page = page;
5✔
7398
            }
7399
        }
7400

7401
        if (delayScrolling) {
106✔
7402
            this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
5✔
7403
                this.scrollDirective(this.verticalScrollContainer,
5✔
7404
                    typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
5!
7405
            });
7406
        } else {
7407
            this.scrollDirective(this.verticalScrollContainer,
101✔
7408
                typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
101✔
7409
        }
7410

7411
        this.scrollToHorizontally(column);
106✔
7412
    }
7413

7414
    /**
7415
     * @hidden
7416
     */
7417
    protected scrollToHorizontally(column: any | number) {
7418
        let columnIndex = typeof column === 'number' ? column : this.getColumnByName(column).visibleIndex;
191✔
7419
        const scrollRow = this.rowList.find(r => !!r.virtDirRow);
233✔
7420
        const virtDir = scrollRow ? scrollRow.virtDirRow : null;
191✔
7421
        if (this.isPinningToStart && this.pinnedColumns.length) {
191✔
7422
            if (columnIndex >= this.pinnedColumns.length) {
1!
7423
                columnIndex -= this.pinnedColumns.length;
×
7424
                this.scrollDirective(virtDir, columnIndex);
×
7425
            }
7426
        } else {
7427
            this.scrollDirective(virtDir, columnIndex);
190✔
7428
        }
7429
    }
7430

7431
    /**
7432
     * @hidden
7433
     */
7434
    protected scrollDirective(directive: IgxGridForOfDirective<any, any[]>, goal: number): void {
7435
        if (!directive) {
381✔
7436
            return;
1✔
7437
        }
7438
        directive.scrollTo(goal);
380✔
7439
    }
7440

7441

7442
    /**
7443
     * @hidden
7444
     */
7445
    protected getColumnWidthSum(): number {
7446
        let colSum = 0;
38✔
7447
        const cols = this.hasColumnLayouts ?
38!
7448
            this.visibleColumns.filter(x => x.columnLayout) : this.visibleColumns.filter(x => !x.columnGroup);
248✔
7449
        cols.forEach((item) => {
38✔
7450
            colSum += parseInt((item.calcWidth || item.defaultWidth), 10) || this.minColumnWidth;
218!
7451
        });
7452
        if (!colSum) {
38!
7453
            return null;
×
7454
        }
7455
        this.cdr.detectChanges();
38✔
7456
        colSum += this.featureColumnsWidth();
38✔
7457
        return colSum;
38✔
7458
    }
7459

7460
    /**
7461
     * Notify changes, reset cache and populateVisibleIndexes.
7462
     *
7463
     * @hidden
7464
     */
7465
    private _columnsReordered(column: IgxColumnComponent) {
7466
        this.notifyChanges();
128✔
7467
        // after reordering is done reset cached column collections.
7468
        this.resetColumnCollections();
128✔
7469
        column.resetCaches();
128✔
7470
    }
7471

7472
    protected buildDataView(_data: any[]) {
7473
        this._dataView = this.isRowPinningToTop ?
13,608✔
7474
            [...this.pinnedDataView, ...this.unpinnedDataView] :
7475
            [...this.unpinnedDataView, ...this.pinnedDataView];
7476
    }
7477

7478
    private _applyWidthHostBinding() {
7479
        let width = this._width;
7,837✔
7480
        if (width === null) {
7,837✔
7481
            let currentWidth = this.calcWidth;
2✔
7482
            if (this.hasVerticalScroll()) {
2!
7483
                currentWidth += this.scrollSize;
×
7484
            }
7485
            width = currentWidth + 'px';
2✔
7486
            this.resetCaches();
2✔
7487
        }
7488
        this._hostWidth = width;
7,837✔
7489
        this.cdr.markForCheck();
7,837✔
7490
    }
7491

7492
    protected verticalScrollHandler(event) {
7493
        this.verticalScrollContainer.onScroll(event);
317✔
7494
        this.disableTransitions = true;
317✔
7495

7496
        this.zone.run(() => {
317✔
7497
            this.zone.onStable.pipe(first()).subscribe(() => {
317✔
7498
                this.verticalScrollContainer.chunkLoad.emit(this.verticalScrollContainer.state);
317✔
7499
                if (this.rowEditable) {
317✔
7500
                    this.changeRowEditingOverlayStateOnScroll(this.crudService.rowInEditMode);
14✔
7501
                }
7502
            });
7503
        });
7504
        this.disableTransitions = false;
317✔
7505

7506
        this.hideOverlays();
317✔
7507
        this.actionStrip?.hide();
317✔
7508
        if (this.actionStrip) {
317✔
7509
            this.actionStrip.context = null;
9✔
7510
        }
7511
        const args: IGridScrollEventArgs = {
317✔
7512
            direction: 'vertical',
7513
            event,
7514
            scrollPosition: this.verticalScrollContainer.scrollPosition
7515
        };
7516
        this.gridScroll.emit(args);
317✔
7517
    }
7518

7519
    protected horizontalScrollHandler(event) {
7520
        const scrollLeft = event.target.scrollLeft;
311✔
7521
        this.headerContainer.onHScroll(scrollLeft);
311✔
7522
        this._horizontalForOfs.forEach(vfor => vfor.onHScroll(scrollLeft));
1,960✔
7523
        this.cdr.markForCheck();
311✔
7524

7525
        this.zone.run(() => {
311✔
7526
            this.zone.onStable.pipe(first()).subscribe(() => {
311✔
7527
                this.parentVirtDir.chunkLoad.emit(this.headerContainer.state);
309✔
7528
                requestAnimationFrame(() => {
309✔
7529
                    this.autoSizeColumnsInView();
309✔
7530
                });
7531
            });
7532
        });
7533
        if (!this.navigation.isColumnFullyVisible(this.navigation.lastColumnIndex)) {
311✔
7534
            this.hideOverlays();
204✔
7535
        }
7536
        const args: IGridScrollEventArgs = { direction: 'horizontal', event, scrollPosition: this.headerContainer.scrollPosition };
311✔
7537
        this.gridScroll.emit(args);
311✔
7538
    }
7539

7540
    protected get renderedActualRowHeight() {
7541
        let border = 1;
495✔
7542
        if (this.rowList.toArray().length > 0) {
495✔
7543
            const rowStyles = document.defaultView.getComputedStyle(this.rowList.first.nativeElement);
348✔
7544
            border = rowStyles.borderBottomWidth ? Math.ceil(parseFloat(rowStyles.borderBottomWidth)) : border;
348✔
7545
        }
7546
        return this.rowHeight + border;
495✔
7547
    }
7548

7549
    private executeCallback(rowIndex, visibleColIndex = -1, cb: (args: any) => void = null) {
×
7550
        if (!cb) {
831✔
7551
            return;
280✔
7552
        }
7553
        let row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
1,622✔
7554
        if (!row) {
551✔
7555
            if ((this as any).totalItemCount) {
8!
7556
                this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
×
7557
                    this.cdr.detectChanges();
×
7558
                    row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
×
7559
                    const cbArgs = this.getNavigationArguments(row, visibleColIndex);
×
7560
                    cb(cbArgs);
×
7561
                });
7562
            }
7563
            const dataViewIndex = this._getDataViewIndex(rowIndex);
8✔
7564
            if (this.dataView[dataViewIndex].detailsData) {
8✔
7565
                this.navigation.setActiveNode({ row: rowIndex });
8✔
7566
                this.cdr.detectChanges();
8✔
7567
            }
7568

7569
            return;
8✔
7570
        }
7571
        const args = this.getNavigationArguments(row, visibleColIndex);
543✔
7572
        cb(args);
543✔
7573
    }
7574

7575
    private getNavigationArguments(row, visibleColIndex) {
7576
        let targetType: GridKeydownTargetType; let target;
7577
        switch (row.nativeElement.tagName.toLowerCase()) {
543!
7578
            case 'igx-grid-groupby-row':
7579
                targetType = 'groupRow';
30✔
7580
                target = row;
30✔
7581
                break;
30✔
7582
            case 'igx-grid-summary-row':
7583
                targetType = 'summaryCell';
50✔
7584
                target = visibleColIndex !== -1 ?
50!
7585
                    row.summaryCells.find(c => c.visibleColumnIndex === visibleColIndex) : row.summaryCells.first;
139✔
7586
                break;
50✔
7587
            case 'igx-child-grid-row':
7588
                targetType = 'hierarchicalRow';
×
7589
                target = row;
×
7590
                break;
×
7591
            default:
7592
                targetType = 'dataCell';
463✔
7593
                target = visibleColIndex !== -1 ? row.cells.find(c => c.visibleColumnIndex === visibleColIndex) : row.cells.first;
1,286!
7594
                break;
463✔
7595
        }
7596
        return { targetType, target };
543✔
7597
    }
7598

7599
    private getNextDataRowIndex(currentRowIndex, previous = false): number {
15✔
7600
        const resolvedIndex = this._getDataViewIndex(currentRowIndex);
30✔
7601
        if (currentRowIndex < 0 || (currentRowIndex === 0 && previous) || (resolvedIndex >= this.dataView.length - 1 && !previous)) {
30!
7602
            return currentRowIndex;
7✔
7603
        }
7604
        // find next/prev record that is editable.
7605
        const nextRowIndex = previous ? this.findPrevEditableDataRowIndex(currentRowIndex) :
23✔
7606
            this.dataView.findIndex((rec, index) =>
7607
                index > resolvedIndex && this.isEditableDataRecordAtIndex(index));
56✔
7608
        const nextDataIndex = this.getDataIndex(nextRowIndex);
23✔
7609
        return nextDataIndex !== -1 ? nextDataIndex : currentRowIndex;
23!
7610
    }
7611

7612
    /**
7613
     * Returns the previous editable row index or -1 if no such row is found.
7614
     *
7615
     * @param currentIndex The index of the current editable record.
7616
     */
7617
    private findPrevEditableDataRowIndex(currentIndex): number {
7618
        let i = this.dataView.length;
8✔
7619
        const resolvedIndex = this._getDataViewIndex(currentIndex);
8✔
7620
        while (i--) {
8✔
7621
            if (i < resolvedIndex && this.isEditableDataRecordAtIndex(i)) {
178✔
7622
                return i;
8✔
7623
            }
7624
        }
7625
        return -1;
×
7626
    }
7627

7628

7629
    /**
7630
     * Returns if the record at the specified data view index is a an editable data record.
7631
     * If record is group rec, summary rec, child rec, ghost rec. etc. it is not editable.
7632
     *
7633
     * @param dataViewIndex The index of that record in the data view.
7634
     *
7635
     */
7636
    // TODO: Consider moving it into CRUD
7637
    private isEditableDataRecordAtIndex(dataViewIndex) {
7638
        const rec = this.dataView[dataViewIndex];
29✔
7639
        return !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData &&
29✔
7640
            !this.isGhostRecordAtIndex(dataViewIndex);
7641
    }
7642

7643
    /**
7644
     * Returns if the record at the specified data view index is a ghost.
7645
     * If record is pinned but is not in pinned area then it is a ghost record.
7646
     *
7647
     * @param dataViewIndex The index of that record in the data view.
7648
     */
7649
    private isGhostRecordAtIndex(dataViewIndex) {
7650
        const isPinned = this.isRecordPinned(this.dataView[dataViewIndex]);
27✔
7651
        const isInPinnedArea = this.isRecordPinnedByViewIndex(dataViewIndex);
27✔
7652
        return isPinned && !isInPinnedArea;
27✔
7653
    }
7654

7655
    private isValidPosition(rowIndex, colIndex): boolean {
7656
        const rows = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).length;
97✔
7657
        const cols = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0 && !col.hidden).length;
777✔
7658
        if (rows < 1 || cols < 1) {
97✔
7659
            return false;
2✔
7660
        }
7661
        if (rowIndex > -1 && rowIndex < this.dataView.length &&
95✔
7662
            colIndex > - 1 && colIndex <= Math.max(...this.visibleColumns.map(c => c.visibleIndex))) {
694✔
7663
            return true;
93✔
7664
        }
7665
        return false;
2✔
7666
    }
7667

7668
    private find(text: string, increment: number, caseSensitive?: boolean, exactMatch?: boolean, scroll?: boolean, endEdit = true) {
195✔
7669
        if (!this.rowList) {
322!
7670
            return 0;
×
7671
        }
7672

7673
        if (endEdit) {
322✔
7674
            this.crudService.endEdit(false);
283✔
7675
        }
7676

7677
        if (!text) {
322!
7678
            this.clearSearch();
×
7679
            return 0;
×
7680
        }
7681

7682
        const caseSensitiveResolved = caseSensitive ? true : false;
322✔
7683
        const exactMatchResolved = exactMatch ? true : false;
322✔
7684
        let rebuildCache = false;
322✔
7685

7686
        if (this._lastSearchInfo.searchText !== text ||
322✔
7687
            this._lastSearchInfo.caseSensitive !== caseSensitiveResolved ||
7688
            this._lastSearchInfo.exactMatch !== exactMatchResolved) {
7689
            this._lastSearchInfo = {
84✔
7690
                searchText: text,
7691
                activeMatchIndex: 0,
7692
                caseSensitive: caseSensitiveResolved,
7693
                exactMatch: exactMatchResolved,
7694
                matchInfoCache: [],
7695
                matchCount: 0,
7696
                content: ''
7697
            };
7698

7699
            rebuildCache = true;
84✔
7700
        } else {
7701
            this._lastSearchInfo.activeMatchIndex += increment;
238✔
7702
        }
7703

7704
        if (rebuildCache) {
322✔
7705
            this.rowList.forEach((row) => {
84✔
7706
                if (row.cells) {
783✔
7707
                    row.cells.forEach((c: IgxGridCellComponent) => {
739✔
7708
                        c.highlightText(text, caseSensitiveResolved, exactMatchResolved);
3,091✔
7709
                    });
7710
                }
7711
            });
7712

7713
            this.rebuildMatchCache();
84✔
7714
        }
7715

7716
        if (this._lastSearchInfo.activeMatchIndex >= this._lastSearchInfo.matchCount) {
322✔
7717
            this._lastSearchInfo.activeMatchIndex = 0;
28✔
7718
        } else if (this._lastSearchInfo.activeMatchIndex < 0) {
294✔
7719
            this._lastSearchInfo.activeMatchIndex = this._lastSearchInfo.matchCount - 1;
8✔
7720
        }
7721

7722
        if (this._lastSearchInfo.matchCount > 0) {
322✔
7723
            const matchInfo = this._lastSearchInfo.matchInfoCache[this._lastSearchInfo.activeMatchIndex];
304✔
7724
            this._lastSearchInfo = { ...this._lastSearchInfo };
304✔
7725

7726
            if (scroll !== false) {
304✔
7727
                this.scrollTo(matchInfo.row, matchInfo.column);
182✔
7728
            }
7729

7730
            this.textHighlightService.setActiveHighlight(this.id, {
304✔
7731
                column: matchInfo.column,
7732
                row: matchInfo.row,
7733
                index: matchInfo.index,
7734
                metadata: matchInfo.metadata,
7735
            });
7736

7737
        } else {
7738
            this.textHighlightService.clearActiveHighlight(this.id);
18✔
7739
        }
7740

7741
        return this._lastSearchInfo.matchCount;
322✔
7742
    }
7743

7744
    private rebuildMatchCache() {
7745
        this._lastSearchInfo.matchInfoCache = [];
211✔
7746

7747
        const caseSensitive = this._lastSearchInfo.caseSensitive;
211✔
7748
        const exactMatch = this._lastSearchInfo.exactMatch;
211✔
7749
        const searchText = caseSensitive ? this._lastSearchInfo.searchText : this._lastSearchInfo.searchText.toLowerCase();
211✔
7750
        const data = this.filteredSortedData;
211✔
7751
        const columnItems = this.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex);
908✔
7752

7753
        data.forEach((dataRow, rowIndex) => {
211✔
7754
            columnItems.forEach((c) => {
3,474✔
7755
                const pipeArgs = this.getColumnByName(c.field).pipeArgs;
14,791✔
7756
                const value = c.formatter ? c.formatter(resolveNestedPath(dataRow, c.field), dataRow) :
14,791!
7757
                    c.dataType === 'number' ? formatNumber(resolveNestedPath(dataRow, c.field), this.locale, pipeArgs.digitsInfo) :
14,791✔
7758
                        c.dataType === 'date'
11,115✔
7759
                            ? formatDate(resolveNestedPath(dataRow, c.field), pipeArgs.format, this.locale, pipeArgs.timezone)
7760
                            : resolveNestedPath(dataRow, c.field);
7761
                if (value !== undefined && value !== null && c.searchable) {
14,791✔
7762
                    let searchValue = caseSensitive ? String(value) : String(value).toLowerCase();
14,576✔
7763

7764
                    if (exactMatch) {
14,576✔
7765
                        if (searchValue === searchText) {
536✔
7766
                            const mic: IMatchInfoCache = {
9✔
7767
                                row: dataRow,
7768
                                column: c.field,
7769
                                index: 0,
7770
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7771
                            };
7772

7773
                            this._lastSearchInfo.matchInfoCache.push(mic);
9✔
7774
                        }
7775
                    } else {
7776
                        let occurrenceIndex = 0;
14,040✔
7777
                        let searchIndex = searchValue.indexOf(searchText);
14,040✔
7778

7779
                        while (searchIndex !== -1) {
14,040✔
7780
                            const mic: IMatchInfoCache = {
2,410✔
7781
                                row: dataRow,
7782
                                column: c.field,
7783
                                index: occurrenceIndex++,
7784
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7785
                            };
7786

7787
                            this._lastSearchInfo.matchInfoCache.push(mic);
2,410✔
7788

7789
                            searchValue = searchValue.substring(searchIndex + searchText.length);
2,410✔
7790
                            searchIndex = searchValue.indexOf(searchText);
2,410✔
7791
                        }
7792
                    }
7793
                }
7794
            });
7795
        });
7796

7797
        this._lastSearchInfo.matchCount = this._lastSearchInfo.matchInfoCache.length;
211✔
7798
    }
7799

7800
    protected updateDefaultRowHeight() {
7801
        if (this.dataRowList.length > 0 && this.dataRowList.first.cells && this.dataRowList.first.cells.length > 0) {
3,639✔
7802
            const height = parseFloat(this.document.defaultView.getComputedStyle(this.dataRowList.first.cells.first.nativeElement)?.getPropertyValue('height'));
111✔
7803
            if (height) {
111!
7804
                this._defaultRowHeight = height;
111✔
7805
            } else {
7806
                this._shouldRecalcRowHeight = true;
×
7807
            }
7808
        }
7809
    }
7810

7811
    // TODO: About to Move to CRUD
7812
    private configureRowEditingOverlay(rowID: any, useOuter = false) {
72✔
7813
        let settings = this.rowEditSettings;
279✔
7814
        const overlay = this.overlayService.getOverlayById(this.rowEditingOverlay.overlayId);
279✔
7815
        if (overlay) {
279✔
7816
            settings = overlay.settings;
73✔
7817
        }
7818
        settings.outlet = useOuter ? this.parentRowOutletDirective : this.rowOutletDirective;
279✔
7819
        this.rowEditPositioningStrategy.settings.container = this.tbody.nativeElement;
279✔
7820
        const pinned = this._pinnedRecordIDs.indexOf(rowID) !== -1;
279✔
7821
        const targetRow = !pinned ?
279✔
7822
            this.gridAPI.get_row_by_key(rowID) as IgxRowDirective
7823
            : this.pinnedRows.find(x => x.key === rowID) as IgxRowDirective;
8✔
7824
        if (!targetRow) {
279!
7825
            return;
×
7826
        }
7827
        settings.target = targetRow.element.nativeElement;
279✔
7828
        this.toggleRowEditingOverlay(true);
279✔
7829
    }
7830

7831
    private handleColumnPinningForGroups(): void {
7832
        // When a column is a group or is inside a group, pin all related.
7833
        const pinnedColumns = [];
3,907✔
7834
        const unpinnedColumns = [];
3,907✔
7835

7836
        this._pinnedColumns.forEach(col => {
3,907✔
7837
            if (col.parent) {
492✔
7838
                col.parent.pinned = true;
113✔
7839
            }
7840
            if (col.columnGroup) {
492✔
7841
                col.children.forEach(child => child.pinned = true);
112✔
7842
            }
7843
        });
7844

7845
        // Make sure we don't exceed unpinned area min width and get pinned and unpinned col collections.
7846
        // We take into account top level columns (top level groups and non groups).
7847
        // If top level is unpinned the pinning handles all children to be unpinned as well.
7848
        for (const column of this._columns) {
3,907✔
7849
            if (column.pinned && !column.parent) {
23,147✔
7850
                pinnedColumns.push(column);
380✔
7851
            } else if (column.pinned && column.parent) {
22,767✔
7852
                if (column.topLevelParent.pinned) {
115!
7853
                    pinnedColumns.push(column);
115✔
7854
                } else {
7855
                    column.pinned = false;
×
7856
                    unpinnedColumns.push(column);
×
7857
                }
7858
            } else {
7859
                unpinnedColumns.push(column);
22,652✔
7860
            }
7861
        }
7862
        // Assign the applicable collections.
7863
        this._pinnedColumns = pinnedColumns;
3,907✔
7864
        this._unpinnedColumns = unpinnedColumns;
3,907✔
7865
    }
7866

7867
    protected shouldRecreateColumns(oldData: any[] | null | undefined, newData: any[] | null | undefined): boolean {
7868
        if (!oldData || !oldData.length) return true;
70✔
7869
        if (!newData || !newData.length) return false;
43!
7870
        return Object.keys(oldData[0]).join() !== Object.keys(newData[0]).join();
43✔
7871
    }
7872

7873
    /**
7874
     * Clears the current navigation service active node
7875
     */
7876
    private clearActiveNode() {
7877
        this.navigation.lastActiveNode = this.navigation.activeNode;
72✔
7878
        this.navigation.activeNode = {} as IActiveNode;
72✔
7879
        this.notifyChanges();
72✔
7880
    }
7881
}
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