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

IgniteUI / igniteui-angular / 17235699916

26 Aug 2025 10:43AM UTC coverage: 91.577% (-0.007%) from 91.584%
17235699916

Pull #16173

github

web-flow
Merge d17471ed7 into 2818f3aea
Pull Request #16173: Export IDatePickerValidationFailedEventArgs - master

13634 of 15974 branches covered (85.35%)

27464 of 29990 relevant lines covered (91.58%)

34331.44 hits per line

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

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

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

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

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

213
    /**
214
     * Gets/Sets the display time for the row adding snackbar notification.
215
     *
216
     * @remarks
217
     * By default it is 6000ms.
218
     */
219
    @Input()
220
    public snackbarDisplayTime = 6000;
4,157✔
221

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

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

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

259
    /**
260
     * Gets/Sets a custom template when empty.
261
     *
262
     * @example
263
     * ```html
264
     * <ng-template igxGridEmpty>
265
     *   <!-- content to show when the grid is empty -->
266
     * </ng-template>
267
     * ```
268
     * Or
269
     * ```html
270
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [emptyGridTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
271
     * ```
272
     */
273
    @Input()
274
    public get emptyGridTemplate(): TemplateRef<IgxGridTemplateContext> {
275
        return this._emptyGridTemplate || this.emptyDirectiveTemplate;
945✔
276
    }
277
    public set emptyGridTemplate(template: TemplateRef<IgxGridTemplateContext>) {
278
        this._emptyGridTemplate = template;
3✔
279
    }
280

281
    /**
282
     * Gets/Sets a custom template for adding row UI when grid is empty.
283
     *
284
     * @example
285
     * ```html
286
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [addRowEmptyTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
287
     * ```
288
     */
289
    @Input()
290
    public addRowEmptyTemplate: TemplateRef<void>;
291

292
    /**
293
     * Gets/Sets a custom template when loading.
294
     *
295
     * @example
296
     * ```html
297
     * <ng-template igxGridLoading>
298
     *   <!-- content to show when the grid is loading -->
299
     * </ng-template>
300
     * ```
301
     * Or
302
     * ```html
303
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [loadingGridTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
304
     * ```
305
     */
306
    @Input()
307
    public get loadingGridTemplate(): TemplateRef<IgxGridTemplateContext> {
308
        return this._loadingGridTemplate || this.loadingDirectiveTemplate;
108✔
309
    }
310
    public set loadingGridTemplate(template: TemplateRef<IgxGridTemplateContext>) {
311
        this._loadingGridTemplate = template;
1✔
312
    }
313

314
    /**
315
     * Get/Set IgxSummaryRow height
316
     */
317
    @Input()
318
    public set summaryRowHeight(value: number) {
319
        this._summaryRowHeight = value | 0;
23✔
320
        this.summaryService.summaryHeight = value;
23✔
321
        if (!this._init) {
23✔
322
            this.reflow();
22✔
323
        }
324
    }
325

326
    public get summaryRowHeight(): number {
327
        if (this.hasSummarizedColumns && this.rootSummariesEnabled) {
259,231✔
328
            return this._summaryRowHeight || this.summaryService.calcMaxSummaryHeight();
175,758✔
329
        }
330
        return 0;
83,473✔
331
    }
332

333
    /** @hidden @internal */
334
    public get hasColumnsToAutosize() {
335
        return this._columns.some(x => x.width === 'fit-content');
91,590✔
336
    }
337

338
    /**
339
     * Gets/Sets the data clone strategy of the grid when in edit mode.
340
     *
341
     * @example
342
     * ```html
343
     *  <igx-grid #grid [data]="localData" [dataCloneStrategy]="customCloneStrategy"></igx-grid>
344
     * ```
345
     */
346
    @Input()
347
    public get dataCloneStrategy(): IDataCloneStrategy {
348
        return this._dataCloneStrategy;
52,343✔
349
    }
350

351
    public set dataCloneStrategy(strategy: IDataCloneStrategy) {
352
        if (strategy) {
1✔
353
            this._dataCloneStrategy = strategy;
1✔
354
            this._transactions.cloneStrategy = strategy;
1✔
355
        }
356
    }
357

358
    /**
359
     * Controls the copy behavior of the grid.
360
     */
361
    @Input()
362
    public clipboardOptions: IClipboardOptions = {
4,157✔
363
        /**
364
         * Enables/disables the copy behavior
365
         */
366
        enabled: true,
367
        /**
368
         * Include the columns headers in the clipboard output.
369
         */
370
        copyHeaders: true,
371
        /**
372
         * Apply the columns formatters (if any) on the data in the clipboard output.
373
         */
374
        copyFormatters: true,
375
        /**
376
         * The separator used for formatting the copy output. Defaults to `\t`.
377
         */
378
        separator: '\t'
379
    };
380

381
    /**
382
     * Emitted after filtering is performed.
383
     *
384
     * @remarks
385
     * Returns the filtering expressions tree of the column for which filtering was performed.
386
     * @example
387
     * ```html
388
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
389
     *              (filteringExpressionsTreeChange)="filteringExprTreeChange($event)"></igx-grid>
390
     * ```
391
     */
392
    @Output()
393
    public filteringExpressionsTreeChange = new EventEmitter<IFilteringExpressionsTree>();
4,157✔
394

395
    /**
396
     * Emitted after advanced filtering is performed.
397
     *
398
     * @remarks
399
     * Returns the advanced filtering expressions tree.
400
     * @example
401
     * ```html
402
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
403
     *           (advancedFilteringExpressionsTreeChange)="advancedFilteringExprTreeChange($event)"></igx-grid>
404
     * ```
405
     */
406
    @Output()
407
    public advancedFilteringExpressionsTreeChange = new EventEmitter<IFilteringExpressionsTree>();
4,157✔
408

409
    /**
410
     * Emitted when grid is scrolled horizontally/vertically.
411
     *
412
     * @example
413
     * ```html
414
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
415
     *              (gridScroll)="onScroll($event)"></igx-grid>
416
     * ```
417
     */
418
    @Output()
419
    public gridScroll = new EventEmitter<IGridScrollEventArgs>();
4,157✔
420

421
    /* treatAsRef */
422
    /**
423
     * Sets a conditional class selector to the grid's row element.
424
     * Accepts an object literal, containing key-value pairs,
425
     * where the key is the name of the CSS class and the value is
426
     * either a callback function that returns a boolean, or boolean, like so:
427
     * ```typescript
428
     * callback = (row: RowType) => { return row.selected > 6; }
429
     * rowClasses = { 'className' : this.callback };
430
     * ```
431
     * ```html
432
     * <igx-grid #grid [data]="Data" [rowClasses] = "rowClasses" [autoGenerate]="true"></igx-grid>
433
     * ```
434
     *
435
     * @memberof IgxColumnComponent
436
     */
437
    @Input()
438
    public rowClasses: any;
439

440
    /* treatAsRef */
441
    /**
442
     * Sets conditional style properties on the grid row element.
443
     * It accepts an object literal where the keys are
444
     * the style properties and the value is an expression to be evaluated.
445
     * ```typescript
446
     * styles = {
447
     *  background: 'yellow',
448
     *  color: (row: RowType) => row.selected : 'red': 'white'
449
     * }
450
     * ```
451
     * ```html
452
     * <igx-grid #grid [data]="Data" [rowStyles]="styles" [autoGenerate]="true"></igx-grid>
453
     * ```
454
     *
455
     * @memberof IgxColumnComponent
456
     */
457
    @Input()
458
    public rowStyles = null;
4,157✔
459

460
    /**
461
     * Gets/Sets the primary key.
462
     *
463
     * @example
464
     * ```html
465
     * <igx-grid #grid [data]="localData" [primaryKey]="'ProductID'" [autoGenerate]="true"></igx-grid>
466
     * ```
467
     */
468
    @WatchChanges()
469
    @Input()
470
    public get primaryKey(): string {
471
        return this._primaryKey;
15,076,428✔
472
    }
473

474
    public set primaryKey(value: string) {
475
        this._primaryKey = value;
1,747✔
476
        this.checkPrimaryKeyField();
1,747✔
477
    }
478

479
    /* blazorSuppress */
480
    /**
481
     * Gets/Sets a unique values strategy used by the Excel Style Filtering
482
     *
483
     * @remarks
484
     * Provides a callback for loading unique column values on demand.
485
     * If this property is provided, the unique values it generates will be used by the Excel Style Filtering.
486
     * @example
487
     * ```html
488
     * <igx-grid [data]="localData" [filterMode]="'excelStyleFilter'" [uniqueColumnValuesStrategy]="columnValuesStrategy"></igx-grid>
489
     * ```
490
     */
491
    @Input()
492
    public uniqueColumnValuesStrategy: (column: ColumnType,
493
        filteringExpressionsTree: IFilteringExpressionsTree,
494
        done: (values: any[]) => void) => void;
495

496
    /** @hidden @internal */
497
    @ContentChildren(IgxGridExcelStyleFilteringComponent, { read: IgxGridExcelStyleFilteringComponent, descendants: false })
498
    public excelStyleFilteringComponents: QueryList<IgxGridExcelStyleFilteringComponent>;
499

500
    /** @hidden @internal */
501
    public get excelStyleFilteringComponent() {
502
        return this.excelStyleFilteringComponents?.first;
271✔
503
    }
504

505
    /** @hidden @internal */
506
    public get headerGroups() {
507
        return this.theadRow.groups;
3✔
508
    }
509

510
    /**
511
     * Emitted when a cell is clicked.
512
     *
513
     * @remarks
514
     * Returns the `IgxGridCell`.
515
     * @example
516
     * ```html
517
     * <igx-grid #grid (cellClick)="cellClick($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
518
     * ```
519
     */
520
    @Output()
521
    public cellClick = new EventEmitter<IGridCellEventArgs>();
4,157✔
522

523
    /**
524
     * Emitted when a row is clicked.
525
     *
526
     * @remarks
527
     * Returns the `IgxGridRow`.
528
     * @example
529
     * ```html
530
     * <igx-grid #grid (rowClick)="rowClick($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
531
     * ```
532
     */
533
    @Output()
534
    public rowClick = new EventEmitter<IGridRowEventArgs>();
4,157✔
535

536

537
    /**
538
     * Emitted when formGroup is created on edit of row/cell.
539
     *
540
     * @example
541
     * ```html
542
     * <igx-grid #grid (formGroupCreated)="formGroupCreated($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
543
     * ```
544
     */
545
    @Output()
546
    public formGroupCreated = new EventEmitter<IGridFormGroupCreatedEventArgs>();
4,157✔
547

548
    /**
549
     * Emitted when grid's validation status changes.
550
     *
551
     * @example
552
     * ```html
553
     * <igx-grid #grid (validationStatusChange)="validationStatusChange($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
554
     * ```
555
     */
556
    @Output()
557
    public validationStatusChange = new EventEmitter<IGridValidationStatusEventArgs>();
4,157✔
558

559
    /**
560
     * Emitted when a cell is selected.
561
     *
562
     * @remarks
563
     *  Returns the `IgxGridCell`.
564
     * @example
565
     * ```html
566
     * <igx-grid #grid (selected)="onCellSelect($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
567
     * ```
568
     */
569
    @Output()
570
    public selected = new EventEmitter<IGridCellEventArgs>();
4,157✔
571

572
    /**
573
     *  Emitted when `IgxGridRowComponent` is selected.
574
     *
575
     * @example
576
     * ```html
577
     * <igx-grid #grid (rowSelectionChanging)="rowSelectionChanging($event)" [data]="localData" [autoGenerate]="true"></igx-grid>
578
     * ```
579
     */
580
    @Output()
581
    public rowSelectionChanging = new EventEmitter<IRowSelectionEventArgs>();
4,157✔
582

583
    /**
584
     *  Emitted when `IgxColumnComponent` is selected.
585
     *
586
     * @example
587
     * ```html
588
     * <igx-grid #grid (columnSelectionChanging)="columnSelectionChanging($event)" [data]="localData" [autoGenerate]="true"></igx-grid>
589
     * ```
590
     */
591
    @Output()
592
    public columnSelectionChanging = new EventEmitter<IColumnSelectionEventArgs>();
4,157✔
593

594
    /**
595
     * Emitted before `IgxColumnComponent` is pinned.
596
     *
597
     * @remarks
598
     * The index at which to insert the column may be changed through the `insertAtIndex` property.
599
     * @example
600
     * ```typescript
601
     * public columnPinning(event) {
602
     *     if (event.column.field === "Name") {
603
     *       event.insertAtIndex = 0;
604
     *     }
605
     * }
606
     * ```
607
     */
608
    @Output()
609
    public columnPin = new EventEmitter<IPinColumnCancellableEventArgs>();
4,157✔
610

611
    /**
612
     * Emitted after `IgxColumnComponent` is pinned.
613
     *
614
     * @remarks
615
     * The index that the column is inserted at may be changed through the `insertAtIndex` property.
616
     * @example
617
     * ```typescript
618
     * public columnPinning(event) {
619
     *     if (event.column.field === "Name") {
620
     *       event.insertAtIndex = 0;
621
     *     }
622
     * }
623
     * ```
624
     */
625
    @Output()
626
    public columnPinned = new EventEmitter<IPinColumnEventArgs>();
4,157✔
627

628
    /**
629
     * Emitted when cell enters edit mode.
630
     *
631
     * @remarks
632
     * This event is cancelable.
633
     * @example
634
     * ```html
635
     * <igx-grid #grid3 (cellEditEnter)="editStart($event)" [data]="data" [primaryKey]="'ProductID'">
636
     * </igx-grid>
637
     * ```
638
     */
639
    @Output()
640
    public cellEditEnter = new EventEmitter<IGridEditEventArgs>();
4,157✔
641

642
    /**
643
     * Emitted when cell exits edit mode.
644
     *
645
     * @example
646
     * ```html
647
     * <igx-grid #grid3 (cellEditExit)="editExit($event)" [data]="data" [primaryKey]="'ProductID'">
648
     * </igx-grid>
649
     * ```
650
     */
651
    @Output()
652
    public cellEditExit = new EventEmitter<IGridEditDoneEventArgs>();
4,157✔
653

654
    /**
655
     * Emitted when cell has been edited.
656
     *
657
     * @remarks
658
     * Event is fired after editing is completed, when the cell is exiting edit mode.
659
     * This event is cancelable.
660
     * @example
661
     * ```html
662
     * <igx-grid #grid3 (cellEdit)="editDone($event)" [data]="data" [primaryKey]="'ProductID'">
663
     * </igx-grid>
664
     * ```
665
     */
666
    @Output()
667
    public cellEdit = new EventEmitter<IGridEditEventArgs>();
4,157✔
668

669
    /* blazorCSSuppress */
670
    /**
671
     * Emitted after cell has been edited and editing has been committed.
672
     *
673
     * @example
674
     * ```html
675
     * <igx-grid #grid3 (cellEditDone)="editDone($event)" [data]="data" [primaryKey]="'ProductID'">
676
     * </igx-grid>
677
     * ```
678
     */
679
    @Output()
680
    public cellEditDone = new EventEmitter<IGridEditDoneEventArgs>();
4,157✔
681

682
    /**
683
     * Emitted when a row enters edit mode.
684
     *
685
     * @remarks
686
     * Emitted when [rowEditable]="true".
687
     * This event is cancelable.
688
     * @example
689
     * ```html
690
     * <igx-grid #grid3 (rowEditEnter)="editStart($event)" [primaryKey]="'ProductID'" [rowEditable]="true">
691
     * </igx-grid>
692
     * ```
693
     */
694
    @Output()
695
    public rowEditEnter = new EventEmitter<IGridEditEventArgs>();
4,157✔
696

697
    /**
698
     * Emitted when exiting edit mode for a row.
699
     *
700
     * @remarks
701
     * Emitted when [rowEditable]="true" & `endEdit(true)` is called.
702
     * Emitted when changing rows during edit mode, selecting an un-editable cell in the edited row,
703
     * performing paging operation, column resizing, pinning, moving or hitting `Done`
704
     * button inside of the rowEditingOverlay, or hitting the `Enter` key while editing a cell.
705
     * This event is cancelable.
706
     * @example
707
     * ```html
708
     * <igx-grid #grid3 (rowEdit)="editDone($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
709
     * </igx-grid>
710
     * ```
711
     */
712
    @Output()
713
    public rowEdit = new EventEmitter<IGridEditEventArgs>();
4,157✔
714

715
    /**
716
     * Emitted after exiting edit mode for a row and editing has been committed.
717
     *
718
     * @remarks
719
     * Emitted when [rowEditable]="true" & `endEdit(true)` is called.
720
     * Emitted when changing rows during edit mode, selecting an un-editable cell in the edited row,
721
     * performing paging operation, column resizing, pinning, moving or hitting `Done`
722
     * button inside of the rowEditingOverlay, or hitting the `Enter` key while editing a cell.
723
     * @example
724
     * ```html
725
     * <igx-grid #grid3 (rowEditDone)="editDone($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
726
     * </igx-grid>
727
     * ```
728
     */
729
    @Output()
730
    public rowEditDone = new EventEmitter<IGridEditDoneEventArgs>();
4,157✔
731

732
    /**
733
     * Emitted when row editing is canceled.
734
     *
735
     * @remarks
736
     * Emits when [rowEditable]="true" & `endEdit(false)` is called.
737
     * Emitted when changing hitting `Esc` key during cell editing and when click on the `Cancel` button
738
     * in the row editing overlay.
739
     * @example
740
     * ```html
741
     * <igx-grid #grid3 (rowEditExit)="editExit($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
742
     * </igx-grid>
743
     * ```
744
     */
745
    @Output()
746
    public rowEditExit = new EventEmitter<IGridEditDoneEventArgs>();
4,157✔
747

748
    /**
749
     * Emitted when a column is initialized.
750
     *
751
     * @remarks
752
     * Returns the column object.
753
     * @example
754
     * ```html
755
     * <igx-grid #grid [data]="localData" (columnInit)="initColumns($event)" [autoGenerate]="true"></igx-grid>
756
     * ```
757
     */
758
    @Output()
759
    public columnInit = new EventEmitter<IgxColumnComponent>();
4,157✔
760

761
    /* blazorInclude */
762
    /**
763
     * @hidden @internal
764
     */
765
    @Output()
766
    public columnsAutogenerated = new EventEmitter<IColumnsAutoGeneratedEventArgs>();
4,157✔
767

768
    /**
769
     * Emitted before sorting expressions are applied.
770
     *
771
     * @remarks
772
     * Returns an `ISortingEventArgs` object. `sortingExpressions` key holds the sorting expressions.
773
     * @example
774
     * ```html
775
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sorting)="sorting($event)"></igx-grid>
776
     * ```
777
     */
778
    @Output()
779
    public sorting = new EventEmitter<ISortingEventArgs>();
4,157✔
780

781
    /**
782
     * Emitted after sorting is completed.
783
     *
784
     * @remarks
785
     * Returns the sorting expression.
786
     * @example
787
     * ```html
788
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingDone)="sortingDone($event)"></igx-grid>
789
     * ```
790
     */
791
    @Output()
792
    public sortingDone = new EventEmitter<ISortingExpression | ISortingExpression[]>();
4,157✔
793

794
    /**
795
     * Emitted before filtering expressions are applied.
796
     *
797
     * @remarks
798
     * Returns an `IFilteringEventArgs` object. `filteringExpressions` key holds the filtering expressions for the column.
799
     * @example
800
     * ```html
801
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true" (filtering)="filtering($event)"></igx-grid>
802
     * ```
803
     */
804
    @Output()
805
    public filtering = new EventEmitter<IFilteringEventArgs>();
4,157✔
806

807
    /**
808
     * Emitted after filtering is performed through the UI.
809
     *
810
     * @remarks
811
     * Returns the filtering expressions tree of the column for which filtering was performed.
812
     * @example
813
     * ```html
814
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true" (filteringDone)="filteringDone($event)"></igx-grid>
815
     * ```
816
     */
817
    @Output()
818
    public filteringDone = new EventEmitter<IFilteringExpressionsTree>();
4,157✔
819

820
    /* blazorCSSuppress */
821
    /**
822
     * Emitted when a row is added.
823
     *
824
     * @remarks
825
     * Returns the data for the new `IgxGridRowComponent` object.
826
     * @example
827
     * ```html
828
     * <igx-grid #grid [data]="localData" (rowAdded)="rowAdded($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
829
     * ```
830
     */
831
    @Output()
832
    public rowAdded = new EventEmitter<IRowDataEventArgs>();
4,157✔
833

834
    /* blazorCSSuppress */
835
    /**
836
     * Emitted when a row is deleted.
837
     *
838
     * @remarks
839
     * Returns an `IRowDataEventArgs` object.
840
     * @example
841
     * ```html
842
     * <igx-grid #grid [data]="localData" (rowDeleted)="rowDeleted($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
843
     * ```
844
     */
845
    @Output()
846
    public rowDeleted = new EventEmitter<IRowDataEventArgs>();
4,157✔
847

848
    /**
849
     * Emmited when deleting a row.
850
     *
851
     * @remarks
852
     * This event is cancelable.
853
     * Returns an IRowDataCancellableEventArgs` object.
854
     * @example
855
     * ```html
856
     * <igx-grid #grid [data]="localData" (rowDelete)="rowDelete($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
857
     * ```
858
     */
859
    @Output()
860
    public rowDelete = new EventEmitter<IRowDataCancelableEventArgs>();
4,157✔
861

862
    /**
863
     * Emmited just before the newly added row is commited.
864
     *
865
     * @remarks
866
     * This event is cancelable.
867
     * Returns an IRowDataCancellableEventArgs` object.
868
     * @example
869
     * ```html
870
     * <igx-grid #grid [data]="localData" (rowAdd)="rowAdd($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
871
     * ```
872
     */
873
    @Output()
874
    public rowAdd = new EventEmitter<IRowDataCancelableEventArgs>();
4,157✔
875

876
    /**
877
     * Emitted after column is resized.
878
     *
879
     * @remarks
880
     * Returns the `IgxColumnComponent` object's old and new width.
881
     * @example
882
     * ```html
883
     * <igx-grid #grid [data]="localData" (columnResized)="resizing($event)" [autoGenerate]="true"></igx-grid>
884
     * ```
885
     */
886
    @Output()
887
    public columnResized = new EventEmitter<IColumnResizeEventArgs>();
4,157✔
888

889
    /**
890
     * Emitted when a cell or row is right clicked.
891
     *
892
     * @remarks
893
     * Returns the `IgxGridCell` object if the immediate context menu target is a cell or an `IgxGridRow` otherwise.
894
     * ```html
895
     * <igx-grid #grid [data]="localData" (contextMenu)="contextMenu($event)" [autoGenerate]="true"></igx-grid>
896
     * ```
897
     */
898
    @Output()
899
    public contextMenu = new EventEmitter<IGridContextMenuEventArgs>();
4,157✔
900

901
    /**
902
     * Emitted when a cell is double clicked.
903
     *
904
     * @remarks
905
     * Returns the `IgxGridCell` object.
906
     * @example
907
     * ```html
908
     * <igx-grid #grid [data]="localData" (doubleClick)="dblClick($event)" [autoGenerate]="true"></igx-grid>
909
     * ```
910
     */
911
    @Output()
912
    public doubleClick = new EventEmitter<IGridCellEventArgs>();
4,157✔
913

914
    /**
915
     * Emitted before column visibility is changed.
916
     *
917
     * @remarks
918
     * Args: { column: any, newValue: boolean }
919
     * @example
920
     * ```html
921
     * <igx-grid (columnVisibilityChanging)="visibilityChanging($event)"></igx-grid>
922
     * ```
923
     */
924
    @Output()
925
    public columnVisibilityChanging = new EventEmitter<IColumnVisibilityChangingEventArgs>();
4,157✔
926

927
    /**
928
     * Emitted after column visibility is changed.
929
     *
930
     * @remarks
931
     * Args: { column: IgxColumnComponent, newValue: boolean }
932
     * @example
933
     * ```html
934
     * <igx-grid (columnVisibilityChanged)="visibilityChanged($event)"></igx-grid>
935
     * ```
936
     */
937
    @Output()
938
    public columnVisibilityChanged = new EventEmitter<IColumnVisibilityChangedEventArgs>();
4,157✔
939

940
    /**
941
     * Emitted when column moving starts.
942
     *
943
     * @remarks
944
     * Returns the moved `IgxColumnComponent` object.
945
     * @example
946
     * ```html
947
     * <igx-grid (columnMovingStart)="movingStart($event)"></igx-grid>
948
     * ```
949
     */
950
    @Output()
951
    public columnMovingStart = new EventEmitter<IColumnMovingStartEventArgs>();
4,157✔
952

953
    /**
954
     * Emitted during the column moving operation.
955
     *
956
     * @remarks
957
     * Returns the source and target `IgxColumnComponent` objects. This event is cancelable.
958
     * @example
959
     * ```html
960
     * <igx-grid (columnMoving)="moving($event)"></igx-grid>
961
     * ```
962
     */
963
    @Output()
964
    public columnMoving = new EventEmitter<IColumnMovingEventArgs>();
4,157✔
965

966
    /**
967
     * Emitted when column moving ends.
968
     *
969
     * @remarks
970
     * Returns the source and target `IgxColumnComponent` objects.
971
     * @example
972
     * ```html
973
     * <igx-grid (columnMovingEnd)="movingEnds($event)"></igx-grid>
974
     * ```
975
     */
976
    @Output()
977
    public columnMovingEnd = new EventEmitter<IColumnMovingEndEventArgs>();
4,157✔
978

979
    /**
980
     * Emitted when keydown is triggered over element inside grid's body.
981
     *
982
     * @remarks
983
     * This event is fired only if the key combination is supported in the grid.
984
     * Return the target type, target object and the original event. This event is cancelable.
985
     * @example
986
     * ```html
987
     *  <igx-grid (gridKeydown)="customKeydown($event)"></igx-grid>
988
     * ```
989
     */
990
    @Output()
991
    public gridKeydown = new EventEmitter<IGridKeydownEventArgs>();
4,157✔
992

993
    /**
994
     * Emitted when start dragging a row.
995
     *
996
     * @remarks
997
     * Return the dragged row.
998
     */
999
    @Output()
1000
    public rowDragStart = new EventEmitter<IRowDragStartEventArgs>();
4,157✔
1001

1002
    /**
1003
     * Emitted when dropping a row.
1004
     *
1005
     * @remarks
1006
     * Return the dropped row.
1007
     */
1008
    @Output()
1009
    public rowDragEnd = new EventEmitter<IRowDragEndEventArgs>();
4,157✔
1010

1011
    /**
1012
     * Emitted when a copy operation is executed.
1013
     *
1014
     * @remarks
1015
     * Fired only if copy behavior is enabled through the [`clipboardOptions`]{@link IgxGridBaseDirective#clipboardOptions}.
1016
     */
1017
    @Output()
1018
    public gridCopy = new EventEmitter<IGridClipboardEvent>();
4,157✔
1019

1020
    /* blazorCSSuppress */
1021
    /**
1022
     * Emitted when the rows are expanded or collapsed.
1023
     *
1024
     * @example
1025
     * ```html
1026
     * <igx-grid [data]="employeeData" (expansionStatesChange)="expansionStatesChange($event)" [autoGenerate]="true"></igx-grid>
1027
     * ```
1028
     */
1029
    @Output()
1030
    public expansionStatesChange = new EventEmitter<Map<any, boolean>>();
4,157✔
1031

1032
    /* blazorInclude */
1033
    /**
1034
     * Emitted when the rows are selected or deselected.
1035
     *
1036
     * @example
1037
     * ```html
1038
     * <igx-grid [data]="employeeData" (selectedRowsChange)="selectedRowsChange($event)" [autoGenerate]="true"></igx-grid>
1039
     * ```
1040
     */
1041
    @Output()
1042
    public selectedRowsChange = new EventEmitter<any[]>();
4,157✔
1043

1044
    /**
1045
     * Emitted when the expanded state of a row gets changed.
1046
     *
1047
     * @example
1048
     * ```html
1049
     * <igx-grid [data]="employeeData" (rowToggle)="rowToggle($event)" [autoGenerate]="true"></igx-grid>
1050
     * ```
1051
     */
1052
    @Output()
1053
    public rowToggle = new EventEmitter<IRowToggleEventArgs>();
4,157✔
1054

1055
    /**
1056
     * Emitted when the pinned state of a row is changed.
1057
     *
1058
     * @example
1059
     * ```html
1060
     * <igx-grid [data]="employeeData" (rowPinning)="rowPin($event)" [autoGenerate]="true"></igx-grid>
1061
     * ```
1062
     */
1063
    @Output()
1064
    public rowPinning = new EventEmitter<IPinRowEventArgs>();
4,157✔
1065

1066
    /**
1067
     * Emitted when the pinned state of a row is changed.
1068
     *
1069
     * @example
1070
     * ```html
1071
     * <igx-grid [data]="employeeData" (rowPinned)="rowPin($event)" [autoGenerate]="true"></igx-grid>
1072
     * ```
1073
     */
1074
    @Output()
1075
    public rowPinned = new EventEmitter<IPinRowEventArgs>();
4,157✔
1076

1077
    /**
1078
     * Emitted when the active node is changed.
1079
     *
1080
     * @example
1081
     * ```
1082
     * <igx-grid [data]="data" [autoGenerate]="true" (activeNodeChange)="activeNodeChange($event)"></igx-grid>
1083
     * ```
1084
     */
1085
    @Output()
1086
    public activeNodeChange = new EventEmitter<IActiveNodeChangeEventArgs>();
4,157✔
1087

1088
    /**
1089
     * Emitted before sorting is performed.
1090
     *
1091
     * @remarks
1092
     * Returns the sorting expressions.
1093
     * @example
1094
     * ```html
1095
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingExpressionsChange)="sortingExprChange($event)"></igx-grid>
1096
     * ```
1097
     */
1098
    @Output()
1099
    public sortingExpressionsChange = new EventEmitter<ISortingExpression[]>();
4,157✔
1100

1101

1102
    /**
1103
     * Emitted when an export process is initiated by the user.
1104
     *
1105
     * @example
1106
     * ```typescript
1107
     * toolbarExporting(event: IGridToolbarExportEventArgs){
1108
     *     const toolbarExporting = event;
1109
     * }
1110
     * ```
1111
     */
1112
    @Output()
1113
    public toolbarExporting = new EventEmitter<IGridToolbarExportEventArgs>();
4,157✔
1114

1115
    /* End of toolbar related definitions */
1116

1117
    /**
1118
     * Emitted when making a range selection.
1119
     *
1120
     * @remarks
1121
     * Range selection can be made either through drag selection or through keyboard selection.
1122
     */
1123
    @Output()
1124
    public rangeSelected = new EventEmitter<GridSelectionRange>();
4,157✔
1125

1126
    /** Emitted after the ngAfterViewInit hook. At this point the grid exists in the DOM */
1127
    @Output()
1128
    public rendered = new EventEmitter<boolean>();
4,157✔
1129

1130
    /**
1131
     * @hidden @internal
1132
     */
1133
    @Output()
1134
    public localeChange = new EventEmitter<boolean>();
4,157✔
1135

1136
    /**
1137
     * Emitted before the grid's data view is changed because of a data operation, rebinding, etc.
1138
     *
1139
     * @example
1140
     * ```typescript
1141
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanging)='handleDataChangingEvent()'></igx-grid>
1142
     * ```
1143
     */
1144
    @Output()
1145
    public dataChanging = new EventEmitter<IForOfDataChangeEventArgs>();
4,157✔
1146

1147
    /**
1148
     * Emitted after the grid's data view is changed because of a data operation, rebinding, etc.
1149
     *
1150
     * @example
1151
     * ```typescript
1152
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanged)='handleDataChangedEvent()'></igx-grid>
1153
     * ```
1154
     */
1155
    @Output()
1156
    public dataChanged = new EventEmitter<IForOfDataChangeEventArgs>();
4,157✔
1157

1158

1159
    /**
1160
     * @hidden @internal
1161
     */
1162
    @ViewChild(IgxSnackbarComponent)
1163
    public addRowSnackbar: IgxSnackbarComponent;
1164

1165
    /**
1166
     * @hidden @internal
1167
     */
1168
    @ViewChild(IgxGridColumnResizerComponent)
1169
    public resizeLine: IgxGridColumnResizerComponent;
1170

1171
    /**
1172
     * @hidden @internal
1173
     */
1174
    @ViewChild('loadingOverlay', { read: IgxToggleDirective, static: true })
1175
    public loadingOverlay: IgxToggleDirective;
1176

1177
    /**
1178
     * @hidden @internal
1179
     */
1180
    @ViewChild('igxLoadingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1181
    public loadingOutlet: IgxOverlayOutletDirective;
1182

1183
    /* reactContentChildren */
1184
    /* blazorInclude */
1185
    /* blazorTreatAsCollection */
1186
    /* blazorCollectionName: ColumnCollection */
1187
    /* ngQueryListName: columnList */
1188
    /**
1189
     * @hidden @internal
1190
     */
1191
    @ContentChildren(IgxColumnComponent, { read: IgxColumnComponent, descendants: true })
1192
    public columnList: QueryList<IgxColumnComponent> = new QueryList<IgxColumnComponent>();
4,157✔
1193

1194
    /* contentChildren */
1195
    /* blazorInclude */
1196
    /* blazorTreatAsCollection */
1197
    /* blazorCollectionName: ActionStripCollection */
1198
    /* blazorCollectionItemName: ActionStrip */
1199
    /* ngQueryListName: actionStripComponents */
1200
    /** @hidden @internal */
1201
    @ContentChildren(IgxActionStripToken)
1202
    protected actionStripComponents: QueryList<IgxActionStripToken>;
1203

1204
    /** @hidden @internal */
1205
    public get actionStrip() {
1206
        return this.actionStripComponents?.first;
4,809✔
1207
    }
1208

1209
    /**
1210
     * @hidden @internal
1211
     */
1212
    @ContentChild(IgxExcelStyleLoadingValuesTemplateDirective, { read: IgxExcelStyleLoadingValuesTemplateDirective, static: true })
1213
    public excelStyleLoadingValuesTemplateDirective: IgxExcelStyleLoadingValuesTemplateDirective;
1214

1215
    /** @hidden @internal */
1216
    @ViewChild('emptyFilteredGrid', { read: TemplateRef, static: true })
1217
    public emptyFilteredGridTemplate: TemplateRef<any>;
1218

1219
    /** @hidden @internal */
1220
    @ViewChild('defaultEmptyGrid', { read: TemplateRef, static: true })
1221
    public emptyGridDefaultTemplate: TemplateRef<any>;
1222

1223
    /**
1224
     * @hidden @internal
1225
     */
1226
    @ViewChild('defaultLoadingGrid', { read: TemplateRef, static: true })
1227
    public loadingGridDefaultTemplate: TemplateRef<any>;
1228

1229
    /**
1230
     * @hidden @internal
1231
     */
1232
    @ViewChild('scrollContainer', { read: IgxGridForOfDirective, static: true })
1233
    public parentVirtDir: IgxGridForOfDirective<any, any[]>;
1234

1235
    /**
1236
     * @hidden
1237
     * @internal
1238
     */
1239
    @ContentChildren(IgxHeadSelectorDirective, { read: TemplateRef, descendants: false })
1240
    public headSelectorsTemplates: QueryList<TemplateRef<IgxHeadSelectorTemplateContext>>;
1241

1242
    /**
1243
     * @hidden
1244
     * @internal
1245
     */
1246
    @ContentChildren(IgxRowSelectorDirective, { read: TemplateRef, descendants: false })
1247
    public rowSelectorsTemplates: QueryList<TemplateRef<IgxRowSelectorTemplateContext>>;
1248

1249
    /**
1250
     * @hidden
1251
     * @internal
1252
     */
1253
    @ContentChildren(IgxRowDragGhostDirective, { read: TemplateRef, descendants: false })
1254
    public dragGhostCustomTemplates: QueryList<TemplateRef<IgxGridRowDragGhostContext>>;
1255

1256

1257
    /**
1258
     * Gets the custom template, if any, used for row drag ghost.
1259
     */
1260
    @Input()
1261
    public get dragGhostCustomTemplate() {
1262
        return this._dragGhostCustomTemplate || this.dragGhostCustomTemplates?.first;
1,987✔
1263
    }
1264

1265
    /**
1266
     * Sets a custom template for the row drag ghost.
1267
     *```html
1268
     * <ng-template #template igxRowDragGhost>
1269
     *    <igx-icon>menu</igx-icon>
1270
     * </ng-template>
1271
     * ```
1272
     * ```typescript
1273
     * @ViewChild("'template'", {read: TemplateRef })
1274
     * public template: TemplateRef<any>;
1275
     * this.grid.dragGhostCustomTemplate = this.template;
1276
     * ```
1277
     */
1278
    public set dragGhostCustomTemplate(template: TemplateRef<IgxGridRowDragGhostContext>) {
1279
        this._dragGhostCustomTemplate = template;
1✔
1280
    }
1281

1282

1283
    /**
1284
     * @hidden @internal
1285
     */
1286
    @ViewChild('verticalScrollContainer', { read: IgxGridForOfDirective, static: true })
1287
    public verticalScrollContainer: IgxGridForOfDirective<any, any[]>;
1288

1289
    /**
1290
     * @hidden @internal
1291
     */
1292
    @ViewChild('verticalScrollHolder', { read: IgxGridForOfDirective, static: true })
1293
    public verticalScroll: IgxGridForOfDirective<any, any[]>;
1294

1295
    /**
1296
     * @hidden @internal
1297
     */
1298
    @ViewChild('scr', { read: ElementRef, static: true })
1299
    public scr: ElementRef;
1300

1301
    /** @hidden @internal */
1302
    @ViewChild('headSelectorBaseTemplate', { read: TemplateRef, static: true })
1303
    public headerSelectorBaseTemplate: TemplateRef<any>;
1304

1305
    /**
1306
     * @hidden @internal
1307
     */
1308
    @ViewChild('footer', { read: ElementRef })
1309
    public footer: ElementRef;
1310

1311
    /** @hidden @internal */
1312
    public get headerContainer() {
1313
        return this.theadRow?.headerForOf;
14,832✔
1314
    }
1315

1316
    /** @hidden @internal */
1317
    public get headerSelectorContainer() {
1318
        return this.theadRow?.headerSelectorContainer;
30,941✔
1319
    }
1320

1321
    /** @hidden @internal */
1322
    public get headerDragContainer() {
1323
        return this.theadRow?.headerDragContainer;
475✔
1324
    }
1325

1326
    /** @hidden @internal */
1327
    public get headerGroupContainer() {
1328
        return this.theadRow?.headerGroupContainer;
30,189✔
1329
    }
1330

1331
    /** @hidden @internal */
1332
    public get filteringRow(): IgxGridFilteringRowComponent {
1333
        return this.theadRow?.filterRow;
1,325✔
1334
    }
1335

1336
    /** @hidden @internal */
1337
    @ViewChild(IgxGridHeaderRowComponent, { static: true })
1338
    public theadRow: IgxGridHeaderRowComponent;
1339

1340
    /** @hidden @internal */
1341
    @ViewChild(IgxGridGroupByAreaComponent)
1342
    public groupArea: IgxGridGroupByAreaComponent;
1343

1344
    /**
1345
     * @hidden @internal
1346
     */
1347
    @ViewChild('tbody', { static: true })
1348
    public tbody: ElementRef;
1349

1350
    @ViewChild(IgxGridBodyDirective, { static: true, read: ElementRef })
1351
    protected tbodyContainer: ElementRef;
1352

1353
    /**
1354
     * @hidden @internal
1355
     */
1356
    @ViewChild('pinContainer', { read: ElementRef })
1357
    public pinContainer: ElementRef;
1358

1359
    /**
1360
     * @hidden @internal
1361
     */
1362
    @ViewChild('tfoot', { static: true })
1363
    public tfoot: ElementRef<HTMLElement>;
1364

1365
    /**
1366
     * @hidden @internal
1367
     */
1368
    @ViewChild('igxRowEditingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1369
    public rowEditingOutletDirective: IgxOverlayOutletDirective;
1370

1371
    /**
1372
     * @hidden @internal
1373
     */
1374
    @ViewChildren(IgxTemplateOutletDirective, { read: IgxTemplateOutletDirective })
1375
    public tmpOutlets: QueryList<any> = new QueryList<any>();
4,157✔
1376

1377
    /**
1378
     * @hidden
1379
     * @internal
1380
     */
1381
    @ViewChild('dragIndicatorIconBase', { read: TemplateRef, static: true })
1382
    public dragIndicatorIconBase: TemplateRef<any>;
1383

1384
    /**
1385
     * @hidden @internal
1386
     */
1387
    @ContentChildren(IgxRowEditTemplateDirective, { descendants: false, read: TemplateRef })
1388
    public rowEditCustomDirectives: QueryList<TemplateRef<IgxGridRowEditTemplateContext>>;
1389

1390
    /**
1391
     * @hidden @internal
1392
     */
1393
    @ContentChildren(IgxRowEditTextDirective, { descendants: false, read: TemplateRef })
1394
    public rowEditTextDirectives: QueryList<TemplateRef<IgxGridRowEditTextTemplateContext>>;
1395

1396
    /**
1397
     * Gets the row edit text template.
1398
     */
1399
    @Input()
1400
    public get rowEditTextTemplate(): TemplateRef<IgxGridRowEditTextTemplateContext> {
1401
        return this._rowEditTextTemplate || this.rowEditTextDirectives?.first;
5,644✔
1402
    }
1403
    /**
1404
     * Sets the row edit text template.
1405
     *```html
1406
     * <ng-template #template igxRowEditText let-rowChangesCount>
1407
     * Changes: {{rowChangesCount}}
1408
     * </ng-template>
1409
     * ```
1410
     *```typescript
1411
     * @ViewChild('template', {read: TemplateRef })
1412
     * public template: TemplateRef<any>;
1413
     * this.grid.rowEditTextTemplate = this.template;
1414
     * ```
1415
     */
1416
    public set rowEditTextTemplate(template: TemplateRef<IgxGridRowEditTextTemplateContext>) {
1417
        this._rowEditTextTemplate = template;
1✔
1418
    }
1419

1420
    /**
1421
     * @hidden @internal
1422
     */
1423
    @ContentChild(IgxRowAddTextDirective, { read: TemplateRef })
1424
    public rowAddText: TemplateRef<IgxGridEmptyTemplateContext>;
1425

1426
    /**
1427
     * Gets the row add text template.
1428
     */
1429
    @Input()
1430
    public get rowAddTextTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
1431
        return this._rowAddTextTemplate || this.rowAddText;
373✔
1432
    }
1433
    /**
1434
     * Sets the row add text template.
1435
     *```html
1436
     * <ng-template #template igxRowAddText>
1437
     * Adding Row
1438
     * </ng-template>
1439
     * ```
1440
     *```typescript
1441
     * @ViewChild('template', {read: TemplateRef })
1442
     * public template: TemplateRef<any>;
1443
     * this.grid.rowAddTextTemplate = this.template;
1444
     * ```
1445
     */
1446
    public set rowAddTextTemplate(template: TemplateRef<IgxGridEmptyTemplateContext>) {
1447
        this._rowAddTextTemplate = template;
1✔
1448
    }
1449

1450
    /**
1451
     * @hidden @internal
1452
     */
1453
    @ContentChildren(IgxRowEditActionsDirective, { descendants: false, read: TemplateRef })
1454
    public rowEditActionsDirectives: QueryList<TemplateRef<IgxGridRowEditActionsTemplateContext>>;
1455

1456
    /**
1457
     * Gets the row edit actions template.
1458
     */
1459
    @Input()
1460
    public get rowEditActionsTemplate(): TemplateRef<IgxGridRowEditActionsTemplateContext> {
1461
        return this._rowEditActionsTemplate || this.rowEditActionsDirectives?.first;
6,021✔
1462
    }
1463
    /**
1464
     * Sets the row edit actions template.
1465
     *```html
1466
     * <ng-template #template igxRowEditActions let-endRowEdit>
1467
     *     <button type="button" igxButton igxRowEditTabStop (click)="endRowEdit(false)">Cancel</button>
1468
     *     <button type="button" igxButton igxRowEditTabStop (click)="endRowEdit(true)">Apply</button>
1469
     * </ng-template>
1470
     * ```
1471
     *```typescript
1472
     * @ViewChild('template', {read: TemplateRef })
1473
     * public template: TemplateRef<any>;
1474
     * this.grid.rowEditActionsTemplate = this.template;
1475
     * ```
1476
     */
1477
    public set rowEditActionsTemplate(template: TemplateRef<IgxGridRowEditActionsTemplateContext>) {
1478
        this._rowEditActionsTemplate = template;
1✔
1479
    }
1480

1481
    /**
1482
     * The custom template, if any, that should be used when rendering a row expand indicator.
1483
     */
1484
    @ContentChild(IgxRowExpandedIndicatorDirective, { read: TemplateRef })
1485
    protected rowExpandedIndicatorDirectiveTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
4,157✔
1486

1487
    /**
1488
     * Gets the row expand indicator template.
1489
    */
1490
    @Input()
1491
    public get rowExpandedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1492
        return this._rowExpandedIndicatorTemplate || this.rowExpandedIndicatorDirectiveTemplate;
12,410✔
1493
    }
1494

1495
    /**
1496
     * Sets the row expand indicator template.
1497
     *```html
1498
     *<ng-template igxRowExpandedIndicator>
1499
     *  <igx-icon role="button">remove</igx-icon>
1500
     *</ng-template>
1501
     * ```
1502
     *```typescript
1503
     * @ViewChild('template', {read: TemplateRef })
1504
     * public template: TemplateRef<any>;
1505
     * this.grid.rowExpandedIndicatorTemplate = this.template;
1506
     * ```
1507
    */
1508
    public set rowExpandedIndicatorTemplate(template: TemplateRef<IgxGridRowTemplateContext>) {
1509
        this._rowExpandedIndicatorTemplate = template;
916✔
1510
    }
1511

1512
    /**
1513
     * The custom template, if any, that should be used when rendering a row collapse indicator.
1514
     */
1515
    @ContentChild(IgxRowCollapsedIndicatorDirective, { read: TemplateRef })
1516
    protected rowCollapsedIndicatorDirectiveTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
4,157✔
1517

1518
    /**
1519
     * Gets the row collapse indicator template.
1520
    */
1521
    @Input()
1522
    public get rowCollapsedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1523
        return this._rowCollapsedIndicatorTemplate || this.rowCollapsedIndicatorDirectiveTemplate;
52,055✔
1524
    }
1525

1526
    /**
1527
     * Sets the row collapse indicator template.
1528
     *```html
1529
     *<ng-template igxRowCollapsedIndicator>
1530
     *  <igx-icon role="button">add</igx-icon>
1531
     *</ng-template>
1532
     * ```
1533
     *```typescript
1534
     * @ViewChild('template', {read: TemplateRef })
1535
     * public template: TemplateRef<any>;
1536
     * this.grid.rowCollapsedIndicatorTemplate = this.template;
1537
     * ```
1538
    */
1539
    public set rowCollapsedIndicatorTemplate(template: TemplateRef<IgxGridRowTemplateContext>) {
1540
        this._rowCollapsedIndicatorTemplate = template;
916✔
1541
    }
1542

1543
    /**
1544
     * The custom template, if any, that should be used when rendering a header expand indicator.
1545
     */
1546
    @ContentChild(IgxHeaderExpandedIndicatorDirective, { read: TemplateRef })
1547
    protected headerExpandedIndicatorDirectiveTemplate: TemplateRef<IgxGridTemplateContext> = null;
4,157✔
1548

1549
    /**
1550
     * Gets the header expand indicator template.
1551
    */
1552
    @Input()
1553
    public get headerExpandedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
1554
        return this._headerExpandIndicatorTemplate || this.headerExpandedIndicatorDirectiveTemplate;
14,220✔
1555
    }
1556

1557
    /**
1558
     * Sets the header expand indicator template.
1559
     *```html
1560
     *<ng-template igxHeaderExpandedIndicator>
1561
     *  <igx-icon role="button">remove</igx-icon>
1562
     *</ng-template>
1563
     * ```
1564
     *```typescript
1565
     * @ViewChild('template', {read: TemplateRef })
1566
     * public template: TemplateRef<any>;
1567
     * this.grid.headerExpandedIndicatorTemplate = this.template;
1568
     * ```
1569
    */
1570
    public set headerExpandedIndicatorTemplate(template: TemplateRef<IgxGridTemplateContext>) {
1571
        this._headerExpandIndicatorTemplate = template;
916✔
1572
    }
1573

1574
    /**
1575
     * The custom template, if any, that should be used when rendering a header collapse indicator.
1576
     */
1577
    @ContentChild(IgxHeaderCollapsedIndicatorDirective, { read: TemplateRef })
1578
    protected headerCollapsedIndicatorDirectiveTemplate: TemplateRef<IgxGridTemplateContext> = null;
4,157✔
1579

1580
    /**
1581
     * Gets the row collapse indicator template.
1582
    */
1583
    @Input()
1584
    public get headerCollapsedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
1585
        return this._headerCollapseIndicatorTemplate || this.headerCollapsedIndicatorDirectiveTemplate;
1,062✔
1586
    }
1587

1588
    /**
1589
     * Sets the row collapse indicator template.
1590
     *```html
1591
     *<ng-template igxHeaderCollapsedIndicator>
1592
     *  <igx-icon role="button">add</igx-icon>
1593
     *</ng-template>
1594
     * ```
1595
     *```typescript
1596
     * @ViewChild('template', {read: TemplateRef })
1597
     * public template: TemplateRef<any>;
1598
     * this.grid.headerCollapsedIndicatorTemplate = this.template;
1599
     * ```
1600
    */
1601
    public set headerCollapsedIndicatorTemplate(template: TemplateRef<IgxGridTemplateContext>) {
1602
        this._headerCollapseIndicatorTemplate = template;
916✔
1603
    }
1604

1605
    /** @hidden @internal */
1606
    @ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef })
1607
    public excelStyleHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,157✔
1608

1609
    /**
1610
     * Gets the excel style header icon.
1611
    */
1612
    @Input()
1613
    public get excelStyleHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1614
        return this._excelStyleHeaderIconTemplate || this.excelStyleHeaderIconDirectiveTemplate;
12,500✔
1615
    }
1616

1617
    /**
1618
     * Sets the excel style header icon.
1619
     *```html
1620
     *<ng-template #template igxExcelStyleHeaderIcon>
1621
     * <igx-icon>filter_alt</igx-icon>
1622
     *</ng-template>
1623
     * ```
1624
     *```typescript
1625
     * @ViewChild('template', {read: TemplateRef })
1626
     * public template: TemplateRef<any>;
1627
     * this.grid.excelStyleHeaderIconTemplate = this.template;
1628
     * ```
1629
    */
1630
    public set excelStyleHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1631
        this._excelStyleHeaderIconTemplate = template;
917✔
1632
    }
1633

1634

1635
    /**
1636
     * @hidden
1637
     * @internal
1638
     */
1639
    @ContentChild(IgxSortAscendingHeaderIconDirective, { read: TemplateRef })
1640
    public sortAscendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,157✔
1641

1642
    /**
1643
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1644
     */
1645
    @Input()
1646
    public get sortAscendingHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1647
        return this._sortAscendingHeaderIconTemplate;
1,071✔
1648
    }
1649

1650
    /**
1651
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1652
     *```html
1653
     * <ng-template #template igxSortAscendingHeaderIcon>
1654
     *    <igx-icon>expand_less</igx-icon>
1655
     * </ng-template>
1656
     * ```
1657
     * ```typescript
1658
     * @ViewChild("'template'", {read: TemplateRef })
1659
     * public template: TemplateRef<any>;
1660
     * this.grid.sortAscendingHeaderIconTemplate = this.template;
1661
     * ```
1662
     */
1663
    public set sortAscendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1664
        this._sortAscendingHeaderIconTemplate = template;
922✔
1665
    }
1666

1667
    /** @hidden @internal */
1668
    @ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef })
1669
    public sortDescendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,157✔
1670

1671
    /**
1672
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1673
     */
1674
    @Input()
1675
    public get sortDescendingHeaderIconTemplate() {
1676
        return this._sortDescendingHeaderIconTemplate;
978✔
1677
    }
1678

1679
    /**
1680
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1681
     *```html
1682
     * <ng-template #template igxSortDescendingHeaderIcon>
1683
     *    <igx-icon>expand_more</igx-icon>
1684
     * </ng-template>
1685
     * ```
1686
     * ```typescript
1687
     * @ViewChild("'template'", {read: TemplateRef })
1688
     * public template: TemplateRef<any>;
1689
     * this.grid.sortDescendingHeaderIconTemplate = this.template;
1690
     * ```
1691
     */
1692
    public set sortDescendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1693
        this._sortDescendingHeaderIconTemplate = template;
922✔
1694
    }
1695

1696
    /**
1697
     * @hidden
1698
     * @internal
1699
     */
1700
    @ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef })
1701
    public sortHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,157✔
1702

1703
    /**
1704
     * Gets custom template, if any, that should be used when rendering a header sorting indicator when columns are not sorted.
1705
     */
1706
    @Input()
1707
    public get sortHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1708
        return this._sortHeaderIconTemplate;
30,034✔
1709
    }
1710

1711
    /**
1712
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are not sorted.
1713
     *```html
1714
     * <ng-template #template igxSortHeaderIcon>
1715
     *    <igx-icon>unfold_more</igx-icon>
1716
     * </ng-template>
1717
     * ```
1718
     * ```typescript
1719
     * @ViewChild("'template'", {read: TemplateRef })
1720
     * public template: TemplateRef<any>;
1721
     * this.grid.sortHeaderIconTemplate = this.template;
1722
     * ```
1723
     */
1724
    public set sortHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1725
        this._sortHeaderIconTemplate = template;
922✔
1726
    }
1727

1728
    /**
1729
     * @hidden
1730
     * @internal
1731
     */
1732
    @ContentChildren(IgxDragIndicatorIconDirective, { read: TemplateRef, descendants: false })
1733
    public dragIndicatorIconTemplates: QueryList<TemplateRef<IgxGridEmptyTemplateContext>>;
1734

1735

1736
    @ContentChild(IgxGridLoadingTemplateDirective, { read: TemplateRef })
1737
    protected loadingDirectiveTemplate: TemplateRef<IgxGridTemplateContext>;
1738

1739
    @ContentChild(IgxGridEmptyTemplateDirective, { read: TemplateRef })
1740
    protected emptyDirectiveTemplate: TemplateRef<IgxGridTemplateContext>;
1741

1742
    /**
1743
     * @hidden @internal
1744
     */
1745
    @ViewChildren(IgxRowEditTabStopDirective)
1746
    public rowEditTabsDEFAULT: QueryList<IgxRowEditTabStopDirective>;
1747

1748
    /**
1749
     * @hidden @internal
1750
     */
1751
    @ContentChildren(IgxRowEditTabStopDirective, { descendants: true })
1752
    public rowEditTabsCUSTOM: QueryList<IgxRowEditTabStopDirective>;
1753

1754
    /**
1755
     * @hidden @internal
1756
     */
1757
    @ViewChild('rowEditingOverlay', { read: IgxToggleDirective })
1758
    public rowEditingOverlay: IgxToggleDirective;
1759

1760
    /**
1761
     * @hidden @internal
1762
     */
1763
    @HostBinding('attr.tabindex')
1764
    public tabindex = 0;
4,157✔
1765

1766
    /**
1767
     * @hidden @internal
1768
     */
1769
    @HostBinding('attr.role')
1770
    public hostRole = 'grid';
4,157✔
1771

1772
    /* contentChildren */
1773
    /* blazorInclude */
1774
    /* blazorTreatAsCollection */
1775
    /* blazorCollectionName: GridToolbarCollection */
1776
    /* ngQueryListName: toolbar */
1777
    /** @hidden @internal */
1778
    @ContentChildren(IgxToolbarToken)
1779
    public toolbar: QueryList<IgxGridToolbarComponent>;
1780

1781
    /* contentChildren */
1782
    /* blazorInclude */
1783
    /* blazorTreatAsCollection */
1784
    /* blazorCollectionName: PaginatorCollection */
1785
    /* ngQueryListName: paginationComponents */
1786
    /** @hidden @internal */
1787
    @ContentChildren(IgxPaginatorToken)
1788
    protected paginationComponents: QueryList<IgxPaginatorComponent>;
1789

1790
    /**
1791
     * @hidden @internal
1792
     */
1793
    @ViewChild('igxFilteringOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1794
    protected _outletDirective: IgxOverlayOutletDirective;
1795

1796
    /**
1797
     * @hidden @internal
1798
     * @igxElementsAnchor
1799
     */
1800
    @ViewChild('sink', { read: ViewContainerRef, static: true })
1801
    public anchor: ViewContainerRef;
1802

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

1809
    /**
1810
     * @hidden @internal
1811
     */
1812
    @ViewChild('defaultCollapsedTemplate', { read: TemplateRef, static: true })
1813
    protected defaultCollapsedTemplate: TemplateRef<any>;
1814

1815
    /**
1816
     * @hidden @internal
1817
     */
1818
    @ViewChild('defaultESFHeaderIcon', { read: TemplateRef, static: true })
1819
    protected defaultESFHeaderIconTemplate: TemplateRef<any>;
1820

1821
    @ViewChildren('summaryRow', { read: IgxSummaryRowComponent })
1822
    protected _summaryRowList: QueryList<IgxSummaryRowComponent>;
1823

1824
    @ViewChildren('row')
1825
    private _rowList: QueryList<IgxGridRowComponent>;
1826

1827
    @ViewChildren('pinnedRow')
1828
    private _pinnedRowList: QueryList<IgxGridRowComponent>;
1829

1830
    /**
1831
     * @hidden @internal
1832
     */
1833
    @ViewChild('defaultRowEditTemplate', { read: TemplateRef, static: true })
1834
    private defaultRowEditTemplate: TemplateRef<IgxGridRowEditTemplateContext>;
1835

1836
    @ViewChildren(IgxRowDirective, { read: IgxRowDirective })
1837
    private _dataRowList: QueryList<IgxRowDirective>;
1838

1839
    @HostBinding('class.igx-grid')
1840
    protected baseClass = 'igx-grid';
4,157✔
1841

1842
    @HostBinding('attr.aria-colcount')
1843
    protected get ariaColCount(): number {
1844
        return this.visibleColumns.length;
33,497✔
1845
    }
1846

1847
    @HostBinding('attr.aria-rowcount')
1848
    protected get ariaRowCount(): number {
1849
        return this._rendered ? this._rowCount : null;
33,497✔
1850
    }
1851

1852
    /**
1853
     * Gets/Sets the resource strings.
1854
     *
1855
     * @remarks
1856
     * By default it uses EN resources.
1857
     */
1858
    @Input()
1859
    public set resourceStrings(value: IGridResourceStrings) {
1860
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
3✔
1861
    }
1862

1863
    public get resourceStrings(): IGridResourceStrings {
1864
        return this._resourceStrings;
235,070✔
1865
    }
1866

1867
    /**
1868
     * Gets/Sets the filtering logic of the `IgxGridComponent`.
1869
     *
1870
     * @remarks
1871
     * The default is AND.
1872
     * @example
1873
     * ```html
1874
     * <igx-grid [data]="Data" [autoGenerate]="true" [filteringLogic]="filtering"></igx-grid>
1875
     * ```
1876
     */
1877
    @WatchChanges()
1878
    @Input()
1879
    public get filteringLogic() {
1880
        return this._filteringExpressionsTree.operator;
333✔
1881
    }
1882

1883
    public set filteringLogic(value: FilteringLogic) {
1884
        this._filteringExpressionsTree.operator = value;
3✔
1885
    }
1886

1887
    /* mustSetInCodePlatforms: WebComponents;Blazor */
1888
    /**
1889
     * Gets/Sets the filtering state.
1890
     *
1891
     * @example
1892
     * ```html
1893
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(filteringExpressionsTree)]="model.filteringExpressions"></igx-grid>
1894
     * ```
1895
     * @remarks
1896
     * Supports two-way binding.
1897
     */
1898
    @WatchChanges()
1899
    @Input()
1900
    public get filteringExpressionsTree() {
1901
        return this._filteringExpressionsTree;
129,076✔
1902
    }
1903

1904
    public set filteringExpressionsTree(value) {
1905
        if (value && isTree(value)) {
912✔
1906
            for (let index = 0; index < value.filteringOperands.length; index++) {
912✔
1907
                if (!(isTree(value.filteringOperands[index]))) {
476✔
1908
                    const newExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, value.filteringOperands[index].fieldName);
7✔
1909
                    newExpressionsTree.filteringOperands.push(value.filteringOperands[index]);
7✔
1910
                    value.filteringOperands[index] = newExpressionsTree;
7✔
1911
                }
1912
            }
1913

1914
            value.type = FilteringExpressionsTreeType.Regular;
912✔
1915
            if (value && this._columns?.length > 0) {
912✔
1916
                this._filteringExpressionsTree = this.getRecreatedTree(value);
758✔
1917
            } else {
1918
                this._filteringExpressionsTree = value;
154✔
1919
            }
1920
            this.filteringPipeTrigger++;
912✔
1921
            this.filteringExpressionsTreeChange.emit(this._filteringExpressionsTree);
912✔
1922

1923
            if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
912✔
1924
                this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1925
                this._filteredData = null;
457✔
1926
            }
1927

1928
            this.filteringService.refreshExpressions();
912✔
1929
            this.selectionService.clearHeaderCBState();
912✔
1930
            this.summaryService.clearSummaryCache();
912✔
1931
            this.notifyChanges();
912✔
1932
        }
1933
    }
1934

1935
    /**
1936
     * Gets/Sets the advanced filtering state.
1937
     *
1938
     * @example
1939
     * ```typescript
1940
     * let advancedFilteringExpressionsTree = this.grid.advancedFilteringExpressionsTree;
1941
     * this.grid.advancedFilteringExpressionsTree = logic;
1942
     * ```
1943
     */
1944
    @WatchChanges()
1945
    @Input()
1946
    public get advancedFilteringExpressionsTree() {
1947
        return this._advancedFilteringExpressionsTree;
69,977✔
1948
    }
1949

1950
    public set advancedFilteringExpressionsTree(value) {
1951
        const filteringEventArgs: IFilteringEventArgs = {
75✔
1952
            owner: this,
1953
            filteringExpressions: value,
1954
            cancel: false
1955
        };
1956

1957
        this.filtering.emit(filteringEventArgs);
75✔
1958

1959
        if (filteringEventArgs.cancel) {
75✔
1960
            return;
1✔
1961
        }
1962

1963
        if (value && isTree(value)) {
74✔
1964
            value.type = FilteringExpressionsTreeType.Advanced;
60✔
1965
            if (this._columns && this._columns.length > 0) {
60✔
1966
                this._advancedFilteringExpressionsTree = this.getRecreatedTree(value);
54✔
1967
            } else {
1968
                this._advancedFilteringExpressionsTree = value;
6✔
1969
            }
1970
            this.filteringPipeTrigger++;
60✔
1971
        } else {
1972
            this._advancedFilteringExpressionsTree = null;
14✔
1973
        }
1974
        this.advancedFilteringExpressionsTreeChange.emit(this._advancedFilteringExpressionsTree);
74✔
1975

1976
        if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
74✔
1977
            this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1978
            this._filteredData = null;
19✔
1979
        }
1980

1981
        this.selectionService.clearHeaderCBState();
74✔
1982
        this.summaryService.clearSummaryCache();
74✔
1983
        this.notifyChanges();
74✔
1984

1985
        // Wait for the change detection to update filtered data through the pipes and then emit the event.
1986
        requestAnimationFrame(() => this.filteringDone.emit(this._advancedFilteringExpressionsTree));
74✔
1987
    }
1988

1989
    /**
1990
     * Gets/Sets the locale.
1991
     *
1992
     * @remarks
1993
     * If not set, returns browser's language.
1994
     */
1995
    @Input()
1996
    public get locale(): string {
1997
        return this._locale;
1,559,325✔
1998
    }
1999

2000
    public set locale(value: string) {
2001
        if (value !== this._locale) {
4,174✔
2002
            this._locale = value;
4,168✔
2003
            this._currencyPositionLeft = undefined;
4,168✔
2004
            this.summaryService.clearSummaryCache();
4,168✔
2005
            this.pipeTrigger++;
4,168✔
2006
            this.notifyChanges();
4,168✔
2007
            this.localeChange.emit();
4,168✔
2008
        }
2009
    }
2010

2011
    @Input()
2012
    public get pagingMode() {
2013
        return this._pagingMode;
7,408✔
2014
    }
2015

2016
    public set pagingMode(val: GridPagingMode) {
2017
        this._pagingMode = val;
2✔
2018
        this.pipeTrigger++;
2✔
2019
        this.notifyChanges(true);
2✔
2020
    }
2021

2022
    /** @hidden @internal */
2023
    public get page(): number {
2024
        return this.paginator?.page || 0;
2,258,773✔
2025
    }
2026

2027
    public set page(val: number) {
2028
        if (this.paginator) {
715✔
2029
            this.paginator.page = val;
331✔
2030
        }
2031
    }
2032

2033
    /** @hidden @internal */
2034
    public get perPage(): number {
2035
        return this.paginator?.perPage || DEFAULT_ITEMS_PER_PAGE;
2,258,684✔
2036
    }
2037

2038
    public set perPage(val: number) {
2039
        if (this.paginator) {
20✔
2040
            this.paginator.perPage = val;
20✔
2041
        }
2042
    }
2043

2044
    /**
2045
     * Gets/Sets if the row selectors are hidden.
2046
     *
2047
     * @remarks
2048
     *  By default row selectors are shown
2049
     */
2050
    @WatchChanges()
2051
    @Input({ transform: booleanAttribute })
2052
    public get hideRowSelectors() {
2053
        return this._hideRowSelectors;
31,792✔
2054
    }
2055

2056
    public set hideRowSelectors(value: boolean) {
2057
        this._hideRowSelectors = value;
21✔
2058
        this.notifyChanges(true);
21✔
2059
    }
2060

2061
    /**
2062
     * Gets/Sets whether rows can be moved.
2063
     *
2064
     * @example
2065
     * ```html
2066
     * <igx-grid #grid [rowDraggable]="true"></igx-grid>
2067
     * ```
2068
     */
2069
    @Input({ transform: booleanAttribute })
2070
    public get rowDraggable(): boolean {
2071
        return this._rowDrag && this.hasVisibleColumns;
331,039✔
2072
    }
2073

2074
    public set rowDraggable(val: boolean) {
2075
        this._rowDrag = val;
47✔
2076
        this.notifyChanges(true);
47✔
2077
    }
2078

2079
    /**
2080
     * Gets/Sets the trigger for validators used when editing the grid.
2081
     *
2082
     * @example
2083
     * ```html
2084
     * <igx-grid #grid validationTrigger='blur'></igx-grid>
2085
     * ```
2086
     */
2087
    @Input()
2088
    public validationTrigger: GridValidationTrigger = 'change';
4,157✔
2089

2090
    /**
2091
     * @hidden
2092
     * @internal
2093
     */
2094
    public rowDragging = false;
4,157✔
2095

2096
    /** @hidden @internal */
2097
    public dragRowID = null;
4,157✔
2098

2099
    /**
2100
     * Gets/Sets whether the rows are editable.
2101
     *
2102
     * @remarks
2103
     * By default it is set to false.
2104
     * @example
2105
     * ```html
2106
     * <igx-grid #grid [rowEditable]="true" [primaryKey]="'ProductID'" ></igx-grid>
2107
     * ```
2108
     */
2109
    @WatchChanges()
2110
    @Input({ transform: booleanAttribute })
2111
    public get rowEditable(): boolean {
2112
        return this._rowEditable;
13,585,981✔
2113
    }
2114

2115
    public set rowEditable(val: boolean) {
2116
        if (!this._init) {
558✔
2117
            this.refreshGridState();
35✔
2118
        }
2119
        this._rowEditable = val;
558✔
2120
        this.notifyChanges();
558✔
2121
    }
2122

2123
    /**
2124
     * Gets/Sets the height.
2125
     *
2126
     * @example
2127
     * ```html
2128
     * <igx-grid #grid [data]="Data" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2129
     * ```
2130
     */
2131
    @WatchChanges()
2132
    @HostBinding('style.height')
2133
    @Input()
2134
    public get height(): string | null {
2135
        return this._height;
37,097✔
2136
    }
2137

2138
    public set height(value: string | null) {
2139
        if (this._height !== value) {
3,092✔
2140
            this._height = value;
3,090✔
2141
            this.nativeElement.style.height = value;
3,090✔
2142
            this.notifyChanges(true);
3,090✔
2143
        }
2144
    }
2145

2146
    /**
2147
     * @hidden @internal
2148
     */
2149
    @HostBinding('style.width')
2150
    public get hostWidth() {
2151
        return this._width || this._hostWidth;
28,061✔
2152
    }
2153

2154
    /**
2155
     * Gets/Sets the width of the grid.
2156
     *
2157
     * @example
2158
     * ```typescript
2159
     * let gridWidth = this.grid.width;
2160
     * ```
2161
     */
2162
    @WatchChanges()
2163
    @Input()
2164
    public get width(): string | null {
2165
        return this._width;
146,374✔
2166
    }
2167

2168
    public set width(value: string | null) {
2169
        if (this._width !== value) {
2,097✔
2170
            this._width = value;
2,097✔
2171
            this.nativeElement.style.width = value;
2,097✔
2172
            this.notifyChanges(true);
2,097✔
2173
        }
2174
    }
2175

2176
    /** @hidden @internal */
2177
    public get headerWidth() {
2178
        return parseInt(this.width, 10) - 17;
×
2179
    }
2180

2181
    /**
2182
     * Gets/Sets the row height.
2183
     *
2184
     * @example
2185
     * ```html
2186
     * <igx-grid #grid [data]="localData" [rowHeight]="100" [autoGenerate]="true"></igx-grid>
2187
     * ```
2188
     */
2189
    @WatchChanges()
2190
    @Input()
2191
    public get rowHeight(): number {
2192
        return this._rowHeight ? this._rowHeight : this.defaultRowHeight;
351,440✔
2193
    }
2194

2195
    public set rowHeight(value: number | string) {
2196
        if (typeof value !== 'number') {
2✔
2197
            value = parseInt(value, 10);
1✔
2198
        }
2199
        this._rowHeight = value;
2✔
2200
    }
2201

2202
    /**
2203
     * Gets/Sets the default width of the columns.
2204
     *
2205
     * @example
2206
     * ```html
2207
     * <igx-grid #grid [data]="localData" [columnWidth]="100" [autoGenerate]="true"></igx-grid>
2208
     * ```
2209
     */
2210
    @WatchChanges()
2211
    @Input()
2212
    public get columnWidth(): string {
2213
        return this._columnWidth;
604✔
2214
    }
2215
    public set columnWidth(value: string) {
2216
        this._columnWidth = value;
224✔
2217
        this.columnWidthSetByUser = true;
224✔
2218
        this.notifyChanges(true);
224✔
2219
    }
2220

2221
    /**
2222
     * Get/Sets the message displayed when there are no records.
2223
     *
2224
     * @example
2225
     * ```html
2226
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
2227
     * ```
2228
     */
2229
    @Input()
2230
    public set emptyGridMessage(value: string) {
2231
        this._emptyGridMessage = value;
×
2232
    }
2233
    public get emptyGridMessage(): string {
2234
        return this._emptyGridMessage || this.resourceStrings.igx_grid_emptyGrid_message;
738✔
2235
    }
2236

2237
    /**
2238
     * Gets/Sets whether the grid is going to show a loading indicator.
2239
     *
2240
     * @example
2241
     * ```html
2242
     * <igx-grid #grid [data]="Data" [isLoading]="true" [autoGenerate]="true"></igx-grid>
2243
     * ```
2244
     */
2245
    @WatchChanges()
2246
    @Input({ transform: booleanAttribute })
2247
    public set isLoading(value: boolean) {
2248
        if (this._isLoading !== value) {
16✔
2249
            this._isLoading = value;
16✔
2250
            if (this.data) {
16✔
2251
                this.evaluateLoadingState();
10✔
2252
            }
2253
        }
2254
        Promise.resolve().then(() => {
16✔
2255
            // wait for the current detection cycle to end before triggering a new one.
2256
            this.notifyChanges();
16✔
2257
        });
2258
    }
2259

2260
    public get isLoading(): boolean {
2261
        return this._isLoading;
95,276✔
2262
    }
2263

2264
    /**
2265
     * Gets/Sets whether the columns should be auto-generated once again after the initialization of the grid
2266
     *
2267
     * @remarks
2268
     * This will allow to bind the grid to remote data and having auto-generated columns at the same time.
2269
     * Note that after generating the columns, this property would be disabled to avoid re-creating
2270
     * columns each time a new data is assigned.
2271
     * @example
2272
     * ```typescript
2273
     *  this.grid.shouldGenerate = true;
2274
     * ```
2275
     * @deprecated in version 18.2.0. Column re-creation now relies on `autoGenerate` instead.
2276
     */
2277
    public get shouldGenerate(): boolean {
2278
        return this.autoGenerate;
×
2279
    }
2280

2281
    public set shouldGenerate(value: boolean) {
2282
        this.autoGenerate = value;
×
2283
    }
2284

2285
    /**
2286
     * Gets/Sets the message displayed when there are no records and the grid is filtered.
2287
     *
2288
     * @example
2289
     * ```html
2290
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
2291
     * ```
2292
     */
2293
    @Input()
2294
    public set emptyFilteredGridMessage(value: string) {
2295
        this._emptyFilteredGridMessage = value;
×
2296
    }
2297

2298
    public get emptyFilteredGridMessage(): string {
2299
        return this._emptyFilteredGridMessage || this.resourceStrings.igx_grid_emptyFilteredGrid_message;
203✔
2300
    }
2301

2302
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
2303
    /**
2304
     * Gets/Sets the initial pinning configuration.
2305
     *
2306
     * @remarks
2307
     * Allows to apply pinning the columns to the start or the end.
2308
     * Note that pinning to both sides at a time is not allowed.
2309
     * @example
2310
     * ```html
2311
     * <igx-grid [pinning]="pinningConfig"></igx-grid>
2312
     * ```
2313
     */
2314
    @Input()
2315
    public get pinning() {
2316
        return this._pinning;
2,617,096✔
2317
    }
2318
    public set pinning(value) {
2319
        if (value !== this._pinning) {
137✔
2320
            this.resetCaches();
137✔
2321
        }
2322
        this._pinning = value;
137✔
2323
    }
2324

2325
    /**
2326
     * Gets/Sets if the filtering is enabled.
2327
     *
2328
     * @example
2329
     * ```html
2330
     * <igx-grid #grid [data]="localData" [allowFiltering]="true" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2331
     * ```
2332
     */
2333
    @Input({ transform: booleanAttribute })
2334
    public get allowFiltering() {
2335
        return this._allowFiltering;
414,901✔
2336
    }
2337

2338
    public set allowFiltering(value) {
2339
        if (this._allowFiltering !== value) {
751✔
2340
            this._allowFiltering = value;
740✔
2341
            this.filteringService.registerSVGIcons();
740✔
2342

2343

2344
            this.filteringService.isFilterRowVisible = false;
740✔
2345
            this.filteringService.filteredColumn = null;
740✔
2346

2347
            this.notifyChanges(true);
740✔
2348
        }
2349
    }
2350

2351
    /**
2352
     * Gets/Sets a value indicating whether the advanced filtering is enabled.
2353
     *
2354
     * @example
2355
     * ```html
2356
     * <igx-grid #grid [data]="localData" [allowAdvancedFiltering]="true" [autoGenerate]="true"></igx-grid>
2357
     * ```
2358
     */
2359
    @Input({ transform: booleanAttribute })
2360
    public get allowAdvancedFiltering() {
2361
        return this._allowAdvancedFiltering;
583✔
2362
    }
2363

2364
    public set allowAdvancedFiltering(value) {
2365
        if (this._allowAdvancedFiltering !== value) {
57✔
2366
            this._allowAdvancedFiltering = value;
56✔
2367
            this.filteringService.registerSVGIcons();
56✔
2368

2369
            if (!this._init) {
56✔
2370
                this.notifyChanges(true);
5✔
2371
            }
2372
        }
2373
    }
2374

2375
    /**
2376
     * Gets/Sets the filter mode.
2377
     *
2378
     * @example
2379
     * ```html
2380
     * <igx-grid #grid [data]="localData" [filterMode]="'quickFilter'" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2381
     * ```
2382
     * @remarks
2383
     * By default it's set to FilterMode.quickFilter.
2384
     */
2385
    @Input()
2386
    public get filterMode() {
2387
        return this._filterMode;
95,556✔
2388
    }
2389

2390
    public set filterMode(value: FilterMode) {
2391
        switch (value) {
189!
2392
            case FilterMode.excelStyleFilter:
2393
            case FilterMode.quickFilter:
2394
                this._filterMode = value;
189✔
2395
                break;
189✔
2396
            default:
2397
                break;
×
2398
        }
2399

2400
        if (this.filteringService.isFilterRowVisible) {
189✔
2401
            this.filteringRow.close();
1✔
2402
        }
2403
        this.notifyChanges(true);
189✔
2404
    }
2405

2406
    /**
2407
     * Gets/Sets the summary position.
2408
     *
2409
     * @example
2410
     * ```html
2411
     * <igx-grid #grid [data]="localData" summaryPosition="top" [autoGenerate]="true"></igx-grid>
2412
     * ```
2413
     * @remarks
2414
     * By default it is bottom.
2415
     */
2416
    @Input()
2417
    public get summaryPosition() {
2418
        return this._summaryPosition;
32,429✔
2419
    }
2420

2421
    public set summaryPosition(value: GridSummaryPosition) {
2422
        this._summaryPosition = value;
18✔
2423
        this.notifyChanges();
18✔
2424
    }
2425

2426
    /**
2427
     * Gets/Sets the summary calculation mode.
2428
     *
2429
     * @example
2430
     * ```html
2431
     * <igx-grid #grid [data]="localData" summaryCalculationMode="rootLevelOnly" [autoGenerate]="true"></igx-grid>
2432
     * ```
2433
     * @remarks
2434
     * By default it is rootAndChildLevels which means the summaries are calculated for the root level and each child level.
2435
     */
2436
    @Input()
2437
    public get summaryCalculationMode() {
2438
        return this._summaryCalculationMode;
277,672✔
2439
    }
2440

2441
    public set summaryCalculationMode(value: GridSummaryCalculationMode) {
2442
        this._summaryCalculationMode = value;
63✔
2443
        if (!this._init) {
63✔
2444
            this.crudService.endEdit(false);
25✔
2445
            this.summaryService.resetSummaryHeight();
25✔
2446
            this.notifyChanges(true);
25✔
2447
        }
2448
    }
2449

2450
    /**
2451
     * Controls whether the summary row is visible when groupBy/parent row is collapsed.
2452
     *
2453
     * @example
2454
     * ```html
2455
     * <igx-grid #grid [data]="localData" [showSummaryOnCollapse]="true" [autoGenerate]="true"></igx-grid>
2456
     * ```
2457
     * @remarks
2458
     * By default showSummaryOnCollapse is set to 'false' which means that the summary row is not visible
2459
     * when the groupBy/parent row is collapsed.
2460
     */
2461
    @Input({ transform: booleanAttribute })
2462
    public get showSummaryOnCollapse() {
2463
        return this._showSummaryOnCollapse;
32,422✔
2464
    }
2465

2466
    public set showSummaryOnCollapse(value: boolean) {
2467
        this._showSummaryOnCollapse = value;
9✔
2468
        this.notifyChanges();
9✔
2469
    }
2470

2471
    /**
2472
     * Gets/Sets the filtering strategy of the grid.
2473
     *
2474
     * @example
2475
     * ```html
2476
     *  <igx-grid #grid [data]="localData" [filterStrategy]="filterStrategy"></igx-grid>
2477
     * ```
2478
     */
2479
    @Input()
2480
    public get filterStrategy(): IFilteringStrategy {
2481
        return this._filterStrategy;
46,957✔
2482
    }
2483

2484
    public set filterStrategy(classRef: IFilteringStrategy) {
2485
        this._filterStrategy = classRef;
38✔
2486
    }
2487

2488
    /**
2489
     * Gets/Sets the sorting strategy of the grid.
2490
     *
2491
     * @example
2492
     * ```html
2493
     *  <igx-grid #grid [data]="localData" [sortStrategy]="sortStrategy"></igx-grid>
2494
     * ```
2495
     */
2496
    @Input()
2497
    public get sortStrategy(): IGridSortingStrategy {
2498
        return this._sortingStrategy;
49,097✔
2499
    }
2500

2501
    public set sortStrategy(value: IGridSortingStrategy) {
2502
        this._sortingStrategy = value;
15✔
2503
    }
2504

2505
    /**
2506
     * Gets/Sets the sorting options - single or multiple sorting.
2507
     * Accepts an `ISortingOptions` object with any of the `mode` properties.
2508
     *
2509
     * @example
2510
     * ```typescript
2511
     * const _sortingOptions: ISortingOptions = {
2512
     *      mode: 'single'
2513
     * }
2514
     * ```html
2515
     * <igx-grid [sortingOptions]="sortingOptions"><igx-grid>
2516
     * ```
2517
     */
2518
    @Input()
2519
    public set sortingOptions(value: ISortingOptions) {
2520
        if (!this._init) {
3✔
2521
            // clear sort only if option is changed runtime. No need to clear on initial load.
2522
            this.clearSort();
2✔
2523
        }
2524
        this._sortingOptions = Object.assign(this._sortingOptions, value);
3✔
2525
    }
2526

2527
    public get sortingOptions() {
2528
        return this._sortingOptions;
29,212✔
2529
    }
2530

2531
    /* blazorByValueArray */
2532
    /* blazorAlwaysWriteback */
2533
    /* @tsTwoWayProperty (true, "SelectedRowsChange", "Detail", false) */
2534
    /* blazorPrimitiveValue */
2535
    /**
2536
     * Gets/Sets the current selection state.
2537
     *
2538
     * @remarks
2539
     * Represents the selected rows' IDs (primary key or rowData)
2540
     * @example
2541
     * ```html
2542
     * <igx-grid [data]="localData" primaryKey="ID" rowSelection="multiple" [selectedRows]="[0, 1, 2]"><igx-grid>
2543
     * ```
2544
     */
2545
    @Input()
2546
    public set selectedRows(rowIDs: any[]) {
2547
        this.selectRows(rowIDs || [], true);
138!
2548
    }
2549

2550
    public get selectedRows(): any[] {
2551
        return this.selectionService.getSelectedRows();
731✔
2552
    }
2553

2554

2555
    /** @hidden @internal */
2556
    public get headerGroupsList(): IgxGridHeaderGroupComponent[] {
2557
        return this.theadRow.groups;
20,833✔
2558
    }
2559

2560
    /** @hidden @internal */
2561
    public get headerCellList(): IgxGridHeaderComponent[] {
2562
        return this.headerGroupsList.map(headerGroup => headerGroup.header).filter(header => header);
5,064✔
2563
    }
2564

2565
    /** @hidden @internal */
2566
    public get filterCellList(): IgxGridFilteringCellComponent[] {
2567
        return this.headerGroupsList.map(group => group.filter).filter(cell => cell);
14,499✔
2568
    }
2569

2570
    /**
2571
     * @hidden @internal
2572
     */
2573
    public get summariesRowList() {
2574
        const res = new QueryList<any>();
2,671✔
2575
        if (!this._summaryRowList) {
2,671!
2576
            return res;
×
2577
        }
2578
        const sumList = this._summaryRowList.filter((item) => item.element.nativeElement.parentElement !== null);
2,671✔
2579
        res.reset(sumList);
2,671✔
2580
        return res;
2,671✔
2581
    }
2582

2583
    /* csSuppress */
2584
    /**
2585
     * A list of `IgxGridRowComponent`.
2586
     *
2587
     * @example
2588
     * ```typescript
2589
     * const rowList = this.grid.rowList;
2590
     * ```
2591
     */
2592
    public get rowList() {
2593
        const res = new QueryList<IgxRowDirective>();
26,548✔
2594
        if (!this._rowList) {
26,548✔
2595
            return res;
1,094✔
2596
        }
2597
        const rList = this._rowList
25,454✔
2598
            .filter((item) => item.element.nativeElement.parentElement !== null)
165,698✔
2599
            .sort((a, b) => a.index - b.index);
167,637✔
2600
        res.reset(rList);
25,454✔
2601
        return res;
25,454✔
2602
    }
2603

2604
    /* csSuppress */
2605
    /**
2606
     * A list of currently rendered `IgxGridRowComponent`'s.
2607
     *
2608
     * @example
2609
     * ```typescript
2610
     * const dataList = this.grid.dataRowList;
2611
     * ```
2612
     */
2613
    public get dataRowList(): QueryList<IgxRowDirective> {
2614
        const res = new QueryList<IgxRowDirective>();
56,321✔
2615
        if (!this._dataRowList) {
56,321✔
2616
            return res;
9,488✔
2617
        }
2618
        const rList = this._dataRowList.filter(item => item.element.nativeElement.parentElement !== null).sort((a, b) => a.index - b.index);
230,204✔
2619
        res.reset(rList);
46,833✔
2620
        return res;
46,833✔
2621
    }
2622

2623
    /**
2624
     * Gets the header row selector template.
2625
     */
2626
    @Input()
2627
    public get headSelectorTemplate(): TemplateRef<IgxHeadSelectorTemplateContext> {
2628
        return this._headSelectorTemplate || this.headSelectorsTemplates?.first;
3,334✔
2629
    }
2630

2631
    /**
2632
     * Sets the header row selector template.
2633
     * ```html
2634
     * <ng-template #template igxHeadSelector let-headContext>
2635
     * {{ headContext.selectedCount }} / {{ headContext.totalCount  }}
2636
     * </ng-template>
2637
     * ```
2638
     * ```typescript
2639
     * @ViewChild("'template'", {read: TemplateRef })
2640
     * public template: TemplateRef<any>;
2641
     * this.grid.headSelectorTemplate = this.template;
2642
     * ```
2643
     */
2644
    public set headSelectorTemplate(template: TemplateRef<IgxHeadSelectorTemplateContext>) {
2645
        this._headSelectorTemplate = template;
1✔
2646
    }
2647

2648
    /**
2649
     * @hidden
2650
     * @internal
2651
     */
2652
    public get isRowPinningToTop() {
2653
        return this.pinning.rows !== RowPinningPosition.Bottom;
682,880✔
2654
    }
2655

2656
    /**
2657
     * Gets the row selector template.
2658
     */
2659
    @Input()
2660
    public get rowSelectorTemplate(): TemplateRef<IgxRowSelectorTemplateContext> {
2661
        return this._rowSelectorTemplate || this.rowSelectorsTemplates?.first;
20,235✔
2662
    }
2663

2664
    /**
2665
         * Sets a custom template for the row selectors.
2666
         * ```html
2667
         * <ng-template #template igxRowSelector let-rowContext>
2668
         *    <igx-checkbox [checked]="rowContext.selected"></igx-checkbox>
2669
         * </ng-template>
2670
         * ```
2671
         * ```typescript
2672
         * @ViewChild("'template'", {read: TemplateRef })
2673
         * public template: TemplateRef<any>;
2674
         * this.grid.rowSelectorTemplate = this.template;
2675
         * ```
2676
         */
2677
    public set rowSelectorTemplate(template: TemplateRef<IgxRowSelectorTemplateContext>) {
2678
        this._rowSelectorTemplate = template;
1✔
2679
    }
2680

2681
    /**
2682
     * @hidden @internal
2683
     */
2684
    public get rowOutletDirective() {
2685
        return this.rowEditingOutletDirective;
4,436✔
2686
    }
2687

2688
    /**
2689
     * @hidden @internal
2690
     */
2691
    public get parentRowOutletDirective() {
2692
        return this.outlet;
1✔
2693
    }
2694

2695
    /**
2696
     * @hidden @internal
2697
     */
2698
    public get rowEditCustom(): TemplateRef<IgxGridRowEditTemplateContext> {
2699
        if (this.rowEditCustomDirectives && this.rowEditCustomDirectives.first) {
6,611✔
2700
            return this.rowEditCustomDirectives.first;
62✔
2701
        }
2702
        return null;
6,549✔
2703
    }
2704

2705
    /**
2706

2707
    /**
2708
     * @hidden @internal
2709
     */
2710
    public get rowEditContainer(): TemplateRef<IgxGridRowEditTemplateContext> {
2711
        return this.rowEditCustom ? this.rowEditCustom : this.defaultRowEditTemplate;
6,041✔
2712
    }
2713

2714
    /**
2715
     * The custom template, if any, that should be used when rendering the row drag indicator icon
2716
     */
2717
    @Input()
2718
    public get dragIndicatorIconTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
2719
        return this._customDragIndicatorIconTemplate || this.dragIndicatorIconTemplates?.first;
3,379✔
2720
    }
2721

2722
    /**
2723
     * Sets a custom template that should be used when rendering the row drag indicator icon.
2724
     *```html
2725
     * <ng-template #template igxDragIndicatorIcon>
2726
     *    <igx-icon>expand_less</igx-icon>
2727
     * </ng-template>
2728
     * ```
2729
     * ```typescript
2730
     * @ViewChild("'template'", {read: TemplateRef })
2731
     * public template: TemplateRef<any>;
2732
     * this.grid.dragIndicatorIconTemplate = this.template;
2733
     * ```
2734
     */
2735
    public set dragIndicatorIconTemplate(val: TemplateRef<IgxGridEmptyTemplateContext>) {
2736
        this._customDragIndicatorIconTemplate = val;
917✔
2737
    }
2738

2739
    /**
2740
     * @hidden @internal
2741
     */
2742
    public get firstEditableColumnIndex(): number {
2743
        const index = this.visibleColumns.filter(col => col.editable)
26✔
2744
            .map(c => c.visibleIndex).sort((a, b) => a - b);
21✔
2745
        return index.length ? index[0] : null;
4!
2746
    }
2747

2748
    /**
2749
     * @hidden @internal
2750
     */
2751
    public get lastEditableColumnIndex(): number {
2752
        const index = this.visibleColumns.filter(col => col.editable)
47✔
2753
            .map(c => c.visibleIndex).sort((a, b) => a > b ? -1 : 1);
55✔
2754
        return index.length ? index[0] : null;
6!
2755
    }
2756

2757
    /**
2758
     * @hidden @internal
2759
     * TODO: Nav service logic doesn't handle 0 results from this querylist
2760
     */
2761
    public get rowEditTabs(): QueryList<IgxRowEditTabStopDirective> {
2762
        return this.rowEditTabsCUSTOM.length ? this.rowEditTabsCUSTOM : this.rowEditTabsDEFAULT;
141✔
2763
    }
2764

2765
    /** @hidden @internal */
2766
    public get activeDescendant() {
2767
        const activeElem = this.navigation.activeNode;
98,690✔
2768

2769
        if (!activeElem || !Object.keys(activeElem).length || activeElem.row < 0) {
98,690✔
2770
            return null;
89,228✔
2771
        }
2772
        return `${this.id}_${activeElem.row}_${activeElem.column}`;
9,462✔
2773
    }
2774

2775
    /** @hidden @internal */
2776
    public get bannerClass(): string {
2777
        const position = this.rowEditPositioningStrategy.isTop ? 'igx-banner__border-top' : 'igx-banner__border-bottom';
6,041✔
2778
        return `igx-banner ${position}`;
6,041✔
2779
    }
2780

2781
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
2782
    /**
2783
     * Gets/Sets the sorting state.
2784
     *
2785
     * @remarks
2786
     * Supports two-way data binding.
2787
     * @example
2788
     * ```html
2789
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(sortingExpressions)]="model.sortingExpressions"></igx-grid>
2790
     * ```
2791
     */
2792
    @WatchChanges()
2793
    @Input()
2794
    public get sortingExpressions(): ISortingExpression[] {
2795
        return this._sortingExpressions;
302,327✔
2796
    }
2797

2798
    public set sortingExpressions(value: ISortingExpression[]) {
2799
        this._sortingExpressions = cloneArray(value);
454✔
2800
        this.sortingExpressionsChange.emit(this._sortingExpressions);
454✔
2801
        this.notifyChanges();
454✔
2802
    }
2803

2804
    /**
2805
     * @hidden @internal
2806
     */
2807
    public get maxLevelHeaderDepth() {
2808
        if (this._maxLevelHeaderDepth === null) {
×
2809
            this._maxLevelHeaderDepth = this.hasColumnLayouts ?
×
2810
                this._columns.reduce((acc, col) => Math.max(acc, col.rowStart), 0) :
×
2811
                this._columns.reduce((acc, col) => Math.max(acc, col.level), 0);
×
2812
        }
2813
        return this._maxLevelHeaderDepth;
×
2814
    }
2815

2816
    /**
2817
     * Gets the number of hidden columns.
2818
     *
2819
     * @example
2820
     * ```typescript
2821
     * const hiddenCol = this.grid.hiddenColumnsCount;
2822
     * ``
2823
     */
2824
    public get hiddenColumnsCount() {
2825
        return this._columns.filter((col) => col.columnGroup === false && col.hidden === true).length;
28,979✔
2826
    }
2827

2828
    /**
2829
     * Gets the number of pinned columns.
2830
     */
2831
    public get pinnedColumnsCount() {
2832
        return this.pinnedColumns.filter(col => !col.columnLayout).length;
113✔
2833
    }
2834

2835
    /**
2836
     * Gets/Sets whether the grid has batch editing enabled.
2837
     * When batch editing is enabled, changes are not made directly to the underlying data.
2838
     * Instead, they are stored as transactions, which can later be committed w/ the `commit` method.
2839
     *
2840
     * @example
2841
     * ```html
2842
     * <igx-grid [batchEditing]="true" [data]="someData">
2843
     * </igx-grid>
2844
     * ```
2845
     */
2846
    @Input({ transform: booleanAttribute })
2847
    public get batchEditing(): boolean {
2848
        return this._batchEditing;
121,211✔
2849
    }
2850

2851
    public set batchEditing(val: boolean) {
2852
        if (val !== this._batchEditing) {
164✔
2853
            delete this._transactions;
152✔
2854
            this._batchEditing = val;
152✔
2855
            this.switchTransactionService(val);
152✔
2856
            this.subscribeToTransactions();
152✔
2857
        }
2858
    }
2859

2860
    /* blazorSuppress */
2861
    /**
2862
     * Get transactions service for the grid.
2863
     */
2864
    public get transactions(): TransactionService<Transaction, State> {
2865
        if (this._diTransactions && !this.batchEditing) {
3,599,437✔
2866
            return this._diTransactions;
1,428✔
2867
        }
2868
        return this._transactions;
3,598,009✔
2869
    }
2870

2871
    /**
2872
     * @hidden @internal
2873
     */
2874
    public get currentRowState(): any {
2875
        return this._currentRowState;
×
2876
    }
2877

2878
    /**
2879
     * @hidden @internal
2880
     */
2881
    public get currencyPositionLeft(): boolean {
2882
        if (this._currencyPositionLeft !== undefined) {
8✔
2883
            return this._currencyPositionLeft;
6✔
2884
        }
2885
        const format = getLocaleNumberFormat(this.locale, NumberFormatStyle.Currency);
2✔
2886
        const formatParts = format.split(',');
2✔
2887
        const i = formatParts.indexOf(formatParts.find(c => c.includes('¤')));
3✔
2888
        return this._currencyPositionLeft = i < 1;
2✔
2889
    }
2890

2891
    /**
2892
     * Gets/Sets cell selection mode.
2893
     *
2894
     * @remarks
2895
     * By default the cell selection mode is multiple
2896
     * @param selectionMode: GridSelectionMode
2897
     */
2898
    @WatchChanges()
2899
    @Input()
2900
    public get cellSelection() {
2901
        return this._cellSelectionMode;
1,227,059✔
2902
    }
2903

2904
    public set cellSelection(selectionMode: GridSelectionMode) {
2905
        this._cellSelectionMode = selectionMode;
33✔
2906
        // if (this.gridAPI.grid) {
2907
        this.selectionService.clear(true);
33✔
2908
        this.notifyChanges();
33✔
2909
        // }
2910
    }
2911

2912
    /**
2913
     * Gets/Sets row selection mode
2914
     *
2915
     * @remarks
2916
     * By default the row selection mode is 'none'
2917
     * Note that in IgxGrid and IgxHierarchicalGrid 'multipleCascade' behaves like 'multiple'
2918
     */
2919
    @WatchChanges()
2920
    @Input()
2921
    public get rowSelection() {
2922
        return this._rowSelectionMode;
303,528✔
2923
    }
2924

2925
    public set rowSelection(selectionMode: GridSelectionMode) {
2926
        this._rowSelectionMode = selectionMode;
527✔
2927
        if (!this._init) {
527✔
2928
            this.selectionService.clearAllSelectedRows();
72✔
2929
            this.notifyChanges(true);
72✔
2930
        }
2931
    }
2932

2933
    /**
2934
     * Gets/Sets column selection mode
2935
     *
2936
     * @remarks
2937
     * By default the row selection mode is none
2938
     * @param selectionMode: GridSelectionMode
2939
     */
2940
    @WatchChanges()
2941
    @Input()
2942
    public get columnSelection() {
2943
        return this._columnSelectionMode;
225,888✔
2944
    }
2945

2946
    public set columnSelection(selectionMode: GridSelectionMode) {
2947
        this._columnSelectionMode = selectionMode;
160✔
2948
        // if (this.gridAPI.grid) {
2949
        this.selectionService.clearAllSelectedColumns();
160✔
2950
        this.notifyChanges(true);
160✔
2951
        // }
2952
    }
2953

2954
    /**
2955
     * @hidden @internal
2956
     */
2957
    public set pagingState(value) {
2958
        this._pagingState = value;
583✔
2959
        if (this.paginator && !this._init) {
583✔
2960
            this.paginator.totalRecords = value.metadata.countRecords;
428✔
2961
        }
2962
    }
2963

2964
    public get pagingState() {
2965
        return this._pagingState;
404✔
2966
    }
2967

2968
    /**
2969
     * @hidden @internal
2970
     */
2971
    public rowEditMessage;
2972

2973
    /**
2974
     * @hidden @internal
2975
     */
2976
    public calcWidth: number;
2977
    /**
2978
     * @hidden @internal
2979
     */
2980
    public calcHeight = 0;
4,157✔
2981
    /**
2982
     * @hidden @internal
2983
     */
2984
    public tfootHeight: number;
2985

2986
    /**
2987
     * @hidden @internal
2988
     */
2989
    public disableTransitions = false;
4,157✔
2990

2991
    /**
2992
     * Represents the last search information.
2993
     */
2994
    public get lastSearchInfo(): ISearchInfo {
2995
        return this._lastSearchInfo;
1,498,679✔
2996
    }
2997

2998
    /**
2999
     * @hidden @internal
3000
     */
3001
    public columnWidthSetByUser = false;
4,157✔
3002

3003
    /**
3004
     * @hidden @internal
3005
     */
3006
    public pinnedRecords: any[];
3007

3008
    /**
3009
     * @hidden @internal
3010
     */
3011
    public unpinnedRecords: any[];
3012

3013
    /**
3014
     * @hidden @internal
3015
     */
3016
    public rendered$ = this.rendered.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true }));
4,157✔
3017

3018
    /** @hidden @internal */
3019
    public resizeNotify = new Subject<void>();
4,157✔
3020

3021
    /** @hidden @internal */
3022
    public rowAddedNotifier = new Subject<IRowDataEventArgs>();
4,157✔
3023

3024
    /** @hidden @internal */
3025
    public rowDeletedNotifier = new Subject<IRowDataEventArgs>();
4,157✔
3026

3027
    /** @hidden @internal */
3028
    public pipeTriggerNotifier = new Subject();
4,157✔
3029

3030
    /** @hidden @internal */
3031
    public _filteredSortedPinnedData: any[];
3032

3033
    /** @hidden @internal */
3034
    public _filteredSortedUnpinnedData: any[];
3035

3036
    /** @hidden @internal */
3037
    public _filteredPinnedData: any[];
3038

3039
    /**
3040
     * @hidden
3041
     */
3042
    public _filteredUnpinnedData;
3043
    /**
3044
     * @hidden @internal
3045
     */
3046
    public _destroyed = false;
4,157✔
3047
    /**
3048
     * @hidden @internal
3049
     */
3050
    public _totalRecords = -1;
4,157✔
3051
    /**
3052
     * @hidden @internal
3053
     */
3054
    public columnsWithNoSetWidths = null;
4,157✔
3055
    /**
3056
     * @hidden @internal
3057
     */
3058
    public pipeTrigger = 0;
4,157✔
3059
    /**
3060
     * @hidden @internal
3061
     */
3062
    public filteringPipeTrigger = 0;
4,157✔
3063

3064
    /**
3065
     * @hidden @internal
3066
     */
3067
    public isColumnWidthSum = false;
4,157✔
3068

3069
    /**
3070
     * @hidden @internal
3071
     */
3072
    public summaryPipeTrigger = 0;
4,157✔
3073
    /**
3074
     * @hidden @internal
3075
     */
3076
    public groupablePipeTrigger = 0;
4,157✔
3077

3078
    /**
3079
    * @hidden @internal
3080
    */
3081
    public EMPTY_DATA = [];
4,157✔
3082

3083
    /** @hidden @internal */
3084
    public get type(): GridType["type"] {
3085
        return 'flat';
24,825✔
3086
    }
3087

3088
    /** @hidden @internal */
3089
    public _baseFontSize: number;
3090

3091
    /**
3092
     * @hidden
3093
     */
3094
    public destroy$ = new Subject<any>();
4,157✔
3095
    /**
3096
     * @hidden
3097
     */
3098
    protected _pagingMode: GridPagingMode = 'local';
4,157✔
3099
    /**
3100
     * @hidden
3101
     */
3102
    protected _pagingState;
3103
    /**
3104
     * @hidden
3105
     */
3106
    protected _hideRowSelectors = false;
4,157✔
3107
    /**
3108
     * @hidden
3109
     */
3110
    protected _rowDrag = false;
4,157✔
3111
    /**
3112
     * @hidden
3113
     */
3114
    protected _columns: IgxColumnComponent[] = [];
4,157✔
3115
    /**
3116
     * @hidden
3117
     */
3118
    protected _pinnedColumns: IgxColumnComponent[] = [];
4,157✔
3119

3120
    /**
3121
     * @hidden
3122
     */
3123
    protected _pinnedStartColumns: IgxColumnComponent[] = [];
4,157✔
3124

3125
    /**
3126
     * @hidden
3127
     */
3128
    protected _pinnedEndColumns: IgxColumnComponent[] = [];
4,157✔
3129

3130
    /**
3131
     * @hidden
3132
     */
3133
    protected _unpinnedColumns: IgxColumnComponent[] = [];
4,157✔
3134
    /**
3135
     * @hidden
3136
     */
3137
    protected _filteringExpressionsTree: IFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And);
4,157✔
3138
    /**
3139
     * @hidden
3140
     */
3141
    protected _advancedFilteringExpressionsTree: IFilteringExpressionsTree;
3142
    /**
3143
     * @hidden
3144
     */
3145
    protected _sortingExpressions: Array<ISortingExpression> = [];
4,157✔
3146
    /**
3147
     * @hidden
3148
     */
3149
    protected _maxLevelHeaderDepth = null;
4,157✔
3150
    /**
3151
     * @hidden
3152
     */
3153
    protected _columnHiding = false;
4,157✔
3154
    /**
3155
     * @hidden
3156
     */
3157
    protected _columnPinning = false;
4,157✔
3158

3159
    protected _pinnedRecordIDs = [];
4,157✔
3160

3161
    /**
3162
     * @hidden
3163
     */
3164
    protected _hasVisibleColumns;
3165
    protected _allowFiltering = false;
4,157✔
3166
    protected _allowAdvancedFiltering = false;
4,157✔
3167
    protected _filterMode: FilterMode = FilterMode.quickFilter;
4,157✔
3168

3169

3170
    protected _defaultTargetRecordNumber = 10;
4,157✔
3171
    protected _expansionStates: Map<any, boolean> = new Map<any, boolean>();
4,157✔
3172
    protected _defaultExpandState = false;
4,157✔
3173
    protected _headerFeaturesWidth = NaN;
4,157✔
3174
    protected _init = true;
4,157✔
3175
    protected _firstAutoResize = true;
4,157✔
3176
    protected _autoSizeColumnsNotify = new Subject<void>();
4,157✔
3177
    protected _cdrRequestRepaint = false;
4,157✔
3178
    protected _userOutletDirective: IgxOverlayOutletDirective;
3179
    protected _transactions: TransactionService<Transaction, State>;
3180
    protected _batchEditing = false;
4,157✔
3181
    protected _sortingOptions: ISortingOptions = { mode: 'multiple' };
4,157✔
3182
    protected _filterStrategy: IFilteringStrategy = new FilteringStrategy();
4,157✔
3183
    protected _autoGeneratedCols = [];
4,157✔
3184
    protected _autoGeneratedColsRefs: ComponentRef<IgxColumnComponent>[] = [];
4,157✔
3185
    protected _dataView = [];
4,157✔
3186
    protected _lastSearchInfo: ISearchInfo = {
4,157✔
3187
        searchText: '',
3188
        caseSensitive: false,
3189
        exactMatch: false,
3190
        activeMatchIndex: 0,
3191
        matchInfoCache: [],
3192
        matchCount: 0,
3193
        content: ''
3194
    };
3195
    protected _hGridSchema: EntityType[];
3196
    protected gridComputedStyles;
3197

3198
    /** @hidden @internal */
3199
    public get paginator() {
3200
        return this.paginationComponents?.first;
3,765,137✔
3201
    }
3202

3203
    /**
3204
     * @hidden @internal
3205
     */
3206
    public get scrollSize() {
3207
        return this.verticalScrollContainer.getScrollNativeSize();
189,246✔
3208
    }
3209

3210
    private _primaryKey: string;
3211
    private _rowEditable = false;
4,157✔
3212
    private _currentRowState: any;
3213
    private _filteredSortedData = null;
4,157✔
3214
    private _filteredData = null;
4,157✔
3215

3216
    private _customDragIndicatorIconTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3217
    private _excelStyleHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext>;
3218
    private _rowSelectorTemplate: TemplateRef<IgxRowSelectorTemplateContext>;
3219
    private _headSelectorTemplate: TemplateRef<IgxHeadSelectorTemplateContext>;
3220
    private _rowEditTextTemplate: TemplateRef<IgxGridRowEditTextTemplateContext>;
3221
    private _rowAddTextTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3222
    private _rowEditActionsTemplate: TemplateRef<IgxGridRowEditActionsTemplateContext>;
3223
    private _dragGhostCustomTemplate: TemplateRef<IgxGridRowDragGhostContext>;
3224
    private _rowExpandedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3225
    private _rowCollapsedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3226
    private _headerExpandIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
3227
    private _headerCollapseIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
3228
    private _emptyGridTemplate: TemplateRef<IgxGridTemplateContext>;
3229
    private _loadingGridTemplate: TemplateRef<IgxGridTemplateContext>;
3230

3231
    private _cdrRequests = false;
4,157✔
3232
    private _resourceStrings = getCurrentResourceStrings(GridResourceStringsEN);
4,157✔
3233
    private _emptyGridMessage = null;
4,157✔
3234
    private _emptyFilteredGridMessage = null;
4,157✔
3235
    private _isLoading = false;
4,157✔
3236
    private _locale: string;
3237
    private overlayIDs = [];
4,157✔
3238
    private _sortingStrategy: IGridSortingStrategy;
3239
    private _pinning: IPinningConfig = { columns: ColumnPinningPosition.Start };
4,157✔
3240
    private _shouldRecalcRowHeight = false;
4,157✔
3241

3242
    private _hostWidth;
3243
    private _advancedFilteringOverlayId: string;
3244
    private _advancedFilteringPositionSettings: PositionSettings = {
4,157✔
3245
        verticalDirection: VerticalAlignment.Middle,
3246
        horizontalDirection: HorizontalAlignment.Center,
3247
        horizontalStartPoint: HorizontalAlignment.Center,
3248
        verticalStartPoint: VerticalAlignment.Middle
3249
    };
3250

3251
    private _advancedFilteringOverlaySettings: OverlaySettings = {
4,157✔
3252
        closeOnOutsideClick: false,
3253
        modal: false,
3254
        positionStrategy: new ConnectedPositioningStrategy(this._advancedFilteringPositionSettings),
3255
    };
3256

3257
    private columnListDiffer;
3258
    private rowListDiffer;
3259
    private _height: string | null = '100%';
4,157✔
3260
    private _width: string | null = '100%';
4,157✔
3261
    private _rowHeight: number | undefined;
3262
    private _horizontalForOfs: Array<IgxGridForOfDirective<any, any[]>> = [];
4,157✔
3263
    private _multiRowLayoutRowSize = 1;
4,157✔
3264
    // Caches
3265
    private _totalWidth = NaN;
4,157✔
3266
    private _pinnedVisible = [];
4,157✔
3267
    private _unpinnedVisible = [];
4,157✔
3268
    private _pinnedStartWidth = NaN;
4,157✔
3269
    private _pinnedEndWidth = NaN;
4,157✔
3270
    private _unpinnedWidth = NaN;
4,157✔
3271
    private _visibleColumns = [];
4,157✔
3272
    private _columnGroups = false;
4,157✔
3273

3274
    private _columnWidth: string;
3275

3276
    private _summaryPosition: GridSummaryPosition = GridSummaryPosition.bottom;
4,157✔
3277
    private _summaryCalculationMode: GridSummaryCalculationMode = GridSummaryCalculationMode.rootAndChildLevels;
4,157✔
3278
    private _showSummaryOnCollapse = false;
4,157✔
3279
    private _summaryRowHeight = 0;
4,157✔
3280
    private _cellSelectionMode: GridSelectionMode = GridSelectionMode.multiple;
4,157✔
3281
    private _rowSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,157✔
3282
    private _selectRowOnClick = true;
4,157✔
3283
    private _columnSelectionMode: GridSelectionMode = GridSelectionMode.none;
4,157✔
3284

3285
    private lastAddedRowIndex;
3286

3287
    private _currencyPositionLeft: boolean;
3288

3289
    private rowEditPositioningStrategy = new RowEditPositionStrategy({
4,157✔
3290
        horizontalDirection: HorizontalAlignment.Right,
3291
        verticalDirection: VerticalAlignment.Bottom,
3292
        horizontalStartPoint: HorizontalAlignment.Left,
3293
        verticalStartPoint: VerticalAlignment.Bottom,
3294
        closeAnimation: null
3295
    });
3296

3297
    private rowEditSettings: OverlaySettings = {
4,157✔
3298
        scrollStrategy: new AbsoluteScrollStrategy(),
3299
        modal: false,
3300
        closeOnOutsideClick: false,
3301
        outlet: this.rowOutletDirective,
3302
        positionStrategy: this.rowEditPositioningStrategy
3303
    };
3304

3305
    private transactionChange$ = new Subject<void>();
4,157✔
3306
    private _rendered = false;
4,157✔
3307
    private readonly DRAG_SCROLL_DELTA = 10;
4,157✔
3308
    private _dataCloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy();
4,157✔
3309
    private _autoSize = false;
4,157✔
3310
    private _sortHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,157✔
3311
    private _sortAscendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,157✔
3312
    private _sortDescendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
4,157✔
3313
    private _gridSize: Size = Size.Large;
4,157✔
3314
    private _defaultRowHeight = 50;
4,157✔
3315
    private _rowCount: number;
3316

3317
    /**
3318
     * @hidden @internal
3319
     */
3320
    public get minColumnWidth() {
3321
        return MINIMUM_COLUMN_WIDTH;
66,270✔
3322
    }
3323

3324
    protected get isCustomSetRowHeight(): boolean {
3325
        return !isNaN(this._rowHeight);
1,217,864✔
3326
    }
3327

3328
    /**
3329
     * @hidden @internal
3330
     */
3331
    public abstract id: string;
3332
    /* blazorSuppress */
3333
    public abstract data: any[] | null;
3334

3335
    /**
3336
     * Returns an array of objects containing the filtered data.
3337
     *
3338
     * @example
3339
     * ```typescript
3340
     * let filteredData = this.grid.filteredData;
3341
     * ```
3342
     */
3343
    public get filteredData() {
3344
        return this._filteredData;
49,302✔
3345
    }
3346

3347
    /**
3348
     * Returns an array containing the filtered sorted data.
3349
     *
3350
     * @example
3351
     * ```typescript
3352
     * const filteredSortedData = this.grid1.filteredSortedData;
3353
     * ```
3354
     */
3355
    public get filteredSortedData(): any[] {
3356
        return this._filteredSortedData;
128,809✔
3357
    }
3358

3359
    /**
3360
     * @hidden @internal
3361
     */
3362
    public get rowChangesCount() {
3363
        if (!this.crudService.row) {
17,300✔
3364
            return 0;
14,559✔
3365
        }
3366
        const f = (obj: any) => {
2,741✔
3367
            let changes = 0;
488✔
3368
            Object.keys(obj).forEach(key => isObject(obj[key]) ? changes += f(obj[key]) : changes++);
510✔
3369
            return changes;
488✔
3370
        };
3371
        if (this.transactions.getState(this.crudService.row.id)?.type === TransactionType.ADD) {
2,741✔
3372
            return this._columns.filter(c => c.field).length;
77✔
3373
        }
3374
        const rowChanges = this.transactions.getAggregatedValue(this.crudService.row.id, false);
2,722✔
3375
        return rowChanges ? f(rowChanges) : 0;
2,722✔
3376
    }
3377

3378
    /**
3379
     * @hidden @internal
3380
     */
3381
    public get dataWithAddedInTransactionRows() {
3382
        const result = cloneArray(this.gridAPI.get_all_data());
11,400✔
3383
        if (this.transactions.enabled) {
11,400✔
3384
            result.push(...this.transactions.getAggregatedChanges(true)
7,012✔
3385
                .filter(t => t.type === TransactionType.ADD)
1,231✔
3386
                .map(t => t.newValue));
356✔
3387
        }
3388

3389
        if (this.crudService.row && this.crudService.row.isAddRow) {
11,400✔
3390
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
386✔
3391
        }
3392

3393
        return result;
11,400✔
3394
    }
3395

3396
    /**
3397
     * @hidden @internal
3398
     */
3399
    public get dataLength() {
3400
        return this.transactions.enabled ? this.dataWithAddedInTransactionRows.length : this.gridAPI.get_all_data().length;
46,426✔
3401
    }
3402

3403
    /**
3404
     * @hidden @internal
3405
     */
3406
    public get template(): TemplateRef<IgxGridTemplateContext> {
3407
        if (this.isLoading && (this.hasZeroResultFilter || this.hasNoData)) {
45,959✔
3408
            return this.loadingGridTemplate ? this.loadingGridTemplate : this.loadingGridDefaultTemplate;
100✔
3409
        }
3410

3411
        if (this.hasZeroResultFilter) {
45,859✔
3412
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
191!
3413
        }
3414

3415
        if (this.hasNoData) {
45,668✔
3416
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate;
744✔
3417
        }
3418
    }
3419

3420
    /**
3421
     * @hidden @internal
3422
     */
3423
    private get hasZeroResultFilter(): boolean {
3424
        return this.filteredData && this.filteredData.length === 0;
46,184✔
3425
    }
3426
    protected get totalCalcWidth() {
3427
        return this.platform.isBrowser ? this.calcWidth : undefined;
43,835!
3428
    }
3429

3430
    protected get renderData() {
3431
        // omit data if not in the browser and size is %
3432
        return !this.platform.isBrowser && this.isPercentHeight ? undefined : this.data;
46,014!
3433
    }
3434

3435
    @HostBinding('style.display')
3436
    protected displayStyle = 'grid';
4,157✔
3437

3438
    @HostBinding('style.grid-template-rows')
3439
    protected templateRows = 'auto auto auto 1fr auto auto';
4,157✔
3440

3441
    /**
3442
     * @hidden @internal
3443
     */
3444
    private get hasNoData(): boolean {
3445
        return !this.data || this.dataLength === 0 || !this.platform.isBrowser;
46,079✔
3446
    }
3447

3448
    /**
3449
     * @hidden @internal
3450
     */
3451
    public get shouldOverlayLoading(): boolean {
3452
        return this.isLoading && !this.hasNoData && !this.hasZeroResultFilter;
49,301✔
3453
    }
3454

3455
    /**
3456
     * @hidden @internal
3457
     */
3458
    public get isMultiRowSelectionEnabled(): boolean {
3459
        return this.rowSelection === GridSelectionMode.multiple
3,394✔
3460
            || this.rowSelection === GridSelectionMode.multipleCascade;
3461
    }
3462

3463
    /**
3464
     * @hidden @internal
3465
     */
3466
    public get isRowSelectable(): boolean {
3467
        return this.rowSelection !== GridSelectionMode.none;
294,348✔
3468
    }
3469

3470
    /**
3471
     * @hidden @internal
3472
     */
3473
    public get isCellSelectable() {
3474
        return this.cellSelection !== GridSelectionMode.none;
8,521✔
3475
    }
3476

3477
    /**
3478
     * @hidden @internal
3479
     */
3480
    public get columnInDrag() {
3481
        return this.gridAPI.cms.column;
275,829✔
3482
    }
3483

3484
    constructor(
3485
        public readonly validation: IgxGridValidationService,
4,157✔
3486
        /** @hidden @internal */
3487
        public readonly selectionService: IgxGridSelectionService,
4,157✔
3488
        protected colResizingService: IgxColumnResizingService,
4,157✔
3489
        @Inject(IGX_GRID_SERVICE_BASE) public readonly gridAPI: GridServiceType,
4,157✔
3490
        protected transactionFactory: IgxFlatTransactionFactory,
4,157✔
3491
        private elementRef: ElementRef<HTMLElement>,
4,157✔
3492
        protected zone: NgZone,
4,157✔
3493
        /** @hidden @internal */
3494
        @Inject(DOCUMENT) public document: any,
4,157✔
3495
        public readonly cdr: ChangeDetectorRef,
4,157✔
3496
        protected differs: IterableDiffers,
4,157✔
3497
        protected viewRef: ViewContainerRef,
4,157✔
3498
        protected injector: Injector,
4,157✔
3499
        protected envInjector: EnvironmentInjector,
4,157✔
3500
        public navigation: IgxGridNavigationService,
4,157✔
3501
        /** @hidden @internal */
3502
        public filteringService: IgxFilteringService,
4,157✔
3503
        protected textHighlightService: IgxTextHighlightService,
4,157✔
3504
        @Inject(IgxOverlayService) protected overlayService: IgxOverlayService,
4,157✔
3505
        /** @hidden @internal */
3506
        public summaryService: IgxGridSummaryService,
4,157✔
3507
        @Inject(LOCALE_ID) private localeId: string,
4,157✔
3508
        protected platform: PlatformUtil,
4,157✔
3509
        @Optional() @Inject(IgxGridTransaction) protected _diTransactions?: TransactionService<Transaction, State>,
4,157✔
3510
    ) {
3511
        this.locale = this.locale || this.localeId;
4,157✔
3512
        this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
4,157✔
3513
        this._transactions.cloneStrategy = this.dataCloneStrategy;
4,157✔
3514
        this.cdr.detach();
4,157✔
3515
        this.selectionService.selectedRowsChange.pipe(takeUntil(this.destroy$)).subscribe((args: any[]) => {
4,157✔
3516
            this.selectedRowsChange.emit(args);
697✔
3517
        });
3518
        IgcTrialWatermark.register();
4,157✔
3519
    }
3520

3521
    /**
3522
     * @hidden
3523
     * @internal
3524
     */
3525
    @HostListener('mouseleave')
3526
    public hideActionStrip() {
3527
        this.actionStrip?.hide();
3✔
3528
    }
3529

3530
    /**
3531
     * @hidden
3532
     * @internal
3533
     */
3534
    public get headerFeaturesWidth() {
3535
        return this._headerFeaturesWidth;
×
3536
    }
3537

3538
    /**
3539
     * @hidden
3540
     * @internal
3541
     */
3542
    public isDetailRecord(_rec) {
3543
        return false;
1,239✔
3544
    }
3545

3546
    /**
3547
     * @hidden
3548
     * @internal
3549
     */
3550
    public isGroupByRecord(_rec) {
3551
        return false;
1,239✔
3552
    }
3553

3554
    /**
3555
     * @hidden @internal
3556
     */
3557
    public isGhostRecord(record: any): boolean {
3558
        return record.ghostRecord !== undefined;
548,835✔
3559
    }
3560
    /**
3561
     * @hidden @internal
3562
     */
3563
    public isAddRowRecord(record: any): boolean {
3564
        return record.addRow !== undefined;
×
3565
    }
3566

3567
    /**
3568
     * @hidden
3569
     * Returns the row index of a row that takes into account the full view data like pinning.
3570
     */
3571
    public getDataViewIndex(rowIndex, pinned) {
3572
        if (pinned && !this.isRowPinningToTop) {
257,260✔
3573
            rowIndex = rowIndex + this.unpinnedDataView.length;
200✔
3574
        } else if (!pinned && this.isRowPinningToTop) {
257,060✔
3575
            rowIndex = rowIndex + this.pinnedDataView.length;
255,300✔
3576
        }
3577
        return rowIndex;
257,260✔
3578
    }
3579

3580
    /**
3581
     * @hidden
3582
     * @internal
3583
     */
3584
    public get hasDetails() {
3585
        return false;
376✔
3586
    }
3587

3588
    /**
3589
     * Returns the state of the grid virtualization.
3590
     *
3591
     * @remarks
3592
     * Includes the start index and how many records are rendered.
3593
     * @example
3594
     * ```typescript
3595
     * const gridVirtState = this.grid1.virtualizationState;
3596
     * ```
3597
     */
3598
    public get virtualizationState() {
3599
        return this.verticalScrollContainer.state;
1,244✔
3600
    }
3601

3602
    /**
3603
     * @hidden
3604
     * @internal
3605
     */
3606
    public hideOverlays() {
3607
        this.overlayIDs.forEach(overlayID => {
528✔
3608
            const overlay = this.overlayService.getOverlayById(overlayID);
1✔
3609

3610
            if (overlay?.visible && !overlay.closeAnimationPlayer?.hasStarted()) {
1✔
3611
                this.overlayService.hide(overlayID);
1✔
3612

3613
                this.nativeElement.focus();
1✔
3614
            }
3615
        });
3616
    }
3617

3618
    /**
3619
     * Returns whether the record is pinned or not.
3620
     *
3621
     * @param rowIndex Index of the record in the `dataView` collection.
3622
     *
3623
     * @hidden
3624
     * @internal
3625
     */
3626
    public isRecordPinnedByViewIndex(rowIndex: number) {
3627
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this.pinnedDataView.length) ||
306,011✔
3628
            (!this.isRowPinningToTop && rowIndex >= this.unpinnedDataView.length);
3629
    }
3630

3631
    /**
3632
     * Returns whether the record is pinned or not.
3633
     *
3634
     * @param rowIndex Index of the record in the `filteredSortedData` collection.
3635
     */
3636
    public isRecordPinnedByIndex(rowIndex: number) {
3637
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this._filteredSortedPinnedData.length) ||
2,419!
3638
            (!this.isRowPinningToTop && rowIndex >= this._filteredSortedUnpinnedData.length);
3639
    }
3640

3641
    /**
3642
     * @hidden
3643
     * @internal
3644
     */
3645
    public isRecordPinned(rec) {
3646
        return this.getInitialPinnedIndex(rec) !== -1;
1,114,314✔
3647
    }
3648

3649
    /**
3650
     * @hidden
3651
     * @internal
3652
     * Returns the record index in order of pinning by the user. Does not consider sorting/filtering.
3653
     */
3654
    public getInitialPinnedIndex(rec) {
3655
        const id = this.gridAPI.get_row_id(rec);
1,114,422✔
3656
        return this._pinnedRecordIDs.indexOf(id);
1,114,422✔
3657
    }
3658

3659
    /**
3660
     * @hidden
3661
     * @internal
3662
     */
3663
    public get hasPinnedRecords() {
3664
        return this._pinnedRecordIDs.length > 0;
499,042✔
3665
    }
3666

3667
    /**
3668
     * @hidden
3669
     * @internal
3670
     */
3671
    public get pinnedRecordsCount() {
3672
        return this._pinnedRecordIDs.length;
7,385✔
3673
    }
3674

3675
    /**
3676
     * @hidden
3677
     * @internal
3678
     */
3679
    public get crudService() {
3680
        return this.gridAPI.crudService;
3,482,529✔
3681
    }
3682

3683
    /**
3684
     * @hidden
3685
     * @internal
3686
     */
3687
    public _setupServices() {
3688
        this.gridAPI.grid = this as any;
3,600✔
3689
        this.crudService.grid = this as any;
3,600✔
3690
        this.selectionService.grid = this as any;
3,600✔
3691
        this.validation.grid = this as any;
3,600✔
3692
        this.navigation.grid = this as any;
3,600✔
3693
        this.filteringService.grid = this as any;
3,600✔
3694
        this.summaryService.grid = this as any;
3,600✔
3695
    }
3696

3697
    /**
3698
     * @hidden
3699
     * @internal
3700
     */
3701
    public _setupListeners() {
3702
        const destructor = takeUntil<any>(this.destroy$);
3,600✔
3703
        fromEvent(this.nativeElement, 'focusout').pipe(filter(() => !!this.navigation.activeNode), destructor).subscribe((event) => {
3,600✔
3704
            const activeNode = this.navigation.activeNode;
685✔
3705
            if (!this.crudService.cell && !!activeNode &&
685!
3706
                ((event.target === this.tbody.nativeElement && activeNode.row >= 0 &&
3707
                    activeNode.row < this.dataView.length)
3708
                    || (event.target === this.theadRow.nativeElement && activeNode.row === -1)
3709
                    || (event.target === this.tfoot.nativeElement && activeNode.row === this.dataView.length)) &&
3710
                !(this.rowEditable && this.crudService.rowEditingBlocked && this.crudService.rowInEditMode)) {
80!
3711
                this.clearActiveNode();
73✔
3712
            }
3713
        });
3714
        this.rowAddedNotifier.pipe(destructor).subscribe(args => this.refreshGridState(args));
3,600✔
3715
        this.rowDeletedNotifier.pipe(destructor).subscribe(args => {
3,600✔
3716
            this.summaryService.deleteOperation = true;
185✔
3717
            this.summaryService.clearSummaryCache(args);
185✔
3718
        });
3719

3720
        this.subscribeToTransactions();
3,600✔
3721

3722
        this.resizeNotify.pipe(
3,600✔
3723
            filter(() => !this._init),
2,957✔
3724
            throttleTime(40, animationFrameScheduler, { leading: true, trailing: true }),
3725
            destructor
3726
        )
3727
            .subscribe(() => {
3728
                this.zone.run(() => {
2,223✔
3729
                    // do not trigger reflow if element is detached.
3730
                    if (this.nativeElement.isConnected) {
2,223✔
3731
                        if (this.shouldResize) {
1,813✔
3732
                            // resizing occurs due to the change of --ig-size css var
3733
                            this._gridSize = this.gridSize;
72✔
3734
                            this.updateDefaultRowHeight();
72✔
3735
                            this._autoSize = this.isPercentHeight && this.calcHeight !== this.getDataBasedBodyHeight();
72✔
3736
                            this.crudService.endEdit(false);
72✔
3737
                            if (this._summaryRowHeight === 0) {
72✔
3738
                                this.summaryService.summaryHeight = 0;
72✔
3739
                            }
3740
                        }
3741
                        this.notifyChanges(true);
1,813✔
3742
                    }
3743
                });
3744
            });
3745

3746
        this.pipeTriggerNotifier.pipe(takeUntil(this.destroy$)).subscribe(() => this.pipeTrigger++);
3,600✔
3747
        this.columnMovingEnd.pipe(destructor).subscribe(() => this.crudService.endEdit(false));
3,600✔
3748

3749
        this.overlayService.opening.pipe(destructor).subscribe((event) => {
3,600✔
3750
            if (this._advancedFilteringOverlayId === event.id) {
855✔
3751
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
55✔
3752
                if (instance) {
55✔
3753
                    instance.initialize(this as any, this.overlayService, event.id);
55✔
3754
                }
3755
            }
3756
        });
3757

3758
        this.overlayService.opened.pipe(destructor).subscribe((event) => {
3,600✔
3759
            const overlaySettings = this.overlayService.getOverlayById(event.id)?.settings;
585✔
3760

3761
            // do not hide the advanced filtering overlay on scroll
3762
            if (this._advancedFilteringOverlayId === event.id) {
585✔
3763
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
53✔
3764
                if (instance) {
53✔
3765
                    instance.lastActiveNode = this.navigation.activeNode;
53✔
3766
                    instance.queryBuilder.setAddButtonFocus();
53✔
3767
                }
3768
                return;
53✔
3769
            }
3770

3771
            // do not hide the overlay if it's attached to a row
3772
            if (this.rowEditingOverlay?.overlayId === event.id) {
532✔
3773
                return;
21✔
3774
            }
3775

3776
            if (overlaySettings?.outlet === this.outlet && this.overlayIDs.indexOf(event.id) === -1) {
511✔
3777
                this.overlayIDs.push(event.id);
360✔
3778
            }
3779
        });
3780

3781
        this.overlayService.closed.pipe(filter(() => !this._init), destructor).subscribe((event) => {
3,600✔
3782
            if (this._advancedFilteringOverlayId === event.id) {
467✔
3783
                this.overlayService.detach(this._advancedFilteringOverlayId);
28✔
3784
                this._advancedFilteringOverlayId = null;
28✔
3785
                return;
28✔
3786
            }
3787

3788
            const ind = this.overlayIDs.indexOf(event.id);
439✔
3789
            if (ind !== -1) {
439✔
3790
                this.overlayIDs.splice(ind, 1);
200✔
3791
            }
3792
        });
3793

3794
        this.verticalScrollContainer.dataChanging.pipe(filter(() => !this._init), destructor).subscribe(($event) => {
6,791✔
3795
            const shouldRecalcSize = this.isPercentHeight &&
3,277✔
3796
                (!this.calcHeight || this.calcHeight === this.getDataBasedBodyHeight() ||
3797
                    this.calcHeight === this.renderedRowHeight * this._defaultTargetRecordNumber);
3798
            if (shouldRecalcSize) {
3,277✔
3799
                this.calculateGridHeight();
112✔
3800
                $event.containerSize = this.calcHeight;
112✔
3801
            }
3802
            this.evaluateLoadingState();
3,277✔
3803
        });
3804

3805
        this.verticalScrollContainer.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3,600✔
3806
            // called to recalc all widths that may have changes as a result of
3807
            // the vert. scrollbar showing/hiding
3808
            this.notifyChanges(true);
870✔
3809
            this.cdr.detectChanges();
870✔
3810
            Promise.resolve().then(() => this.headerContainer.updateScroll());
870✔
3811
        });
3812

3813

3814
        this.headerContainer?.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3,600✔
3815
            // the horizontal scrollbar showing/hiding
3816
            // update scrollbar visibility and recalc heights
3817
            this.notifyChanges(true);
1,388✔
3818
            this.cdr.detectChanges();
1,388✔
3819
        });
3820

3821
        this.verticalScrollContainer.contentSizeChange.pipe(filter(() => !this._init), throttleTime(30), destructor).subscribe(() => {
3,600✔
3822
            this.notifyChanges(true);
728✔
3823
        });
3824

3825
        // notifier for column autosize requests
3826
        this._autoSizeColumnsNotify.pipe(
3,600✔
3827
            throttleTime(0, this.platform.isBrowser ? animationFrameScheduler : undefined, { leading: false, trailing: true }),
3,600!
3828
            destructor
3829
        )
3830
            .subscribe(() => {
3831
                this.autoSizeColumnsInView();
15✔
3832
                this._firstAutoResize = false;
15✔
3833
            });
3834
    }
3835

3836
    /**
3837
     * @hidden
3838
     */
3839
    public ngOnInit() {
3840
        this._setupServices();
3,600✔
3841
        this._setupListeners();
3,600✔
3842
        this.rowListDiffer = this.differs.find([]).create(null);
3,600✔
3843
        // compare based on field, not on object ref.
3844
        this.columnListDiffer = this.differs.find([]).create((_index, col: ColumnType) => col.field);
17,706✔
3845
        this.calcWidth = this.width && this.width.indexOf('%') === -1 ? parseInt(this.width, 10) : 0;
3,600✔
3846
        this.gridComputedStyles = this.document.defaultView.getComputedStyle(this.nativeElement);
3,600✔
3847
    }
3848

3849
    /**
3850
     * @hidden
3851
     * @internal
3852
     */
3853
    public resetColumnsCaches() {
3854
        this._columns.forEach(column => column.resetCaches());
220,428✔
3855
    }
3856

3857
    /**
3858
     * @hidden @internal
3859
     */
3860
    public generateRowID(): string | number {
3861
        const primaryColumn = this._columns.find(col => col.field === this.primaryKey);
61✔
3862
        const idType = this.data.length ?
59✔
3863
            this.resolveDataTypes(this.data[0][this.primaryKey]) : primaryColumn ? primaryColumn.dataType : 'string';
1!
3864
        return idType === 'string' ? getUUID() : FAKE_ROW_ID--;
59✔
3865
    }
3866

3867
    /**
3868
     * @hidden
3869
     * @internal
3870
     */
3871
    public resetForOfCache() {
3872
        const firstVirtRow = this.dataRowList.first;
34,789✔
3873
        if (firstVirtRow) {
34,789✔
3874
            if (this._cdrRequests) {
17,274✔
3875
                firstVirtRow.virtDirRow.cdr.detectChanges();
6,808✔
3876
            }
3877
            firstVirtRow.virtDirRow.assumeMaster();
17,274✔
3878
        }
3879
    }
3880

3881
    /**
3882
     * @hidden
3883
     * @internal
3884
     */
3885
    public setFilteredData(data, pinned: boolean) {
3886
        if (this.hasPinnedRecords && pinned) {
1,532✔
3887
            this._filteredPinnedData = data || [];
49✔
3888
            const filteredUnpinned = this._filteredUnpinnedData || [];
49✔
3889
            const filteredData = [... this._filteredPinnedData, ...filteredUnpinned];
49✔
3890
            this._filteredData = filteredData.length > 0 ? filteredData : this._filteredUnpinnedData;
49✔
3891
        } else if (this.hasPinnedRecords && !pinned) {
1,483✔
3892
            this._filteredUnpinnedData = data;
47✔
3893
        } else {
3894
            this._filteredData = data;
1,436✔
3895
        }
3896
    }
3897

3898
    /**
3899
     * @hidden
3900
     * @internal
3901
     */
3902
    public resetColumnCollections() {
3903
        if (this.hasColumnLayouts) {
34,934✔
3904
            this._columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
12,233✔
3905
        }
3906
        this._visibleColumns.length = 0;
34,934✔
3907
        this._pinnedVisible.length = 0;
34,934✔
3908
        this._unpinnedVisible.length = 0;
34,934✔
3909
    }
3910

3911
    /**
3912
     * @hidden
3913
     * @internal
3914
     */
3915
    public resetCachedWidths() {
3916
        this._unpinnedWidth = NaN;
46,786✔
3917
        this._pinnedStartWidth = NaN;
46,786✔
3918
        this._pinnedEndWidth = NaN;
46,786✔
3919
        this._totalWidth = NaN;
46,786✔
3920
    }
3921

3922
    /**
3923
     * @hidden
3924
     * @internal
3925
     */
3926
    public resetCaches(recalcFeatureWidth = true) {
10,705✔
3927
        if (recalcFeatureWidth) {
34,789✔
3928
            this._headerFeaturesWidth = NaN;
34,780✔
3929
            this.summaryService.summaryHeight = 0;
34,780✔
3930
        }
3931
        this.resetColumnsCaches();
34,789✔
3932
        this.resetColumnCollections();
34,789✔
3933
        this.resetForOfCache();
34,789✔
3934
        this.resetCachedWidths();
34,789✔
3935
        this.hasVisibleColumns = undefined;
34,789✔
3936
        this._columnGroups = this._columns.some(col => col.columnGroup);
163,398✔
3937
    }
3938

3939
    /**
3940
     * @hidden
3941
     */
3942
    public ngAfterContentInit() {
3943
        if (this.sortHeaderIconDirectiveTemplate) {
3,466✔
3944
            this.sortHeaderIconTemplate = this.sortHeaderIconDirectiveTemplate;
5✔
3945
        }
3946

3947
        if (this.sortAscendingHeaderIconDirectiveTemplate) {
3,466✔
3948
            this.sortAscendingHeaderIconTemplate = this.sortAscendingHeaderIconDirectiveTemplate;
5✔
3949
        }
3950

3951
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
3,466✔
3952
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
5✔
3953
        }
3954

3955
        this.setupColumns();
3,466✔
3956
        this.toolbar.changes.pipe(filter(() => !this._init), takeUntil(this.destroy$)).subscribe(() => this.notifyChanges(true));
3,466✔
3957
        this.setUpPaginator();
3,466✔
3958
        this.paginationComponents.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,466✔
3959
            this.setUpPaginator();
64✔
3960
        });
3961
        if (this.actionStrip) {
3,466✔
3962
            this.actionStrip.menuOverlaySettings.outlet = this.outlet;
115✔
3963
        }
3964
    }
3965

3966
    /**
3967
     * @hidden @internal
3968
     */
3969
    public dataRebinding(event: IForOfDataChangeEventArgs) {
3970
        if (event.state.chunkSize == 0) {
6,791✔
3971
            this._shouldRecalcRowHeight = true;
3,628✔
3972
        }
3973
        this.dataChanging.emit(event);
6,791✔
3974
    }
3975

3976
    /**
3977
     * @hidden @internal
3978
     */
3979
    public dataRebound(event: IForOfDataChangeEventArgs) {
3980
        this.selectionService.clearHeaderCBState();
6,791✔
3981
        if (this._shouldRecalcRowHeight) {
6,791✔
3982
            this._shouldRecalcRowHeight = false;
3,628✔
3983
            this.updateDefaultRowHeight();
3,628✔
3984
        }
3985
        this.dataChanged.emit(event);
6,791✔
3986
    }
3987

3988
    /** @hidden @internal */
3989
    public createFilterDropdown(column: ColumnType, options: OverlaySettings) {
3990
        options.outlet = this.outlet;
207✔
3991
        if (this.excelStyleFilteringComponent) {
207✔
3992
            this.excelStyleFilteringComponent.initialize(column, this.overlayService);
21✔
3993
            const id = this.overlayService.attach(this.excelStyleFilteringComponent.element, options);
21✔
3994
            this.excelStyleFilteringComponent.overlayComponentId = id;
21✔
3995
            return id;
21✔
3996
        }
3997
        const id = this.overlayService.attach(IgxGridExcelStyleFilteringComponent, this.viewRef, options);
186✔
3998
        return id;
186✔
3999
    }
4000

4001
    /** @hidden @internal */
4002
    public setUpPaginator() {
4003
        if (this.paginator) {
3,533✔
4004
            this.paginator.pageChange
222✔
4005
                .pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
114✔
4006
                .pipe(takeUntil(this.destroy$))
4007
                .subscribe(() => {
4008
                    this.selectionService.clear(true);
114✔
4009
                    this.crudService.endEdit(false);
114✔
4010
                    this.pipeTrigger++;
114✔
4011
                    this.navigateTo(0);
114✔
4012
                    this.notifyChanges();
114✔
4013
                });
4014
            this.paginator.perPageChange
222✔
4015
                .pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
70✔
4016
                .pipe(takeUntil(this.destroy$))
4017
                .subscribe(() => {
4018
                    this.selectionService.clear(true);
70✔
4019
                    this.page = 0;
70✔
4020
                    this.crudService.endEdit(false);
70✔
4021
                    this.notifyChanges();
70✔
4022
                });
4023
        } else {
4024
            this.markForCheck();
3,311✔
4025
        }
4026
    }
4027

4028
    /**
4029
     * @hidden
4030
     * @internal
4031
     */
4032
    public setFilteredSortedData(data, pinned: boolean) {
4033
        data = data || [];
6,528✔
4034
        if (this.pinnedRecordsCount > 0) {
6,528✔
4035
            if (pinned) {
341✔
4036
                this._filteredSortedPinnedData = data;
174✔
4037
                this.pinnedRecords = data;
174✔
4038
                this._filteredSortedData = this.isRowPinningToTop ? [... this._filteredSortedPinnedData, ... this._filteredSortedUnpinnedData] :
174✔
4039
                    [... this._filteredSortedUnpinnedData, ... this._filteredSortedPinnedData];
4040
                this.refreshSearch(true, false);
174✔
4041
            } else {
4042
                this._filteredSortedUnpinnedData = data;
167✔
4043
            }
4044
        } else {
4045
            this._filteredSortedData = data;
6,187✔
4046
            this.refreshSearch(true, false);
6,187✔
4047
        }
4048
        this.buildDataView(data);
6,528✔
4049
    }
4050

4051
    /**
4052
     * @hidden @internal
4053
     */
4054
    public resetHorizontalVirtualization() {
4055
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
49,812✔
4056
        this._horizontalForOfs = [
6,766✔
4057
            ...this._dataRowList.filter(elementFilter).map(item => item.virtDirRow),
46,668✔
4058
            ...this._summaryRowList.filter(elementFilter).map(item => item.virtDirRow)
3,144✔
4059
        ];
4060
    }
4061

4062
    /**
4063
     * @hidden @internal
4064
     */
4065
    public _setupRowObservers() {
4066
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
17,777✔
4067
        const extractForOfs = pipe(map((collection: any[]) => collection.filter(elementFilter).map(item => item.virtDirRow)));
17,777✔
4068
        const rowListObserver = extractForOfs(this._dataRowList.changes);
3,614✔
4069
        const summaryRowObserver = extractForOfs(this._summaryRowList.changes);
3,614✔
4070
        rowListObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,614✔
4071
            this.resetHorizontalVirtualization();
2,736✔
4072
        });
4073
        summaryRowObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,614✔
4074
            this.resetHorizontalVirtualization();
318✔
4075
        });
4076
        this.resetHorizontalVirtualization();
3,614✔
4077
    }
4078

4079
    /**
4080
     * @hidden @internal
4081
     */
4082
    public _zoneBegoneListeners() {
4083
        this.zone.runOutsideAngular(() => {
3,614✔
4084
            this.verticalScrollHandler = this.verticalScrollHandler.bind(this);
3,614✔
4085
            this.horizontalScrollHandler = this.horizontalScrollHandler.bind(this);
3,614✔
4086
            this.verticalScrollContainer.getScroll().addEventListener('scroll', this.verticalScrollHandler);
3,614✔
4087
            this.headerContainer?.getScroll().addEventListener('scroll', this.horizontalScrollHandler);
3,614✔
4088
            if (this.hasColumnsToAutosize) {
3,614✔
4089
                this.headerContainer?.dataChanged.pipe(takeUntil(this.destroy$)).subscribe(() => {
15✔
4090
                    this.cdr.detectChanges();
35✔
4091
                    this.zone.onStable.pipe(first()).subscribe(() => {
35✔
4092
                        this.autoSizeColumnsInView();
35✔
4093
                    });
4094
                });
4095
            }
4096
            // Window resize observer not needed because when you resize the window element the tbody container always resize so
4097
            // it would always notify resizing, thus a change detection and recalculation of sizes will occur
4098
            resizeObservable(this.nativeElement).pipe(first(), takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
3,614✔
4099
            resizeObservable(this.tbodyContainer.nativeElement).pipe(takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
3,614✔
4100
        });
4101
    }
4102

4103
    /**
4104
     * @hidden
4105
     */
4106
    public ngAfterViewInit() {
4107
        this.initPinning();
3,614✔
4108
        this.calculateGridSizes();
3,614✔
4109
        this._init = false;
3,614✔
4110
        this.cdr.reattach();
3,614✔
4111
        this._setupRowObservers();
3,614✔
4112
        this._zoneBegoneListeners();
3,614✔
4113

4114
        const vertScrDC = this.verticalScrollContainer.displayContainer;
3,614✔
4115
        vertScrDC.addEventListener('scroll', this.preventContainerScroll);
3,614✔
4116

4117
        this._pinnedRowList.changes
3,614✔
4118
            .pipe(takeUntil(this.destroy$))
4119
            .subscribe((change: QueryList<IgxGridRowComponent>) => {
4120
                this.onPinnedRowsChanged(change);
182✔
4121
            });
4122

4123
        this.addRowSnackbar?.clicked.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,614✔
4124
            const rec = this.filteredSortedData[this.lastAddedRowIndex];
4✔
4125
            this.scrollTo(rec, 0);
4✔
4126
            this.addRowSnackbar.close();
4✔
4127
        });
4128

4129
        // Keep the stream open for future subscribers
4130
        this.rendered$.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,614✔
4131
            if (this.paginator) {
2,019✔
4132
                this.paginator.totalRecords = this.totalRecords ? this.totalRecords : this.paginator.totalRecords;
73✔
4133
                this.paginator.overlaySettings = { outlet: this.outlet };
73✔
4134
            }
4135
            if (this.hasColumnsToAutosize) {
2,019✔
4136
                this.autoSizeColumnsInView();
14✔
4137
            }
4138
            this._calculateRowCount();
2,019✔
4139
            this._rendered = true;
2,019✔
4140
        });
4141
        Promise.resolve().then(() => this.rendered.next(true));
3,614✔
4142

4143
    }
4144

4145
    /**
4146
     * @hidden @internal
4147
     */
4148
    public notifyChanges(repaint = false) {
15,087✔
4149
        this._cdrRequests = true;
500,053✔
4150
        this._cdrRequestRepaint = repaint;
500,053✔
4151
        this.cdr.markForCheck();
500,053✔
4152
    }
4153

4154
    /**
4155
     * @hidden @internal
4156
     */
4157
    public ngDoCheck() {
4158
        if (this._init) {
18,153✔
4159
            return;
4,199✔
4160
        }
4161

4162
        if (this._cdrRequestRepaint) {
13,954✔
4163
            this.resetNotifyChanges();
4,067✔
4164
            this.calculateGridSizes();
4,067✔
4165
            this.refreshSearch(true);
4,067✔
4166
            return;
4,067✔
4167
        }
4168

4169
        if (this._cdrRequests) {
9,887✔
4170
            this.resetNotifyChanges();
4,269✔
4171
            this.cdr.detectChanges();
4,269✔
4172
        }
4173
    }
4174

4175
    /**
4176
     * @hidden
4177
     * @internal
4178
     */
4179
    public getDragGhostCustomTemplate() {
4180

4181
        return this.dragGhostCustomTemplate;
1,987✔
4182
    }
4183

4184
    /**
4185
     * @hidden @internal
4186
     */
4187
    public ngOnDestroy() {
4188
        this.tmpOutlets.forEach((tmplOutlet) => {
3,432✔
4189
            tmplOutlet.cleanCache();
25,245✔
4190
        });
4191
        this._autoGeneratedColsRefs.forEach(ref => ref.destroy());
4,230✔
4192
        this._autoGeneratedColsRefs = [];
3,432✔
4193

4194
        this.destroy$.next(true);
3,432✔
4195
        this.destroy$.complete();
3,432✔
4196
        this.transactionChange$.next();
3,432✔
4197
        this.transactionChange$.complete();
3,432✔
4198
        this._destroyed = true;
3,432✔
4199

4200
        this.textHighlightService.destroyGroup(this.id);
3,432✔
4201

4202
        if (this._advancedFilteringOverlayId) {
3,432!
4203
            this.overlayService.detach(this._advancedFilteringOverlayId);
×
4204
            delete this._advancedFilteringOverlayId;
×
4205
        }
4206

4207
        this.overlayIDs.forEach(overlayID => {
3,432✔
4208
            const overlay = this.overlayService.getOverlayById(overlayID);
23✔
4209

4210
            if (overlay && !overlay.detached) {
23✔
4211
                this.overlayService.detach(overlayID);
13✔
4212
            }
4213
        });
4214

4215

4216
        this.zone.runOutsideAngular(() => {
3,432✔
4217
            this.verticalScrollContainer?.getScroll()?.removeEventListener('scroll', this.verticalScrollHandler);
3,432✔
4218
            this.headerContainer?.getScroll()?.removeEventListener('scroll', this.horizontalScrollHandler);
3,432✔
4219
            const vertScrDC = this.verticalScrollContainer?.displayContainer;
3,432✔
4220
            vertScrDC?.removeEventListener('scroll', this.preventContainerScroll);
3,432✔
4221
        });
4222
    }
4223

4224
    /**
4225
     * Toggles the specified column's visibility.
4226
     *
4227
     * @example
4228
     * ```typescript
4229
     * this.grid1.toggleColumnVisibility({
4230
     *       column: this.grid1.columns[0],
4231
     *       newValue: true
4232
     * });
4233
     * ```
4234
     */
4235
    public toggleColumnVisibility(args: IColumnVisibilityChangedEventArgs) {
4236
        const col = args.column ? this._columns.find((c) => c === args.column) : undefined;
×
4237

4238
        if (!col) {
×
4239
            return;
×
4240
        }
4241
        col.toggleVisibility(args.newValue);
×
4242
    }
4243

4244
    /* blazorCSSuppress */
4245
    /**
4246
     * Gets/Sets a list of key-value pairs [row ID, expansion state].
4247
     *
4248
     * @remarks
4249
     * Includes only states that differ from the default one.
4250
     * Supports two-way binding.
4251
     * @example
4252
     * ```html
4253
     * <igx-grid #grid [data]="data" [(expansionStates)]="model.expansionStates">
4254
     * </igx-grid>
4255
     * ```
4256
     */
4257
    @Input()
4258
    public get expansionStates() {
4259
        return this._expansionStates;
233,906✔
4260
    }
4261

4262
    /* blazorCSSuppress */
4263
    public set expansionStates(value) {
4264
        this._expansionStates = new Map<any, boolean>(value);
695✔
4265
        this.expansionStatesChange.emit(this._expansionStates);
695✔
4266
        this.notifyChanges(true);
695✔
4267
        if (this.gridAPI.grid) {
695✔
4268
            this.cdr.detectChanges();
602✔
4269
        }
4270
    }
4271

4272
    /**
4273
     * Expands all rows.
4274
     *
4275
     * @example
4276
     * ```typescript
4277
     * this.grid.expandAll();
4278
     * ```
4279
     */
4280
    public expandAll() {
4281
        this._defaultExpandState = true;
8✔
4282
        this.expansionStates = new Map<any, boolean>();
8✔
4283
    }
4284

4285
    /**
4286
     * Collapses all rows.
4287
     *
4288
     * @example
4289
     * ```typescript
4290
     * this.grid.collapseAll();
4291
     * ```
4292
     */
4293
    public collapseAll() {
4294
        this._defaultExpandState = false;
2✔
4295
        this.expansionStates = new Map<any, boolean>();
2✔
4296
    }
4297

4298
    /**
4299
     * Expands the row by its id.
4300
     *
4301
     * @remarks
4302
     * ID is either the primaryKey value or the data record instance.
4303
     * @example
4304
     * ```typescript
4305
     * this.grid.expandRow(rowID);
4306
     * ```
4307
     * @param rowID The row id - primaryKey value or the data record instance.
4308
     */
4309
    public expandRow(rowID: any) {
4310
        this.gridAPI.set_row_expansion_state(rowID, true);
56✔
4311
    }
4312

4313
    /**
4314
     * Collapses the row by its id.
4315
     *
4316
     * @remarks
4317
     * ID is either the primaryKey value or the data record instance.
4318
     * @example
4319
     * ```typescript
4320
     * this.grid.collapseRow(rowID);
4321
     * ```
4322
     * @param rowID The row id - primaryKey value or the data record instance.
4323
     */
4324
    public collapseRow(rowID: any) {
4325
        this.gridAPI.set_row_expansion_state(rowID, false);
9✔
4326
    }
4327

4328

4329
    /**
4330
     * Toggles the row by its id.
4331
     *
4332
     * @remarks
4333
     * ID is either the primaryKey value or the data record instance.
4334
     * @example
4335
     * ```typescript
4336
     * this.grid.toggleRow(rowID);
4337
     * ```
4338
     * @param rowID The row id - primaryKey value or the data record instance.
4339
     */
4340
    public toggleRow(rowID: any) {
4341
        const rec = this.gridAPI.get_rec_by_id(rowID);
87✔
4342
        const state = this.gridAPI.get_row_expansion_state(rec);
87✔
4343
        this.gridAPI.set_row_expansion_state(rowID, !state);
87✔
4344
    }
4345

4346
    /**
4347
     * @hidden
4348
     * @internal
4349
     */
4350
    public getDefaultExpandState(_rec: any) {
4351
        return this._defaultExpandState;
9,830✔
4352
    }
4353

4354
    /**
4355
     * Gets the native element.
4356
     *
4357
     * @example
4358
     * ```typescript
4359
     * const nativeEl = this.grid.nativeElement.
4360
     * ```
4361
     */
4362
    public get nativeElement() {
4363
        return this.elementRef.nativeElement;
42,511✔
4364
    }
4365

4366
    /**
4367
     * Gets/Sets the outlet used to attach the grid's overlays to.
4368
     *
4369
     * @remarks
4370
     * If set, returns the outlet defined outside the grid. Otherwise returns the grid's internal outlet directive.
4371
     */
4372
    @Input()
4373
    public get outlet() {
4374
        return this.resolveOutlet();
202,150✔
4375
    }
4376

4377
    public set outlet(val: IgxOverlayOutletDirective) {
4378
        this._userOutletDirective = val;
×
4379
    }
4380

4381

4382
    /**
4383
     * Gets the default row height.
4384
     *
4385
     * @example
4386
     * ```typescript
4387
     * const rowHeigh = this.grid.defaultRowHeight;
4388
     * ```
4389
     */
4390
    public get defaultRowHeight(): number {
4391
        return this._defaultRowHeight;
350,940✔
4392
    }
4393

4394
    /**
4395
     * @hidden @internal
4396
     */
4397
    public get defaultSummaryHeight(): number {
4398
        switch (this.gridSize) {
15,165✔
4399
            case Size.Medium:
4400
                return 30;
29✔
4401
            case Size.Small:
4402
                return 24;
123✔
4403
            default:
4404
                return 36;
15,013✔
4405
        }
4406
    }
4407

4408
    /**
4409
     * Returns the `IgxGridHeaderGroupComponent`'s minimum allowed width.
4410
     *
4411
     * @remarks
4412
     * Used internally for restricting header group component width.
4413
     * The values below depend on the header cell default right/left padding values.
4414
     */
4415
    public get defaultHeaderGroupMinWidth(): number {
4416
        switch (this.gridSize) {
481,063✔
4417
            case Size.Medium:
4418
                return 32;
38,396✔
4419
            case Size.Small:
4420
                return 24;
3,385✔
4421
            default:
4422
                return 48;
439,282✔
4423
        }
4424
    }
4425

4426
    /** @hidden @internal */
4427
    public get pinnedStartWidth() {
4428
        if (!isNaN(this._pinnedStartWidth)) {
91,717✔
4429
            return this._pinnedStartWidth;
70,378✔
4430
        }
4431
        this._pinnedStartWidth = this.getPinnedStartWidth();
21,339✔
4432
        return this._pinnedStartWidth;
21,339✔
4433
    }
4434

4435
    /** @hidden @internal */
4436
    public get pinnedEndWidth() {
4437
        if (!isNaN(this._pinnedEndWidth)) {
136,665✔
4438
            return this._pinnedEndWidth;
115,349✔
4439
        }
4440
        this._pinnedEndWidth = this.getPinnedEndWidth();
21,316✔
4441
        return this._pinnedEndWidth;
21,316✔
4442
    }
4443

4444
    /** @hidden @internal */
4445
    public get unpinnedWidth() {
4446
        if (!isNaN(this._unpinnedWidth)) {
374,113✔
4447
            return this._unpinnedWidth;
350,307✔
4448
        }
4449
        this._unpinnedWidth = this.getUnpinnedWidth();
23,806✔
4450
        return this._unpinnedWidth;
23,806✔
4451
    }
4452

4453
    /**
4454
     * @hidden @internal
4455
     */
4456
    public isHorizontalScrollHidden = false;
4,157✔
4457

4458
    /**
4459
     * @hidden @internal
4460
     * Gets the header cell inner width for auto-sizing.
4461
     */
4462
    public getHeaderCellWidth(element: HTMLElement): ISizeInfo {
4463
        const range = this.document.createRange();
39✔
4464
        const headerWidth = this.platform.getNodeSizeViaRange(range,
39✔
4465
            element,
4466
            element.parentElement);
4467

4468
        const headerStyle = this.document.defaultView.getComputedStyle(element);
39✔
4469
        const headerPadding = parseFloat(headerStyle.paddingLeft) + parseFloat(headerStyle.paddingRight) +
39✔
4470
            parseFloat(headerStyle.borderRightWidth);
4471

4472
        // Take into consideration the header group element, since column pinning applies borders to it if its not a columnGroup.
4473
        const headerGroupStyle = this.document.defaultView.getComputedStyle(element.parentElement);
39✔
4474
        const borderSize = parseFloat(headerGroupStyle.borderRightWidth) + parseFloat(headerGroupStyle.borderLeftWidth);
39✔
4475
        return { width: Math.ceil(headerWidth), padding: Math.ceil(headerPadding + borderSize) };
39✔
4476
    }
4477

4478
    /**
4479
     * @hidden @internal
4480
     * Gets the combined width of the columns that are specific to the enabled grid features. They are fixed.
4481
     */
4482
    public featureColumnsWidth(expander?: ElementRef) {
4483
        if (Number.isNaN(this._headerFeaturesWidth)) {
131,192✔
4484
            // TODO: platformUtil.isBrowser check
4485
            const rowSelectArea = this.headerSelectorContainer?.nativeElement?.getBoundingClientRect ?
29,191✔
4486
                this.headerSelectorContainer.nativeElement.getBoundingClientRect().width : 0;
4487
            const rowDragArea = this.rowDraggable && this.headerDragContainer?.nativeElement?.getBoundingClientRect ?
29,191✔
4488
                this.headerDragContainer.nativeElement.getBoundingClientRect().width : 0;
4489
            const groupableArea = this.headerGroupContainer?.nativeElement?.getBoundingClientRect ?
29,191✔
4490
                this.headerGroupContainer.nativeElement.getBoundingClientRect().width : 0;
4491
            const expanderWidth = expander?.nativeElement?.getBoundingClientRect ? expander.nativeElement.getBoundingClientRect().width : 0;
29,191✔
4492
            this._headerFeaturesWidth = rowSelectArea + rowDragArea + groupableArea + expanderWidth;
29,191✔
4493
        }
4494
        return this._headerFeaturesWidth;
131,192✔
4495
    }
4496

4497
    /**
4498
     * @hidden @internal
4499
     */
4500
    public get summariesMargin() {
4501
        return this.featureColumnsWidth();
22,228✔
4502
    }
4503

4504
    /**
4505
     * Gets an array of `IgxColumnComponent`s.
4506
     *
4507
     * @example
4508
     * ```typescript
4509
     * const colums = this.grid.columns.
4510
     * ```
4511
     */
4512
    public get columns(): IgxColumnComponent[] {
4513
        return this._columns || [];
32,497!
4514
    }
4515

4516
    /**
4517
     * Gets an array of the pinned `IgxColumnComponent`s.
4518
     *
4519
     * @example
4520
     * ```typescript
4521
     * const pinnedColumns = this.grid.pinnedColumns.
4522
     * ```
4523
     */
4524
    public get pinnedColumns(): IgxColumnComponent[] {
4525
        if (this._pinnedVisible.length) {
9,611✔
4526
            return this._pinnedVisible;
4,579✔
4527
        }
4528
        this._pinnedVisible = this._pinnedColumns.filter(col => !col.hidden);
5,032✔
4529
        return this._pinnedVisible;
5,032✔
4530
    }
4531

4532
    /**
4533
     * Gets an array of the pinned to the left `IgxColumnComponent`s.
4534
     *
4535
     * @example
4536
     * ```typescript
4537
     * const pinnedColumns = this.grid.pinnedStartColumns.
4538
     * ```
4539
     */
4540
    public get pinnedStartColumns(): IgxColumnComponent[] {
4541
        return this._pinnedStartColumns.filter(col => !col.hidden);
2,984,076✔
4542
    }
4543

4544
    /**
4545
     * Gets an array of the pinned to the right `IgxColumnComponent`s.
4546
     *
4547
     * @example
4548
     * ```typescript
4549
     * const pinnedColumns = this.grid.pinnedEndColumns.
4550
     * ```
4551
     */
4552
    public get pinnedEndColumns(): IgxColumnComponent[] {
4553
        return this._pinnedEndColumns.filter(col => !col.hidden);
1,874,603✔
4554
    }
4555

4556
    /* csSuppress */
4557
    /**
4558
     * Gets an array of the pinned `IgxRowComponent`s.
4559
     *
4560
     * @example
4561
     * ```typescript
4562
     * const pinnedRow = this.grid.pinnedRows;
4563
     * ```
4564
     */
4565
    public get pinnedRows(): IgxGridRowComponent[] {
4566
        return this._pinnedRowList.toArray().sort((a, b) => a.index - b.index);
147✔
4567
    }
4568

4569
    /**
4570
     * Gets an array of unpinned `IgxColumnComponent`s.
4571
     *
4572
     * @example
4573
     * ```typescript
4574
     * const unpinnedColumns = this.grid.unpinnedColumns.
4575
     * ```
4576
     */
4577
    public get unpinnedColumns(): IgxColumnComponent[] {
4578
        if (this._unpinnedVisible.length) {
826,734✔
4579
            return this._unpinnedVisible;
799,240✔
4580
        }
4581
        this._unpinnedVisible = this._unpinnedColumns.filter((col) => !col.hidden);
159,388✔
4582
        return this._unpinnedVisible;
27,494✔
4583
    }
4584

4585
    /**
4586
     * Gets the `width` to be set on `IgxGridHeaderGroupComponent`.
4587
     */
4588
    public getHeaderGroupWidth(column: IgxColumnComponent): string {
4589
        return this.hasColumnLayouts
×
4590
            ? ''
4591
            : `${Math.max(parseFloat(column.calcWidth), this.defaultHeaderGroupMinWidth)}px`;
4592
    }
4593

4594
    /**
4595
     * Returns the `IgxColumnComponent` by field name.
4596
     *
4597
     * @example
4598
     * ```typescript
4599
     * const myCol = this.grid1.getColumnByName("ID");
4600
     * ```
4601
     * @param name
4602
     */
4603
    public getColumnByName(name: string): IgxColumnComponent {
4604
        return this._columns.find((col) => col.field === name);
136,870✔
4605
    }
4606

4607
    public getColumnByVisibleIndex(index: number): IgxColumnComponent {
4608
        return this.visibleColumns.find((col) =>
1,974✔
4609
            !col.columnGroup && !col.columnLayout &&
11,553✔
4610
            col.visibleIndex === index
4611
        );
4612
    }
4613

4614
    /**
4615
     * Recalculates all widths of columns that have size set to `auto`.
4616
     *
4617
     * @example
4618
     * ```typescript
4619
     * this.grid1.recalculateAutoSizes();
4620
     * ```
4621
     */
4622
    public recalculateAutoSizes() {
4623
        // reset auto-size and calculate it again.
4624
        this._columns.forEach(x => x.autoSize = undefined);
12✔
4625
        this.resetCaches();
2✔
4626
        this.zone.onStable.pipe(first()).subscribe(() => {
2✔
4627
            this.cdr.detectChanges();
2✔
4628
            this.autoSizeColumnsInView();
2✔
4629
        });
4630
    }
4631

4632
    /**
4633
     * Returns an array of visible `IgxColumnComponent`s.
4634
     *
4635
     * @example
4636
     * ```typescript
4637
     * const visibleColumns = this.grid.visibleColumns.
4638
     * ```
4639
     */
4640
    public get visibleColumns(): IgxColumnComponent[] {
4641
        if (this._visibleColumns.length) {
191,670✔
4642
            return this._visibleColumns;
157,736✔
4643
        }
4644
        this._visibleColumns = this._columns.filter(c => !c.hidden);
194,726✔
4645
        return this._visibleColumns;
33,934✔
4646
    }
4647

4648
    /**
4649
     * Returns the total number of records.
4650
     *
4651
     * @remarks
4652
     * Only functions when paging is enabled.
4653
     * @example
4654
     * ```typescript
4655
     * const totalRecords = this.grid.totalRecords;
4656
     * ```
4657
     */
4658
    @Input()
4659
    public get totalRecords(): number {
4660
        return this._totalRecords >= 0 ? this._totalRecords : this.pagingState?.metadata.countRecords;
375✔
4661
    }
4662

4663
    public set totalRecords(total: number) {
4664
        if (total >= 0) {
1✔
4665
            if (this.paginator) {
1✔
4666
                this.paginator.totalRecords = total;
1✔
4667
            }
4668
            this._totalRecords = total;
1✔
4669
            this.pipeTrigger++;
1✔
4670
            this.notifyChanges();
1✔
4671
        }
4672
    }
4673

4674
    /** @hidden @internal */
4675
    public get totalWidth(): number {
4676
        if (!isNaN(this._totalWidth)) {
11,453✔
4677
            return this._totalWidth;
3,209✔
4678
        }
4679
        // Take only top level columns
4680
        const cols = this.visibleColumns.filter(col => col.level === 0 && !col.pinned);
51,216✔
4681
        let totalWidth = 0;
8,244✔
4682
        let i = 0;
8,244✔
4683
        for (i; i < cols.length; i++) {
8,244✔
4684
            totalWidth += parseFloat(cols[i].calcWidth) || 0;
40,630!
4685
        }
4686
        this._totalWidth = totalWidth;
8,244✔
4687
        return totalWidth;
8,244✔
4688
    }
4689

4690
    /**
4691
     * @hidden
4692
     * @internal
4693
     */
4694
    public get showRowSelectors(): boolean {
4695
        return this.isRowSelectable && this.hasVisibleColumns && !this.hideRowSelectors;
282,468✔
4696
    }
4697

4698
    /**
4699
     * @hidden
4700
     * @internal
4701
     */
4702
    public get showAddButton() {
4703
        return this.rowEditable && this.dataView.length === 0 && this._columns.length > 0;
916✔
4704
    }
4705

4706
    /**
4707
     * @hidden
4708
     * @internal
4709
     */
4710
    public get showDragIcons(): boolean {
4711
        return this.rowDraggable && this._columns.length > this.hiddenColumnsCount;
×
4712
    }
4713

4714
    /**
4715
     * @hidden
4716
     * @internal
4717
     */
4718
    protected _getDataViewIndex(index: number): number {
4719
        let newIndex = index;
62,933✔
4720
        if ((index < 0 || index >= this.dataView.length) && this.pagingMode === 'remote' && this.page !== 0) {
62,933!
4721
            newIndex = index - this.perPage * this.page;
×
4722
        } else if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
62,933!
4723
            newIndex = index - this.gridAPI.grid.virtualizationState.startIndex;
×
4724
        }
4725
        return newIndex;
62,933✔
4726
    }
4727

4728
    /**
4729
     * @hidden
4730
     * @internal
4731
     */
4732
    protected getDataIndex(dataViewIndex: number): number {
4733
        let newIndex = dataViewIndex;
23✔
4734
        if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
23!
4735
            newIndex = dataViewIndex + this.gridAPI.grid.virtualizationState.startIndex;
×
4736
        }
4737
        return newIndex;
23✔
4738
    }
4739

4740
    /**
4741
     * Places a column before or after the specified target column.
4742
     *
4743
     * @example
4744
     * ```typescript
4745
     * grid.moveColumn(column, target);
4746
     * ```
4747
     */
4748
    public moveColumn(column: IgxColumnComponent, target: IgxColumnComponent, pos: DropPosition = DropPosition.AfterDropTarget) {
52✔
4749
        // M.A. May 11th, 2021 #9508 Make the event cancelable
4750
        const eventArgs: IColumnMovingEndEventArgs = { source: column, target, cancel: false };
155✔
4751

4752
        this.columnMovingEnd.emit(eventArgs);
155✔
4753

4754
        if (eventArgs.cancel) {
155✔
4755
            return;
1✔
4756
        }
4757

4758
        if (column === target || (column.level !== target.level) ||
154✔
4759
            (column.topLevelParent !== target.topLevelParent)) {
4760
            return;
22✔
4761
        }
4762

4763
        if (column.level) {
132✔
4764
            this._moveChildColumns(column.parent, column, target, pos);
16✔
4765
        }
4766

4767
        // let columnPinStateChanged;
4768
        // pinning and unpinning will work correctly even without passing index
4769
        // but is easier to calclulate the index here, and later use it in the pinning event args
4770
        if (target.pinned && !column.pinned) {
132✔
4771
            const pinnedIndex = target.pinningPosition === ColumnPinningPosition.Start ? this.pinnedStartColumns.indexOf(target) : this.pinnedEndColumns.indexOf(target);
10✔
4772
            const index = pos === DropPosition.AfterDropTarget ? pinnedIndex + 1 : pinnedIndex;
10✔
4773
            column.pin(index, target.pinningPosition);
10✔
4774
        }
4775

4776
        if (!target.pinned && column.pinned) {
132✔
4777
            const unpinnedIndex = this._unpinnedColumns.indexOf(target);
4✔
4778
            const index = pos === DropPosition.AfterDropTarget ? unpinnedIndex + 1 : unpinnedIndex;
4✔
4779
            column.unpin(index);
4✔
4780
        }
4781

4782
        // both are pinned but are in different sides
4783
        if (target.pinned && column.pinned && target.pinningPosition !== column.pinningPosition) {
132!
4784
            column.pinningPosition = target.pinningPosition;
×
4785
        }
4786

4787
        // if (target.pinned && column.pinned && !columnPinStateChanged) {
4788
        //     this._reorderColumns(column, target, pos, this._pinnedColumns);
4789
        // }
4790

4791
        // if (!target.pinned && !column.pinned && !columnPinStateChanged) {
4792
        //     this._reorderColumns(column, target, pos, this._unpinnedColumns);
4793
        // }
4794

4795
        this._moveColumns(column, target, pos);
132✔
4796
        this._columnsReordered(column);
132✔
4797
    }
4798

4799
    /**
4800
     * Triggers change detection for the `IgxGridComponent`.
4801
     * Calling markForCheck also triggers the grid pipes explicitly, resulting in all updates being processed.
4802
     * May degrade performance if used when not needed, or if misused:
4803
     * ```typescript
4804
     * // DON'Ts:
4805
     * // don't call markForCheck from inside a loop
4806
     * // don't call markForCheck when a primitive has changed
4807
     * grid.data.forEach(rec => {
4808
     *  rec = newValue;
4809
     *  grid.markForCheck();
4810
     * });
4811
     *
4812
     * // DOs
4813
     * // call markForCheck after updating a nested property
4814
     * grid.data.forEach(rec => {
4815
     *  rec.nestedProp1.nestedProp2 = newValue;
4816
     * });
4817
     * grid.markForCheck();
4818
     * ```
4819
     *
4820
     * @example
4821
     * ```typescript
4822
     * grid.markForCheck();
4823
     * ```
4824
     */
4825
    public markForCheck() {
4826
        this.pipeTrigger++;
3,315✔
4827
        this.cdr.detectChanges();
3,315✔
4828
    }
4829

4830
    /* csSuppress */
4831
    /**
4832
     * Creates a new `IgxGridRowComponent` and adds the data record to the end of the data source.
4833
     *
4834
     * @example
4835
     * ```typescript
4836
     * this.grid1.addRow(record);
4837
     * ```
4838
     * @param data
4839
     */
4840
    public addRow(data: any): void {
4841
        // commit pending states prior to adding a row
4842
        this.crudService.endEdit(true);
171✔
4843
        this.gridAPI.addRowToData(data);
171✔
4844

4845
        this.pipeTrigger++;
171✔
4846
        this.rowAddedNotifier.next({ data: data, rowData: data, owner: this, primaryKey: data[this.primaryKey], rowKey: data[this.primaryKey] });
171✔
4847
        this.notifyChanges();
171✔
4848
    }
4849

4850
    /* blazorCSSuppress */
4851
    /**
4852
     * Removes the `IgxGridRowComponent` and the corresponding data record by primary key.
4853
     *
4854
     * @remarks
4855
     * Requires that the `primaryKey` property is set.
4856
     * The method accept rowSelector as a parameter, which is the rowID.
4857
     * @example
4858
     * ```typescript
4859
     * this.grid1.deleteRow(0);
4860
     * ```
4861
     * @param rowSelector
4862
     */
4863
    public deleteRow(rowSelector: any): any {
4864
        if (this.primaryKey !== undefined && this.primaryKey !== null) {
82✔
4865
            return this.deleteRowById(rowSelector);
82✔
4866
        }
4867
    }
4868

4869
    /** @hidden */
4870
    public deleteRowById(rowId: any): any {
4871
        const args: IRowDataCancelableEventArgs = {
79✔
4872
            rowID: rowId,
4873
            primaryKey: rowId,
4874
            rowKey: rowId,
4875
            rowData: this.getRowData(rowId),
4876
            data: this.getRowData(rowId),
4877
            oldValue: this.getRowData(rowId),
4878
            owner: this,
4879
            isAddRow: false,
4880
            cancel: false
4881
        };
4882
        this.rowDelete.emit(args);
79✔
4883
        if (args.cancel) {
79!
4884
            return;
×
4885
        }
4886

4887
        const record = this.gridAPI.deleteRowById(rowId);
79✔
4888
        if (record !== null && record !== undefined) {
79✔
4889
            const rowDeletedEventArgs: IRowDataEventArgs = {
78✔
4890
                data: record,
4891
                rowData: record,
4892
                owner: this,
4893
                primaryKey: record[this.primaryKey],
4894
                rowKey: record[this.primaryKey]
4895
            };
4896
            this.rowDeleted.emit(rowDeletedEventArgs);
78✔
4897
        }
4898
        return record;
79✔
4899
    }
4900

4901
    /* blazorCSSuppress */
4902
    /**
4903
     * Updates the `IgxGridRowComponent` and the corresponding data record by primary key.
4904
     *
4905
     * @remarks
4906
     * Requires that the `primaryKey` property is set.
4907
     * @example
4908
     * ```typescript
4909
     * this.gridWithPK.updateCell('Updated', 1, 'ProductName');
4910
     * ```
4911
     * @param value the new value which is to be set.
4912
     * @param rowSelector corresponds to rowID.
4913
     * @param column corresponds to column field.
4914
     */
4915
    public updateCell(value: any, rowSelector: any, column: string): void {
4916
        if (this.isDefined(this.primaryKey)) {
19✔
4917
            const col = this._columns.find(c => c.field === column);
47✔
4918
            if (col) {
19✔
4919
                // Simplify
4920
                const rowData = this.gridAPI.getRowData(rowSelector);
19✔
4921
                const index = this.gridAPI.get_row_index_in_data(rowSelector);
19✔
4922
                // If row passed is invalid
4923
                if (index < 0) {
19✔
4924
                    return;
1✔
4925
                }
4926

4927
                const id = {
18✔
4928
                    rowID: rowSelector,
4929
                    columnID: col.index,
4930
                    rowIndex: index
4931
                };
4932

4933
                const cell = new IgxCell(id, index, col, rowData[col.field], value, rowData, this as any);
18✔
4934
                const formControl = this.validation.getFormControl(cell.id.rowID, cell.column.field);
18✔
4935
                formControl.setValue(value);
18✔
4936
                this.gridAPI.update_cell(cell);
18✔
4937
                this.cdr.detectChanges();
18✔
4938
            }
4939
        }
4940
    }
4941

4942
    /* blazorCSSuppress */
4943
    /**
4944
     * Updates the `IgxGridRowComponent`
4945
     *
4946
     * @remarks
4947
     * The row is specified by
4948
     * rowSelector parameter and the data source record with the passed value.
4949
     * This method will apply requested update only if primary key is specified in the grid.
4950
     * @example
4951
     * ```typescript
4952
     * grid.updateRow({
4953
     *       ProductID: 1, ProductName: 'Spearmint', InStock: true, UnitsInStock: 1, OrderDate: new Date('2005-03-21')
4954
     *   }, 1);
4955
     * ```
4956
     * @param value–
4957
     * @param rowSelector correspond to rowID
4958
     */
4959
    // TODO: prevent event invocation
4960
    public updateRow(value: any, rowSelector: any): void {
4961
        if (this.isDefined(this.primaryKey)) {
32✔
4962
            const editableCell = this.crudService.cell;
32✔
4963
            if (editableCell && editableCell.id.rowID === rowSelector) {
32!
4964
                this.crudService.endCellEdit();
×
4965
            }
4966
            const row = new IgxEditRow(rowSelector, -1, this.gridAPI.getRowData(rowSelector), this as any);
32✔
4967
            this.gridAPI.update_row(row, value);
32✔
4968

4969
            // TODO: fix for #5934 and probably break for #5763
4970
            // consider adding of third optional boolean parameter in updateRow.
4971
            // If developer set this parameter to true we should call notifyChanges(true), and
4972
            // vise-versa if developer set it to false we should call notifyChanges(false).
4973
            // The parameter should default to false
4974
            this.notifyChanges();
32✔
4975
        }
4976
    }
4977

4978
    /**
4979
     * Returns the data that is contained in the row component.
4980
     *
4981
     * @remarks
4982
     * If the primary key is not specified the row selector match the row data.
4983
     * @example
4984
     * ```typescript
4985
     * const data = grid.getRowData(94741);
4986
     * ```
4987
     * @param rowSelector correspond to rowID
4988
     */
4989
    public getRowData(rowSelector: any): any {
4990
        if (!this.primaryKey) {
427✔
4991
            return rowSelector;
19✔
4992
        }
4993
        const data = this.gridAPI.get_all_data(this.transactions.enabled);
408✔
4994
        const index = this.gridAPI.get_row_index_in_data(rowSelector);
408✔
4995
        return index < 0 ? {} : data[index];
408✔
4996
    }
4997

4998
    /**
4999
     * Sort a single `IgxColumnComponent`.
5000
     *
5001
     * @remarks
5002
     * Sort the `IgxGridComponent`'s `IgxColumnComponent` based on the provided array of sorting expressions.
5003
     * @example
5004
     * ```typescript
5005
     * this.grid.sort({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false });
5006
     * ```
5007
     */
5008
    public sort(expression: ISortingExpression | Array<ISortingExpression>): void {
5009
        const sortingState = cloneArray(this.sortingExpressions);
159✔
5010

5011
        if (expression instanceof Array) {
159✔
5012
            for (const each of expression) {
3✔
5013
                this.gridAPI.prepare_sorting_expression([sortingState], each);
6✔
5014
            }
5015
        } else {
5016
            if (this._sortingOptions.mode === 'single') {
156✔
5017
                this._columns.forEach((col) => {
4✔
5018
                    if (!(col.field === expression.fieldName)) {
12✔
5019
                        this.clearSort(col.field);
8✔
5020
                    }
5021
                });
5022
            }
5023
            this.gridAPI.prepare_sorting_expression([sortingState], expression);
156✔
5024
        }
5025

5026
        const eventArgs: ISortingEventArgs = { owner: this, sortingExpressions: sortingState, cancel: false };
159✔
5027
        this.sorting.emit(eventArgs);
159✔
5028

5029
        if (eventArgs.cancel) {
159!
5030
            return;
×
5031
        }
5032

5033
        this.crudService.endEdit(false);
159✔
5034
        if (expression instanceof Array) {
159✔
5035
            this.gridAPI.sort_multiple(expression);
3✔
5036
        } else {
5037
            this.gridAPI.sort(expression);
156✔
5038
        }
5039
        requestAnimationFrame(() => this.sortingDone.emit(expression));
159✔
5040
    }
5041

5042
    /**
5043
     * Filters a single `IgxColumnComponent`.
5044
     *
5045
     * @example
5046
     * ```typescript
5047
     * public filter(term) {
5048
     *      this.grid.filter("ProductName", term, IgxStringFilteringOperand.instance().condition("contains"));
5049
     * }
5050
     * ```
5051
     * @param name
5052
     * @param value
5053
     * @param conditionOrExpressionTree
5054
     * @param ignoreCase
5055
     */
5056
    public filter(name: string, value: any, conditionOrExpressionTree?: IFilteringOperation | IFilteringExpressionsTree,
5057
        ignoreCase?: boolean) {
5058
        this.filteringService.filter(name, value, conditionOrExpressionTree, ignoreCase);
274✔
5059
    }
5060

5061
    /**
5062
     * Filters all the `IgxColumnComponent` in the `IgxGridComponent` with the same condition.
5063
     *
5064
     * @example
5065
     * ```typescript
5066
     * grid.filterGlobal('some', IgxStringFilteringOperand.instance().condition('contains'));
5067
     * ```
5068
     * @param value
5069
     * @param condition
5070
     * @param ignoreCase
5071
     * @deprecated in version 19.0.0.
5072
     */
5073
    public filterGlobal(value: any, condition, ignoreCase?) {
5074
        this.filteringService.filterGlobal(value, condition, ignoreCase);
3✔
5075
    }
5076

5077
    /**
5078
     * Enables summaries for the specified column and applies your customSummary.
5079
     *
5080
     * @remarks
5081
     * If you do not provide the customSummary, then the default summary for the column data type will be applied.
5082
     * @example
5083
     * ```typescript
5084
     * grid.enableSummaries([{ fieldName: 'ProductName' }, { fieldName: 'ID' }]);
5085
     * ```
5086
     * Enable summaries for the listed columns.
5087
     * @example
5088
     * ```typescript
5089
     * grid.enableSummaries('ProductName');
5090
     * ```
5091
     * @param rest
5092
     */
5093
    public enableSummaries(...rest) {
5094
        if (rest.length === 1 && Array.isArray(rest[0])) {
10✔
5095
            this._multipleSummaries(rest[0], true);
7✔
5096
        } else {
5097
            this._summaries(rest[0], true, rest[1]);
3✔
5098
        }
5099
    }
5100

5101
    /**
5102
     * Disable summaries for the specified column.
5103
     *
5104
     * @example
5105
     * ```typescript
5106
     * grid.disableSummaries('ProductName');
5107
     * ```
5108
     * @remarks
5109
     * Disable summaries for the listed columns.
5110
     * @example
5111
     * ```typescript
5112
     * grid.disableSummaries([{ fieldName: 'ProductName' }]);
5113
     * ```
5114
     */
5115
    public disableSummaries(...rest) {
5116
        if (rest.length === 1 && Array.isArray(rest[0])) {
7✔
5117
            this._disableMultipleSummaries(rest[0]);
5✔
5118
        } else {
5119
            this._summaries(rest[0], false);
2✔
5120
        }
5121
    }
5122

5123
    /**
5124
     * If name is provided, clears the filtering state of the corresponding `IgxColumnComponent`.
5125
     *
5126
     * @remarks
5127
     * Otherwise clears the filtering state of all `IgxColumnComponent`s.
5128
     * @example
5129
     * ```typescript
5130
     * this.grid.clearFilter();
5131
     * ```
5132
     * @param name
5133
     */
5134
    public clearFilter(name?: string) {
5135
        this.filteringService.clearFilter(name);
147✔
5136
    }
5137

5138
    /**
5139
     * If name is provided, clears the sorting state of the corresponding `IgxColumnComponent`.
5140
     *
5141
     * @remarks
5142
     * otherwise clears the sorting state of all `IgxColumnComponent`.
5143
     * @example
5144
     * ```typescript
5145
     * this.grid.clearSort();
5146
     * ```
5147
     * @param name
5148
     */
5149
    public clearSort(name?: string) {
5150
        if (!name) {
35✔
5151
            this.sortingExpressions = [];
22✔
5152
            return;
22✔
5153
        }
5154
        if (!this.gridAPI.get_column_by_name(name)) {
13!
5155
            return;
×
5156
        }
5157
        this.gridAPI.clear_sort(name);
13✔
5158
    }
5159

5160
    /**
5161
     * @hidden @internal
5162
     */
5163
    public refreshGridState(_args?) {
5164
        this.crudService.endEdit(true);
287✔
5165
        this.selectionService.clearHeaderCBState();
287✔
5166
        this.summaryService.clearSummaryCache();
287✔
5167
        this.summaryPipeTrigger++;
287✔
5168
        this.cdr.detectChanges();
287✔
5169
    }
5170

5171
    // TODO: We have return values here. Move them to event args ??
5172

5173
    /**
5174
     * Pins a column by field name.
5175
     *
5176
     * @remarks
5177
     * Returns whether the operation is successful.
5178
     * @example
5179
     * ```typescript
5180
     * this.grid.pinColumn("ID");
5181
     * ```
5182
     * @param columnName
5183
     * @param index
5184
     * @param pinningPosition
5185
     */
5186
    public pinColumn(columnName: string | IgxColumnComponent, index?: number, pinningPosition?: ColumnPinningPosition): boolean {
5187
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
38✔
5188
        return col.pin(index, pinningPosition);
38✔
5189
    }
5190

5191
    /**
5192
     * Unpins a column by field name. Returns whether the operation is successful.
5193
     *
5194
     * @example
5195
     * ```typescript
5196
     * this.grid.pinColumn("ID");
5197
     * ```
5198
     * @param columnName
5199
     * @param index
5200
     */
5201
    public unpinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
5202
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
20✔
5203
        return col.unpin(index);
20✔
5204
    }
5205

5206
    /* csSuppress */
5207
    /**
5208
     * Pin the row by its id.
5209
     *
5210
     * @remarks
5211
     * ID is either the primaryKey value or the data record instance.
5212
     * @example
5213
     * ```typescript
5214
     * this.grid.pinRow(rowID);
5215
     * ```
5216
     * @param rowID The row id - primaryKey value or the data record instance.
5217
     * @param index The index at which to insert the row in the pinned collection.
5218
     */
5219
    public pinRow(rowID: any, index?: number, row?: RowType): boolean {
5220
        if (this._pinnedRecordIDs.indexOf(rowID) !== -1) {
134✔
5221
            return false;
1✔
5222
        }
5223
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, index, row, true);
133✔
5224
        this.rowPinning.emit(eventArgs);
133✔
5225

5226
        if (eventArgs.cancel) {
133✔
5227
            return;
1✔
5228
        }
5229
        this.crudService.endEdit(false);
132✔
5230

5231
        const insertIndex = typeof eventArgs.insertAtIndex === 'number' ? eventArgs.insertAtIndex : this._pinnedRecordIDs.length;
132✔
5232
        this._pinnedRecordIDs.splice(insertIndex, 0, rowID);
132✔
5233
        this.pipeTrigger++;
132✔
5234
        if (this.gridAPI.grid) {
132✔
5235
            this.cdr.detectChanges();
130✔
5236
            this.rowPinned.emit(eventArgs);
130✔
5237
        }
5238

5239
        return true;
132✔
5240
    }
5241

5242
    /* csSuppress */
5243
    /**
5244
     * Unpin the row by its id.
5245
     *
5246
     * @remarks
5247
     * ID is either the primaryKey value or the data record instance.
5248
     * @example
5249
     * ```typescript
5250
     * this.grid.unpinRow(rowID);
5251
     * ```
5252
     * @param rowID The row id - primaryKey value or the data record instance.
5253
     */
5254
    public unpinRow(rowID: any, row?: RowType): boolean {
5255
        const index = this._pinnedRecordIDs.indexOf(rowID);
24✔
5256
        if (index === -1) {
24!
5257
            return false;
×
5258
        }
5259

5260
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, null, row, false);
24✔
5261
        this.rowPinning.emit(eventArgs);
24✔
5262

5263
        if (eventArgs.cancel) {
24✔
5264
            return;
1✔
5265
        }
5266

5267
        this.crudService.endEdit(false);
23✔
5268
        this._pinnedRecordIDs.splice(index, 1);
23✔
5269
        this.pipeTrigger++;
23✔
5270
        if (this.gridAPI.grid) {
23✔
5271
            this.cdr.detectChanges();
23✔
5272
            this.rowPinned.emit(eventArgs);
23✔
5273
        }
5274

5275
        return true;
23✔
5276
    }
5277

5278
    /** @hidden @internal */
5279
    public get pinnedRowHeight() {
5280
        const containerHeight = this.pinContainer ? this.pinContainer.nativeElement.offsetHeight : 0;
83,290✔
5281
        return this.hasPinnedRecords ? containerHeight : 0;
83,290✔
5282
    }
5283

5284
    /** @hidden @internal */
5285
    public get totalHeight() {
5286
        const height = this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
43,835✔
5287
        return this.platform.isBrowser ? height : undefined;
43,835!
5288
    }
5289

5290
    /**
5291
     * Recalculates grid width/height dimensions.
5292
     *
5293
     * @remarks
5294
     * Should be run when changing DOM elements dimentions manually that affect the grid's size.
5295
     * @example
5296
     * ```typescript
5297
     * this.grid.reflow();
5298
     * ```
5299
     */
5300
    public reflow() {
5301
        this.calculateGridSizes();
326✔
5302
    }
5303

5304
    /**
5305
     * Finds the next occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
5306
     *
5307
     * @remarks
5308
     * Returns how many times the grid contains the string.
5309
     * @example
5310
     * ```typescript
5311
     * this.grid.findNext("financial");
5312
     * ```
5313
     * @param text the string to search.
5314
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
5315
     * @param exactMatch optionally, if the text should match the entire value  (defaults to false).
5316
     */
5317
    public findNext(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
5318
        return this.find(text, 1, caseSensitive, exactMatch);
155✔
5319
    }
5320

5321
    /**
5322
     * Finds the previous occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
5323
     *
5324
     * @remarks
5325
     * Returns how many times the grid contains the string.
5326
     * @example
5327
     * ```typescript
5328
     * this.grid.findPrev("financial");
5329
     * ```
5330
     * @param text the string to search.
5331
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
5332
     * @param exactMatch optionally, if the text should match the entire value (defaults to false).
5333
     */
5334
    public findPrev(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
5335
        return this.find(text, -1, caseSensitive, exactMatch);
40✔
5336
    }
5337

5338
    /**
5339
     * Reapplies the existing search.
5340
     *
5341
     * @remarks
5342
     * Returns how many times the grid contains the last search.
5343
     * @example
5344
     * ```typescript
5345
     * this.grid.refreshSearch();
5346
     * ```
5347
     * @param updateActiveInfo
5348
     */
5349
    public refreshSearch(updateActiveInfo?: boolean, endEdit = true): number {
4,154✔
5350
        if (this._lastSearchInfo.searchText) {
10,122✔
5351
            this.rebuildMatchCache();
127✔
5352

5353
            if (updateActiveInfo) {
127✔
5354
                const activeInfo = this.textHighlightService.highlightGroupsMap.get(this.id);
126✔
5355
                this._lastSearchInfo.matchInfoCache.forEach((match, i) => {
126✔
5356
                    if (match.column === activeInfo.column &&
1,552✔
5357
                        match.row === activeInfo.row &&
5358
                        match.index === activeInfo.index &&
5359
                        compareMaps(match.metadata, activeInfo.metadata)) {
5360
                        this._lastSearchInfo.activeMatchIndex = i;
111✔
5361
                    }
5362
                });
5363
            }
5364

5365
            return this.find(this._lastSearchInfo.searchText,
127✔
5366
                0,
5367
                this._lastSearchInfo.caseSensitive,
5368
                this._lastSearchInfo.exactMatch,
5369
                false,
5370
                endEdit);
5371
        } else {
5372
            return 0;
9,995✔
5373
        }
5374
    }
5375

5376
    /**
5377
     * Removes all the highlights in the cell.
5378
     *
5379
     * @example
5380
     * ```typescript
5381
     * this.grid.clearSearch();
5382
     * ```
5383
     */
5384
    public clearSearch() {
5385
        this._lastSearchInfo = {
1✔
5386
            searchText: '',
5387
            caseSensitive: false,
5388
            exactMatch: false,
5389
            activeMatchIndex: 0,
5390
            matchInfoCache: [],
5391
            matchCount: 0,
5392
            content: ''
5393
        };
5394

5395
        this.rowList.forEach((row) => {
1✔
5396
            if (row.cells) {
10✔
5397
                row.cells.forEach((c: IgxGridCellComponent) => {
10✔
5398
                    c.clearHighlight();
40✔
5399
                });
5400
            }
5401
        });
5402
    }
5403

5404
    /** @hidden @internal */
5405
    public get hasEditableColumns(): boolean {
5406
        return this._columns.some((col) => col.editable);
6✔
5407
    }
5408

5409
    /** @hidden @internal */
5410
    public get hasSummarizedColumns(): boolean {
5411
        const summarizedColumns = this._columns.filter(col => col.hasSummary && !col.hidden);
2,123,911✔
5412
        return summarizedColumns.length > 0;
335,690✔
5413
    }
5414

5415
    /**
5416
     * @hidden @internal
5417
     */
5418
    public get rootSummariesEnabled(): boolean {
5419
        return this.summaryCalculationMode !== GridSummaryCalculationMode.childLevelsOnly;
245,214✔
5420
    }
5421

5422
    /**
5423
     * @hidden @internal
5424
     */
5425
    public get hasVisibleColumns(): boolean {
5426
        if (this._hasVisibleColumns === undefined) {
78,745✔
5427
            return this._columns ? this._columns.some(c => !c.hidden) : false;
79,900!
5428
        }
5429
        return this._hasVisibleColumns;
×
5430
    }
5431

5432
    public set hasVisibleColumns(value) {
5433
        this._hasVisibleColumns = value;
34,789✔
5434
    }
5435

5436
    /** @hidden @internal */
5437
    public get hasMovableColumns(): boolean {
5438
        return this.moving;
×
5439
    }
5440

5441
    /** @hidden @internal */
5442
    public get hasColumnGroups(): boolean {
5443
        return this._columnGroups;
267,994✔
5444
    }
5445

5446
    /** @hidden @internal */
5447
    public get hasColumnLayouts() {
5448
        return !!this._columns.some(col => col.columnLayout);
24,613,419✔
5449
    }
5450

5451

5452
    /**
5453
     * @hidden @internal
5454
     */
5455
    public get multiRowLayoutRowSize() {
5456
        return this._multiRowLayoutRowSize;
20,341✔
5457
    }
5458

5459
    /**
5460
     * @hidden
5461
     */
5462
    protected get rowBasedHeight() {
5463
        return this.dataLength * this.rowHeight;
×
5464
    }
5465

5466
    /**
5467
     * @hidden
5468
     */
5469
    protected get isPercentWidth() {
5470
        return this.width && this.width.indexOf('%') !== -1;
42,693✔
5471
    }
5472

5473
    protected get shouldResize(): boolean {
5474
        return this._gridSize !== this.gridSize;
1,813✔
5475
    }
5476

5477
    /**
5478
     * @hidden @internal
5479
     */
5480
    public get isPercentHeight() {
5481
        return this._height && this._height.indexOf('%') !== -1;
15,968✔
5482
    }
5483

5484
    /**
5485
     * @hidden
5486
     */
5487
    protected get defaultTargetBodyHeight(): number {
5488
        const allItems = this.dataLength;
326✔
5489
        return this.renderedActualRowHeight * Math.min(this._defaultTargetRecordNumber,
326✔
5490
            this.paginator ? Math.min(allItems, this.paginator.perPage) : allItems);
326✔
5491
    }
5492

5493
    /**
5494
     * @hidden @internal
5495
     * The rowHeight input is bound to min-height css prop of rows that adds a 1px border in all cases
5496
     */
5497
    public get renderedRowHeight(): number {
5498
        return this.rowHeight + 1;
86,233✔
5499
    }
5500

5501
    /**
5502
     * @hidden @internal
5503
     */
5504
    public get outerWidth() {
5505
        return this.hasVerticalScroll() ? this.calcWidth + this.scrollSize : this.calcWidth;
3,861✔
5506
    }
5507

5508
    /**
5509
     * @hidden @internal
5510
     * Gets the size of the grid
5511
     */
5512
    public get gridSize(): Size {
5513
        return this.gridComputedStyles?.getPropertyValue('--component-size') || Size.Large;
542,470✔
5514
    }
5515

5516
    /**
5517
     * @hidden @internal
5518
     * Gets the visible content height that includes header + tbody + footer.
5519
     */
5520
    public getVisibleContentHeight() {
5521
        let height = this.theadRow.nativeElement.clientHeight + this.tbody.nativeElement.clientHeight;
50✔
5522
        if (this.hasSummarizedColumns) {
50✔
5523
            height += this.tfoot.nativeElement.clientHeight;
5✔
5524
        }
5525
        return height;
50✔
5526
    }
5527

5528
    /**
5529
     * @hidden @internal
5530
     */
5531
    public getPossibleColumnWidth(baseWidth: number = null, minColumnWidth: number = null) {
147,098✔
5532
        let computedWidth;
5533
        if (baseWidth !== null) {
73,577!
5534
            computedWidth = baseWidth;
×
5535
        } else {
5536
            computedWidth = this.calcWidth ||
73,577✔
5537
                parseFloat(this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width'));
5538
        }
5539

5540
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
689,234✔
5541

5542

5543
        // Column layouts related
5544
        let visibleCols = [];
73,577✔
5545
        const columnBlocks = this.visibleColumns.filter(c => c.columnGroup);
689,234✔
5546
        const colsPerBlock = columnBlocks.map(block => block.getInitialChildColumnSizes(block.children));
123,169✔
5547
        const combinedBlocksSize = colsPerBlock.reduce((acc, item) => acc + item.length, 0);
123,169✔
5548
        colsPerBlock.forEach(blockCols => visibleCols = visibleCols.concat(blockCols));
123,169✔
5549
        //
5550

5551
        const columnsWithSetWidths = this.hasColumnLayouts ?
73,577✔
5552
            visibleCols.filter(c => c.widthSetByUser) :
166,319✔
5553
            visibleChildColumns.filter(c => (c.widthSetByUser || c.widthConstrained) && c.width !== 'fit-content');
300,790✔
5554

5555
        const columnsToSize = this.hasColumnLayouts ?
73,577✔
5556
            combinedBlocksSize - columnsWithSetWidths.length :
5557
            visibleChildColumns.length - columnsWithSetWidths.length;
5558
        const sumExistingWidths = columnsWithSetWidths
73,577✔
5559
            .reduce((prev, curr) => {
5560
                const colInstance = this.hasColumnLayouts ? curr.ref : curr;
39,205✔
5561
                const colWidth = !colInstance.widthConstrained ? curr.width : colInstance.calcPixelWidth;
39,205✔
5562
                let widthValue = parseFloat(colWidth);
39,205✔
5563
                if (isNaN(widthValue)) {
39,205!
5564
                    widthValue = MINIMUM_COLUMN_WIDTH;
×
5565
                }
5566
                const currWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1 ?
39,205✔
5567
                    widthValue / 100 * computedWidth :
5568
                    widthValue;
5569
                // apply constraints, since constraint may change width
5570
                const constrainedWidth = this.hasColumnLayouts ? currWidth : colInstance.getConstrainedSizePx(currWidth);
39,205✔
5571
                return prev + constrainedWidth;
39,205✔
5572
            }, 0);
5573

5574
        // When all columns are hidden, return 0px width
5575
        if (!sumExistingWidths && !columnsToSize) {
73,577✔
5576
            return '0px';
1,604✔
5577
        }
5578
        computedWidth -= this.featureColumnsWidth();
71,973✔
5579

5580
        const minColWidth = minColumnWidth || this.minColumnWidth;
71,973✔
5581

5582
        const columnWidth = !Number.isFinite(sumExistingWidths) ?
71,973!
5583
            Math.max(computedWidth / columnsToSize, minColWidth) :
5584
            Math.max((computedWidth - sumExistingWidths) / columnsToSize, minColWidth);
5585

5586
        return columnWidth + 'px';
71,973✔
5587
    }
5588

5589
    /**
5590
     * @hidden @internal
5591
     */
5592
    public hasVerticalScroll() {
5593
        if (this._init) {
190,504✔
5594
            return false;
83,825✔
5595
        }
5596
        const isScrollable = this.verticalScrollContainer ? this.verticalScrollContainer.isScrollable() : false;
106,679✔
5597
        return !!(this.calcWidth && this.dataView && this.dataView.length > 0 && isScrollable);
106,679✔
5598
    }
5599

5600
    /**
5601
     * Gets calculated width of the pinned areas.
5602
     *
5603
     * @example
5604
     * ```typescript
5605
     * const pinnedWidth = this.grid.getPinnedStartWidth();
5606
     * ```
5607
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
5608
     */
5609
    public getPinnedStartWidth(takeHidden = false) {
20,741✔
5610
        const fc = takeHidden ? this._pinnedStartColumns : this.pinnedStartColumns;
45,145!
5611
        let sum = 0;
45,145✔
5612
        for (const col of fc) {
45,145✔
5613
            if (col.level === 0) {
6,263✔
5614
                sum += parseFloat(col.calcWidth);
3,675✔
5615
            }
5616
        }
5617
        // includes features at start
5618
        sum += this.featureColumnsWidth();
45,145✔
5619

5620
        return sum;
45,145✔
5621
    }
5622

5623
    /**
5624
 * Gets calculated width of the pinned areas.
5625
 *
5626
 * @example
5627
 * ```typescript
5628
 * const pinnedWidth = this.grid.getPinnedEndWidth();
5629
 * ```
5630
 * @param takeHidden If we should take into account the hidden columns in the pinned area.
5631
 */
5632
    public getPinnedEndWidth(takeHidden = false) {
21,316✔
5633
        const fc = takeHidden ? this._pinnedEndColumns : this.pinnedEndColumns;
45,122!
5634
        let sum = 0;
45,122✔
5635
        for (const col of fc) {
45,122✔
5636
            if (col.level === 0) {
434✔
5637
                sum += parseFloat(col.calcWidth);
322✔
5638
            }
5639
        }
5640
        return sum;
45,122✔
5641
    }
5642

5643
    /**
5644
     * @hidden @internal
5645
     */
5646
    public isColumnGrouped(_fieldName: string): boolean {
5647
        return false;
×
5648
    }
5649

5650
    /**
5651
     * @hidden @internal
5652
     * TODO: REMOVE
5653
     */
5654
    public onHeaderSelectorClick(event) {
5655
        if (!this.isMultiRowSelectionEnabled) {
7!
5656
            return;
×
5657
        }
5658
        if (this.selectionService.areAllRowSelected()) {
7✔
5659
            this.selectionService.clearRowSelection(event);
1✔
5660
        } else {
5661
            this.selectionService.selectAllRows(event);
6✔
5662
        }
5663
    }
5664

5665
    /**
5666
     * @hidden @internal
5667
     */
5668
    public get headSelectorBaseAriaLabel() {
5669
        if (this._filteringExpressionsTree.filteringOperands.length > 0) {
3,169✔
5670
            return this.selectionService.areAllRowSelected() ? 'Deselect all filtered' : 'Select all filtered';
112✔
5671
        }
5672

5673
        return this.selectionService.areAllRowSelected() ? 'Deselect all' : 'Select all';
3,057✔
5674
    }
5675

5676
    /**
5677
     * @hidden
5678
     * @internal
5679
     */
5680
    public get totalRowsCountAfterFilter() {
5681
        if (this.data) {
3,334✔
5682
            return this.selectionService.allData.length;
3,334✔
5683
        }
5684

5685
        return 0;
×
5686
    }
5687

5688
    /** @hidden @internal */
5689
    public get pinnedDataView(): any[] {
5690
        return this.pinnedRecords ? this.pinnedRecords : [];
266,322✔
5691
    }
5692

5693
    /** @hidden @internal */
5694
    public get unpinnedDataView(): any[] {
5695
        return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer?.igxForOf || [];
15,708✔
5696
    }
5697

5698
    /**
5699
     * Returns the currently transformed paged/filtered/sorted/grouped/pinned/unpinned row data, displayed in the grid.
5700
     *
5701
     * @example
5702
     * ```typescript
5703
     *      const dataView = this.grid.dataView;
5704
     * ```
5705
     */
5706
    public get dataView() {
5707
        return this._dataView;
418,905✔
5708
    }
5709

5710
    /**
5711
     * Gets/Sets whether clicking over a row should select/deselect it
5712
     *
5713
     * @remarks
5714
     * By default it is set to true
5715
     * @param enabled: boolean
5716
     */
5717
    @WatchChanges()
5718
    @Input({ transform: booleanAttribute })
5719
    public get selectRowOnClick() {
5720
        return this._selectRowOnClick;
111✔
5721
    }
5722

5723
    public set selectRowOnClick(enabled: boolean) {
5724
        this._selectRowOnClick = enabled;
13✔
5725
    }
5726

5727
    /**
5728
     * Select specified rows by ID.
5729
     *
5730
     * @example
5731
     * ```typescript
5732
     * this.grid.selectRows([1,2,5], true);
5733
     * ```
5734
     * @param rowIDs
5735
     * @param clearCurrentSelection if true clears the current selection
5736
     */
5737
    public selectRows(rowIDs: any[], clearCurrentSelection?: boolean) {
5738
        this.selectionService.selectRowsWithNoEvent(rowIDs, clearCurrentSelection);
264✔
5739
        this.notifyChanges();
264✔
5740
    }
5741

5742
    /**
5743
     * Deselect specified rows by ID.
5744
     *
5745
     * @example
5746
     * ```typescript
5747
     * this.grid.deselectRows([1,2,5]);
5748
     * ```
5749
     * @param rowIDs
5750
     */
5751
    public deselectRows(rowIDs: any[]) {
5752
        this.selectionService.deselectRowsWithNoEvent(rowIDs);
19✔
5753
        this.notifyChanges();
19✔
5754
    }
5755

5756
    /**
5757
     * Selects all rows
5758
     *
5759
     * @remarks
5760
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
5761
     * If you set the parameter onlyFilterData to false that will select all rows in the grid exept deleted rows.
5762
     * @example
5763
     * ```typescript
5764
     * this.grid.selectAllRows();
5765
     * this.grid.selectAllRows(false);
5766
     * ```
5767
     * @param onlyFilterData
5768
     */
5769
    public selectAllRows(onlyFilterData = true) {
29✔
5770
        const data = onlyFilterData && this.filteredData ? this.filteredData : this.gridAPI.get_all_data(true);
30✔
5771
        const rowIDs = this.selectionService.getRowIDs(data).filter(rID => !this.gridAPI.row_deleted_transaction(rID));
429✔
5772
        this.selectRows(rowIDs);
30✔
5773
    }
5774

5775
    /**
5776
     * Deselects all rows
5777
     *
5778
     * @remarks
5779
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
5780
     * If you set the parameter onlyFilterData to false that will deselect all rows in the grid exept deleted rows.
5781
     * @example
5782
     * ```typescript
5783
     * this.grid.deselectAllRows();
5784
     * ```
5785
     * @param onlyFilterData
5786
     */
5787
    public deselectAllRows(onlyFilterData = true) {
5✔
5788
        if (onlyFilterData && this.filteredData && this.filteredData.length > 0) {
6✔
5789
            this.deselectRows(this.selectionService.getRowIDs(this.filteredData));
1✔
5790
        } else {
5791
            this.selectionService.clearAllSelectedRows();
5✔
5792
            this.notifyChanges();
5✔
5793
        }
5794
    }
5795

5796
    /**
5797
     * Deselect selected cells.
5798
     * @example
5799
     * ```typescript
5800
     * this.grid.clearCellSelection();
5801
     * ```
5802
     */
5803
    public clearCellSelection(): void {
5804
        this.selectionService.clear(true);
8✔
5805
        this.notifyChanges();
8✔
5806
    }
5807

5808
    /**
5809
     * @hidden @internal
5810
     */
5811
    public dragScroll(delta: { left: number; top: number }): void {
5812
        const horizontal = this.headerContainer.getScroll();
1✔
5813
        const vertical = this.verticalScrollContainer.getScroll();
1✔
5814
        const { left, top } = delta;
1✔
5815

5816
        horizontal.scrollLeft += left * this.DRAG_SCROLL_DELTA;
1✔
5817
        vertical.scrollTop += top * this.DRAG_SCROLL_DELTA;
1✔
5818
    }
5819

5820
    /**
5821
     * @hidden @internal
5822
     */
5823
    public isDefined(arg: any): boolean {
5824
        return arg !== undefined && arg !== null;
67,810✔
5825
    }
5826

5827
    /**
5828
     * Select range(s) of cells between certain rows and columns of the grid.
5829
     */
5830
    public selectRange(arg: GridSelectionRange | GridSelectionRange[] | null | undefined): void {
5831
        if (!this.isDefined(arg)) {
170✔
5832
            this.clearCellSelection();
6✔
5833
            return;
6✔
5834
        }
5835
        if (arg instanceof Array) {
164✔
5836
            arg.forEach(range => this.setSelection(range));
6✔
5837
        } else {
5838
            this.setSelection(arg);
161✔
5839
        }
5840
        this.notifyChanges();
161✔
5841
    }
5842

5843
    /**
5844
     * @hidden @internal
5845
     */
5846
    public columnToVisibleIndex(field: string | number): number {
5847
        const visibleColumns = this.visibleColumns;
341✔
5848
        if (typeof field === 'number') {
341✔
5849
            return field;
208✔
5850
        }
5851
        return visibleColumns.find(column => column.field === field).visibleIndex;
322✔
5852
    }
5853

5854
    /**
5855
     * @hidden @internal
5856
     */
5857
    public setSelection(range: GridSelectionRange): void {
5858
        const startNode = { row: range.rowStart, column: this.columnToVisibleIndex(range.columnStart) };
169✔
5859
        const endNode = { row: range.rowEnd, column: this.columnToVisibleIndex(range.columnEnd) };
167✔
5860

5861
        this.selectionService.pointerState.node = startNode;
166✔
5862
        this.selectionService.selectRange(endNode, this.selectionService.pointerState);
166✔
5863
        this.selectionService.addRangeMeta(endNode, this.selectionService.pointerState);
166✔
5864
        this.selectionService.initPointerState();
166✔
5865
    }
5866

5867
    /**
5868
     * Get the currently selected ranges in the grid.
5869
     */
5870
    public getSelectedRanges(): GridSelectionRange[] {
5871
        return this.selectionService.ranges;
348✔
5872
    }
5873

5874
    /**
5875
     *
5876
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
5877
     *
5878
     * @remarks
5879
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5880
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5881
     */
5882
    public getSelectedData(formatters = false, headers = false) {
4✔
5883
        const source = this.filteredSortedData;
167✔
5884
        return this.extractDataFromSelection(source, formatters, headers);
167✔
5885
    }
5886

5887
    /**
5888
     * Get current selected columns.
5889
     *
5890
     * @example
5891
     * Returns an array with selected columns
5892
     * ```typescript
5893
     * const selectedColumns = this.grid.selectedColumns();
5894
     * ```
5895
     */
5896
    public selectedColumns(): ColumnType[] {
5897
        const fields = this.selectionService.getSelectedColumns();
67✔
5898
        return fields.map(field => this.getColumnByName(field)).filter(field => field);
67✔
5899
    }
5900

5901
    /**
5902
     * Select specified columns.
5903
     *
5904
     * @example
5905
     * ```typescript
5906
     * this.grid.selectColumns(['ID','Name'], true);
5907
     * ```
5908
     * @param columns
5909
     * @param clearCurrentSelection if true clears the current selection
5910
     */
5911
    public selectColumns(columns: string[] | ColumnType[], clearCurrentSelection?: boolean) {
5912
        let fieldToSelect: string[] = [];
12✔
5913
        if (columns.length === 0 || typeof columns[0] === 'string') {
12✔
5914
            fieldToSelect = columns as string[];
7✔
5915
        } else {
5916
            (columns as ColumnType[]).forEach(col => {
5✔
5917
                if (col.columnGroup) {
18!
5918
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
×
5919
                    fieldToSelect = [...fieldToSelect, ...children];
×
5920
                } else {
5921
                    fieldToSelect.push(col.field);
18✔
5922
                }
5923
            });
5924
        }
5925

5926
        this.selectionService.selectColumnsWithNoEvent(fieldToSelect, clearCurrentSelection);
12✔
5927
        this.notifyChanges();
12✔
5928
    }
5929

5930
    /**
5931
     * Deselect specified columns by field.
5932
     *
5933
     * @example
5934
     * ```typescript
5935
     * this.grid.deselectColumns(['ID','Name']);
5936
     * ```
5937
     * @param columns
5938
     */
5939
    public deselectColumns(columns: string[] | ColumnType[]) {
5940
        let fieldToDeselect: string[] = [];
3✔
5941
        if (columns.length === 0 || typeof columns[0] === 'string') {
3✔
5942
            fieldToDeselect = columns as string[];
2✔
5943
        } else {
5944
            (columns as ColumnType[]).forEach(col => {
1✔
5945
                if (col.columnGroup) {
2!
5946
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
×
5947
                    fieldToDeselect = [...fieldToDeselect, ...children];
×
5948
                } else {
5949
                    fieldToDeselect.push(col.field);
2✔
5950
                }
5951
            });
5952
        }
5953
        this.selectionService.deselectColumnsWithNoEvent(fieldToDeselect);
3✔
5954
        this.notifyChanges();
3✔
5955
    }
5956

5957
    /**
5958
     * Deselects all columns
5959
     *
5960
     * @example
5961
     * ```typescript
5962
     * this.grid.deselectAllColumns();
5963
     * ```
5964
     */
5965
    public deselectAllColumns() {
5966
        this.selectionService.clearAllSelectedColumns();
3✔
5967
        this.notifyChanges();
3✔
5968
    }
5969

5970
    /**
5971
     * Selects all columns
5972
     *
5973
     * @example
5974
     * ```typescript
5975
     * this.grid.deselectAllColumns();
5976
     * ```
5977
     */
5978
    public selectAllColumns() {
5979
        this.selectColumns(this._columns.filter(c => !c.columnGroup));
15✔
5980
    }
5981

5982
    /**
5983
     *
5984
     * Returns an array of the current columns selection in the form of `[{ column.field: cell.value }, ...]`.
5985
     *
5986
     * @remarks
5987
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5988
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5989
     */
5990
    public getSelectedColumnsData(formatters = false, headers = false) {
16✔
5991
        const source = this.filteredSortedData ? this.filteredSortedData : this.data;
20!
5992
        return this.extractDataFromColumnsSelection(source, formatters, headers);
20✔
5993
    }
5994

5995

5996
    /** @hidden @internal **/
5997
    public combineSelectedCellAndColumnData(columnData: any[], formatters = false, headers = false) {
×
5998
        const source = this.filteredSortedData;
×
5999
        return this.extractDataFromSelection(source, formatters, headers, columnData);
×
6000
    }
6001

6002
    /**
6003
     * @hidden @internal
6004
     */
6005
    public preventContainerScroll = (evt) => {
4,157✔
6006
        if (evt.target.scrollTop !== 0) {
×
6007
            this.verticalScrollContainer.addScroll(evt.target.scrollTop);
×
6008
            evt.target.scrollTop = 0;
×
6009
        }
6010
        if (evt.target.scrollLeft !== 0) {
×
6011
            this.headerContainer.scrollPosition += evt.target.scrollLeft;
×
6012
            evt.target.scrollLeft = 0;
×
6013
        }
6014
    };
6015

6016
    /**
6017
     * @hidden
6018
     * @internal
6019
     */
6020
    public copyHandler(event) {
6021
        const eventPathElements = event.composedPath().map(el => el.tagName?.toLowerCase());
98✔
6022
        if (eventPathElements.includes('igx-grid-filtering-row') ||
13✔
6023
            eventPathElements.includes('igx-grid-filtering-cell')) {
6024
            return;
1✔
6025
        }
6026

6027
        const selectedColumns = this.gridAPI.grid.selectedColumns();
12✔
6028
        const columnData = this.getSelectedColumnsData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
6029
        let selectedData;
6030
        if (event.type === 'copy') {
12✔
6031
            selectedData = this.getSelectedData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
12✔
6032
        }
6033

6034
        let data = [];
12✔
6035
        let result;
6036

6037
        if (event.code === 'KeyC' && (event.ctrlKey || event.metaKey) && event.currentTarget.className === 'igx-grid-thead__wrapper') {
12!
6038
            if (selectedData.length) {
×
6039
                if (columnData.length === 0) {
×
6040
                    result = this.prepareCopyData(event, selectedData);
×
6041
                } else {
6042
                    data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
×
6043
                        this.clipboardOptions.copyHeaders);
6044
                    result = this.prepareCopyData(event, data[0], data[1]);
×
6045
                }
6046
            } else {
6047
                data = columnData;
×
6048
                result = this.prepareCopyData(event, data);
×
6049
            }
6050

6051
            navigator.clipboard.writeText(result).then().catch(e => console.error(e));
×
6052
        } else if (!this.clipboardOptions.enabled || this.crudService.cellInEditMode || event.type === 'keydown') {
12✔
6053
            return;
2✔
6054
        } else {
6055
            if (selectedColumns.length) {
10!
6056
                data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
×
6057
                    this.clipboardOptions.copyHeaders);
6058
                result = this.prepareCopyData(event, data[0], data[1]);
×
6059
            } else {
6060
                data = selectedData;
10✔
6061
                result = this.prepareCopyData(event, data);
10✔
6062
            }
6063
            event.clipboardData.setData('text/plain', result);
10✔
6064
        }
6065
    }
6066

6067
    /**
6068
     * @hidden @internal
6069
     */
6070
    public prepareCopyData(event, data, keys?) {
6071
        const ev = { data, cancel: false } as IGridClipboardEvent;
10✔
6072
        this.gridCopy.emit(ev);
10✔
6073

6074
        if (ev.cancel) {
10✔
6075
            return;
1✔
6076
        }
6077

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

6081
        if (!this.clipboardOptions.copyHeaders) {
9✔
6082
            result = result.substring(result.indexOf('\n') + 1);
2✔
6083
        }
6084

6085
        if (data && data.length > 0 && Object.values(data[0]).length === 1) {
9✔
6086
            result = result.slice(0, -2);
4✔
6087
        }
6088

6089
        event.preventDefault();
9✔
6090

6091
        /* Necessary for the hiearachical case but will probably have to
6092
           change how getSelectedData is propagated in the hiearachical grid
6093
        */
6094
        event.stopPropagation();
9✔
6095

6096
        return result;
9✔
6097
    }
6098

6099
    /**
6100
     * @hidden @internal
6101
     */
6102
    public showSnackbarFor(index: number) {
6103
        this.addRowSnackbar.actionText = index === -1 ? '' : this.resourceStrings.igx_grid_snackbar_addrow_actiontext;
29✔
6104
        this.lastAddedRowIndex = index;
29✔
6105
        this.addRowSnackbar.open();
29✔
6106
    }
6107

6108
    /* blazorCsSuppress */
6109
    /**
6110
     * Navigates to a position in the grid based on provided `rowindex` and `visibleColumnIndex`.
6111
     *
6112
     * @remarks
6113
     * Also can execute a custom logic over the target element,
6114
     * through a callback function that accepts { targetType: GridKeydownTargetType, target: Object }
6115
     * @example
6116
     * ```typescript
6117
     *  this.grid.navigateTo(10, 3, (args) => { args.target.nativeElement.focus(); });
6118
     * ```
6119
     */
6120
    public navigateTo(rowIndex: number, visibleColIndex = -1, cb: (args: any) => void = null) {
411✔
6121
        const totalItems = (this as any).totalItemCount ?? this.dataView.length - 1;
839✔
6122
        if (rowIndex < 0 || rowIndex > totalItems || (visibleColIndex !== -1
839!
6123
            && this._columns.map(col => col.visibleIndex).indexOf(visibleColIndex) === -1)) {
5,117✔
6124
            return;
×
6125
        }
6126
        if (this.dataView.slice(rowIndex, rowIndex + 1).find(rec => rec.expression || rec.childGridsData)) {
839✔
6127
            visibleColIndex = -1;
43✔
6128
        }
6129
        // If the target row is pinned no need to scroll as well.
6130
        const shouldScrollVertically = this.navigation.shouldPerformVerticalScroll(rowIndex, visibleColIndex);
839✔
6131
        const shouldScrollHorizontally = this.navigation.shouldPerformHorizontalScroll(visibleColIndex, rowIndex);
839✔
6132
        if (shouldScrollVertically) {
839✔
6133
            this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () => {
144✔
6134
                if (shouldScrollHorizontally) {
140✔
6135
                    this.navigation.performHorizontalScrollToCell(visibleColIndex, () =>
17✔
6136
                        this.executeCallback(rowIndex, visibleColIndex, cb));
17✔
6137
                } else {
6138
                    this.executeCallback(rowIndex, visibleColIndex, cb);
123✔
6139
                }
6140
            });
6141
        } else if (shouldScrollHorizontally) {
695✔
6142
            this.navigation.performHorizontalScrollToCell(visibleColIndex, () => {
95✔
6143
                if (shouldScrollVertically) {
95!
6144
                    this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () =>
×
6145
                        this.executeCallback(rowIndex, visibleColIndex, cb));
×
6146
                } else {
6147
                    this.executeCallback(rowIndex, visibleColIndex, cb);
95✔
6148
                }
6149
            });
6150
        } else {
6151
            this.executeCallback(rowIndex, visibleColIndex, cb);
600✔
6152
        }
6153
    }
6154

6155
    /* blazorCsSuppress */
6156
    /**
6157
     * Returns `ICellPosition` which defines the next cell,
6158
     * according to the current position, that match specific criteria.
6159
     *
6160
     * @remarks
6161
     * You can pass callback function as a third parameter of `getPreviousCell` method.
6162
     * The callback function accepts IgxColumnComponent as a param
6163
     * @example
6164
     * ```typescript
6165
     *  const nextEditableCellPosition = this.grid.getNextCell(0, 3, (column) => column.editable);
6166
     * ```
6167
     */
6168
    public getNextCell(currRowIndex: number, curVisibleColIndex: number,
6169
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
×
6170
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
477✔
6171
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
56✔
6172
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
56✔
6173
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
2✔
6174
        }
6175
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => a - b) :
460!
6176
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => a - b);
×
6177
        const nextCellIndex = colIndexes.find(index => index > curVisibleColIndex);
147✔
6178
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
54✔
6179
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && nextCellIndex !== undefined) {
54✔
6180
            return { rowIndex: currRowIndex, visibleColumnIndex: nextCellIndex };
39✔
6181
        } else {
6182
            const nextIndex = this.getNextDataRowIndex(currRowIndex)
15✔
6183
            if (colIndexes.length === 0 || nextIndex === currRowIndex) {
15!
6184
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
×
6185
            } else {
6186
                return { rowIndex: nextIndex, visibleColumnIndex: colIndexes[0] };
15✔
6187
            }
6188
        }
6189
    }
6190

6191
    /* blazorCsSuppress */
6192
    /**
6193
     * Returns `ICellPosition` which defines the previous cell,
6194
     * according to the current position, that match specific criteria.
6195
     *
6196
     * @remarks
6197
     * You can pass callback function as a third parameter of `getPreviousCell` method.
6198
     * The callback function accepts IgxColumnComponent as a param
6199
     * @example
6200
     * ```typescript
6201
     *  const previousEditableCellPosition = this.grid.getPreviousCell(0, 3, (column) => column.editable);
6202
     * ```
6203
     */
6204
    public getPreviousCell(currRowIndex: number, curVisibleColIndex: number,
6205
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
1✔
6206
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
300✔
6207
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
41✔
6208
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
41✔
6209
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
2✔
6210
        }
6211
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => b - a) :
238✔
6212
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => b - a);
4✔
6213
        const prevCellIndex = colIndexes.find(index => index < curVisibleColIndex);
141✔
6214
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
39✔
6215
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && prevCellIndex !== undefined) {
39✔
6216
            return { rowIndex: currRowIndex, visibleColumnIndex: prevCellIndex };
24✔
6217
        } else {
6218
            const prevIndex = this.getNextDataRowIndex(currRowIndex, true);
15✔
6219
            if (colIndexes.length === 0 || prevIndex === currRowIndex) {
15✔
6220
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
7✔
6221
            } else {
6222
                return { rowIndex: prevIndex, visibleColumnIndex: colIndexes[0] };
8✔
6223
            }
6224
        }
6225
    }
6226

6227
    /**
6228
     * @hidden
6229
     * @internal
6230
     */
6231
    public endRowEditTabStop(commit = true, event?: Event) {
×
6232
        const canceled = this.crudService.endEdit(commit, event);
15✔
6233

6234
        if (canceled) {
15✔
6235
            return true;
3✔
6236
        }
6237

6238
        this.navigation.restoreActiveNodeFocus();
12✔
6239
    }
6240

6241
    /**
6242
     * @hidden @internal
6243
     */
6244
    public trackColumnChanges(_index, col) {
6245
        return col.field + col._calcWidth.toString();
1,728,602✔
6246
    }
6247

6248
    /**
6249
     * @hidden
6250
     */
6251
    public isExpandedGroup(_group: IGroupByRecord): boolean {
6252
        return undefined;
×
6253
    }
6254

6255
    /**
6256
     * @hidden @internal
6257
     * TODO: MOVE to CRUD
6258
     */
6259
    public openRowOverlay(id) {
6260
        this.configureRowEditingOverlay(id, this.rowList.length <= MIN_ROW_EDITING_COUNT_THRESHOLD);
209✔
6261

6262
        this.rowEditingOverlay.open(this.rowEditSettings);
209✔
6263
        this.rowEditingOverlay.element.addEventListener('wheel', this.rowEditingWheelHandler);
209✔
6264
    }
6265

6266
    /**
6267
     * @hidden @internal
6268
     */
6269
    public closeRowEditingOverlay() {
6270
        this.rowEditingOverlay.element.removeEventListener('wheel', this.rowEditingWheelHandler);
130✔
6271
        this.rowEditPositioningStrategy.isTopInitialPosition = null;
130✔
6272
        this.rowEditingOverlay.close();
130✔
6273
        this.rowEditingOverlay.element.parentElement.style.display = '';
130✔
6274
    }
6275

6276
    /**
6277
     * @hidden @internal
6278
     */
6279
    public toggleRowEditingOverlay(show) {
6280
        const rowStyle = this.rowEditingOverlay.element.style;
280✔
6281
        if (show) {
280!
6282
            rowStyle.display = 'block';
280✔
6283
        } else {
6284
            rowStyle.display = 'none';
×
6285
        }
6286
    }
6287

6288
    /**
6289
     * @hidden @internal
6290
     */
6291
    public repositionRowEditingOverlay(row: RowType) {
6292
        if (row && !this.rowEditingOverlay.collapsed) {
895✔
6293
            const rowStyle = this.rowEditingOverlay.element.parentElement.style;
71✔
6294
            if (row) {
71!
6295
                rowStyle.display = '';
71✔
6296
                this.configureRowEditingOverlay(row.key);
71✔
6297
                this.rowEditingOverlay.reposition();
71✔
6298
            } else {
6299
                rowStyle.display = 'none';
×
6300
            }
6301
        }
6302
    }
6303

6304
    /**
6305
     * @hidden @internal
6306
     */
6307
    public cachedViewLoaded(args: ICachedViewLoadedEventArgs) {
6308
        if (this.hasHorizontalScroll()) {
629✔
6309
            const tmplId = args.context.templateID.type;
444✔
6310
            const index = args.context.index;
444✔
6311
            args.view.detectChanges();
444✔
6312
            this.zone.onStable.pipe(first()).subscribe(() => {
444✔
6313
                const row = tmplId === 'dataRow' ? this.gridAPI.get_row_by_index(index) : null;
444✔
6314
                const summaryRow = tmplId === 'summaryRow' ? this.summariesRowList.find((sr) => sr.dataRowIndex === index) : null;
444✔
6315
                if (row && row instanceof IgxRowDirective) {
444✔
6316
                    this._restoreVirtState(row);
267✔
6317
                } else if (summaryRow) {
177✔
6318
                    this._restoreVirtState(summaryRow);
43✔
6319
                }
6320
            });
6321
        }
6322
    }
6323

6324
    /**
6325
     * Opens the advanced filtering dialog.
6326
     */
6327
    public openAdvancedFilteringDialog(overlaySettings?: OverlaySettings) {
6328
        const settings = overlaySettings ? overlaySettings : this._advancedFilteringOverlaySettings;
58!
6329
        if (!this._advancedFilteringOverlayId) {
58✔
6330
            this._advancedFilteringOverlaySettings.target =
55✔
6331
                (this as any).rootGrid ? (this as any).rootGrid.nativeElement : this.nativeElement;
55✔
6332
            this._advancedFilteringOverlaySettings.outlet = this.outlet;
55✔
6333

6334
            this._advancedFilteringOverlayId = this.overlayService.attach(
55✔
6335
                IgxAdvancedFilteringDialogComponent,
6336
                this.viewRef,
6337
                settings);
6338
            this.overlayService.show(this._advancedFilteringOverlayId);
55✔
6339
        }
6340
    }
6341

6342
    /**
6343
     * Closes the advanced filtering dialog.
6344
     *
6345
     * @param applyChanges indicates whether the changes should be applied
6346
     */
6347
    public closeAdvancedFilteringDialog(applyChanges: boolean) {
6348
        if (this._advancedFilteringOverlayId) {
10✔
6349
            const advancedFilteringOverlay = this.overlayService.getOverlayById(this._advancedFilteringOverlayId);
10✔
6350
            const advancedFilteringDialog = advancedFilteringOverlay.componentRef.instance as IgxAdvancedFilteringDialogComponent;
10✔
6351

6352
            if (applyChanges) {
10✔
6353
                advancedFilteringDialog.applyChanges();
2✔
6354
            }
6355
            advancedFilteringDialog.closeDialog();
10✔
6356
        }
6357
    }
6358

6359
    /**
6360
     * @hidden @internal
6361
     */
6362
    public getEmptyRecordObjectFor(inRow: RowType) {
6363
        const row = { ...inRow?.data };
49✔
6364
        Object.keys(row).forEach(key => row[key] = undefined);
213✔
6365
        const id = this.generateRowID();
49✔
6366
        row[this.primaryKey] = id;
49✔
6367
        return { rowID: id, data: row, recordRef: row };
49✔
6368
    }
6369

6370
    /**
6371
     * @hidden @internal
6372
     */
6373
    public hasHorizontalScroll() {
6374
        return Math.round(this.totalWidth - this.unpinnedWidth) > 0 && this.width !== null;
11,453✔
6375
    }
6376

6377
    /**
6378
     * @hidden @internal
6379
     */
6380
    public isSummaryRow(rowData): boolean {
6381
        return rowData && rowData.summaries && (rowData.summaries instanceof Map);
442,797✔
6382
    }
6383

6384
    /**
6385
     * @hidden @internal
6386
     */
6387
    public triggerPipes() {
6388
        this.pipeTrigger++;
129✔
6389
        this.cdr.detectChanges();
129✔
6390
    }
6391

6392
    /**
6393
     * @hidden
6394
     */
6395
    public rowEditingWheelHandler = (event: WheelEvent) => {
4,157✔
6396
        if (event.deltaY > 0) {
×
6397
            this.verticalScrollContainer.scrollNext();
×
6398
        } else {
6399
            this.verticalScrollContainer.scrollPrev();
×
6400
        }
6401
    }
6402

6403
    /**
6404
     * @hidden
6405
     */
6406
    public getUnpinnedIndexById(id) {
6407
        return this.unpinnedRecords.findIndex(x => x[this.primaryKey] === id);
102✔
6408
    }
6409

6410
    /**
6411
     * Finishes the row transactions on the current row and returns whether the grid editing was canceled.
6412
     *
6413
     * @remarks
6414
     * If `commit === true`, passes them from the pending state to the data (or transaction service)
6415
     * @example
6416
     * ```html
6417
     * <button type="button" igxButton (click)="grid.endEdit(true)">Commit Row</button>
6418
     * ```
6419
     * @param commit
6420
     */
6421
    // TODO: Facade for crud service refactoring. To be removed
6422
    // TODO: do not remove this, as it is used in rowEditTemplate, but mark is as internal and hidden
6423
    /* blazorCSSuppress */
6424
    public endEdit(commit = true, event?: Event): boolean {
1✔
6425
        if (!this.crudService.cellInEditMode && !this.crudService.rowInEditMode) {
7✔
6426
            return;
1✔
6427
        }
6428
        const document = this.nativeElement?.getRootNode() as Document | ShadowRoot;
6✔
6429
        const focusWithin = this.nativeElement?.contains(document.activeElement);
6✔
6430

6431
        const success = this.crudService.endEdit(commit, event);
6✔
6432

6433
        if (focusWithin) {
6✔
6434
            // restore focus for navigation
6435
            this.navigation.restoreActiveNodeFocus();
2✔
6436
        } else if (this.navigation.activeNode) {
4✔
6437
            // grid already lost focus, clear active node
6438
            this.clearActiveNode();
4✔
6439
        }
6440

6441
        return success;
6✔
6442
    }
6443

6444
    /**
6445
     * Enters add mode by spawning the UI under the specified row by rowID.
6446
     *
6447
     * @remarks
6448
     * If null is passed as rowID, the row adding UI is spawned as the first record in the data view
6449
     * @remarks
6450
     * Spawning the UI to add a child for a record only works if you provide a rowID
6451
     * @example
6452
     * ```typescript
6453
     * this.grid.beginAddRowById('ALFKI');
6454
     * this.grid.beginAddRowById('ALFKI', true);
6455
     * this.grid.beginAddRowById(null);
6456
     * ```
6457
     * @param rowID - The rowID to spawn the add row UI for, or null to spawn it as the first record in the data view
6458
     * @param asChild - Whether the record should be added as a child. Only applicable to igxTreeGrid.
6459
     */
6460
    public beginAddRowById(rowID: any, asChild?: boolean): void {
6461
        let index = rowID;
4✔
6462
        if (rowID == null) {
4✔
6463
            if (asChild) {
3!
6464
                console.warn('The record cannot be added as a child to an unspecified record.');
×
6465
                return;
×
6466
            }
6467
            index = null;
3✔
6468
        } else {
6469
            // find the index of the record with that PK
6470
            index = this.gridAPI.get_rec_index_by_id(rowID, this.dataView);
1✔
6471
            if (index === -1) {
1!
6472
                console.warn('No row with the specified ID was found.');
×
6473
                return;
×
6474
            }
6475
        }
6476

6477
        this._addRowForIndex(index, asChild);
4✔
6478
    }
6479

6480
    protected _addRowForIndex(index: number, asChild?: boolean) {
6481
        if (!this.dataView.length) {
4!
6482
            this.beginAddRowForIndex(index, asChild);
×
6483
            return;
×
6484
        }
6485
        // check if the index is valid - won't support anything outside the data view
6486
        if (index >= 0 && index < this.dataView.length) {
4!
6487
            // check if the index is in the view port
6488
            if ((index < this.virtualizationState.startIndex ||
4✔
6489
                index >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) &&
6490
                !this.isRecordPinnedByViewIndex(index)) {
6491
                this.verticalScrollContainer.chunkLoad
1✔
6492
                    .pipe(first(), takeUntil(this.destroy$))
6493
                    .subscribe(() => {
6494
                        this.beginAddRowForIndex(index, asChild);
1✔
6495
                    });
6496
                this.navigateTo(index);
1✔
6497
                this.notifyChanges(true);
1✔
6498
                return;
1✔
6499
            }
6500
            this.beginAddRowForIndex(index, asChild);
3✔
6501
        } else {
6502
            console.warn('The row with the specified PK or index is outside of the current data view.');
×
6503
        }
6504
    }
6505

6506
    /* csSuppress */
6507
    /**
6508
     * Enters add mode by spawning the UI at the specified index.
6509
     *
6510
     * @remarks
6511
     * Accepted values for index are integers from 0 to this.grid.dataView.length
6512
     * @example
6513
     * ```typescript
6514
     * this.grid.beginAddRowByIndex(0);
6515
     * ```
6516
     * @param index - The index to spawn the UI at. Accepts integers from 0 to this.grid.dataView.length
6517
     */
6518
    public beginAddRowByIndex(index: number): void {
6519
        if (index === 0) {
1✔
6520
            return this.beginAddRowById(null);
1✔
6521
        }
6522
        return this._addRowForIndex(index - 1);
×
6523
    }
6524

6525
    /**
6526
     * @hidden
6527
     */
6528
    public preventHeaderScroll(args) {
6529
        if (args.target.scrollLeft !== 0) {
×
6530
            (this.navigation as any).forOfDir().getScroll().scrollLeft = args.target.scrollLeft;
×
6531
            args.target.scrollLeft = 0;
×
6532
        }
6533
    }
6534

6535
    protected beginAddRowForIndex(index: number, asChild = false) {
3✔
6536
        // TODO is row from rowList suitable for enterAddRowMode
6537
        const row = index == null ?
4✔
6538
            null : this.rowList.find(r => r.index === index);
2✔
6539
        if (row !== undefined) {
4!
6540
            this.crudService.enterAddRowMode(row, asChild);
4✔
6541
        } else {
6542
            console.warn('No row with the specified PK or index was found.');
×
6543
        }
6544
    }
6545

6546
    protected switchTransactionService(val: boolean) {
6547
        if (val) {
244✔
6548
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);
243✔
6549
        } else {
6550
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
1✔
6551
        }
6552

6553
        if (this.dataCloneStrategy) {
244✔
6554
            this._transactions.cloneStrategy = this.dataCloneStrategy;
244✔
6555
        }
6556
    }
6557

6558
    protected subscribeToTransactions(): void {
6559
        this.transactionChange$.next();
3,844✔
6560
        this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$)))
3,844✔
6561
            .subscribe(this.transactionStatusUpdate.bind(this));
6562
    }
6563

6564
    protected transactionStatusUpdate(event: StateUpdateEvent) {
6565
        let actions: Action<Transaction>[] = [];
324✔
6566
        if (event.origin === TransactionEventOrigin.REDO) {
324✔
6567
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.DELETE) : [];
44!
6568
        } else if (event.origin === TransactionEventOrigin.UNDO) {
285✔
6569
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.ADD) : [];
54!
6570
        }
6571
        if (actions.length > 0) {
324✔
6572
            for (const action of actions) {
22✔
6573
                if (this.selectionService.isRowSelected(action.transaction.id)) {
27!
6574
                    this.selectionService.deselectRow(action.transaction.id);
×
6575
                }
6576
            }
6577
        }
6578
        if (event.origin === TransactionEventOrigin.REDO || event.origin === TransactionEventOrigin.UNDO) {
324✔
6579
            event.actions.forEach(x => {
88✔
6580
                if (x.transaction.type === TransactionType.UPDATE) {
98✔
6581
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
43✔
6582
                    this.validation.update(x.transaction.id, value ?? x.recordRef);
43✔
6583
                } else if (x.transaction.type === TransactionType.DELETE || x.transaction.type === TransactionType.ADD) {
55✔
6584
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
55✔
6585
                    if (value) {
55✔
6586
                        this.validation.create(x.transaction.id, value ?? x.recordRef);
29!
6587
                        this.validation.update(x.transaction.id, value ?? x.recordRef);
29!
6588
                        this.validation.markAsTouched(x.transaction.id);
29✔
6589
                    } else {
6590
                        this.validation.clear(x.transaction.id);
26✔
6591
                    }
6592
                }
6593

6594
            });
6595
        }
6596

6597
        this.selectionService.clearHeaderCBState();
324✔
6598
        this.summaryService.clearSummaryCache();
324✔
6599
        this.pipeTrigger++;
324✔
6600
        this.notifyChanges();
324✔
6601
    }
6602

6603
    protected writeToData(rowIndex: number, value: any) {
6604
        mergeObjects(this.gridAPI.get_all_data()[rowIndex], value);
×
6605
    }
6606

6607
    protected _restoreVirtState(row) {
6608
        // check virtualization state of data record added from cache
6609
        // in case state is no longer valid - update it.
6610
        const rowForOf = row.virtDirRow;
310✔
6611
        const gridScrLeft = rowForOf.getScroll().scrollLeft;
310✔
6612
        rowForOf.onHScroll(gridScrLeft);
310✔
6613
        rowForOf.cdr.detectChanges();
310✔
6614
    }
6615

6616
    protected changeRowEditingOverlayStateOnScroll(row: RowType) {
6617
        if (!this.rowEditable || !this.rowEditingOverlay || this.rowEditingOverlay.collapsed) {
12✔
6618
            return;
12✔
6619
        }
6620
        if (!row) {
×
6621
            this.toggleRowEditingOverlay(false);
×
6622
        } else {
6623
            this.repositionRowEditingOverlay(row);
×
6624
        }
6625
    }
6626

6627
    /**
6628
     * Should be called when data and/or isLoading input changes so that the overlay can be
6629
     * hidden/shown based on the current value of shouldOverlayLoading
6630
     */
6631
    protected evaluateLoadingState() {
6632
        if (this.shouldOverlayLoading) {
3,287✔
6633
            // a new overlay should be shown
6634
            const overlaySettings: OverlaySettings = {
15✔
6635
                outlet: this.loadingOutlet,
6636
                closeOnOutsideClick: false,
6637
                positionStrategy: new ContainerPositionStrategy()
6638
            };
6639
            this.loadingOverlay.open(overlaySettings);
15✔
6640
        } else {
6641
            this.loadingOverlay.close();
3,272✔
6642
        }
6643
    }
6644

6645
    /**
6646
     * @hidden
6647
     * Sets grid width i.e. this.calcWidth
6648
     */
6649
    protected calculateGridWidth() {
6650
        let width;
6651

6652
        if (this.isPercentWidth) {
11,977✔
6653
            /* width in %*/
6654
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width');
5,506✔
6655
            width = computed.indexOf('%') === -1 ? parseFloat(computed) : null;
5,506✔
6656
        } else {
6657
            width = parseInt(this.width, 10);
6,471✔
6658
        }
6659

6660
        if (!width && this.nativeElement) {
11,977✔
6661
            width = this.nativeElement.offsetWidth;
32✔
6662
        }
6663

6664

6665
        if (this.width === null || !width) {
11,977✔
6666
            this.isColumnWidthSum = true;
32✔
6667
            width = this.getColumnWidthSum();
32✔
6668
        } else {
6669
            this.isColumnWidthSum = false;
11,945✔
6670
        }
6671

6672
        if (this.hasVerticalScroll() && this.width !== null) {
11,977✔
6673
            width -= this.scrollSize;
2,478✔
6674
        }
6675
        if ((Number.isFinite(width) || width === null) && width !== this.calcWidth) {
11,977!
6676
            this.calcWidth = width;
3,874✔
6677
        }
6678
        this._derivePossibleWidth();
11,977✔
6679
    }
6680

6681
    /**
6682
     * @hidden
6683
     * Sets columns defaultWidth property
6684
     */
6685
    protected _derivePossibleWidth() {
6686
        if (!this.columnWidthSetByUser) {
11,977✔
6687
            this._columnWidth = this.width !== null ? this.getPossibleColumnWidth() : this.minColumnWidth + 'px';
11,236✔
6688
        }
6689
        this._columns.forEach((column: IgxColumnComponent) => {
11,977✔
6690
            if (this.hasColumnLayouts && parseFloat(this._columnWidth)) {
76,171✔
6691
                const columnWidthCombined = parseFloat(this._columnWidth) * (column.colEnd ? column.colEnd - column.colStart : 1);
3,864✔
6692
                column.defaultWidth = columnWidthCombined + 'px';
3,864✔
6693
            } else {
6694
                // D.K. March 29th, 2021 #9145 Consider min/max width when setting defaultWidth property
6695
                column.defaultWidth = this.getExtremumBasedColWidth(column);
72,307✔
6696
                column.resetCaches();
72,307✔
6697
            }
6698
        });
6699
        this.resetCachedWidths();
11,977✔
6700
    }
6701

6702
    /**
6703
     * @hidden
6704
     * @internal
6705
     */
6706
    protected getExtremumBasedColWidth(column: IgxColumnComponent): string {
6707
        let width = this._columnWidth;
72,307✔
6708
        if (width && typeof width !== 'string') {
72,307!
6709
            width = String(width);
×
6710
        }
6711
        const minWidth = width.indexOf('%') === -1 ? column.userSetMinWidthPx : column.minWidthPercent;
72,307✔
6712
        const maxWidth = width.indexOf('%') === -1 ? column.maxWidthPx : column.maxWidthPercent;
72,307✔
6713
        if (column.hidden) {
72,307✔
6714
            return width;
1,349✔
6715
        }
6716

6717
        if (minWidth > parseFloat(width)) {
70,958✔
6718
            width = String(column.minWidth);
6✔
6719
        } else if (maxWidth < parseFloat(width)) {
70,952✔
6720
            width = String(column.maxWidth);
1,127✔
6721
        }
6722

6723
        // if no px or % are defined in maxWidth/minWidth consider it px
6724
        if (width.indexOf('%') === -1 && width.indexOf('px') === -1) {
70,958✔
6725
            width += 'px';
276✔
6726
        }
6727
        return width;
70,958✔
6728
    }
6729

6730
    protected resetNotifyChanges() {
6731
        this._cdrRequestRepaint = false;
8,336✔
6732
        this._cdrRequests = false;
8,336✔
6733
    }
6734

6735
    /** @hidden @internal */
6736
    public resolveOutlet() {
6737
        return this._userOutletDirective ? this._userOutletDirective : this._outletDirective;
242,699!
6738
    }
6739

6740
    /**
6741
     * Reorder columns in the main columnList and _columns collections.
6742
     *
6743
     * @hidden
6744
     */
6745
    protected _moveColumns(from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6746
        const orderedList = this._pinnedStartColumns.concat(this._unpinnedColumns, this._pinnedEndColumns);
144✔
6747
        const list = orderedList;
144✔
6748
        this._reorderColumns(from, to, pos, list);
144✔
6749
        const newList = this._resetColumnList(list);
144✔
6750
        this.updateColumns(newList);
144✔
6751
    }
6752

6753

6754
    /**
6755
     * Update internal column's collection.
6756
     * @hidden
6757
     */
6758
    public updateColumns(newColumns: IgxColumnComponent[]) {
6759
        // update internal collections to retain order.
6760
        this._pinnedColumns = newColumns
5,827✔
6761
            .filter((c) => c.pinned);
35,245✔
6762
        this._pinnedStartColumns = newColumns.filter((c) => c.pinned && c.pinningPosition === ColumnPinningPosition.Start);
35,245✔
6763
        this._pinnedEndColumns = newColumns.filter((c) => c.pinned && c.pinningPosition === ColumnPinningPosition.End);
35,245✔
6764
        this._unpinnedColumns = newColumns.filter((c) => !c.pinned);
35,245✔
6765
        this._columns = newColumns;
5,827✔
6766
        if (this._columns && this._columns.length && this._filteringExpressionsTree) {
5,827✔
6767
            this._filteringExpressionsTree = this.getRecreatedTree(this._filteringExpressionsTree);
5,385✔
6768
        }
6769
        if (this._columns && this._columns.length && this._advancedFilteringExpressionsTree) {
5,827✔
6770
            this._advancedFilteringExpressionsTree = this.getRecreatedTree(this._advancedFilteringExpressionsTree);
7✔
6771
        }
6772
        this.resetCaches();
5,827✔
6773
    }
6774

6775
    /**
6776
     * @hidden
6777
     */
6778
    protected _resetColumnList(list?) {
6779
        if (!list) {
144!
6780
            list = this._columns;
×
6781
        }
6782
        let newList = [];
144✔
6783
        list.filter(c => c.level === 0).forEach(p => {
1,255✔
6784
            newList.push(p);
880✔
6785
            if (p.columnGroup) {
880✔
6786
                newList = newList.concat(p.allChildren);
138✔
6787
            }
6788
        });
6789
        return newList;
144✔
6790
    }
6791

6792
    /**
6793
     * Reorders columns inside the passed column collection.
6794
     * When reordering column group collection, the collection is not flattened.
6795
     * In all other cases, the columns collection is flattened, this is why adittional calculations on the dropIndex are done.
6796
     *
6797
     * @hidden
6798
     */
6799
    protected _reorderColumns(from: IgxColumnComponent, to: IgxColumnComponent, position: DropPosition, columnCollection: any[],
6800
        inGroup = false) {
144✔
6801
        const fromIndex = columnCollection.indexOf(from);
160✔
6802
        const childColumnsCount = inGroup ? 1 : from.allChildren.length + 1;
160✔
6803
        columnCollection.splice(fromIndex, childColumnsCount);
160✔
6804
        let dropIndex = columnCollection.indexOf(to);
160✔
6805
        if (position === DropPosition.AfterDropTarget) {
160✔
6806
            dropIndex++;
90✔
6807
            if (!inGroup && to.columnGroup) {
90✔
6808
                dropIndex += to.allChildren.length;
21✔
6809
            }
6810
        }
6811
        columnCollection.splice(dropIndex, 0, from);
160✔
6812
    }
6813

6814
    /**
6815
     * Reorder column group collection.
6816
     *
6817
     * @hidden
6818
     */
6819
    protected _moveChildColumns(parent: IgxColumnComponent, from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6820
        const buffer = parent.children.toArray();
16✔
6821
        this._reorderColumns(from, to, pos, buffer, true);
16✔
6822
        parent.children.reset(buffer);
16✔
6823
    }
6824

6825
    /**
6826
     * @hidden @internal
6827
     */
6828
    protected setupColumns() {
6829
        if (this.autoGenerate) {
3,402✔
6830
            this.autogenerateColumns();
924✔
6831
        } else {
6832
            this._columns = this.getColumnList();
2,478✔
6833
        }
6834
        if (this._columns && this._columns.length && this._filteringExpressionsTree) {
3,402✔
6835
            this._filteringExpressionsTree = this.getRecreatedTree(this._filteringExpressionsTree);
3,345✔
6836
        }
6837
        if (this._columns && this._columns.length && this._advancedFilteringExpressionsTree) {
3,402✔
6838
            this._advancedFilteringExpressionsTree = this.getRecreatedTree(this._advancedFilteringExpressionsTree);
6✔
6839
        }
6840

6841
        this.initColumns(this._columns, (col: IgxColumnComponent) => this.columnInit.emit(col));
22,149✔
6842
        this.columnListDiffer.diff(this.columnList);
3,402✔
6843
        this._calculateRowCount();
3,402✔
6844

6845
        this.columnList.changes
3,402✔
6846
            .pipe(takeUntil(this.destroy$))
6847
            .subscribe((change: QueryList<IgxColumnComponent>) => {
6848
                this.onColumnsChanged(change);
72✔
6849
            });
6850
    }
6851

6852
    protected getColumnList() {
6853
        return this.columnList.toArray().filter((col) => col.grid === this);
15,643✔
6854
    }
6855

6856
    /**
6857
     * @hidden
6858
     */
6859
    protected deleteRowFromData(rowID: any, index: number) {
6860
        //  if there is a row (index !== 0) delete it
6861
        //  if there is a row in ADD or UPDATE state change it's state to DELETE
6862
        if (index !== -1) {
×
6863
            if (this.transactions.enabled) {
×
6864
                const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null };
×
6865
                this.transactions.add(transaction, this.data[index]);
×
6866
            } else {
6867
                this.data.splice(index, 1);
×
6868
            }
6869
        } else {
6870
            const state: State = this.transactions.getState(rowID);
×
6871
            this.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef);
×
6872
        }
6873
    }
6874

6875

6876
    /**
6877
     * @hidden @internal
6878
     */
6879
    protected getDataBasedBodyHeight(): number {
6880
        return !this.data || (this.data.length < this._defaultTargetRecordNumber) ?
1,070✔
6881
            0 : this.defaultTargetBodyHeight;
6882
    }
6883

6884
    /**
6885
     * @hidden @internal
6886
     */
6887
    protected onPinnedRowsChanged(change: QueryList<IgxGridRowComponent>) {
6888
        const diff = this.rowListDiffer.diff(change);
182✔
6889
        if (diff) {
182✔
6890
            this.notifyChanges(true);
182✔
6891
        }
6892
    }
6893

6894
    /**
6895
     * @hidden
6896
     */
6897
    protected onColumnsChanged(change: QueryList<IgxColumnComponent>) {
6898
        const diff = this.columnListDiffer.diff(change);
61✔
6899

6900
        if (this.autoGenerate && this._columns.length === 0 && this._autoGeneratedCols.length > 0) {
61!
6901
            // In Ivy if there are nested conditional templates the content children are re-evaluated
6902
            // hence autogenerated columns are cleared and need to be reset.
6903
            this.updateColumns(this._autoGeneratedCols);
×
6904
            return;
×
6905
        }
6906
        if (diff) {
61✔
6907
            let added = false;
61✔
6908
            let removed = false;
61✔
6909
            let pinning = false;
61✔
6910
            diff.forEachAddedItem((record: IterableChangeRecord<IgxColumnComponent>) => {
61✔
6911
                if (record.item.grid !== this) {
924!
6912
                    return;
×
6913
                }
6914
                added = true;
924✔
6915
                if (record.item.pinned) {
924✔
6916
                    this._pinnedColumns.push(record.item);
1✔
6917
                    if (record.item.pinningPosition === ColumnPinningPosition.Start) {
1!
6918
                        this._pinnedStartColumns.push(record.item);
1✔
6919
                    } else {
6920
                        this._pinnedEndColumns.push(record.item);
×
6921
                    }
6922
                    pinning = true;
1✔
6923
                } else {
6924
                    this._unpinnedColumns.push(record.item);
923✔
6925
                }
6926
            });
6927

6928
            this.initColumns(this.getColumnList(), (col: IgxColumnComponent) => this.columnInit.emit(col));
1,174✔
6929
            if (pinning) {
61✔
6930
                this.initPinning();
1✔
6931
            }
6932

6933
            diff.forEachRemovedItem((record: IterableChangeRecord<IgxColumnComponent | IgxColumnGroupComponent>) => {
61✔
6934
                if (record.item.grid !== this) {
118!
6935
                    return;
×
6936
                }
6937
                const isColumnGroup = record.item instanceof IgxColumnGroupComponent;
118✔
6938
                if (!isColumnGroup) {
118✔
6939
                    // Clear Grouping
6940
                    this.gridAPI.clear_groupby(record.item.field);
115✔
6941

6942
                    // Clear Filtering
6943
                    this.filteringService.clear_filter(record.item.field);
115✔
6944

6945
                    // Close filter row
6946
                    if (this.filteringService.isFilterRowVisible
115!
6947
                        && this.filteringService.filteredColumn
6948
                        && this.filteringService.filteredColumn.field === record.item.field) {
6949
                        this.filteringRow.close();
×
6950
                    }
6951

6952
                    // Clear Sorting
6953
                    this.gridAPI.clear_sort(record.item.field);
115✔
6954

6955
                    // Remove column selection
6956
                    this.selectionService.deselectColumnsWithNoEvent([record.item.field]);
115✔
6957
                }
6958
                removed = true;
118✔
6959
            });
6960

6961
            this.resetCaches();
61✔
6962

6963
            if (added || removed) {
61✔
6964
                this.onColumnsAddedOrRemoved();
60✔
6965
            }
6966
        }
6967
    }
6968

6969
    protected checkPrimaryKeyField() {
6970
        if (this.primaryKey && this.data?.length && !(this.primaryKey in this.data[0])) {
4,712✔
6971
            console.warn(`Field "${this.primaryKey}" is not defined in the data. Set \`primaryKey\` to a valid field.`);
9✔
6972
        }
6973
    }
6974

6975
    /**
6976
     * @hidden @internal
6977
     */
6978
    protected onColumnsAddedOrRemoved() {
6979
        this.summaryService.clearSummaryCache();
60✔
6980
        Promise.resolve().then(() => {
60✔
6981
            // `onColumnsChanged` can be executed midway a current detectChange cycle and markForCheck will be ignored then.
6982
            // This ensures that we will wait for the current cycle to end so we can trigger a new one and ngDoCheck to fire.
6983
            this.notifyChanges(true);
60✔
6984
        });
6985
    }
6986

6987
    /**
6988
     * @hidden
6989
     */
6990
    protected calculateGridSizes(recalcFeatureWidth = true) {
7,520✔
6991
        /*
6992
            TODO: (R.K.) This layered lasagne should be refactored
6993
            ASAP. The reason I have to reset the caches so many times is because
6994
            after teach `detectChanges` call they are filled with invalid
6995
            state. Of course all of this happens midway through the grid
6996
            sizing process which of course, uses values from the caches, thus resulting
6997
            in a broken layout.
6998
        */
6999
        this.cdr.detectChanges();
8,028✔
7000
        this.resetCaches(recalcFeatureWidth);
8,028✔
7001
        const hasScroll = this.hasVerticalScroll();
8,028✔
7002
        const hasHScroll = !this.isHorizontalScrollHidden;
8,028✔
7003
        this.calculateGridWidth();
8,028✔
7004
        this.resetCaches(recalcFeatureWidth);
8,028✔
7005
        this.cdr.detectChanges();
8,028✔
7006
        this.calculateGridHeight();
8,028✔
7007

7008
        if (this.rowEditable) {
8,028✔
7009
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
888✔
7010
        }
7011

7012
        if (this.filteringService.isFilterRowVisible) {
8,028✔
7013
            this.filteringRow.resetChipsArea();
153✔
7014
        }
7015

7016
        this.cdr.detectChanges();
8,028✔
7017
        // in case scrollbar has appeared recalc to size correctly.
7018
        if (hasScroll !== this.hasVerticalScroll()) {
8,028✔
7019
            this.calculateGridWidth();
168✔
7020
            this.cdr.detectChanges();
168✔
7021
        }
7022

7023
        // in case horizontal scrollbar has appeared recalc to size correctly.
7024
        if (hasHScroll !== this.hasHorizontalScroll()) {
8,028✔
7025
            this.isHorizontalScrollHidden = !this.hasHorizontalScroll();
2,787✔
7026
            this.cdr.detectChanges();
2,787✔
7027
            this.calculateGridHeight();
2,787✔
7028
            this.cdr.detectChanges();
2,787✔
7029
        }
7030
        if (this.zone.isStable) {
8,028✔
7031
            this.zone.run(() => {
211✔
7032
                this._applyWidthHostBinding();
211✔
7033
                this.cdr.detectChanges();
211✔
7034
            });
7035
        } else {
7036
            this.zone.onStable.pipe(first()).subscribe(() => {
7,817✔
7037
                this.zone.run(() => {
7,817✔
7038
                    this._applyWidthHostBinding();
7,817✔
7039
                });
7040
            });
7041
        }
7042
        this.resetCaches(recalcFeatureWidth);
8,028✔
7043
        if (this.hasColumnsToAutosize) {
8,028✔
7044
            this.cdr.detectChanges();
20✔
7045
            this.zone.onStable.pipe(first()).subscribe(() => {
20✔
7046
                this._autoSizeColumnsNotify.next();
20✔
7047
            });
7048
        }
7049
    }
7050

7051
    /**
7052
     * @hidden
7053
     * Sets TBODY height i.e. this.calcHeight
7054
     */
7055
    protected calculateGridHeight() {
7056

7057
        this.calcHeight = this._calculateGridBodyHeight();
10,563✔
7058
        if (this.pinnedRowHeight && this.calcHeight) {
10,563✔
7059
            this.calcHeight -= this.pinnedRowHeight;
107✔
7060
        }
7061
    }
7062

7063
    /**
7064
     * @hidden
7065
     */
7066
    protected getGroupAreaHeight(): number {
7067
        return 0;
2,851✔
7068
    }
7069

7070
    /**
7071
     * @hidden
7072
     */
7073
    protected getComputedHeight(elem) {
7074
        return elem.offsetHeight ? parseFloat(this.document.defaultView.getComputedStyle(elem).getPropertyValue('height')) : 0;
37,578✔
7075
    }
7076
    /**
7077
     * @hidden
7078
     */
7079
    protected getFooterHeight(): number {
7080
        return this.summaryRowHeight || this.getComputedHeight(this.tfoot.nativeElement);
9,760✔
7081
    }
7082
    /**
7083
     * @hidden
7084
     */
7085
    protected getTheadRowHeight(): number {
7086
        // D.P.: Before CSS loads,theadRow computed height will be 'auto'->NaN, so use 0 fallback
7087
        const height = this.getComputedHeight(this.theadRow.nativeElement) || 0;
9,760✔
7088
        return (!this.allowFiltering || (this.allowFiltering && this.filterMode !== FilterMode.quickFilter)) ?
9,760✔
7089
            height - this.getFilterCellHeight() :
7090
            height;
7091
    }
7092

7093
    /**
7094
     * @hidden
7095
     */
7096
    protected getToolbarHeight(): number {
7097
        let toolbarHeight = 0;
9,760✔
7098
        if (this.toolbar.first) {
9,760✔
7099
            toolbarHeight = this.getComputedHeight(this.toolbar.first.nativeElement);
271✔
7100
        }
7101
        return toolbarHeight;
9,760✔
7102
    }
7103

7104
    /**
7105
     * @hidden
7106
     */
7107
    protected getPagingFooterHeight(): number {
7108
        let pagingHeight = 0;
9,760✔
7109
        if (this.footer) {
9,760✔
7110
            const height = this.getComputedHeight(this.footer.nativeElement);
9,760✔
7111
            pagingHeight = this.footer.nativeElement.firstElementChild ?
9,760✔
7112
                height : 0;
7113
        }
7114
        return pagingHeight;
9,760✔
7115
    }
7116

7117
    /**
7118
     * @hidden
7119
     */
7120
    protected getFilterCellHeight(): number {
7121
        const headerGroupNativeEl = (this.headerGroupsList.length !== 0) ?
8,060✔
7122
            this.headerGroupsList[0].nativeElement : null;
7123
        const filterCellNativeEl = (headerGroupNativeEl) ?
8,060✔
7124
            headerGroupNativeEl.querySelector('igx-grid-filtering-cell') as HTMLElement : null;
7125
        return (filterCellNativeEl) ? filterCellNativeEl.offsetHeight : 0;
8,060✔
7126
    }
7127

7128
    /**
7129
     * @hidden
7130
     */
7131
    protected _calculateGridBodyHeight(): number {
7132
        if (!this._height) {
10,563✔
7133
            return null;
803✔
7134
        }
7135
        const actualTheadRow = this.getTheadRowHeight();
9,760✔
7136
        const footerHeight = this.getFooterHeight();
9,760✔
7137
        const toolbarHeight = this.getToolbarHeight();
9,760✔
7138
        const pagingHeight = this.getPagingFooterHeight();
9,760✔
7139
        const groupAreaHeight = this.getGroupAreaHeight();
9,760✔
7140
        const scrHeight = this.getComputedHeight(this.scr.nativeElement);
9,760✔
7141
        const renderedHeight = toolbarHeight + actualTheadRow +
9,760✔
7142
            footerHeight + pagingHeight + groupAreaHeight +
7143
            scrHeight;
7144

7145
        let gridHeight = 0;
9,760✔
7146

7147
        if (this.isPercentHeight) {
9,760✔
7148
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('height');
2,106✔
7149
            const autoSize = this._shouldAutoSize(renderedHeight);
2,106✔
7150
            if (autoSize || computed.indexOf('%') !== -1) {
2,106✔
7151
                const bodyHeight = this.getDataBasedBodyHeight();
870✔
7152
                return bodyHeight > 0 ? bodyHeight : null;
870✔
7153
            }
7154
            gridHeight = parseFloat(computed);
1,236✔
7155
        } else {
7156
            gridHeight = parseInt(this._height, 10);
7,654✔
7157
        }
7158
        const height = Math.abs(gridHeight - renderedHeight);
8,890✔
7159

7160
        if (Math.round(height) === 0 || isNaN(gridHeight)) {
8,890✔
7161
            const bodyHeight = this.defaultTargetBodyHeight;
18✔
7162
            return bodyHeight > 0 ? bodyHeight : null;
18✔
7163
        }
7164
        return height;
8,872✔
7165
    }
7166

7167
    protected checkContainerSizeChange() {
7168
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
99!
7169
        const origHeight = parentElement.offsetHeight;
99✔
7170
        this.nativeElement.style.display = 'none';
99✔
7171
        const height = parentElement.offsetHeight;
99✔
7172
        this.nativeElement.style.display = '';
99✔
7173
        return origHeight !== height;
99✔
7174
    }
7175

7176
    protected _shouldAutoSize(renderedHeight) {
7177
        this.tbody.nativeElement.style.display = 'none';
1,352✔
7178
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
1,352✔
7179
        let res = !parentElement ||
1,352✔
7180
            parentElement.clientHeight === 0 ||
7181
            parentElement.clientHeight === renderedHeight;
7182
        if (parentElement && (res || this._autoSize)) {
1,352✔
7183
            // If grid causes the parent container to extend (for example when container is flex)
7184
            // we should always auto-size since the actual size of the container will continuously change as the grid renders elements.
7185
            this._autoSize = false;
99✔
7186
            res = this.checkContainerSizeChange();
99✔
7187
        }
7188
        this.tbody.nativeElement.style.display = '';
1,352✔
7189
        return res;
1,352✔
7190
    }
7191

7192
    /**
7193
     * @hidden
7194
     * Gets calculated width of the unpinned area
7195
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
7196
     */
7197
    protected getUnpinnedWidth(takeHidden = false) {
23,806✔
7198
        let width = this.isPercentWidth ?
23,806✔
7199
            this.calcWidth :
7200
            parseInt(this.width, 10) || parseInt(this.hostWidth, 10) || this.calcWidth;
13,350✔
7201
        if (this.hasVerticalScroll() && !this.isPercentWidth) {
23,806✔
7202
            width -= this.scrollSize;
4,385✔
7203
        }
7204

7205
        return width - (this.getPinnedStartWidth(takeHidden) + this.getPinnedEndWidth(takeHidden));
23,806✔
7206
    }
7207

7208
    /**
7209
     * @hidden
7210
     */
7211
    protected _summaries(fieldName: string, hasSummary: boolean, summaryOperand?: any) {
7212
        const column = this.gridAPI.get_column_by_name(fieldName);
27✔
7213
        if (column) {
27✔
7214
            column.hasSummary = hasSummary;
27✔
7215
            if (summaryOperand) {
27✔
7216
                if (this.rootSummariesEnabled) {
2✔
7217
                    this.summaryService.retriggerRootPipe++;
2✔
7218
                }
7219
                column.summaries = summaryOperand;
2✔
7220
            }
7221
        }
7222
    }
7223

7224
    /**
7225
     * @hidden
7226
     */
7227
    protected _multipleSummaries(expressions: ISummaryExpression[], hasSummary: boolean) {
7228
        expressions.forEach((element) => {
7✔
7229
            this._summaries(element.fieldName, hasSummary, element.customSummary);
9✔
7230
        });
7231
    }
7232
    /**
7233
     * @hidden
7234
     */
7235
    protected _disableMultipleSummaries(expressions) {
7236
        expressions.forEach((column) => {
5✔
7237
            const columnName = column && column.fieldName ? column.fieldName : column;
13✔
7238
            this._summaries(columnName, false);
13✔
7239
        });
7240
    }
7241

7242
    /**
7243
     * @hidden
7244
     */
7245
    public resolveDataTypes(rec) {
7246
        if (typeof rec === 'number') {
6,652✔
7247
            return GridColumnDataType.Number;
3,899✔
7248
        } else if (typeof rec === 'boolean') {
2,753✔
7249
            return GridColumnDataType.Boolean;
153✔
7250
        } else if (typeof rec === 'object' && rec instanceof Date) {
2,600✔
7251
            return GridColumnDataType.Date;
144✔
7252
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
2,456✔
7253
            return GridColumnDataType.Image;
1✔
7254
        }
7255
        return GridColumnDataType.String;
2,455✔
7256
    }
7257

7258
    /**
7259
     * @hidden
7260
     */
7261
    protected autogenerateColumns() {
7262
        const data = this.gridAPI.get_data();
674✔
7263
        const fields = this.generateDataFields(data);
674✔
7264
        const columns = [];
674✔
7265

7266
        this._autoGeneratedColsRefs.forEach(ref => ref.destroy());
674✔
7267
        this._autoGeneratedColsRefs = [];
674✔
7268
        fields.forEach((field) => {
674✔
7269
            const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
4,374✔
7270
            ref.instance.field = field;
4,374✔
7271
            ref.instance.dataType = this.resolveDataTypes(data[0][field]);
4,374✔
7272
            ref.changeDetectorRef.detectChanges();
4,374✔
7273
            this._autoGeneratedColsRefs.push(ref);
4,374✔
7274
            columns.push(ref.instance);
4,374✔
7275
        });
7276
        this._autoGeneratedCols = columns;
674✔
7277

7278
        this.updateColumns(columns);
674✔
7279
        this.columnsAutogenerated.emit({ columns: this._autoGeneratedCols });
674✔
7280
    }
7281

7282
    protected generateDataFields(data: any[]): string[] {
7283
        return Object.keys(data && data.length !== 0 ? data[0] : [])
680✔
7284
            .filter(key => !this.autoGenerateExclude.includes(key));
4,652✔
7285
    }
7286

7287
    /**
7288
     * @hidden
7289
     */
7290
    protected initColumns(collection: IgxColumnComponent[], cb: (args: any) => void = null) {
×
7291
        this._columnGroups = collection.some(col => col.columnGroup);
17,674✔
7292
        if (this.hasColumnLayouts) {
3,677✔
7293
            // Set overall row layout size
7294
            collection.forEach((col) => {
143✔
7295
                if (col.columnLayout) {
1,384✔
7296
                    const layoutSize = col.children ?
288!
7297
                        col.children.reduce((acc, val) => Math.max(val.rowStart + val.gridRowSpan - 1, acc), 1) :
1,086✔
7298
                        1;
7299
                    this._multiRowLayoutRowSize = Math.max(layoutSize, this._multiRowLayoutRowSize);
288✔
7300
                }
7301
            });
7302
        }
7303
        if (this.hasColumnLayouts && this.hasColumnGroups) {
3,677✔
7304
            // invalid configuration - multi-row and column groups
7305
            // remove column groups
7306
            const columnLayoutColumns = collection.filter((col) => col.columnLayout || col.columnLayoutChild);
1,384✔
7307
            collection = columnLayoutColumns;
143✔
7308
        }
7309
        this._maxLevelHeaderDepth = null;
3,677✔
7310
        collection.forEach((column: IgxColumnComponent) => {
3,677✔
7311
            column.defaultWidth = this.columnWidthSetByUser ? this._columnWidth : column.defaultWidth ? column.defaultWidth : '';
24,033✔
7312

7313
            if (cb) {
24,033✔
7314
                cb(column);
24,033✔
7315
            }
7316
        });
7317

7318
        this.updateColumns(collection);
3,677✔
7319

7320
        if (this.hasColumnLayouts) {
3,677✔
7321
            collection.forEach((column: IgxColumnComponent) => {
143✔
7322
                column.populateVisibleIndexes();
1,374✔
7323
            });
7324
        }
7325
    }
7326

7327
    /**
7328
     * @hidden
7329
     */
7330
    protected reinitPinStates() {
7331
        this._pinnedColumns = this._columns
210✔
7332
            .filter((c) => c.pinned).sort((a, b) => this._pinnedColumns.indexOf(a) - this._pinnedColumns.indexOf(b));
2,628✔
7333
        this._pinnedStartColumns = this._columns.filter((c) => c.pinned && c.pinningPosition === ColumnPinningPosition.Start)
2,628✔
7334
            .sort((a, b) => this._pinnedStartColumns.indexOf(a) - this._pinnedStartColumns.indexOf(b));
477✔
7335
        this._pinnedEndColumns = this._columns.filter((c) => c.pinned && c.pinningPosition === ColumnPinningPosition.End)
2,628✔
7336
            .sort((a, b) => this._pinnedEndColumns.indexOf(a) - this._pinnedEndColumns.indexOf(b));
7✔
7337
        this._unpinnedColumns = this.hasColumnGroups ? this._columns.filter((c) => !c.pinned) :
2,321✔
7338
            this._columns.filter((c) => !c.pinned)
307✔
7339
                .sort((a, b) => this._unpinnedColumns.indexOf(a) - this._unpinnedColumns.indexOf(b));
227✔
7340
    }
7341

7342
    protected extractDataFromSelection(source: any[], formatters = false, headers = false, columnData?: any[]): any[] {
×
7343
        let columnsArray: IgxColumnComponent[];
7344
        let record = {};
246✔
7345
        let selectedData = [];
246✔
7346
        let keys = [];
246✔
7347
        const selectionCollection = new Map();
246✔
7348
        const keysAndData = [];
246✔
7349
        const activeEl = this.selectionService.activeElement;
246✔
7350

7351
        if (this.type === 'hierarchical') {
246✔
7352
            const expansionRowIndexes = [];
2✔
7353
            for (const [key, value] of this.expansionStates.entries()) {
2✔
7354
                if (value) {
×
7355
                    const rowIndex = this.gridAPI.get_rec_index_by_id(key, this.dataView);
×
7356
                    expansionRowIndexes.push(rowIndex);
×
7357
                }
7358
            }
7359
            if (this.selectionService.selection.size > 0) {
2!
7360
                if (expansionRowIndexes.length > 0) {
2!
7361
                    for (const [key, value] of this.selectionService.selection.entries()) {
×
7362
                        const updatedKey = key;
×
7363
                        let subtract = 0;
×
7364
                        expansionRowIndexes.forEach((row) => {
×
7365
                            if (updatedKey > Number(row)) {
×
7366
                                subtract++;
×
7367
                            }
7368
                        });
7369
                        selectionCollection.set(updatedKey - subtract, value);
×
7370
                    }
7371
                }
7372
            } else if (activeEl) {
×
7373
                let subtract = 0;
×
7374
                if (expansionRowIndexes.length > 0) {
×
7375
                    expansionRowIndexes.forEach(row => {
×
7376
                        if (activeEl.row > Number(row)) {
×
7377
                            subtract++;
×
7378
                        }
7379
                    });
7380
                    activeEl.row -= subtract;
×
7381
                }
7382
            }
7383
        }
7384

7385
        const totalItems = (this as any).totalItemCount ?? 0;
246✔
7386
        const isRemote = totalItems && totalItems > this.dataView.length;
246!
7387
        let selectionMap;
7388
        if (this.type === 'hierarchical' && selectionCollection.size > 0) {
246!
7389
            selectionMap = isRemote ? Array.from(selectionCollection) :
×
7390
                Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length);
×
7391
        } else {
7392
            selectionMap = isRemote ? Array.from(this.selectionService.selection) :
246!
7393
                Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length);
883✔
7394
        }
7395

7396
        if (this.cellSelection === GridSelectionMode.single && activeEl) {
246✔
7397
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
10✔
7398
        }
7399

7400
        if (this.cellSelection === GridSelectionMode.none && activeEl) {
246✔
7401
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
5✔
7402
        }
7403

7404
        if (columnData) {
246!
7405
            selectedData = columnData;
×
7406
        }
7407

7408
        // eslint-disable-next-line prefer-const
7409
        for (let [row, set] of selectionMap) {
246✔
7410
            row = this.paginator && (this.pagingMode === 'local' && source === this.filteredSortedData) ? row + (this.perPage * this.page) : row;
788✔
7411
            row = isRemote ? row - this.virtualizationState.startIndex : row;
788!
7412
            if (!source[row] || source[row].detailsData !== undefined) {
788✔
7413
                continue;
40✔
7414
            }
7415
            const temp = Array.from(set);
748✔
7416
            for (const each of temp) {
748✔
7417
                columnsArray = this.getSelectableColumnsAt(each);
2,512✔
7418
                columnsArray.forEach((col) => {
2,512✔
7419
                    if (col) {
2,518✔
7420
                        const key = this.type !== 'pivot' && headers ? col.header || col.field : col.field;
2,316!
7421
                        const rowData = source[row].ghostRecord ? source[row].recordRef : source[row];
2,316!
7422
                        const value = this.type === 'pivot' ? rowData.aggregationValues.get(col.field)
2,316!
7423
                            : resolveNestedPath(rowData, columnFieldPath(col.field));
7424
                        record[key] = formatters && col.formatter ? col.formatter(value, rowData) : value;
2,316✔
7425
                        if (columnData) {
2,316!
7426
                            if (!record[key]) {
×
7427
                                record[key] = '';
×
7428
                            }
7429
                            record[key] = record[key].toString().concat('recordRow-' + row);
×
7430
                        }
7431
                    }
7432
                });
7433
            }
7434
            if (Object.keys(record).length) {
748✔
7435
                if (columnData) {
746!
7436
                    if (!keys.length) {
×
7437
                        keys = Object.keys(columnData[0]);
×
7438
                    }
7439
                    for (const [key, value] of Object.entries(record)) {
×
7440
                        if (!keys.includes(key)) {
×
7441
                            keys.push(key);
×
7442
                        }
7443
                        let c: any = value;
×
7444
                        const rowNumber = +c.split('recordRow-')[1];
×
7445
                        c = c.split('recordRow-')[0];
×
7446
                        record[key] = c;
×
7447
                        const mergedObj = Object.assign(selectedData[rowNumber], record);
×
7448
                        selectedData[rowNumber] = mergedObj;
×
7449
                    }
7450
                } else {
7451
                    selectedData.push(record);
746✔
7452
                }
7453
            }
7454
            record = {};
748✔
7455
        }
7456

7457
        if (keys.length) {
246!
7458
            keysAndData.push(selectedData);
×
7459
            keysAndData.push(keys);
×
7460
            return keysAndData;
×
7461
        } else {
7462
            return selectedData;
246✔
7463
        }
7464
    }
7465

7466
    protected getSelectableColumnsAt(index) {
7467
        if (this.hasColumnLayouts) {
2,512✔
7468
            const visibleLayoutColumns = this.visibleColumns
2✔
7469
                .filter(col => col.columnLayout)
20✔
7470
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
2✔
7471
            const colLayout = visibleLayoutColumns[index];
2✔
7472
            return colLayout ? colLayout.children.toArray() : [];
2!
7473
        } else {
7474
            const visibleColumns = this.visibleColumns
2,510✔
7475
                .filter(col => !col.columnGroup)
14,120✔
7476
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
11,790✔
7477
            return [visibleColumns[index]];
2,510✔
7478
        }
7479
    }
7480

7481
    protected autoSizeColumnsInView() {
7482
        if (!this.hasColumnsToAutosize) return;
375✔
7483
        const vState = this.headerContainer.state;
29✔
7484
        let colResized = false;
29✔
7485
        const unpinnedInView = this.headerContainer.igxGridForOf.slice(vState.startIndex, vState.startIndex + vState.chunkSize).flatMap(x => x.columnGroup ? x.allChildren : x);
117✔
7486
        const columnsInView = this.pinnedColumns.concat(unpinnedInView as IgxColumnComponent[]);
29✔
7487
        for (const col of columnsInView) {
29✔
7488
            if (!col.autoSize && col.headerCell) {
120✔
7489
                const cellsContentWidths = [];
84✔
7490
                if (col._cells.length !== this.rowList.length) {
84!
7491
                    this.rowList.forEach(x => x.cdr.detectChanges());
×
7492
                }
7493
                const cells = this._dataRowList.map(x => x.cells.find(c => c.column === col));
2,397✔
7494
                cells.forEach((cell) => cellsContentWidths.push(cell?.nativeElement?.offsetWidth || 0));
606!
7495
                let maxForCells = Math.max(...cellsContentWidths);
84✔
7496
                const header = this.headerCellList.find(x => x.column === col);
316✔
7497
                cellsContentWidths.push(header.nativeElement.offsetWidth);
84✔
7498
                const max = Math.max(...cellsContentWidths);
84✔
7499
                // in cases with template contains something, like a webcomponent,
7500
                // that renders fully only after it is already injected in the DOM,
7501
                // and initially renders as empty, skip measuring it.
7502
                let emptyCellWithPaddingOnly = 0;
84✔
7503
                if (cells.length > 0 && !!col.bodyTemplate) {
84✔
7504
                    const cellStyle = this.document.defaultView.getComputedStyle(cells[0].nativeElement);
1✔
7505
                    emptyCellWithPaddingOnly = parseFloat(cellStyle.paddingLeft) + parseFloat(cellStyle.paddingRight);
1✔
7506
                } else {
7507
                    maxForCells = max;
83✔
7508
                }
7509

7510
                if (max === 0 || (maxForCells <= emptyCellWithPaddingOnly && this._firstAutoResize)) {
84!
7511
                    // cells not in DOM yet or content not fully initialized.
7512
                    continue;
6✔
7513
                }
7514
                let maxSize = Math.ceil(Math.max(...cellsContentWidths)) + 1;
78✔
7515
                if (col.maxWidth && maxSize > col.maxWidthPx) {
78✔
7516
                    maxSize = col.maxWidthPx;
3✔
7517
                } else if (maxSize < col.userSetMinWidthPx) {
75✔
7518
                    maxSize = col.userSetMinWidthPx;
1✔
7519
                }
7520
                col.autoSize = maxSize;
78✔
7521
                col.resetCaches();
78✔
7522
                colResized = true;
78✔
7523
            }
7524
        }
7525
        if (colResized) {
29✔
7526
            this.resetCachedWidths();
20✔
7527
            this.cdr.detectChanges();
20✔
7528
        }
7529

7530
        if (this.isColumnWidthSum) {
29✔
7531
            this.calcWidth = this.getColumnWidthSum();
2✔
7532
        }
7533
    }
7534

7535
    protected extractDataFromColumnsSelection(source: any[], formatters = false, headers = false): any[] {
×
7536
        let record = {};
20✔
7537
        const selectedData = [];
20✔
7538
        const selectedColumns = this.selectedColumns();
20✔
7539
        if (selectedColumns.length === 0) {
20✔
7540
            return [];
12✔
7541
        }
7542

7543
        for (const data of source) {
8✔
7544
            selectedColumns.forEach((col) => {
71✔
7545
                const key = headers ? col.header || col.field : col.field;
142!
7546
                record[key] = formatters && col.formatter ? col.formatter(data[col.field], data)
142!
7547
                    : data[col.field];
7548
            });
7549

7550
            if (Object.keys(record).length) {
71✔
7551
                selectedData.push(record);
71✔
7552
            }
7553
            record = {};
71✔
7554
        }
7555
        return selectedData;
8✔
7556
    }
7557

7558
    /**
7559
     * @hidden
7560
     */
7561
    protected initPinning() {
7562
        this.calculateGridWidth();
3,967✔
7563
        this.resetCaches();
3,967✔
7564
        this.handleColumnPinningForGroups();
3,967✔
7565
        this.notifyChanges();
3,967✔
7566
    }
7567

7568
    /**
7569
     * @hidden
7570
     */
7571
    protected scrollTo(row: any | number, column: any | number, inCollection = this._filteredSortedUnpinnedData): void {
3✔
7572
        let delayScrolling = false;
106✔
7573

7574
        if (this.paginator && typeof (row) !== 'number') {
106✔
7575
            const rowIndex = inCollection.indexOf(row);
16✔
7576
            const page = Math.floor(rowIndex / this.perPage);
16✔
7577

7578
            if (this.page !== page) {
16✔
7579
                delayScrolling = true;
5✔
7580
                this.page = page;
5✔
7581
            }
7582
        }
7583

7584
        if (delayScrolling) {
106✔
7585
            this.verticalScrollContainer.dataChanged.pipe(first(), takeUntil(this.destroy$)).subscribe(() => {
5✔
7586
                this.scrollDirective(this.verticalScrollContainer,
5✔
7587
                    typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
5!
7588
            });
7589
        } else {
7590
            this.scrollDirective(this.verticalScrollContainer,
101✔
7591
                typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
101✔
7592
        }
7593

7594
        this.scrollToHorizontally(column);
106✔
7595
    }
7596

7597
    /**
7598
     * @hidden
7599
     */
7600
    protected scrollToHorizontally(column: any | number) {
7601
        let columnIndex = typeof column === 'number' ? column : this.getColumnByName(column).visibleIndex;
191✔
7602
        const scrollRow = this.rowList.find(r => !!r.virtDirRow);
233✔
7603
        const virtDir = scrollRow ? scrollRow.virtDirRow : null;
191✔
7604
        if (this.pinnedStartColumns.length) {
191✔
7605
            if (columnIndex >= this.pinnedStartColumns.length) {
1!
7606
                columnIndex -= this.pinnedStartColumns.length;
×
7607
                this.scrollDirective(virtDir, columnIndex);
×
7608
            }
7609
        } else {
7610
            this.scrollDirective(virtDir, columnIndex);
190✔
7611
        }
7612
    }
7613

7614
    /**
7615
     * @hidden
7616
     */
7617
    protected scrollDirective(directive: IgxGridForOfDirective<any, any[]>, goal: number): void {
7618
        if (!directive) {
381✔
7619
            return;
1✔
7620
        }
7621
        directive.scrollTo(goal);
380✔
7622
    }
7623

7624

7625
    /**
7626
     * @hidden
7627
     */
7628
    protected getColumnWidthSum(): number {
7629
        let colSum = 0;
34✔
7630
        const cols = this.hasColumnLayouts ?
34!
7631
            this.visibleColumns.filter(x => x.columnLayout) : this.visibleColumns.filter(x => !x.columnGroup);
234✔
7632
        cols.forEach((item) => {
34✔
7633
            colSum += parseInt((item.calcWidth || item.defaultWidth), 10) || this.minColumnWidth;
201!
7634
        });
7635
        if (!colSum) {
34!
7636
            return null;
×
7637
        }
7638
        this.cdr.detectChanges();
34✔
7639
        colSum += this.featureColumnsWidth();
34✔
7640
        return colSum;
34✔
7641
    }
7642

7643
    /**
7644
     * Notify changes, reset cache and populateVisibleIndexes.
7645
     *
7646
     * @hidden
7647
     */
7648
    private _columnsReordered(column: IgxColumnComponent) {
7649
        this.notifyChanges();
132✔
7650
        // after reordering is done reset cached column collections.
7651
        this.resetColumnCollections();
132✔
7652
        column.resetCaches();
132✔
7653
    }
7654

7655
    protected buildDataView(_data: any[]) {
7656
        this._dataView = this.isRowPinningToTop ?
13,714✔
7657
            [...this.pinnedDataView, ...this.unpinnedDataView] :
7658
            [...this.unpinnedDataView, ...this.pinnedDataView];
7659
    }
7660

7661
    private _applyWidthHostBinding() {
7662
        let width = this._width;
8,028✔
7663
        if (width === null) {
8,028✔
7664
            let currentWidth = this.calcWidth;
2✔
7665
            if (this.hasVerticalScroll()) {
2!
7666
                currentWidth += this.scrollSize;
×
7667
            }
7668
            width = currentWidth + 'px';
2✔
7669
            this.resetCaches();
2✔
7670
        }
7671
        this._hostWidth = width;
8,028✔
7672
        this.cdr.markForCheck();
8,028✔
7673
    }
7674

7675
    protected verticalScrollHandler(event) {
7676
        this.verticalScrollContainer.onScroll(event);
321✔
7677
        this.disableTransitions = true;
321✔
7678

7679
        this.zone.run(() => {
321✔
7680
            this.zone.onStable.pipe(first()).subscribe(() => {
321✔
7681
                this.verticalScrollContainer.chunkLoad.emit(this.verticalScrollContainer.state);
321✔
7682
                if (this.rowEditable) {
321✔
7683
                    this.changeRowEditingOverlayStateOnScroll(this.crudService.rowInEditMode);
12✔
7684
                }
7685
            });
7686
        });
7687
        this.disableTransitions = false;
321✔
7688

7689
        this.hideOverlays();
321✔
7690
        this.actionStrip?.hide();
321✔
7691
        if (this.actionStrip) {
321✔
7692
            this.actionStrip.context = null;
7✔
7693
        }
7694
        const args: IGridScrollEventArgs = {
321✔
7695
            direction: 'vertical',
7696
            event,
7697
            scrollPosition: this.verticalScrollContainer.scrollPosition
7698
        };
7699
        this.gridScroll.emit(args);
321✔
7700
    }
7701

7702
    protected horizontalScrollHandler(event) {
7703
        const scrollLeft = event.target.scrollLeft;
309✔
7704
        this.headerContainer.onHScroll(scrollLeft);
309✔
7705
        this._horizontalForOfs.forEach(vfor => vfor.onHScroll(scrollLeft));
1,952✔
7706
        this.cdr.markForCheck();
309✔
7707

7708
        this.zone.run(() => {
309✔
7709
            this.zone.onStable.pipe(first()).subscribe(() => {
309✔
7710
                this.parentVirtDir.chunkLoad.emit(this.headerContainer.state);
309✔
7711
                requestAnimationFrame(() => {
309✔
7712
                    this.autoSizeColumnsInView();
309✔
7713
                });
7714
            });
7715
        });
7716
        if (!this.navigation.isColumnFullyVisible(this.navigation.lastColumnIndex)) {
309✔
7717
            this.hideOverlays();
207✔
7718
        }
7719
        const args: IGridScrollEventArgs = { direction: 'horizontal', event, scrollPosition: this.headerContainer.scrollPosition };
309✔
7720
        this.gridScroll.emit(args);
309✔
7721
    }
7722

7723
    protected get renderedActualRowHeight() {
7724
        let border = 1;
504✔
7725
        if (this.rowList.toArray().length > 0) {
504✔
7726
            const rowStyles = this.document.defaultView.getComputedStyle(this.rowList.first.nativeElement);
354✔
7727
            border = rowStyles.borderBottomWidth ? Math.ceil(parseFloat(rowStyles.borderBottomWidth)) : border;
354✔
7728
        }
7729
        return this.rowHeight + border;
504✔
7730
    }
7731

7732
    private executeCallback(rowIndex, visibleColIndex = -1, cb: (args: any) => void = null) {
×
7733
        if (!cb) {
835✔
7734
            return;
281✔
7735
        }
7736
        let row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
1,623✔
7737
        if (!row) {
554✔
7738
            if ((this as any).totalItemCount) {
8!
7739
                this.verticalScrollContainer.dataChanged.pipe(first(), takeUntil(this.destroy$)).subscribe(() => {
×
7740
                    this.cdr.detectChanges();
×
7741
                    row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
×
7742
                    const cbArgs = this.getNavigationArguments(row, visibleColIndex);
×
7743
                    cb(cbArgs);
×
7744
                });
7745
            }
7746
            const dataViewIndex = this._getDataViewIndex(rowIndex);
8✔
7747
            if (this.dataView[dataViewIndex].detailsData) {
8✔
7748
                this.navigation.setActiveNode({ row: rowIndex });
8✔
7749
                this.cdr.detectChanges();
8✔
7750
            }
7751

7752
            return;
8✔
7753
        }
7754
        const args = this.getNavigationArguments(row, visibleColIndex);
546✔
7755
        cb(args);
546✔
7756
    }
7757

7758
    private getNavigationArguments(row, visibleColIndex) {
7759
        let targetType: GridKeydownTargetType; let target;
7760
        switch (row.nativeElement.tagName.toLowerCase()) {
546!
7761
            case 'igx-grid-groupby-row':
7762
                targetType = 'groupRow';
30✔
7763
                target = row;
30✔
7764
                break;
30✔
7765
            case 'igx-grid-summary-row':
7766
                targetType = 'summaryCell';
50✔
7767
                target = visibleColIndex !== -1 ?
50!
7768
                    row.summaryCells.find(c => c.visibleColumnIndex === visibleColIndex) : row.summaryCells.first;
139✔
7769
                break;
50✔
7770
            case 'igx-child-grid-row':
7771
                targetType = 'hierarchicalRow';
×
7772
                target = row;
×
7773
                break;
×
7774
            default:
7775
                targetType = 'dataCell';
466✔
7776
                target = visibleColIndex !== -1 ? row.cells.find(c => c.visibleColumnIndex === visibleColIndex) : row.cells.first;
1,296!
7777
                break;
466✔
7778
        }
7779
        return { targetType, target };
546✔
7780
    }
7781

7782
    private getNextDataRowIndex(currentRowIndex, previous = false): number {
15✔
7783
        const resolvedIndex = this._getDataViewIndex(currentRowIndex);
30✔
7784
        if (currentRowIndex < 0 || (currentRowIndex === 0 && previous) || (resolvedIndex >= this.dataView.length - 1 && !previous)) {
30!
7785
            return currentRowIndex;
7✔
7786
        }
7787
        // find next/prev record that is editable.
7788
        const nextRowIndex = previous ? this.findPrevEditableDataRowIndex(currentRowIndex) :
23✔
7789
            this.dataView.findIndex((_rec, index) =>
7790
                index > resolvedIndex && this.isEditableDataRecordAtIndex(index));
56✔
7791
        const nextDataIndex = this.getDataIndex(nextRowIndex);
23✔
7792
        return nextDataIndex !== -1 ? nextDataIndex : currentRowIndex;
23!
7793
    }
7794

7795
    /**
7796
     * Returns the previous editable row index or -1 if no such row is found.
7797
     *
7798
     * @param currentIndex The index of the current editable record.
7799
     */
7800
    private findPrevEditableDataRowIndex(currentIndex): number {
7801
        let i = this.dataView.length;
8✔
7802
        const resolvedIndex = this._getDataViewIndex(currentIndex);
8✔
7803
        while (i--) {
8✔
7804
            if (i < resolvedIndex && this.isEditableDataRecordAtIndex(i)) {
178✔
7805
                return i;
8✔
7806
            }
7807
        }
7808
        return -1;
×
7809
    }
7810

7811

7812
    /**
7813
     * Returns if the record at the specified data view index is a an editable data record.
7814
     * If record is group rec, summary rec, child rec, ghost rec. etc. it is not editable.
7815
     *
7816
     * @param dataViewIndex The index of that record in the data view.
7817
     *
7818
     */
7819
    // TODO: Consider moving it into CRUD
7820
    private isEditableDataRecordAtIndex(dataViewIndex) {
7821
        const rec = this.dataView[dataViewIndex];
29✔
7822
        return !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData &&
29✔
7823
            !this.isGhostRecordAtIndex(dataViewIndex);
7824
    }
7825

7826
    /**
7827
     * Returns if the record at the specified data view index is a ghost.
7828
     * If record is pinned but is not in pinned area then it is a ghost record.
7829
     *
7830
     * @param dataViewIndex The index of that record in the data view.
7831
     */
7832
    private isGhostRecordAtIndex(dataViewIndex) {
7833
        const isPinned = this.isRecordPinned(this.dataView[dataViewIndex]);
27✔
7834
        const isInPinnedArea = this.isRecordPinnedByViewIndex(dataViewIndex);
27✔
7835
        return isPinned && !isInPinnedArea;
27✔
7836
    }
7837

7838
    private isValidPosition(rowIndex, colIndex): boolean {
7839
        const rows = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).length;
97✔
7840
        const cols = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0 && !col.hidden).length;
777✔
7841
        if (rows < 1 || cols < 1) {
97✔
7842
            return false;
2✔
7843
        }
7844
        if (rowIndex > -1 && rowIndex < this.dataView.length &&
95✔
7845
            colIndex > - 1 && colIndex <= Math.max(...this.visibleColumns.map(c => c.visibleIndex))) {
695✔
7846
            return true;
93✔
7847
        }
7848
        return false;
2✔
7849
    }
7850

7851
    private find(text: string, increment: number, caseSensitive?: boolean, exactMatch?: boolean, scroll?: boolean, endEdit = true) {
195✔
7852
        if (!this.rowList) {
322!
7853
            return 0;
×
7854
        }
7855

7856
        if (endEdit) {
322✔
7857
            this.crudService.endEdit(false);
283✔
7858
        }
7859

7860
        if (!text) {
322!
7861
            this.clearSearch();
×
7862
            return 0;
×
7863
        }
7864

7865
        const caseSensitiveResolved = caseSensitive ? true : false;
322✔
7866
        const exactMatchResolved = exactMatch ? true : false;
322✔
7867
        let rebuildCache = false;
322✔
7868

7869
        if (this._lastSearchInfo.searchText !== text ||
322✔
7870
            this._lastSearchInfo.caseSensitive !== caseSensitiveResolved ||
7871
            this._lastSearchInfo.exactMatch !== exactMatchResolved) {
7872
            this._lastSearchInfo = {
84✔
7873
                searchText: text,
7874
                activeMatchIndex: 0,
7875
                caseSensitive: caseSensitiveResolved,
7876
                exactMatch: exactMatchResolved,
7877
                matchInfoCache: [],
7878
                matchCount: 0,
7879
                content: ''
7880
            };
7881

7882
            rebuildCache = true;
84✔
7883
        } else {
7884
            this._lastSearchInfo.activeMatchIndex += increment;
238✔
7885
        }
7886

7887
        if (rebuildCache) {
322✔
7888
            this.rowList.forEach((row) => {
84✔
7889
                if (row.cells) {
784✔
7890
                    row.cells.forEach((c: IgxGridCellComponent) => {
740✔
7891
                        c.highlightText(text, caseSensitiveResolved, exactMatchResolved);
3,097✔
7892
                    });
7893
                }
7894
            });
7895

7896
            this.rebuildMatchCache();
84✔
7897
        }
7898

7899
        if (this._lastSearchInfo.activeMatchIndex >= this._lastSearchInfo.matchCount) {
322✔
7900
            this._lastSearchInfo.activeMatchIndex = 0;
28✔
7901
        } else if (this._lastSearchInfo.activeMatchIndex < 0) {
294✔
7902
            this._lastSearchInfo.activeMatchIndex = this._lastSearchInfo.matchCount - 1;
8✔
7903
        }
7904

7905
        if (this._lastSearchInfo.matchCount > 0) {
322✔
7906
            const matchInfo = this._lastSearchInfo.matchInfoCache[this._lastSearchInfo.activeMatchIndex];
304✔
7907
            this._lastSearchInfo = { ...this._lastSearchInfo };
304✔
7908

7909
            if (scroll !== false) {
304✔
7910
                this.scrollTo(matchInfo.row, matchInfo.column);
182✔
7911
            }
7912

7913
            this.textHighlightService.setActiveHighlight(this.id, {
304✔
7914
                column: matchInfo.column,
7915
                row: matchInfo.row,
7916
                index: matchInfo.index,
7917
                metadata: matchInfo.metadata,
7918
            });
7919

7920
        } else {
7921
            this.textHighlightService.clearActiveHighlight(this.id);
18✔
7922
        }
7923

7924
        return this._lastSearchInfo.matchCount;
322✔
7925
    }
7926

7927
    private rebuildMatchCache() {
7928
        this._lastSearchInfo.matchInfoCache = [];
211✔
7929

7930
        const caseSensitive = this._lastSearchInfo.caseSensitive;
211✔
7931
        const exactMatch = this._lastSearchInfo.exactMatch;
211✔
7932
        const searchText = caseSensitive ? this._lastSearchInfo.searchText : this._lastSearchInfo.searchText.toLowerCase();
211✔
7933
        const data = this.filteredSortedData;
211✔
7934
        const columnItems = this.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex);
908✔
7935
        const columnsPathParts = columnItems.map(col => columnFieldPath(col.field));
891✔
7936

7937
        data.forEach((dataRow, rowIndex) => {
211✔
7938
            columnItems.forEach((c, cid) => {
3,474✔
7939
                const pipeArgs = this.getColumnByName(c.field).pipeArgs;
14,791✔
7940
                const value = c.formatter ? c.formatter(resolveNestedPath(dataRow, columnsPathParts[cid]), dataRow) :
14,791!
7941
                    c.dataType === 'number' ? formatNumber(resolveNestedPath(dataRow, columnsPathParts[cid]) as number, this.locale, pipeArgs.digitsInfo) :
14,791✔
7942
                        c.dataType === 'date'
11,115✔
7943
                            ? formatDate(resolveNestedPath(dataRow, columnsPathParts[cid]) as string, pipeArgs.format, this.locale, pipeArgs.timezone)
7944
                            : resolveNestedPath(dataRow, columnsPathParts[cid]);
7945
                if (value !== undefined && value !== null && c.searchable) {
14,791✔
7946
                    let searchValue = caseSensitive ? String(value) : String(value).toLowerCase();
14,576✔
7947

7948
                    if (exactMatch) {
14,576✔
7949
                        if (searchValue === searchText) {
536✔
7950
                            const mic: IMatchInfoCache = {
9✔
7951
                                row: dataRow,
7952
                                column: c.field,
7953
                                index: 0,
7954
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7955
                            };
7956

7957
                            this._lastSearchInfo.matchInfoCache.push(mic);
9✔
7958
                        }
7959
                    } else {
7960
                        let occurrenceIndex = 0;
14,040✔
7961
                        let searchIndex = searchValue.indexOf(searchText);
14,040✔
7962

7963
                        while (searchIndex !== -1) {
14,040✔
7964
                            const mic: IMatchInfoCache = {
2,410✔
7965
                                row: dataRow,
7966
                                column: c.field,
7967
                                index: occurrenceIndex++,
7968
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7969
                            };
7970

7971
                            this._lastSearchInfo.matchInfoCache.push(mic);
2,410✔
7972

7973
                            searchValue = searchValue.substring(searchIndex + searchText.length);
2,410✔
7974
                            searchIndex = searchValue.indexOf(searchText);
2,410✔
7975
                        }
7976
                    }
7977
                }
7978
            });
7979
        });
7980

7981
        this._lastSearchInfo.matchCount = this._lastSearchInfo.matchInfoCache.length;
211✔
7982
    }
7983

7984
    protected updateDefaultRowHeight() {
7985
        if (this.dataRowList.length > 0 && this.dataRowList.first.cells && this.dataRowList.first.cells.length > 0) {
3,700✔
7986
            const height = parseFloat(this.document.defaultView.getComputedStyle(this.dataRowList.first.cells.first.nativeElement)?.getPropertyValue('height'));
122✔
7987
            if (height) {
122!
7988
                this._defaultRowHeight = height;
122✔
7989
            } else {
7990
                this._shouldRecalcRowHeight = true;
×
7991
            }
7992
        }
7993
    }
7994

7995
    // TODO: About to Move to CRUD
7996
    private configureRowEditingOverlay(rowID: any, useOuter = false) {
71✔
7997
        let settings = this.rowEditSettings;
280✔
7998
        const overlay = this.overlayService.getOverlayById(this.rowEditingOverlay.overlayId);
280✔
7999
        if (overlay) {
280✔
8000
            settings = overlay.settings;
73✔
8001
        }
8002
        settings.outlet = useOuter ? this.parentRowOutletDirective : this.rowOutletDirective;
280✔
8003
        this.rowEditPositioningStrategy.settings.container = this.tbody.nativeElement;
280✔
8004
        const pinned = this._pinnedRecordIDs.indexOf(rowID) !== -1;
280✔
8005
        const targetRow = !pinned ?
280✔
8006
            this.gridAPI.get_row_by_key(rowID) as IgxRowDirective
8007
            : this.pinnedRows.find(x => x.key === rowID) as IgxRowDirective;
8✔
8008
        if (!targetRow) {
280!
8009
            return;
×
8010
        }
8011
        settings.target = targetRow.element.nativeElement;
280✔
8012
        this.toggleRowEditingOverlay(true);
280✔
8013
    }
8014

8015
    private handleColumnPinningForGroups(): void {
8016
        // When a column is a group or is inside a group, pin all related.
8017
        const pinnedColumns = [];
3,967✔
8018
        const unpinnedColumns = [];
3,967✔
8019

8020
        this._pinnedColumns.forEach(col => {
3,967✔
8021
            if (col.parent) {
504✔
8022
                col.parent.pinned = true;
113✔
8023
            }
8024
            if (col.columnGroup) {
504✔
8025
                col.children.forEach(child => child.pinned = true);
112✔
8026
            }
8027
        });
8028

8029
        // Make sure we don't exceed unpinned area min width and get pinned and unpinned col collections.
8030
        // We take into account top level columns (top level groups and non groups).
8031
        // If top level is unpinned the pinning handles all children to be unpinned as well.
8032
        for (const column of this._columns) {
3,967✔
8033
            if (column.pinned && !column.parent) {
23,763✔
8034
                pinnedColumns.push(column);
392✔
8035
            } else if (column.pinned && column.parent) {
23,371✔
8036
                if (column.topLevelParent.pinned) {
115!
8037
                    pinnedColumns.push(column);
115✔
8038
                } else {
8039
                    column.pinned = false;
×
8040
                    unpinnedColumns.push(column);
×
8041
                }
8042
            } else {
8043
                unpinnedColumns.push(column);
23,256✔
8044
            }
8045
        }
8046
        // Assign the applicable collections.
8047
        this._pinnedColumns = pinnedColumns;
3,967✔
8048
        this._pinnedStartColumns = pinnedColumns.filter((c) => c.pinned && c.pinningPosition === ColumnPinningPosition.Start);
3,967✔
8049
        this._pinnedEndColumns = pinnedColumns.filter((c) => c.pinned && c.pinningPosition === ColumnPinningPosition.End);
3,967✔
8050
        this._unpinnedColumns = unpinnedColumns;
3,967✔
8051
    }
8052

8053
    protected shouldRecreateColumns(oldData: any[] | null | undefined, newData: any[] | null | undefined): boolean {
8054
        if (!oldData || !oldData.length) return true;
72✔
8055
        if (!newData || !newData.length) return false;
45!
8056
        return Object.keys(oldData[0]).join() !== Object.keys(newData[0]).join();
45✔
8057
    }
8058

8059
    /**
8060
     * Clears the current navigation service active node
8061
     */
8062
    private clearActiveNode() {
8063
        this.navigation.lastActiveNode = this.navigation.activeNode;
77✔
8064
        this.navigation.activeNode = {} as IActiveNode;
77✔
8065
        this.notifyChanges();
77✔
8066
    }
8067

8068
    private getRecreatedTree(value: IFilteringExpressionsTree): IFilteringExpressionsTree {
8069
        if (this._hGridSchema) {
9,555✔
8070
            return recreateTree(value, this._hGridSchema, true) as IFilteringExpressionsTree;
16✔
8071
        } else {
8072
            return recreateTreeFromFields(value, this._columns) as IFilteringExpressionsTree;
9,539✔
8073
        }
8074
    }
8075

8076
    private _calculateRowCount(): void {
8077
        if (this.verticalScrollContainer?.isRemote) {
5,421✔
8078
            this._rowCount = this.verticalScrollContainer.totalItemCount ?? 0;
3!
8079
        } else if (this.paginator) {
5,418✔
8080
            this._rowCount = this.totalRecords ?? 0;
230✔
8081
        } else {
8082
            this._rowCount = this.verticalScrollContainer?.igxForOf?.length ?? 0;
5,188✔
8083
        }
8084
        this._rowCount += 1; // include header row
5,421✔
8085
    }
8086
}
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