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

IgniteUI / igniteui-angular / 6850015339

13 Nov 2023 12:33PM CUT coverage: 92.266% (-0.02%) from 92.282%
6850015339

push

github

web-flow
Merge pull request #13669 from IgniteUI/mdragnev/esf-15.1.x

Add support for navigation in the ESF search list [15.1.x]

15350 of 18038 branches covered (0.0%)

46 of 54 new or added lines in 3 files covered. (85.19%)

24 existing lines in 4 files now uncovered.

26925 of 29182 relevant lines covered (92.27%)

29682.16 hits per line

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

92.38
/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts
1
import { DOCUMENT, formatNumber, getLocaleNumberFormat, NumberFormatStyle } from '@angular/common';
2
import {
3
    AfterContentInit,
4
    AfterViewInit,
5
    ApplicationRef,
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
    QueryList,
30
    TemplateRef,
31
    ViewChild,
32
    ViewChildren,
33
    ViewContainerRef
34
} from '@angular/core';
35
import { formatDate, resizeObservable } from '../core/utils';
36
import { IgcTrialWatermark } from 'igniteui-trial-watermark';
37
import { Subject, pipe, fromEvent, animationFrameScheduler, merge } from 'rxjs';
38
import { takeUntil, first, filter, throttleTime, map, shareReplay, takeWhile } from 'rxjs/operators';
39
import { cloneArray, mergeObjects, compareMaps, resolveNestedPath, isObject, PlatformUtil } from '../core/utils';
40
import { GridColumnDataType } from '../data-operations/data-util';
41
import { FilteringLogic, IFilteringExpression } from '../data-operations/filtering-expression.interface';
42
import { IGroupByRecord } from '../data-operations/groupby-record.interface';
43
import { IForOfDataChangingEventArgs, IgxGridForOfDirective } from '../directives/for-of/for_of.directive';
44
import { IgxTextHighlightDirective } from '../directives/text-highlight/text-highlight.directive';
45
import { ISummaryExpression } from './summaries/grid-summary';
46
import { RowEditPositionStrategy, IPinningConfig } from './grid.common';
47
import { IgxGridToolbarComponent } from './toolbar/grid-toolbar.component';
48
import { IgxRowDirective } from './row.directive';
49
import { IgxOverlayOutletDirective, IgxToggleDirective } from '../directives/toggle/toggle.directive';
50
import {
51
    FilteringExpressionsTree, IFilteringExpressionsTree, FilteringExpressionsTreeType
52
} from '../data-operations/filtering-expressions-tree';
53
import { IFilteringOperation } from '../data-operations/filtering-condition';
54
import { Transaction, TransactionType, TransactionService, State } from '../services/public_api';
55
import {
56
    IgxRowAddTextDirective,
57
    IgxRowEditTemplateDirective,
58
    IgxRowEditTabStopDirective,
59
    IgxRowEditTextDirective,
60
    IgxRowEditActionsDirective
61
} from './grid.rowEdit.directive';
2✔
62
import { IgxGridNavigationService, IActiveNode } from './grid-navigation.service';
2✔
63
import { IDisplayDensityOptions, DisplayDensityToken, DisplayDensityBase, DisplayDensity } from '../core/displayDensity';
2✔
64
import { IgxFilteringService } from './filtering/grid-filtering.service';
2✔
65
import { IgxGridFilteringCellComponent } from './filtering/base/grid-filtering-cell.component';
66
import { WatchChanges } from './watch-changes';
67
import { IgxGridHeaderGroupComponent } from './headers/grid-header-group.component';
68
import { IGridResourceStrings } from '../core/i18n/grid-resources';
69
import { CurrentResourceStrings } from '../core/i18n/resources';
70
import { IgxGridSummaryService } from './summaries/grid-summary.service';
2✔
71
import { IgxSummaryRowComponent } from './summaries/summary-row.component';
2✔
72
import { IgxGridSelectionService } from './selection/selection.service';
73
import { IgxEditRow, IgxCell, IgxAddRow } from './common/crud.service';
21✔
74
import { ICachedViewLoadedEventArgs, IgxTemplateOutletDirective } from '../directives/template-outlet/template_outlet.directive';
21✔
75
import { IgxExcelStyleLoadingValuesTemplateDirective } from './filtering/excel-style/excel-style-search.component';
21✔
76
import { IgxGridColumnResizerComponent } from './resizing/resizer.component';
20✔
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 {
202,998✔
81
    IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandIndicatorDirective,
134,407✔
82
    IgxHeaderCollapseIndicatorDirective, IgxExcelStyleHeaderIconDirective, IgxSortAscendingHeaderIconDirective,
83
    IgxSortDescendingHeaderIconDirective, IgxSortHeaderIconDirective
68,591✔
84
} from './grid/grid.directives';
85
import {
86
    GridKeydownTargetType,
84,923✔
87
    GridSelectionMode,
88
    GridSummaryPosition,
89
    GridSummaryCalculationMode,
48,213✔
90
    FilterMode,
91
    ColumnPinningPosition,
92
    RowPinningPosition,
1!
93
    GridPagingMode,
1✔
94
    GridValidationTrigger
1✔
95
} from './common/enums';
96
import {
97
    IGridCellEventArgs,
98
    IRowSelectionEventArgs,
99
    IPinColumnEventArgs,
248✔
100
    IGridEditEventArgs,
101
    IRowDataEventArgs,
102
    IColumnResizeEventArgs,
3✔
103
    IColumnMovingStartEventArgs,
104
    IColumnMovingEventArgs,
105
    IColumnMovingEndEventArgs,
1,229✔
106
    IGridKeydownEventArgs,
107
    IRowDragStartEventArgs,
108
    IRowDragEndEventArgs,
109
    IGridClipboardEvent,
110
    IGridToolbarExportEventArgs,
111
    ISearchInfo,
112
    ICellPosition,
113
    IRowToggleEventArgs,
114
    IColumnSelectionEventArgs,
115
    IPinRowEventArgs,
116
    IGridScrollEventArgs,
117
    IGridEditDoneEventArgs,
118
    IActiveNodeChangeEventArgs,
119
    ISortingEventArgs,
120
    IFilteringEventArgs,
121
    IColumnVisibilityChangedEventArgs,
1✔
122
    IColumnVisibilityChangingEventArgs,
123
    IPinColumnCancellableEventArgs
124
} from './common/events';
11,237✔
125
import { IgxAdvancedFilteringDialogComponent } from './filtering/advanced-filtering/advanced-filtering-dialog.component';
126
import {
127
    ColumnType,
28,885✔
128
    GridServiceType,
129
    GridType,
130
    IGridFormGroupCreatedEventArgs,
426✔
131
    IGridValidationStatusEventArgs,
132
    IgxGridEmptyTemplateContext,
133
    IgxGridHeaderTemplateContext,
28,137✔
134
    IgxGridRowDragGhostContext,
135
    IgxGridRowEditActionsTemplateContext,
136
    IgxGridRowEditTemplateContext,
1,363✔
137
    IgxGridRowEditTextTemplateContext,
138
    IgxGridRowTemplateContext,
139
    IgxGridTemplateContext,
4,367✔
140
    IgxHeadSelectorTemplateContext,
141
    IgxRowSelectorTemplateContext,
142
    IGX_GRID_SERVICE_BASE,
143
    ISizeInfo,
144
    RowType
145
} from './common/grid.interface';
146
import { DropPosition } from './moving/moving.service';
147
import { IgxHeadSelectorDirective, IgxRowSelectorDirective } from './selection/row-selectors';
148
import { IgxColumnComponent } from './columns/column.component';
149
import { IgxColumnGroupComponent } from './columns/column-group.component';
150
import { IgxRowDragGhostDirective, IgxDragIndicatorIconDirective } from './row-drag.directive';
151
import { IgxSnackbarComponent } from '../snackbar/snackbar.component';
152
import { v4 as uuidv4 } from 'uuid';
153
import { IgxActionStripComponent } from '../action-strip/action-strip.component';
154
import { IgxGridRowComponent } from './grid/grid-row.component';
155
import { IPageEventArgs } from '../paginator/paginator-interfaces';
1✔
156
import { IgxPaginatorComponent } from '../paginator/paginator.component';
157
import { IgxGridHeaderRowComponent } from './headers/grid-header-row.component';
158
import { IgxGridGroupByAreaComponent } from './grouping/grid-group-by-area.component';
346✔
159
import { IgxFlatTransactionFactory, TRANSACTION_TYPE } from '../services/transaction/transaction-factory.service';
160
import { ISortingOptions } from './columns/interfaces';
161
import { GridSelectionRange, IgxGridTransaction } from './common/types';
162
import { VerticalAlignment, HorizontalAlignment, PositionSettings, OverlaySettings } from '../services/overlay/utilities';
163
import { IgxOverlayService } from '../services/overlay/overlay';
164
import { ConnectedPositioningStrategy } from '../services/overlay/position/connected-positioning-strategy';
165
import { ContainerPositionStrategy } from '../services/overlay/position/container-position-strategy';
166
import { AbsoluteScrollStrategy } from '../services/overlay/scroll/absolute-scroll-strategy';
167
import { Action, StateUpdateEvent, TransactionEventOrigin } from '../services/transaction/transaction';
168
import { ISortingExpression } from '../data-operations/sorting-strategy';
169
import { IGridSortingStrategy } from './common/strategy';
170
import { IgxGridExcelStyleFilteringComponent } from './filtering/excel-style/grid.excel-style-filtering.component';
171
import { IgxGridHeaderComponent } from './headers/grid-header.component';
172
import { IgxGridFilteringRowComponent } from './filtering/base/grid-filtering-row.component';
173
import { DefaultDataCloneStrategy, IDataCloneStrategy } from '../data-operations/data-clone-strategy';
174
import { IgxGridCellComponent } from './cell.component';
1✔
175
import { IgxGridValidationService } from './grid/grid-validation.service';
176

177
IgcTrialWatermark.register();
4,717✔
178

179
let FAKE_ROW_ID = -1;
180
const DEFAULT_ITEMS_PER_PAGE = 15;
181
const MINIMUM_COLUMN_WIDTH = 136;
182
// By default row editing overlay outlet is inside grid body so that overlay is hidden below grid header when scrolling.
183
// 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.
184
// Default row editing overlay height is higher then row height that is why the case is valid also for row with 2 rows.
185
// More accurate calculation is not possible, cause row editing overlay is still not shown and we don't know its height,
186
// but in the same time we need to set row editing overlay outlet before opening the overlay itself.
187
const MIN_ROW_EDITING_COUNT_THRESHOLD = 2;
188

189
@Directive()
190
export abstract class IgxGridBaseDirective extends DisplayDensityBase implements GridType,
191
    OnInit, DoCheck, OnDestroy, AfterContentInit, AfterViewInit {
192

193
    /**
194
     * Gets/Sets the display time for the row adding snackbar notification.
1✔
195
     *
196
     * @remarks
197
     * By default it is 6000ms.
9,123✔
198
     */
199
    @Input()
200
    public snackbarDisplayTime = 6000;
201

202
    /**
203
     * Gets/Sets whether to auto-generate the columns.
204
     *
205
     * @remarks
206
     * The default value is false. When set to true, it will override all columns declared through code or in markup.
207
     * @example
208
     * ```html
209
     * <igx-grid [data]="Data" [autoGenerate]="true"></igx-grid>
210
     * ```
211
     */
212
    @Input()
213
    public autoGenerate = false;
822✔
214

215
    /**
216
     * Gets/Sets a list of property keys to be excluded from the generated column collection
959✔
217
     * @remarks
218
     * The collection is only used during initialization and changing it will not cause any changes in the generated columns at runtime
219
     * unless the grid is destroyed and recreated. To modify the columns visible in the UI at runtime, please use their
220
     * [hidden](https://www.infragistics.com/products/ignite-ui-angular/docs/typescript/latest/classes/IgxColumnComponent.html#hidden) property.
221
     * @example
222
     * ```html
223
     * <igx-grid data=[Data] [autoGenerate]="true" [autoGenerateExclude]="['ProductName', 'Count']"></igx-grid>
224
     * ```
225
     * ```typescript
226
     * const Data = [{ 'Id': '1', 'ProductName': 'name1', 'Description': 'description1', 'Count': 5 }]
227
     * ```
228
     */
229
    @Input()
230
    public autoGenerateExclude: string[] = [];
231

232
    /**
827✔
233
     * Controls whether columns moving is enabled in the grid.
234
     *
235
     */
879✔
236
    @Input()
237
    public moving = false;
238

239
    /**
240
     * Gets/Sets a custom template when empty.
241
     *
242
     * @example
243
     * ```html
244
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [emptyGridTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
245
     * ```
246
     */
247
    @Input()
248
    public emptyGridTemplate: TemplateRef<void>;
249

250
    /**
251
     * Gets/Sets a custom template for adding row UI when grid is empty.
827✔
252
     *
253
     * @example
254
     * ```html
23,160✔
255
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [addRowEmptyTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
256
     * ```
257
     */
258
    @Input()
259
    public addRowEmptyTemplate: TemplateRef<void>;
260

261
    /**
262
     * Gets/Sets a custom template when loading.
263
     *
264
     * @example
265
     * ```html
266
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [loadingGridTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
267
     * ```
268
     */
269
    @Input()
270
    public loadingGridTemplate: TemplateRef<void>;
827✔
271

272
    /**
273
     * Get/Set IgxSummaryRow height
31,414✔
274
     */
275
    @Input()
276
    public set summaryRowHeight(value: number) {
2✔
277
        this._summaryRowHeight = value | 0;
278
        this.summaryService.summaryHeight = value;
279
        if (!this._init) {
213,089✔
280
            this.reflow();
3,885✔
281
        }
282
    }
213,089✔
283

284
    public get summaryRowHeight(): number {
285
        if (this.hasSummarizedColumns && this.rootSummariesEnabled) {
305✔
286
            return this._summaryRowHeight || this.summaryService.calcMaxSummaryHeight();
287
        }
288
        return 0;
3✔
289
    }
290

291
    public get hasColumnsToAutosize() {
107,212✔
292
        return this._columns.some(x => x.width === 'fit-content');
293
    }
294

899!
295
    /**
899✔
296
     * Gets/Sets the data clone strategy of the grid when in edit mode.
899✔
297
     *
492✔
298
     * @example
7✔
299
     * ```html
7✔
300
     *  <igx-grid #grid [data]="localData" [dataCloneStrategy]="customCloneStrategy"></igx-grid>
7✔
301
     * ```
302
     */
303
    @Input()
899✔
304
    public get dataCloneStrategy(): IDataCloneStrategy {
899✔
305
        return this._dataCloneStrategy;
899✔
306
    }
899✔
307

899✔
308
    public set dataCloneStrategy(strategy: IDataCloneStrategy) {
309
        if (strategy) {
433✔
310
            this._dataCloneStrategy = strategy;
311
            this._transactions.cloneStrategy = strategy;
899✔
312
        }
899✔
313
    }
899✔
314

899✔
315
    /**
316
     * Controls the copy behavior of the grid.
317
     */
318
    @Input()
48,242✔
319
    public clipboardOptions = {
320
        /**
321
         * Enables/disables the copy behavior
94✔
322
         */
78✔
323
        enabled: true,
78✔
324
        /**
78✔
325
         * Include the columns headers in the clipboard output.
326
         */
327
        copyHeaders: true,
16✔
328
        /**
329
         * Apply the columns formatters (if any) on the data in the clipboard output.
94✔
330
         */
94✔
331
        copyFormatters: true,
332
        /**
18✔
333
         * The separator used for formatting the copy output. Defaults to `\t`.
334
         */
94✔
335
        separator: '\t'
94✔
336
    };
94✔
337

338
    /**
94✔
339
     * Emitted after filtering is performed.
340
     *
341
     * @remarks
1,215,636✔
342
     * Returns the filtering expressions tree of the column for which filtering was performed.
343
     * @example
344
     * ```html
3,900✔
345
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
3,895✔
346
     *              (filteringExpressionsTreeChange)="filteringExprTreeChange($event)"></igx-grid>
3,895✔
347
     * ```
3,895✔
348
     */
3,895✔
349
    @Output()
3,895✔
350
    public filteringExpressionsTreeChange = new EventEmitter<IFilteringExpressionsTree>();
3,895✔
351

352
    /**
353
     * Emitted after advanced filtering is performed.
354
     *
8,683✔
355
     * @remarks
356
     * Returns the advanced filtering expressions tree.
357
     * @example
2✔
358
     * ```html
2✔
359
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
2✔
360
     *           (advancedFilteringExpressionsTreeChange)="advancedFilteringExprTreeChange($event)"></igx-grid>
361
     * ```
362
     */
×
363
    @Output()
364
    public advancedFilteringExpressionsTreeChange = new EventEmitter<IFilteringExpressionsTree>();
365

70✔
366
    /**
70✔
367
     * Emitted when grid is scrolled horizontally/vertically.
368
     *
369
     * @example
1,682,177✔
370
     * ```html
371
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
372
     *              (gridScroll)="onScroll($event)"></igx-grid>
667✔
373
     * ```
265✔
374
     */
375
    @Output()
376
    public gridScroll = new EventEmitter<IGridScrollEventArgs>();
377

1,682,066✔
378
    /**
379
     * @deprecated in version 12.1.0. Use the corresponding output exposed by the `igx-paginator` component instead
380
     *
93✔
381
     * Emitted after the current page is changed.
93✔
382
     *
36✔
383
     *
384
     * @example
385
     * ```html
386
     * <igx-grid (pageChange)="onPageChange($event)"></igx-grid>
24,027✔
387
     * ```
388
     * ```typescript
389
     * public onPageChange(page: number) {
20✔
390
     *   this.currentPage = page;
20✔
391
     * }
392
     * ```
393
     */
254,435✔
394
    @Output()
395
    public pageChange = new EventEmitter<number>();
396

46✔
397
    /**
46✔
398
     * @deprecated in version 12.1.0. Use the corresponding output exposed by the `igx-paginator` component instead
399
     *
400
     * Emitted when `perPage` property value of the grid is changed.
10,163,461✔
401
     *
402
     *
403
     * @example
521✔
404
     * ```html
34✔
405
     * <igx-grid #grid (perPageChange)="onPerPageChange($event)" [autoGenerate]="true"></igx-grid>
406
     * ```
521✔
407
     * ```typescript
521✔
408
     * public onPerPageChange(perPage: number) {
409
     *   this.perPage = perPage;
410
     * }
34,813✔
411
     * ```
412
     */
413
    @Output()
2,957✔
414
    public perPageChange = new EventEmitter<number>();
2,955✔
415

2,955✔
416
    /**
2,955✔
417
     * @deprecated in version 12.2.0. We suggest using `rowClasses` property instead
418
     *
419
     * Gets/Sets the styling classes applied to all even `IgxGridRowComponent`s in the grid.
420
     *
31,420✔
421
     *
422
     * @example
423
     * ```html
167,918✔
424
     * <igx-grid #grid [data]="Data" [evenRowCSS]="'igx-grid--my-even-class'" [autoGenerate]="true"></igx-grid>
425
     * ```
426
     */
2,004!
427
    @Input()
2,004✔
428
    public evenRowCSS = 'igx-grid__tr--even';
2,004✔
429

2,004✔
430
    /**
431
     * @deprecated in version 12.2.0. We suggest using `rowClasses` property instead
432
     *
433
     * Gets/Sets the styling classes applied to all odd `IgxGridRowComponent`s in the grid.
434
     *
435
     *
436
     * @example
437
     * ```html
438
     * <igx-grid #grid [data]="Data" [evenRowCSS]="'igx-grid--my-odd-class'" [autoGenerate]="true"></igx-grid>
439
     * ```
440
     */
441
    @Input()
×
442
    public oddRowCSS = 'igx-grid__tr--odd';
443

444
    /**
1,170,233!
445
     * Sets a conditional class selector to the grid's row element.
446
     * Accepts an object literal, containing key-value pairs,
447
     * where the key is the name of the CSS class and the value is
×
448
     * either a callback function that returns a boolean, or boolean, like so:
×
449
     * ```typescript
450
     * callback = (row: RowType) => { return row.selected > 6; }
×
451
     * rowClasses = { 'className' : this.callback };
452
     * ```
453
     * ```html
609✔
454
     * <igx-grid #grid [data]="Data" [rowClasses] = "rowClasses" [autoGenerate]="true"></igx-grid>
455
     * ```
456
     *
229✔
457
     * @memberof IgxColumnComponent
229✔
458
     */
229✔
459
    @Input()
460
    public rowClasses: any;
461

×
462
    /**
463
     * Sets conditional style properties on the grid row element.
464
     * It accepts an object literal where the keys are
474✔
465
     * the style properties and the value is an expression to be evaluated.
466
     * ```typescript
467
     * styles = {
12!
468
     *  background: 'yellow',
12✔
469
     *  color: (row: RowType) => row.selected : 'red': 'white'
12✔
470
     * }
6✔
471
     * ```
472
     * ```html
473
     * <igx-grid #grid [data]="Data" [rowStyles]="styles" [autoGenerate]="true"></igx-grid>
12✔
474
     * ```
475
     *
12✔
476
     * @memberof IgxColumnComponent
477
     */
478
    @Input()
479
    public rowStyles = null;
76,349✔
480

481
    /**
482
     * Gets/Sets the primary key.
×
483
     *
484
     * @example
485
     * ```html
195✔
486
     * <igx-grid #grid [data]="localData" [primaryKey]="'ProductID'" [autoGenerate]="true"></igx-grid>
487
     * ```
488
     */
2,313,045✔
489
    @WatchChanges()
490
    @Input()
491
    public primaryKey: any;
123!
492

123✔
493
    /**
494
     * Gets/Sets a unique values strategy used by the Excel Style Filtering
123✔
495
     *
496
     * @remarks
497
     * Provides a callback for loading unique column values on demand.
331,937✔
498
     * If this property is provided, the unique values it generates will be used by the Excel Style Filtering.
499
     * @example
500
     * ```html
689✔
501
     * <igx-grid [data]="localData" [filterMode]="'excelStyleFilter'" [uniqueColumnValuesStrategy]="columnValuesStrategy"></igx-grid>
678✔
502
     * ```
678✔
503
     */
678✔
504
    @Input()
18✔
505
    public uniqueColumnValuesStrategy: (column: ColumnType,
506
        filteringExpressionsTree: IFilteringExpressionsTree,
678✔
507
        done: (values: any[]) => void) => void;
678✔
508

678✔
509
    /** @hidden @internal */
510
    @ContentChildren(IgxGridExcelStyleFilteringComponent, { read: IgxGridExcelStyleFilteringComponent, descendants: false })
511
    public excelStyleFilteringComponents: QueryList<IgxGridExcelStyleFilteringComponent>;
512

1,147✔
513
    /** @hidden @internal */
514
    public get excelStyleFilteringComponent() {
515
        return this.excelStyleFilteringComponents?.first;
71!
516
    }
71✔
517

71✔
518
    public get headerGroups() {
71✔
519
        return this.theadRow.groups;
5✔
520
    }
521

522
    /**
523
     * Emitted when a cell is clicked.
524
     *
68,998✔
525
     * @remarks
526
     * Returns the `IgxGridCell`.
527
     * @example
169!
528
     * ```html
529
     * <igx-grid #grid (cellClick)="cellClick($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
530
     * ```
169✔
531
     */
169✔
532
    @Output()
533
    public cellClick = new EventEmitter<IGridCellEventArgs>();
×
534

535

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

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

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

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

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

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

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

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

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

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

51,356✔
668
    /**
5,539✔
669
     * Emitted after cell has been edited and editing has been committed.
670
     *
225,981✔
671
     * @example
45,817✔
672
     * ```html
45,817✔
673
     * <igx-grid #grid3 (cellEditDone)="editDone($event)" [data]="data" [primaryKey]="'ProductID'">
674
     * </igx-grid>
675
     * ```
2,662✔
676
     */
677
    @Output()
678
    public cellEditDone = new EventEmitter<IGridEditDoneEventArgs>();
679

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

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

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

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

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

759
    /**
760
     * Emitted before sorting expressions are applied.
761
     *
762
     * @remarks
763
     * Returns an `ISortingEventArgs` object. `sortingExpressions` key holds the sorting expressions.
764
     * @example
765
     * ```html
766
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sorting)="sorting($event)"></igx-grid>
767
     * ```
768
     */
769
    @Output()
770
    public sorting = new EventEmitter<ISortingEventArgs>();
771

772
    /**
822✔
773
     * Emitted after sorting is completed.
774
     *
775
     * @remarks
776
     * Returns the sorting expression.
777
     * @example
778
     * ```html
26✔
779
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingDone)="sortingDone($event)"></igx-grid>
21✔
780
     * ```
4!
781
     */
782
    @Output()
783
    public sortingDone = new EventEmitter<ISortingExpression | ISortingExpression[]>();
784

785
    /**
786
     * Emitted before filtering expressions are applied.
47✔
787
     *
55✔
788
     * @remarks
6!
789
     * Returns an `IFilteringEventArgs` object. `filteringExpressions` key holds the filtering expressions for the column.
790
     * @example
791
     * ```html
792
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true" (filtering)="filtering($event)"></igx-grid>
793
     * ```
794
     */
795
    @Output()
141✔
796
    public filtering = new EventEmitter<IFilteringEventArgs>();
797

798
    /**
108,327✔
799
     * Emitted after filtering is performed through the UI.
108,327✔
800
     *
93,616✔
801
     * @remarks
802
     * Returns the filtering expressions tree of the column for which filtering was performed.
14,711✔
803
     * @example
804
     * ```html
805
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true" (filteringDone)="filteringDone($event)"></igx-grid>
806
     * ```
807
     */
4,730✔
808
    @Output()
4,730✔
809
    public filteringDone = new EventEmitter<IFilteringExpressionsTree>();
810

811
    /**
251,586✔
812
     * @deprecated in version 12.1.0. Use the corresponding output exposed by the `igx-paginator` component instead
813
     *
814
     * Emitted after paging is performed.
436✔
815
     *
436✔
816
     *
436✔
817
     * @remarks
818
     * Returns an object consisting of the previous and next pages.
819
     * @example
820
     * ```html
821
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true" (pagingDone)="pagingDone($event)"></igx-grid>
822
     * ```
26,726✔
823
     */
2,554✔
824
    @Output()
1,364✔
825
    public pagingDone = new EventEmitter<IPageEventArgs>();
15,290✔
826

827
    /**
26,726✔
828
     * Emitted when a row is added.
829
     *
830
     * @remarks
831
     * Returns the data for the new `IgxGridRowComponent` object.
832
     * @example
833
     * ```html
834
     * <igx-grid #grid [data]="localData" (rowAdded)="rowAdded($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
835
     * ```
836
     */
837
    @Output()
838
    public rowAdded = new EventEmitter<IRowDataEventArgs>();
23,240✔
839

840
    /**
841
     * Emitted when a row is deleted.
842
     *
843
     * @remarks
844
     * Returns an `IRowDataEventArgs` object.
100✔
845
     * @example
846
     * ```html
847
     * <igx-grid #grid [data]="localData" (rowDeleted)="rowDeleted($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
406,444✔
848
     * ```
849
     */
850
    @Output()
156✔
851
    public rowDeleted = new EventEmitter<IRowDataEventArgs>();
145✔
852

145✔
853
    /**
145✔
854
     * Emmited when deleting a row.
145✔
855
     *
856
     * @remarks
857
     * This event is cancelable.
858
     * Returns an `IGridEditEventArgs` object.
859
     * @example
860
     * ```html
861
     * <igx-grid #grid [data]="localData" (rowDelete)="rowDelete($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
5,621,554✔
862
     * ```
1,923✔
863
     */
864
    @Output()
5,619,631✔
865
    public rowDelete = new EventEmitter<IGridEditEventArgs>();
866

867
    /**
868
     * Emmited just before the newly added row is commited.
869
     *
870
     * @remarks
×
871
     * This event is cancelable.
872
     * Returns an `IGridEditEventArgs` object.
873
     * @example
874
     * ```html
875
     * <igx-grid #grid [data]="localData" (rowAdd)="rowAdd($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
876
     * ```
8✔
877
     */
6✔
878
    @Output()
879
    public rowAdd = new EventEmitter<IGridEditEventArgs>();
2✔
880

2✔
881
    /**
3✔
882
     * Emitted after column is resized.
2✔
883
     *
884
     * @remarks
885
     * Returns the `IgxColumnComponent` object's old and new width.
915,512✔
886
     * @example
887
     * ```html
888
     * <igx-grid #grid [data]="localData" (columnResized)="resizing($event)" [autoGenerate]="true"></igx-grid>
33✔
889
     * ```
890
     */
33✔
891
    @Output()
33✔
892
    public columnResized = new EventEmitter<IColumnResizeEventArgs>();
893

894
    /**
895
     * Emitted when a cell is right clicked.
229,497✔
896
     *
897
     * @remarks
898
     * Returns the `IgxGridCell` object.
494✔
899
     * ```html
494✔
900
     * <igx-grid #grid [data]="localData" (contextMenu)="contextMenu($event)" [autoGenerate]="true"></igx-grid>
72✔
901
     * ```
72✔
902
     */
903
    @Output()
904
    public contextMenu = new EventEmitter<IGridCellEventArgs>();
905

180,881✔
906
    /**
907
     * Emitted when a cell is double clicked.
908
     *
135✔
909
     * @remarks
910
     * Returns the `IgxGridCell` object.
135✔
911
     * @example
135✔
912
     * ```html
913
     * <igx-grid #grid [data]="localData" (doubleClick)="dblClick($event)" [autoGenerate]="true"></igx-grid>
914
     * ```
915
     */
916
    @Output()
917
    public doubleClick = new EventEmitter<IGridCellEventArgs>();
918

574✔
919
    /**
574✔
920
     * Emitted before column visibility is changed.
426✔
921
     *
922
     * @remarks
923
     * Args: { column: any, newValue: boolean }
924
     * @example
175✔
925
     * ```html
926
     * <igx-grid (columnVisibilityChanging)="visibilityChanging($event)"></igx-grid>
927
     * ```
928
     */
2,755,609✔
929
    @Output()
930
    public columnVisibilityChanging = new EventEmitter<IColumnVisibilityChangingEventArgs>();
931

932
    /**
933
     * Emitted after column visibility is changed.
934
     *
151,611✔
935
     * @remarks
936
     * Args: { column: IgxColumnComponent, newValue: boolean }
937
     * @example
938
     * ```html
939
     * <igx-grid (columnVisibilityChanged)="visibilityChanged($event)"></igx-grid>
940
     * ```
62,867✔
941
     */
942
    @Output()
943
    public columnVisibilityChanged = new EventEmitter<IColumnVisibilityChangedEventArgs>();
944

945
    /**
946
     * Emitted when column moving starts.
947
     *
948
     * @remarks
949
     * Returns the moved `IgxColumnComponent` object.
950
     * @example
951
     * ```html
39,806✔
952
     * <igx-grid (columnMovingStart)="movingStart($event)"></igx-grid>
953
     * ```
954
     */
955
    @Output()
956
    public columnMovingStart = new EventEmitter<IColumnMovingStartEventArgs>();
957

958
    /**
959
     * Emitted during the column moving operation.
960
     *
961
     * @remarks
962
     * Returns the source and target `IgxColumnComponent` objects. This event is cancelable.
100,615✔
963
     * @example
964
     * ```html
965
     * <igx-grid (columnMoving)="moving($event)"></igx-grid>
966
     * ```
967
     */
968
    @Output()
13,449✔
969
    public columnMoving = new EventEmitter<IColumnMovingEventArgs>();
10,957✔
970

971
    /**
2,492✔
972
     * Emitted when column moving ends.
422✔
973
     *
441✔
974
     * @remarks
422✔
975
     * Returns the source and target `IgxColumnComponent` objects.
976
     * @example
2,492✔
977
     * ```html
77✔
978
     * <igx-grid (columnMovingEnd)="movingEnds($event)"></igx-grid>
979
     * ```
2,473✔
980
     */
2,473✔
981
    @Output()
982
    public columnMovingEnd = new EventEmitter<IColumnMovingEndEventArgs>();
983

984
    /**
985
     * Emitted when keydown is triggered over element inside grid's body.
986
     *
9,788✔
987
     * @remarks
9,788✔
988
     * This event is fired only if the key combination is supported in the grid.
5,722✔
989
     * Return the target type, target object and the original event. This event is cancelable.
1,064✔
990
     * @example
329✔
991
     * ```html
992
     *  <igx-grid (gridKeydown)="customKeydown($event)"></igx-grid>
9,788✔
993
     * ```
359✔
994
     */
995
    @Output()
9,788✔
996
    public gridKeydown = new EventEmitter<IGridKeydownEventArgs>();
997

998
    /**
999
     * Emitted when start dragging a row.
1000
     *
1001
     * @remarks
36,881✔
1002
     * Return the dragged row.
1003
     */
1004
    @Output()
1005
    public rowDragStart = new EventEmitter<IRowDragStartEventArgs>();
1006

1007
    /**
36,595✔
1008
     * Emitted when dropping a row.
57✔
1009
     *
1010
     * @remarks
36,538✔
1011
     * Return the dropped row.
183!
1012
     */
1013
    @Output()
36,355✔
1014
    public rowDragEnd = new EventEmitter<IRowDragEndEventArgs>();
479!
1015

1016
    /**
1017
     * Emitted when a copy operation is executed.
1018
     *
1019
     * @remarks
1020
     * Fired only if copy behavior is enabled through the [`clipboardOptions`]{@link IgxGridBaseDirective#clipboardOptions}.
1021
     */
36,725✔
1022
    @Output()
1023
    public gridCopy = new EventEmitter<IGridClipboardEvent>();
1024

1025
    /**
1026
     * @hidden @internal
1027
     */
36,590✔
1028
    @Output()
1029
    public expansionStatesChange = new EventEmitter<Map<any, boolean>>();
1030

1031
    /**
1032
     * Emitted when the expanded state of a row gets changed.
1033
     *
39,742✔
1034
     * @example
1035
     * ```html
1036
     * <igx-grid [data]="employeeData" (rowToggle)="rowToggle($event)" [autoGenerate]="true"></igx-grid>
1037
     * ```
1038
     */
1039
    @Output()
2,747✔
1040
    public rowToggle = new EventEmitter<IRowToggleEventArgs>();
1041

1042
    /**
1043
     * Emitted when the pinned state of a row is changed.
1044
     *
1045
     * @example
1046
     * ```html
221,463✔
1047
     * <igx-grid [data]="employeeData" (rowPinning)="rowPin($event)" [autoGenerate]="true"></igx-grid>
1048
     * ```
1049
     */
1050
    @Output()
1051
    public rowPinning = new EventEmitter<IPinRowEventArgs>();
1052

8,069✔
1053
    /**
1054
     * Emitted when the pinned state of a row is changed.
1055
     *
1056
     * @example
1057
     * ```html
1058
     * <igx-grid [data]="employeeData" (rowPinned)="rowPin($event)" [autoGenerate]="true"></igx-grid>
217,970✔
1059
     * ```
1060
     */
1061
    @Output()
3,885✔
1062
    public rowPinned = new EventEmitter<IPinRowEventArgs>();
3,885✔
1063

3,885✔
1064
    /**
3,885✔
1065
     * Emmited when the active node is changed.
3,885✔
1066
     *
3,885✔
1067
     * @example
3,885✔
1068
     * ```
3,885✔
1069
     * <igx-grid [data]="data" [autoGenerate]="true" (activeNodeChange)="activeNodeChange($event)"></igx-grid>
3,885✔
1070
     * ```
3,885✔
1071
     */
3,885✔
1072
    @Output()
3,885✔
1073
    public activeNodeChange = new EventEmitter<IActiveNodeChangeEventArgs>();
3,885✔
1074

3,885✔
1075
    /**
3,885✔
1076
     * Emitted before sorting is performed.
3,885✔
1077
     *
3,885✔
1078
     * @remarks
3,885✔
1079
     * Returns the sorting expressions.
3,885✔
1080
     * @example
3,885✔
1081
     * ```html
3,885✔
1082
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingExpressionsChange)="sortingExprChange($event)"></igx-grid>
3,885✔
1083
     * ```
3,885✔
1084
     */
3,885✔
1085
    @Output()
3,885✔
1086
    public sortingExpressionsChange = new EventEmitter<ISortingExpression[]>();
3,885✔
1087

3,885✔
1088

3,885✔
1089
    /**
1090
     * Emitted when an export process is initiated by the user.
1091
     *
1092
     * @example
1093
     * ```typescript
1094
     * toolbarExporting(event: IGridToolbarExportEventArgs){
1095
     *     const toolbarExporting = event;
1096
     * }
1097
     * ```
1098
     */
1099
    @Output()
1100
    public toolbarExporting = new EventEmitter<IGridToolbarExportEventArgs>();
1101

1102
    /* End of toolbar related definitions */
1103

1104
    /**
1105
     * Emitted when making a range selection.
1106
     *
3,885✔
1107
     * @remarks
3,885✔
1108
     * Range selection can be made either through drag selection or through keyboard selection.
3,885✔
1109
     */
3,885✔
1110
    @Output()
3,885✔
1111
    public rangeSelected = new EventEmitter<GridSelectionRange>();
3,885✔
1112

3,885✔
1113
    /** Emitted after the ngAfterViewInit hook. At this point the grid exists in the DOM */
3,885✔
1114
    @Output()
3,885✔
1115
    public rendered = new EventEmitter<boolean>();
3,885✔
1116

3,885✔
1117
    /**
3,885✔
1118
     * @hidden @internal
3,885✔
1119
     */
3,885✔
1120
    @Output()
3,885✔
1121
    public localeChange = new EventEmitter<boolean>();
3,885✔
1122

3,885✔
1123
    /**
3,885✔
1124
     * Emitted before the grid's data view is changed because of a data operation, rebinding, etc.
3,885✔
1125
     *
3,885✔
1126
     * @example
3,885✔
1127
     * ```typescript
3,885✔
1128
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanging)='handleDataChangingEvent()'></igx-grid>
3,885✔
1129
     * ```
3,885✔
1130
     */
3,885✔
1131
    @Output()
3,885✔
1132
    public dataChanging = new EventEmitter<IForOfDataChangingEventArgs>();
3,885✔
1133

3,885✔
1134
    /**
3,885✔
1135
     * Emitted after the grid's data view is changed because of a data operation, rebinding, etc.
3,885✔
1136
     *
3,885✔
1137
     * @example
3,885✔
1138
     * ```typescript
3,885✔
1139
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanged)='handleDataChangedEvent()'></igx-grid>
3,885✔
1140
     * ```
3,885✔
1141
     */
3,885✔
1142
    @Output()
3,885✔
1143
    public dataChanged = new EventEmitter<any>();
3,885✔
1144

3,885✔
1145

3,885✔
1146
    /**
3,885✔
1147
     * @hidden @internal
3,885✔
1148
     */
3,885✔
1149
    @ViewChild(IgxSnackbarComponent)
3,885✔
1150
    public addRowSnackbar: IgxSnackbarComponent;
3,885✔
1151

3,885✔
1152
    /**
3,885✔
1153
     * @hidden @internal
3,885✔
1154
     */
3,885✔
1155
    @ViewChild(IgxGridColumnResizerComponent)
3,885✔
1156
    public resizeLine: IgxGridColumnResizerComponent;
3,885✔
1157

3,885✔
1158
    /**
3,885✔
1159
     * @hidden @internal
3,885✔
1160
     */
3,885✔
1161
    @ViewChild('loadingOverlay', { read: IgxToggleDirective, static: true })
3,885✔
1162
    public loadingOverlay: IgxToggleDirective;
3,885✔
1163

3,885✔
1164
    /**
3,885✔
1165
     * @hidden @internal
3,885✔
1166
     */
3,885✔
1167
    @ViewChild('igxLoadingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
3,885✔
1168
    public loadingOutlet: IgxOverlayOutletDirective;
3,885✔
1169

3,885✔
1170
    /**
3,885✔
1171
     * @hidden @internal
3,885✔
1172
     */
3,885✔
1173
    @ContentChildren(IgxColumnComponent, { read: IgxColumnComponent, descendants: true })
3,885✔
1174
    public columnList: QueryList<IgxColumnComponent> = new QueryList<IgxColumnComponent>();
3,885✔
1175

3,885✔
1176
    @ContentChild(IgxActionStripComponent)
3,885✔
1177
    public actionStrip: IgxActionStripComponent;
1178

1179
    /**
1180
     * @hidden @internal
1181
     */
3,885✔
1182
    @ContentChild(IgxExcelStyleLoadingValuesTemplateDirective, { read: IgxExcelStyleLoadingValuesTemplateDirective, static: true })
1183
    public excelStyleLoadingValuesTemplateDirective: IgxExcelStyleLoadingValuesTemplateDirective;
1184

1185
    /**
1186
     * A template reference for the template when the filtered grid is empty.
1187
     *
1188
     * @example
3,885✔
1189
     * ```
1190
     * const emptyTempalte = this.grid.emptyGridTemplate;
1191
     * ```
1192
     */
3,885✔
1193
    @ViewChild('emptyFilteredGrid', { read: TemplateRef, static: true })
1194
    public emptyFilteredGridTemplate: TemplateRef<any>;
1195

1196
    /**
3,885✔
1197
     * A template reference for the template when the grid is empty.
1198
     *
1199
     * @example
1200
     * ```
3,885✔
1201
     * const emptyTempalte = this.grid.emptyGridTemplate;
1202
     * ```
1203
     */
1204
    @ViewChild('defaultEmptyGrid', { read: TemplateRef, static: true })
3,885✔
1205
    public emptyGridDefaultTemplate: TemplateRef<any>;
1206

1207
    /**
1208
     * @hidden @internal
1209
     */
1210
    @ViewChild('defaultLoadingGrid', { read: TemplateRef, static: true })
1211
    public loadingGridDefaultTemplate: TemplateRef<any>;
1212

1213
    /**
1214
     * @hidden @internal
3,885✔
1215
     */
1216
    @ViewChild('scrollContainer', { read: IgxGridForOfDirective, static: true })
1217
    public parentVirtDir: IgxGridForOfDirective<any>;
1218

3,885✔
1219
    /**
1220
     * @hidden
3,885✔
1221
     * @internal
1222
     */
3,885✔
1223
    @ContentChildren(IgxHeadSelectorDirective, { read: TemplateRef, descendants: false })
1224
    public headSelectorsTemplates: QueryList<TemplateRef<IgxHeadSelectorTemplateContext>>;
3,885✔
1225

1226
    /**
3,885✔
1227
     * @hidden
1228
     * @internal
1229
     */
1230
    @ContentChildren(IgxRowSelectorDirective, { read: TemplateRef, descendants: false })
3,885✔
1231
    public rowSelectorsTemplates: QueryList<TemplateRef<IgxRowSelectorTemplateContext>>;
1232

1233
    /**
1234
     * @hidden
3,885✔
1235
     * @internal
1236
     */
1237
    @ContentChildren(IgxRowDragGhostDirective, { read: TemplateRef, descendants: false })
1238
    public dragGhostCustomTemplates: QueryList<TemplateRef<IgxGridRowDragGhostContext>>;
3,885✔
1239

1240

1241
    /**
1242
     * Gets the custom template, if any, used for row drag ghost.
3,885✔
1243
     */
1244
    @Input()
1245
    public get dragGhostCustomTemplate() {
1246
        return this._dragGhostCustomTemplate || this.dragGhostCustomTemplates?.first;
3,885✔
1247
    }
1248

1249
    /**
1250
     * Sets a custom template for the row drag ghost.
3,885✔
1251
     *```html
1252
     * <ng-template #template igxRowDragGhost>
1253
     *    <igx-icon>menu</igx-icon>
1254
     * </ng-template>
3,885✔
1255
     * ```
1256
     * ```typescript
1257
     * @ViewChild("'template'", {read: TemplateRef })
1258
     * public template: TemplateRef<any>;
3,885✔
1259
     * this.grid.dragGhostCustomTemplate = this.template;
3,885✔
1260
     * ```
1261
     */
1262
    public set dragGhostCustomTemplate(template: TemplateRef<IgxGridRowDragGhostContext>) {
1263
        this._dragGhostCustomTemplate = template;
3,885✔
1264
    }
1265

1266

1267
    /**
3,885✔
1268
     * @hidden @internal
1269
     */
1270
    @ViewChild('verticalScrollContainer', { read: IgxGridForOfDirective, static: true })
1271
    public verticalScrollContainer: IgxGridForOfDirective<any>;
3,885✔
1272

1273
    /**
1274
     * @hidden @internal
1275
     */
3,885✔
1276
    @ViewChild('verticalScrollHolder', { read: IgxGridForOfDirective, static: true })
1277
    public verticalScroll: IgxGridForOfDirective<any>;
1278

1279
    /**
3,885✔
1280
     * @hidden @internal
1281
     */
1282
    @ViewChild('scr', { read: ElementRef, static: true })
1283
    public scr: ElementRef;
3,885✔
1284

1285
    /** @hidden @internal */
1286
    @ViewChild('headSelectorBaseTemplate', { read: TemplateRef, static: true })
1287
    public headerSelectorBaseTemplate: TemplateRef<any>;
3,885✔
1288

1289
    /**
1290
     * @hidden @internal
1291
     */
3,885✔
1292
    @ViewChild('footer', { read: ElementRef })
1293
    public footer: ElementRef;
1294

1295
    public get headerContainer() {
3,885✔
1296
        return this.theadRow?.headerForOf;
1297
    }
1298

1299
    public get headerSelectorContainer() {
3,885✔
1300
        return this.theadRow?.headerSelectorContainer;
1301
    }
1302

1303
    public get headerDragContainer() {
3,885✔
1304
        return this.theadRow?.headerDragContainer;
1305
    }
1306

1307
    public get headerGroupContainer() {
3,885✔
1308
        return this.theadRow?.headerGroupContainer;
1309
    }
1310

1311
    public get filteringRow(): IgxGridFilteringRowComponent {
3,885✔
1312
        return this.theadRow?.filterRow;
1313
    }
1314

1315
    /** @hidden @internal */
3,885✔
1316
    @ViewChild(IgxGridHeaderRowComponent, { static: true })
3,885✔
1317
    public theadRow: IgxGridHeaderRowComponent;
3,885✔
1318

3,885✔
1319
    /** @hidden @internal */
3,885✔
1320
    @ViewChild(IgxGridGroupByAreaComponent)
3,885✔
1321
    public groupArea: IgxGridGroupByAreaComponent;
3,885✔
1322

3,885✔
1323
    /**
3,885✔
1324
     * @hidden @internal
3,885✔
1325
     */
3,885✔
1326
    @ViewChild('tbody', { static: true })
3,885✔
1327
    public tbody: ElementRef;
3,885✔
1328

3,885✔
1329
    /**
3,885✔
1330
     * @hidden @internal
3,885✔
1331
     */
3,885✔
1332
    @ViewChild('pinContainer', { read: ElementRef })
3,885✔
1333
    public pinContainer: ElementRef;
3,885✔
1334

3,885✔
1335
    /**
3,885✔
1336
     * @hidden @internal
3,885✔
1337
     */
3,885✔
1338
    @ViewChild('tfoot', { static: true })
3,885✔
1339
    public tfoot: ElementRef<HTMLElement>;
3,885✔
1340

3,885✔
1341
    /**
1342
     * @hidden @internal
1343
     */
1344
    @ViewChild('igxRowEditingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1345
    public rowEditingOutletDirective: IgxOverlayOutletDirective;
1346

3,885✔
1347
    /**
1348
     * @hidden @internal
1349
     */
1350
    @ViewChildren(IgxTemplateOutletDirective, { read: IgxTemplateOutletDirective })
1351
    public tmpOutlets: QueryList<any> = new QueryList<any>();
3,885✔
1352

3,885✔
1353
    /**
3,885✔
1354
     * @hidden
3,885✔
1355
     * @internal
1356
     */
3,885✔
1357
    @ViewChild('dragIndicatorIconBase', { read: TemplateRef, static: true })
3,885✔
1358
    public dragIndicatorIconBase: TemplateRef<any>;
3,885✔
1359

3,885✔
1360
    /**
3,885✔
1361
     * @hidden @internal
3,885✔
1362
     */
3,885✔
1363
    @ContentChildren(IgxRowEditTemplateDirective, { descendants: false, read: TemplateRef })
3,885✔
1364
    public rowEditCustomDirectives: QueryList<TemplateRef<IgxGridRowEditTemplateContext>>;
3,885✔
1365

3,885✔
1366
    /**
3,885✔
1367
     * @hidden @internal
3,885✔
1368
     */
3,885✔
1369
    @ContentChildren(IgxRowEditTextDirective, { descendants: false, read: TemplateRef })
3,885✔
1370
    public rowEditTextDirectives: QueryList<TemplateRef<IgxGridRowEditTextTemplateContext>>;
3,885✔
1371

3,885✔
1372
    /**
1373
     * Gets the row edit text template.
1374
     */
1375
    @Input()
1376
    public get rowEditTextTemplate(): TemplateRef<IgxGridRowEditTextTemplateContext> {
1377
        return this._rowEditTextTemplate || this.rowEditTextDirectives?.first;
1378
    }
3,885✔
1379
    /**
1380
     * Sets the row edit text template.
1381
     *```html
1382
     * <ng-template #template igxRowEditText let-rowChangesCount>
1383
     * Changes: {{rowChangesCount}}
1384
     * </ng-template>
1385
     * ```
3,885✔
1386
     *```typescript
3,885✔
1387
     * @ViewChild('template', {read: TemplateRef })
3,885✔
1388
     * public template: TemplateRef<any>;
3,885✔
1389
     * this.grid.rowEditTextTemplate = this.template;
3,885✔
1390
     * ```
3,885✔
1391
     */
3,885✔
1392
    public set rowEditTextTemplate(template: TemplateRef<IgxGridRowEditTextTemplateContext>) {
3,885✔
1393
        this._rowEditTextTemplate = template;
1394
    }
1395

1396
    /**
3,885✔
1397
     * @hidden @internal
×
1398
     */
×
1399
    @ContentChild(IgxRowAddTextDirective, { read: TemplateRef })
×
1400
    public rowAddText: TemplateRef<IgxGridEmptyTemplateContext>;
1401

×
1402
    /**
×
1403
     * Gets the row add text template.
×
1404
     */
1405
    @Input()
1406
    public get rowAddTextTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
3,885✔
1407
        return this._rowAddTextTemplate || this.rowAddText;
3,885✔
1408
    }
3,885✔
1409
    /**
3,885✔
1410
     * Sets the row add text template.
1411
     *```html
1412
     * <ng-template #template igxRowAddText>
4✔
1413
     * Adding Row
1414
     * </ng-template>
1415
     * ```
1416
     *```typescript
1417
     * @ViewChild('template', {read: TemplateRef })
1418
     * public template: TemplateRef<any>;
1419
     * this.grid.rowAddTextTemplate = this.template;
1,055✔
1420
     * ```
1421
     */
1422
    public set rowAddTextTemplate(template: TemplateRef<IgxGridEmptyTemplateContext>) {
1423
        this._rowAddTextTemplate = template;
1424
    }
1425

1426
    /**
1,221✔
1427
     * @hidden @internal
1428
     */
1429
    @ContentChildren(IgxRowEditActionsDirective, { descendants: false, read: TemplateRef })
1430
    public rowEditActionsDirectives: QueryList<TemplateRef<IgxGridRowEditActionsTemplateContext>>;
1431

1432
    /**
1433
     * Gets the row edit actions template.
1,221✔
1434
     */
1435
    @Input()
1436
    public get rowEditActionsTemplate(): TemplateRef<IgxGridRowEditActionsTemplateContext> {
1437
        return this._rowEditActionsTemplate || this.rowEditActionsDirectives?.first;
1438
    }
1439
    /**
411,202✔
1440
     * Sets the row edit actions template.
1441
     *```html
1442
     * <ng-template #template igxRowEditActions let-endRowEdit>
1443
     *     <button type="button" igxButton igxRowEditTabStop (click)="endRowEdit(false)">Cancel</button>
1444
     *     <button type="button" igxButton igxRowEditTabStop (click)="endRowEdit(true)">Apply</button>
1445
     * </ng-template>
×
1446
     * ```
1447
     *```typescript
1448
     * @ViewChild('template', {read: TemplateRef })
1449
     * public template: TemplateRef<any>;
1450
     * this.grid.rowEditActionsTemplate = this.template;
1451
     * ```
1452
     */
191,139✔
1453
    public set rowEditActionsTemplate(template: TemplateRef<IgxGridRowEditActionsTemplateContext>) {
187✔
1454
        this._rowEditActionsTemplate = template;
1455
    }
190,952✔
1456

189,256✔
1457
    /**
1458
     * The custom template, if any, that should be used when rendering a row expand indicator.
191,139✔
1459
     */
1460
    @ContentChild(IgxRowExpandedIndicatorDirective, { read: TemplateRef })
1461
    public rowExpandedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
1462

1463
    /**
1464
     * The custom template, if any, that should be used when rendering a row collapse indicator.
1465
     */
371✔
1466
    @ContentChild(IgxRowCollapsedIndicatorDirective, { read: TemplateRef })
1467
    public rowCollapsedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
1468

1469
    /**
1470
     * The custom template, if any, that should be used when rendering a header expand indicator.
1471
     */
1472
    @ContentChild(IgxHeaderExpandIndicatorDirective, { read: TemplateRef })
1473
    public headerExpandIndicatorTemplate: TemplateRef<IgxGridTemplateContext> = null;
1474

1475
    /**
1476
     * The custom template, if any, that should be used when rendering a header collapse indicator.
1477
     */
1478
    @ContentChild(IgxHeaderCollapseIndicatorDirective, { read: TemplateRef })
1,211✔
1479
    public headerCollapseIndicatorTemplate: TemplateRef<IgxGridTemplateContext> = null;
1480

1481
    /**
1482
     * The custom template, if any, that should be used when rendering a row expand indicator.
1483
     */
1484
    @ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef })
1485
    public excelStyleHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
535✔
1486

1✔
1487
    /**
1!
1488
     * Gets the excel style header icon.
1✔
1489
    */
1✔
1490
    @Input()
1491
    public get excelStyleHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1492
        return this._excelStyleHeaderIconTemplate || this.excelStyleHeaderIconDirectiveTemplate;
1493
    }
1494

1495
    /**
1496
     * Sets the excel style header icon.
1497
     *```html
1498
     *<ng-template #template igxExcelStyleHeaderIcon>
1499
     * <igx-icon>filter_alt</igx-icon>
1500
     *</ng-template>
1501
     * ```
1502
     *```typescript
276,476✔
1503
     * @ViewChild('template', {read: TemplateRef })
1504
     * public template: TemplateRef<any>;
1505
     * this.grid.excelStyleHeaderIconTemplate = this.template;
1506
     * ```
1507
    */
1508
    public set excelStyleHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1509
        this._excelStyleHeaderIconTemplate = template;
1510
    }
1511

2,166!
1512

1513
    /**
1514
     * @hidden
1515
     * @internal
1516
     */
1517
    @ContentChild(IgxSortAscendingHeaderIconDirective, { read: TemplateRef })
1518
    public sortAscendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
1519

835,718✔
1520
    /**
1521
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1522
     */
1523
    @Input()
1524
    public get sortAscendingHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1525
        return this._sortAscendingHeaderIconTemplate;
1526
    }
1527

835,826✔
1528
    /**
835,826✔
1529
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1530
     *```html
1531
     * <ng-template #template igxSortAscendingHeaderIcon>
1532
     *    <igx-icon>expand_less</igx-icon>
1533
     * </ng-template>
1534
     * ```
1535
     * ```typescript
432,126✔
1536
     * @ViewChild("'template'", {read: TemplateRef })
1537
     * public template: TemplateRef<any>;
1538
     * this.grid.sortAscendingHeaderIconTemplate = this.template;
1539
     * ```
1540
     */
1541
    public set sortAscendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1542
        this._sortAscendingHeaderIconTemplate = template;
6,666✔
1543
    }
1544

1545

1546
    @ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef })
1547
    public sortDescendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
1548

1549
    /**
3,535,425✔
1550
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1551
     */
1552
    @Input()
1553
    public get sortDescendingHeaderIconTemplate() {
1554
        return this._sortDescendingHeaderIconTemplate;
1555
    }
1556

3,407✔
1557
    /**
3,407✔
1558
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in desc order.
3,407✔
1559
     *```html
3,407✔
1560
     * <ng-template #template igxSortDescendingHeaderIcon>
3,407✔
1561
     *    <igx-icon>expand_more</igx-icon>
3,407✔
1562
     * </ng-template>
3,407✔
1563
     * ```
1564
     * ```typescript
1565
     * @ViewChild("'template'", {read: TemplateRef })
1566
     * public template: TemplateRef<any>;
1567
     * this.grid.sortDescendingHeaderIconTemplate = this.template;
1568
     * ```
1569
     */
3,407✔
1570
    public set sortDescendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
3,407✔
1571
        this._sortDescendingHeaderIconTemplate = template;
603!
1572
    }
1573

1574
    /**
1575
     * @hidden
1576
     * @internal
1577
     */
52!
1578
    @ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef })
50✔
1579
    public sortHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
50✔
1580

50✔
1581
    /**
1582
     * Gets custom template, if any, that should be used when rendering a header sorting indicator when columns are not sorted.
1583
     */
3,407✔
1584
    @Input()
3,407✔
1585
    public get sortHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
185✔
1586
        return this._sortHeaderIconTemplate;
185✔
1587
    }
1588

3,407✔
1589
    /**
3,407✔
1590
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are not sorted.
1591
     *```html
1,587✔
1592
     * <ng-template #template igxSortHeaderIcon>
1593
     *    <igx-icon>unfold_more</igx-icon>
1,587✔
1594
     * </ng-template>
1,226✔
1595
     * ```
1596
     * ```typescript
1597
     * @ViewChild("'template'", {read: TemplateRef })
1598
     * public template: TemplateRef<any>;
3,407✔
1599
     * this.grid.sortHeaderIconTemplate = this.template;
3,407✔
1600
     * ```
3,407✔
1601
     */
819✔
1602
    public set sortHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
79✔
1603
        this._sortHeaderIconTemplate = template;
79!
1604
    }
79✔
1605

1606
    /**
1607
     * @hidden
1608
     * @internal
3,407✔
1609
     */
507✔
1610
    @ContentChildren(IgxDragIndicatorIconDirective, { read: TemplateRef, descendants: false })
1611
    public dragIndicatorIconTemplates: QueryList<TemplateRef<IgxGridEmptyTemplateContext>>;
507✔
1612

75✔
1613
    /**
75!
1614
     * @hidden @internal
75✔
1615
     */
75✔
1616
    @ViewChildren(IgxRowEditTabStopDirective)
1617
    public rowEditTabsDEFAULT: QueryList<IgxRowEditTabStopDirective>;
75✔
1618

1619
    /**
1620
     * @hidden @internal
432✔
1621
     */
20✔
1622
    @ContentChildren(IgxRowEditTabStopDirective, { descendants: true })
1623
    public rowEditTabsCUSTOM: QueryList<IgxRowEditTabStopDirective>;
412✔
1624

294✔
1625
    /**
1626
     * @hidden @internal
1627
     */
3,407✔
1628
    @ViewChild('rowEditingOverlay', { read: IgxToggleDirective })
451✔
1629
    public rowEditingOverlay: IgxToggleDirective;
29✔
1630

29✔
1631
    /**
29✔
1632
     * @hidden @internal
1633
     */
422✔
1634
    @HostBinding('attr.tabindex')
422✔
1635
    public tabindex = 0;
173✔
1636

1637
    /**
1638
     * @hidden @internal
6,450✔
1639
     */
3,113✔
1640
    @HostBinding('attr.role')
1641
    public hostRole = 'grid';
1642

3,113✔
1643
    /** @hidden @internal */
105✔
1644
    @ContentChildren(IgxGridToolbarComponent)
105✔
1645
    public toolbar: QueryList<IgxGridToolbarComponent>;
1646

3,113✔
1647
    /** @hidden @internal */
1648
    @ContentChildren(IgxPaginatorComponent)
3,407✔
1649
    protected paginationComponents: QueryList<IgxPaginatorComponent>;
1650

1651
    /**
698✔
1652
     * @hidden @internal
698✔
1653
     */
1654
    @ViewChild('igxFilteringOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
3,407✔
1655
    protected _outletDirective: IgxOverlayOutletDirective;
1,235✔
1656

1657
    /**
3,407✔
1658
     * @hidden @internal
42✔
1659
     */
42✔
1660
    @ViewChild('defaultExpandedTemplate', { read: TemplateRef, static: true })
42!
1661
    protected defaultExpandedTemplate: TemplateRef<any>;
42✔
1662

1663
    /**
42✔
1664
     * @hidden @internal
1665
     */
1666
    @ViewChild('defaultCollapsedTemplate', { read: TemplateRef, static: true })
1667
    protected defaultCollapsedTemplate: TemplateRef<any>;
1668

1669
    /**
1670
     * @hidden @internal
3,407✔
1671
     */
3,407✔
1672
    @ViewChild('defaultESFHeaderIcon', { read: TemplateRef, static: true })
3,407✔
1673
    protected defaultESFHeaderIconTemplate: TemplateRef<any>;
3,407✔
1674

1675
    @ViewChildren('summaryRow', { read: IgxSummaryRowComponent })
16,808✔
1676
    protected _summaryRowList: QueryList<IgxSummaryRowComponent>;
3,407✔
1677

3,407✔
1678
    @ViewChildren('row')
1679
    private _rowList: QueryList<IgxGridRowComponent>;
1680

1681
    @ViewChildren('pinnedRow')
1682
    private _pinnedRowList: QueryList<IgxGridRowComponent>;
1683

1684
    /**
202,630✔
1685
     * @hidden @internal
1686
     */
1687
    @ViewChild('defaultRowEditTemplate', { read: TemplateRef, static: true })
1688
    private defaultRowEditTemplate: TemplateRef<IgxGridRowEditTemplateContext>;
1689

1690
    @ViewChildren(IgxRowDirective, { read: IgxRowDirective })
59✔
1691
    private _dataRowList: QueryList<IgxRowDirective>;
57✔
1692

1!
1693
    @HostBinding('class')
57✔
1694
    private get hostClass(): string {
1695
        return this.getComponentDensityClass('igx-grid');
1696
    }
1697

1698
    /**
1699
     * Gets/Sets the resource strings.
1700
     *
32,028✔
1701
     * @remarks
32,028✔
1702
     * By default it uses EN resources.
15,578✔
1703
     */
5,410✔
1704
    @Input()
1705
    public set resourceStrings(value: IGridResourceStrings) {
15,578✔
1706
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
1707
    }
1708

1709
    public get resourceStrings(): IGridResourceStrings {
1710
        if (!this._resourceStrings) {
1711
            this._resourceStrings = CurrentResourceStrings.GridResStrings;
1712
        }
1713
        return this._resourceStrings;
1,534✔
1714
    }
49✔
1715

49✔
1716
    /**
49✔
1717
     * Gets/Sets the filtering logic of the `IgxGridComponent`.
49✔
1718
     *
1719
     * @remarks
1,485✔
1720
     * The default is AND.
47✔
1721
     * @example
1722
     * ```html
1723
     * <igx-grid [data]="Data" [autoGenerate]="true" [filteringLogic]="filtering"></igx-grid>
1,438✔
1724
     * ```
1725
     */
1726
    @WatchChanges()
1727
    @Input()
1728
    public get filteringLogic() {
1729
        return this._filteringExpressionsTree.operator;
1730
    }
1731

32,168✔
1732
    public set filteringLogic(value: FilteringLogic) {
32,168✔
1733
        this._filteringExpressionsTree.operator = value;
32,168✔
1734
    }
1735

1736
    /**
1737
     * Gets/Sets the filtering state.
1738
     *
1739
     * @example
1740
     * ```html
43,219✔
1741
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(filteringExpressionsTree)]="model.filteringExpressions"></igx-grid>
43,219✔
1742
     * ```
43,219✔
1743
     * @remarks
1744
     * Supports two-way binding.
1745
     */
1746
    @WatchChanges()
1747
    @Input()
1748
    public get filteringExpressionsTree() {
10,020✔
1749
        return this._filteringExpressionsTree;
32,028✔
1750
    }
32,022✔
1751

1752
    public set filteringExpressionsTree(value) {
32,028✔
1753
        if (value && value instanceof FilteringExpressionsTree) {
32,028✔
1754
            const val = (value as FilteringExpressionsTree);
32,028✔
1755
            for (let index = 0; index < val.filteringOperands.length; index++) {
32,028✔
1756
                if (!(val.filteringOperands[index] instanceof FilteringExpressionsTree)) {
32,028✔
1757
                    const newExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, val.filteringOperands[index].fieldName);
153,094✔
1758
                    newExpressionsTree.filteringOperands.push(val.filteringOperands[index] as IFilteringExpression);
1759
                    val.filteringOperands[index] = newExpressionsTree;
1760
                }
1761
            }
1762

1763
            value.type = FilteringExpressionsTreeType.Regular;
3,302✔
1764
            this._filteringExpressionsTree = value;
5✔
1765
            this.filteringPipeTrigger++;
1766
            this.filteringExpressionsTreeChange.emit(this._filteringExpressionsTree);
3,302✔
1767

5✔
1768
            if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
1769
                this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
3,302✔
1770
                this._filteredData = null;
5✔
1771
            }
1772

3,302✔
1773
            this.filteringService.refreshExpressions();
3,302✔
1774
            this.selectionService.clearHeaderCBState();
3,302✔
1775
            this.summaryService.clearSummaryCache();
3,302✔
1776
            this.notifyChanges();
63✔
1777
        }
1778
    }
3,302✔
1779

107✔
1780
    /**
1781
     * Gets/Sets the advanced filtering state.
1782
     *
1783
     * @example
1784
     * ```typescript
1785
     * let advancedFilteringExpressionsTree = this.grid.advancedFilteringExpressionsTree;
1786
     * this.grid.advancedFilteringExpressionsTree = logic;
6,198✔
1787
     * ```
1788
     */
1789
    @WatchChanges()
1790
    @Input()
1791
    public get advancedFilteringExpressionsTree() {
1792
        return this._advancedFilteringExpressionsTree;
6,198✔
1793
    }
6,198✔
1794

1795
    public set advancedFilteringExpressionsTree(value) {
1796
        if (value && value instanceof FilteringExpressionsTree) {
1797
            value.type = FilteringExpressionsTreeType.Advanced;
190✔
1798
            this._advancedFilteringExpressionsTree = value;
190✔
1799
            this.filteringPipeTrigger++;
19✔
1800
        } else {
19✔
1801
            this._advancedFilteringExpressionsTree = null;
19✔
1802
        }
19✔
1803
        this.advancedFilteringExpressionsTreeChange.emit(this._advancedFilteringExpressionsTree);
1804

171✔
1805
        if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
171✔
1806
            this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
171✔
1807
            this._filteredData = null;
171✔
1808
        }
171✔
1809

1810
        this.selectionService.clearHeaderCBState();
1811
        this.summaryService.clearSummaryCache();
171✔
1812
        this.notifyChanges();
171✔
1813

171✔
1814
        // Wait for the change detection to update filtered data through the pipes and then emit the event.
1815
        requestAnimationFrame(() => this.filteringDone.emit(this._advancedFilteringExpressionsTree));
1816
    }
1817

3,366✔
1818
    /**
212✔
1819
     * Gets/Sets the locale.
1820
     *
112✔
1821
     * @remarks
1822
     * If not set, returns browser's language.
212✔
1823
     */
1824
    @Input()
112✔
1825
    public get locale(): string {
112✔
1826
        return this._locale;
112✔
1827
    }
112✔
1828

112✔
1829
    public set locale(value: string) {
112✔
1830
        if (value !== this._locale) {
1831
            this._locale = value;
212✔
1832
            this._currencyPositionLeft = undefined;
1833
            this.summaryService.clearSummaryCache();
72✔
1834
            this.pipeTrigger++;
72✔
1835
            this.notifyChanges();
72✔
1836
            this.localeChange.next();
72✔
1837
        }
72✔
1838
    }
1839

1840
    @Input()
1841
    public get pagingMode() {
3,154✔
1842
        return this._pagingMode;
1843
    }
1844

1845
    public set pagingMode(val: GridPagingMode) {
1846
        this._pagingMode = val;
1847
        this.pipeTrigger++;
1848
        this.notifyChanges(true);
1849
    }
6,180✔
1850

6,180✔
1851
    /**
341✔
1852
     * @deprecated in version 12.1.0. Define `igx-paginator` as a grid child component and paging will be enabled, otherwise disabled.
174✔
1853
     *
174✔
1854
     * Gets/Sets whether the paging feature is enabled.
174✔
1855
     *
1856
     *
174✔
1857
     * @remarks
1858
     * The default state is disabled (false).
1859
     * @example
167✔
1860
     * ```html
1861
     * <!-- old -->
1862
     * <igx-grid #grid [data]="Data" [paging]="true" [autoGenerate]="true"></igx-grid>
1863
     *
5,839✔
1864
     * <!-- new -->
5,839✔
1865
     * <igx-grid #grid [data]="Data" [autoGenerate]="true">
1866
     *   <igx-paginator></igx-paginator>
6,180✔
1867
     * </igx-grid>
1868
     * ```
1869
     */
1870
    @Input()
1871
    public get paging(): boolean {
1872
        return this._paging;
47,747✔
1873
    }
6,466✔
1874

44,609✔
1875
    public set paging(value: boolean) {
3,138✔
1876
        this._paging = value;
1877
        this.pipeTrigger++;
1878
    }
1879

1880
    /**
1881
     * @deprecated in version 12.1.0. Use `page` property from `igx-paginator` component instance instead.
1882
     *
17,027✔
1883
     * Gets/Sets the current page index.
17,027✔
1884
     *
3,420✔
1885
     *
3,420✔
1886
     * @example
3,420✔
1887
     * ```html
2,637✔
1888
     * <!-- old -->
1889
     * <igx-grid #grid [data]="Data" [page]="model.page" [autoGenerate]="true"></igx-grid>
3,420✔
1890
     *
313✔
1891
     * <!-- new -->
1892
     * <igx-grid #grid [data]="Data" [autoGenerate]="true">
3,420✔
1893
     *   <igx-paginator [(page)]="model.page"></igx-paginator>
1894
     * </igx-grid>
1895
     * ```
1896
     * @remarks
1897
     * Supports two-way binding.
1898
     */
3,420✔
1899
    @Input()
3,420✔
1900
    public get page(): number {
3,420✔
1901
        return this.paginator?.page || 0;
3,420✔
1902
    }
12✔
1903

20✔
1904
    public set page(val: number) {
20✔
1905
        if (this.paginator) {
19✔
1906
            this.paginator.page = val;
1907
        }
1908
    }
1909

3,420✔
1910
    /**
3,420✔
1911
     * @deprecated in version 12.1.0. Use `perPage` property from `igx-paginator` component instance instead
1912
     *
1913
     * Gets/Sets the number of visible items per page.
1914
     *
1915
     *
1916
     * @remarks
1917
     * The default is 15.
3,420✔
1918
     * @example
3,420✔
1919
     * ```html
3,420✔
1920
     * <!-- old -->
3,420✔
1921
     * <igx-grid #grid [data]="Data" [perPage]="model.perPage" [autoGenerate]="true"></igx-grid>
3,420✔
1922
     *
3,420✔
1923
     * <!-- new -->
3,420✔
1924
     * <igx-grid #grid [data]="Data" [autoGenerate]="true">
3,420✔
1925
     *   <igx-paginator [(perPage)]="model.perPage"></igx-paginator>
3,420✔
1926
     * </igx-grid>
1927
     * ```
1928
     */
182✔
1929
    @Input()
1930
    public get perPage(): number {
3,420✔
1931
        return this.paginator?.perPage || DEFAULT_ITEMS_PER_PAGE;
4✔
1932
    }
4✔
1933

4✔
1934
    public set perPage(val: number) {
1935
        this._perPage = val;
1936
        if (this.paginator) {
3,420✔
1937
            this.paginator.perPage = val;
1,945✔
1938
        }
72✔
1939
    }
72✔
1940

72✔
1941
    /**
1942
     * Gets/Sets if the row selectors are hidden.
1,945✔
1943
     *
11✔
1944
     * @remarks
1945
     *  By default row selectors are shown
1,945✔
1946
     */
1947
    @WatchChanges()
3,420✔
1948
    @Input()
1949
    public get hideRowSelectors() {
1950
        return this._hideRowSelectors;
1951
    }
1952

14,552✔
1953
    public set hideRowSelectors(value: boolean) {
450,688✔
1954
        this._hideRowSelectors = value;
450,688✔
1955
        this.notifyChanges(true);
450,688✔
1956
    }
1957

1958
    /**
1959
     * Gets/Sets whether rows can be moved.
1960
     *
1961
     * @example
17,000✔
1962
     * ```html
17,000✔
1963
     * <igx-grid #grid [rowDraggable]="true"></igx-grid>
3,903✔
1964
     * ```
1965
     */
13,097✔
1966
    @Input()
3,627✔
1967
    public get rowDraggable(): boolean {
3,627✔
1968
        return this._rowDrag && this.hasVisibleColumns;
3,627✔
1969
    }
3,627✔
1970

1971
    /**
9,470✔
1972
     * Gets/Sets the trigger for validators used when editing the grid.
3,993✔
1973
     *
3,993✔
1974
     * @example
1975
     * ```html
1976
     * <igx-grid #grid validationTrigger='blur'></igx-grid>
1977
     * ```
1978
     */
1979
    @Input()
1980
    public validationTrigger: GridValidationTrigger = 'change';
1981

1,229✔
1982

1983
    public set rowDraggable(val: boolean) {
1984
        this._rowDrag = val;
1985
        this.notifyChanges(true);
1986
    }
1987

3,342✔
1988
    /**
24,751✔
1989
     * @hidden
1990
     * @internal
3,342✔
1991
     */
3,342✔
1992
    public rowDragging = false;
3,342✔
1993

3,342✔
1994
    /**
3,342✔
1995
     * Gets the row ID that is being dragged.
3,342!
1996
     *
×
1997
     * @remarks
×
1998
     * The row ID is either the primaryKey value or the data record instance.
1999
     */
3,342✔
2000
    public dragRowID = null;
120✔
2001

120✔
2002
    /**
110✔
2003
     * Gets/Sets whether the rows are editable.
2004
     *
2005
     * @remarks
3,342✔
2006
     * By default it is set to false.
3,342✔
2007
     * @example
3,342✔
2008
     * ```html
3,342✔
2009
     * <igx-grid #grid [rowEditable]="true" [primaryKey]="'ProductID'" ></igx-grid>
3,342✔
2010
     * ```
2011
     */
2012
    @WatchChanges()
2013
    @Input()
2014
    public get rowEditable(): boolean {
2015
        return this._rowEditable;
2016
    }
2017

2018
    public set rowEditable(val: boolean) {
2019
        if (!this._init) {
2020
            this.refreshGridState();
2021
        }
2022
        this._rowEditable = val;
2023
        this.notifyChanges();
2024
    }
×
2025

×
2026
    /**
×
2027
     * Gets/Sets the height.
2028
     *
×
2029
     * @example
2030
     * ```html
2031
     * <igx-grid #grid [data]="Data" [height]="'305px'" [autoGenerate]="true"></igx-grid>
192,202✔
2032
     * ```
2033
     */
2034
    @WatchChanges()
656✔
2035
    @HostBinding('style.height')
656✔
2036
    @Input()
656✔
2037
    public get height(): string | null {
656✔
2038
        return this._height;
566✔
2039
    }
2040

2041
    public set height(value: string | null) {
2042
        if (this._height !== value) {
2043
            this._height = value;
2044
            this.nativeElement.style.height = value;
2045
            this.notifyChanges(true);
2046
        }
2047
    }
2048

2049
    /**
2050
     * @hidden @internal
8✔
2051
     */
8✔
2052
    @HostBinding('style.width')
2053
    public get hostWidth() {
2054
        return this._width || this._hostWidth;
2055
    }
2056

2057
    /**
2058
     * Gets/Sets the width of the grid.
2059
     *
2060
     * @example
2061
     * ```typescript
2062
     * let gridWidth = this.grid.width;
2✔
2063
     * ```
2✔
2064
     */
2065
    @WatchChanges()
2066
    @Input()
2067
    public get width(): string | null {
2068
        return this._width;
2069
    }
2070

2071
    public set width(value: string | null) {
2072
        if (this._width !== value) {
2073
            this._width = value;
2074
            this.nativeElement.style.width = value;
2075
            this.notifyChanges(true);
2076
        }
2077
    }
50✔
2078

2079
    /**
2080
     * Gets the width of the header.
2081
     *
2082
     * @example
2083
     * ```html
2084
     * let gridHeaderWidth = this.grid.headerWidth;
2085
     * ```
2086
     */
2087
    public get headerWidth() {
2088
        return parseInt(this.width, 10) - 17;
2089
    }
2090

2091
    /**
8✔
2092
     * Gets/Sets the row height.
2093
     *
2094
     * @example
2095
     * ```html
2096
     * <igx-grid #grid [data]="localData" [rowHeight]="100" [autoGenerate]="true"></igx-grid>
2097
     * ```
2098
     */
2099
    @WatchChanges()
2100
    @Input()
2101
    public get rowHeight(): number {
2102
        return this._rowHeight ? this._rowHeight : this.defaultRowHeight;
2103
    }
2104

2105
    public set rowHeight(value: number | string) {
79✔
2106
        if (typeof value !== 'number') {
79✔
2107
            value = parseInt(value, 10);
79✔
2108
        }
2109
        this._rowHeight = value;
2110
    }
2111

2112
    /**
2113
     * Gets/Sets the default width of the columns.
2114
     *
7,105✔
2115
     * @example
2116
     * ```html
2117
     * <igx-grid #grid [data]="localData" [columnWidth]="100" [autoGenerate]="true"></igx-grid>
2118
     * ```
2119
     */
2120
    @WatchChanges()
2121
    @Input()
2122
    public get columnWidth(): string {
2123
        return this._columnWidth;
2124
    }
2125
    public set columnWidth(value: string) {
41,435✔
2126
        this._columnWidth = value;
2127
        this.columnWidthSetByUser = true;
2128
        this.notifyChanges(true);
167,115✔
2129
    }
2130

2131
    /**
×
2132
     * Get/Sets the message displayed when there are no records.
2133
     *
2134
     * @example
2135
     * ```html
2136
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
2137
     * ```
2138
     */
2139
    @Input()
2140
    public set emptyGridMessage(value: string) {
2141
        this._emptyGridMessage = value;
2142
    }
1,195,289✔
2143
    public get emptyGridMessage(): string {
2144
        return this._emptyGridMessage || this.resourceStrings.igx_grid_emptyGrid_message;
4,791✔
2145
    }
2146

16,674✔
2147
    /**
2148
     * Gets/Sets whether the grid is going to show a loading indicator.
1,173,824✔
2149
     *
2150
     * @example
2151
     * ```html
2152
     * <igx-grid #grid [data]="Data" [isLoading]="true" [autoGenerate]="true"></igx-grid>
2153
     * ```
2154
     */
2155
    @WatchChanges()
11,898✔
2156
    @Input()
2157
    public set isLoading(value: boolean) {
27✔
2158
        if (this._isLoading !== value) {
2159
            this._isLoading = value;
74✔
2160
            if (!!this.data) {
2161
                this.evaluateLoadingState();
11,797✔
2162
            }
2163
        }
2164
        Promise.resolve().then(() => {
2165
            // wait for the current detection cycle to end before triggering a new one.
2166
            this.notifyChanges();
2167
        });
2168
    }
2169

2170
    public get isLoading(): boolean {
2171
        return this._isLoading;
2172
    }
366,392✔
2173

2174
    /**
1,036✔
2175
     * Gets/Sets whether the columns should be auto-generated once again after the initialization of the grid
2176
     *
2,828✔
2177
     * @remarks
2178
     * This will allow to bind the grid to remote data and having auto-generated columns at the same time.
362,528✔
2179
     * Note that after generating the columns, this property would be disabled to avoid re-creating
2180
     * columns each time a new data is assigned.
2181
     * @example
2182
     * ```typescript
2183
     *  this.grid.shouldGenerate = true;
2184
     * ```
2185
     */
2186
    public shouldGenerate: boolean;
2187

2188
    /**
2189
     * Gets/Sets the message displayed when there are no records and the grid is filtered.
2190
     *
182,369✔
2191
     * @example
162,260✔
2192
     * ```html
2193
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
20,109✔
2194
     * ```
20,109✔
2195
     */
2196
    @Input()
2197
    public set emptyFilteredGridMessage(value: string) {
2198
        this._emptyFilteredGridMessage = value;
2199
    }
2200

2201
    public get emptyFilteredGridMessage(): string {
2202
        return this._emptyFilteredGridMessage || this.resourceStrings.igx_grid_emptyFilteredGrid_message;
2203
    }
2204

2205
    /**
311,260✔
2206
     * Gets/Sets the initial pinning configuration.
289,750✔
2207
     *
2208
     * @remarks
21,510✔
2209
     * Allows to apply pinning the columns to the start or the end.
21,510✔
2210
     * Note that pinning to both sides at a time is not allowed.
2211
     * @example
2212
     * ```html
2213
     * <igx-grid [pinning]="pinningConfig"></igx-grid>
2214
     * ```
2215
     */
36,628✔
2216
    @Input()
36,628✔
2217
    public get pinning() {
2218
        return this._pinning;
2219
    }
2220
    public set pinning(value) {
2221
        if (value !== this._pinning) {
2222
            this.resetCaches();
2223
        }
35✔
2224
        this._pinning = value;
35✔
2225
    }
35✔
2226

35✔
2227
    /**
2228
     * Gets/Sets if the filtering is enabled.
2229
     *
35✔
2230
     * @example
35✔
2231
     * ```html
35✔
2232
     * <igx-grid #grid [data]="localData" [allowFiltering]="true" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2233
     * ```
2234
     */
2235
    @Input()
2236
    public get allowFiltering() {
2237
        return this._allowFiltering;
2238
    }
120,063✔
2239

2240
    public set allowFiltering(value) {
27,220✔
2241
        if (this._allowFiltering !== value) {
2242
            this._allowFiltering = value;
27,220✔
2243
            this.filteringService.registerSVGIcons();
2244

27,220✔
2245
            if (!this._init) {
2246
                this.calcGridHeadRow();
27,220✔
2247
            }
27,220✔
2248

2249
            this.filteringService.isFilterRowVisible = false;
120,063✔
2250
            this.filteringService.filteredColumn = null;
2251

2252
            this.notifyChanges(true);
2253
        }
2254
    }
2255

17,659✔
2256
    /**
2257
     * Gets/Sets a value indicating whether the advanced filtering is enabled.
2258
     *
2259
     * @example
2260
     * ```html
2261
     * <igx-grid #grid [data]="localData" [allowAdvancedFiltering]="true" [autoGenerate]="true"></igx-grid>
2262
     * ```
2263
     */
2264
    @Input()
2265
    public get allowAdvancedFiltering() {
2266
        return this._allowAdvancedFiltering;
29,100!
2267
    }
2268

2269
    public set allowAdvancedFiltering(value) {
2270
        if (this._allowAdvancedFiltering !== value) {
2271
            this._allowAdvancedFiltering = value;
2272
            this.filteringService.registerSVGIcons();
2273

2274
            if (!this._init) {
2275
                this.notifyChanges(true);
2276
            }
2277
        }
3,144,250✔
2278
    }
253,083✔
2279

2280
    /**
2,891,167✔
2281
     * Gets/Sets the filter mode.
2,891,167✔
2282
     *
2283
     * @example
2284
     * ```html
2285
     * <igx-grid #grid [data]="localData" [filterMode]="'quickFilter'" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2286
     * ```
2287
     * @remarks
2288
     * By default it's set to FilterMode.quickFilter.
2289
     */
2290
    @Input()
2291
    public get filterMode() {
2292
        return this._filterMode;
145✔
2293
    }
2294

2295
    public set filterMode(value: FilterMode) {
2296
        switch (value) {
2297
            case FilterMode.excelStyleFilter:
2298
            case FilterMode.quickFilter:
2299
                this._filterMode = value;
2300
                break;
2301
            default:
2302
                break;
2303
        }
613,508✔
2304

589,194✔
2305
        if (this.filteringService.isFilterRowVisible) {
2306
            this.filteringRow.close();
142,035✔
2307
        }
24,314✔
2308
        this.notifyChanges(true);
2309
    }
2310

2311
    /**
2312
     * Gets/Sets the summary position.
2313
     *
×
2314
     * @example
2315
     * ```html
2316
     * <igx-grid #grid [data]="localData" summaryPosition="top" [autoGenerate]="true"></igx-grid>
2317
     * ```
2318
     * @remarks
2319
     * By default it is bottom.
2320
     */
2321
    @Input()
2322
    public get summaryPosition() {
2323
        return this._summaryPosition;
2324
    }
2325

2326
    public set summaryPosition(value: GridSummaryPosition) {
2327
        this._summaryPosition = value;
120,599✔
2328
        this.notifyChanges();
2329
    }
2330

11,885✔
2331
    /**
2332
     * Gets/Sets the summary calculation mode.
2333
     *
2334
     * @example
2335
     * ```html
2336
     * <igx-grid #grid [data]="localData" summaryCalculationMode="rootLevelOnly" [autoGenerate]="true"></igx-grid>
2337
     * ```
2338
     * @remarks
2339
     * By default it is rootAndChildLevels which means the summaries are calculated for the root level and each child level.
2340
     */
2341
    @Input()
2342
    public get summaryCalculationMode() {
2343
        return this._summaryCalculationMode;
12✔
2344
    }
2✔
2345

2✔
2346
    public set summaryCalculationMode(value: GridSummaryCalculationMode) {
2✔
2347
        this._summaryCalculationMode = value;
2✔
2348
        if (!this._init) {
2349
            this.crudService.endEdit(false);
2350
            this.summaryService.resetSummaryHeight();
2351
            this.notifyChanges(true);
2352
        }
2353
    }
2354

2355
    /**
2356
     * Controls whether the summary row is visible when groupBy/parent row is collapsed.
2357
     *
2358
     * @example
2359
     * ```html
167,122✔
2360
     * <igx-grid #grid [data]="localData" [showSummaryOnCollapse]="true" [autoGenerate]="true"></igx-grid>
133,595✔
2361
     * ```
2362
     * @remarks
194,532✔
2363
     * By default showSummaryOnCollapse is set to 'false' which means that the summary row is not visible
33,527✔
2364
     * when the groupBy/parent row is collapsed.
2365
     */
2366
    @Input()
2367
    public get showSummaryOnCollapse() {
2368
        return this._showSummaryOnCollapse;
2369
    }
2370

2371
    public set showSummaryOnCollapse(value: boolean) {
2372
        this._showSummaryOnCollapse = value;
2373
        this.notifyChanges();
2374
    }
2375

2376
    /**
2377
     * Gets/Sets the filtering strategy of the grid.
29✔
2378
     *
2379
     * @example
2380
     * ```html
2381
     *  <igx-grid #grid [data]="localData" [filterStrategy]="filterStrategy"></igx-grid>
2382
     * ```
2383
     */
2384
    @Input()
2385
    public get filterStrategy(): IFilteringStrategy {
2386
        return this._filterStrategy;
2387
    }
2388

2389
    public set filterStrategy(classRef: IFilteringStrategy) {
2390
        this._filterStrategy = classRef;
2391
    }
×
2392

2393
    /**
2394
     * Gets/Sets the sorting strategy of the grid.
2395
     *
2396
     * @example
2397
     * ```html
2398
     *  <igx-grid #grid [data]="localData" [sortStrategy]="sortStrategy"></igx-grid>
2399
     * ```
2400
     */
2401
    @Input()
2402
    public get sortStrategy(): IGridSortingStrategy {
2403
        return this._sortingStrategy;
2404
    }
2405

2406
    public set sortStrategy(value: IGridSortingStrategy) {
8✔
2407
        this._sortingStrategy = value;
2408
    }
2409

2410
    /**
2411
     * Gets/Sets the sorting options - single or multiple sorting.
2412
     * Accepts an `ISortingOptions` object with any of the `mode` properties.
2413
     *
2414
     * @example
2415
     * ```typescript
2416
     * const _sortingOptions: ISortingOptions = {
2417
     *      mode: 'single'
2418
     * }
4✔
2419
     * ```html
2420
     * <igx-grid [sortingOptions]="sortingOptions"><igx-grid>
2421
     * ```
146✔
2422
     */
2423
    @Input()
2424
    public set sortingOptions(value: ISortingOptions) {
1!
2425
        if (!this._init) {
1!
2426
            // clear sort only if option is changed runtime. No need to clear on initial load.
1✔
2427
            this.clearSort();
2428
        }
1✔
2429
        this._sortingOptions = Object.assign(this._sortingOptions, value);
1✔
2430
    }
1✔
2431

2432
    public get sortingOptions() {
2433
        return this._sortingOptions;
2434
    }
2435

2436
    /**
2437
     * Gets/Sets the current selection state.
2438
     *
2439
     * @remarks
2440
     * Represents the selected rows' IDs (primary key or rowData)
2441
     * @example
2442
     * ```html
2443
     * <igx-grid [data]="localData" primaryKey="ID" rowSelection="multiple" [selectedRows]="[0, 1, 2]"><igx-grid>
2444
     * ```
2445
     */
×
2446
    @Input()
2447
    public set selectedRows(rowIDs: any[]) {
2448
        this.selectRows(rowIDs || [], true);
2449
    }
2450

2451
    public get selectedRows(): any[] {
2452
        return this.selectionService.getSelectedRows();
2453
    }
2454

2455

2456
    /**
37,244✔
2457
     * A list of all `IgxGridHeaderGroupComponent`.
17,014✔
2458
     *
2459
     * @example
2460
     * ```typescript
127,677✔
2461
     * const headerGroupsList = this.grid.headerGroupsList;
20,230✔
2462
     * ```
20,230✔
2463
     */
20,230✔
2464
    public get headerGroupsList(): IgxGridHeaderGroupComponent[] {
103,853✔
2465
        return this.theadRow.groups;
2466
    }
20,230✔
2467

20,230✔
2468
    /**
2469
     * A list of all `IgxGridHeaderComponent`.
2470
     *
2471
     * @example
2472
     * ```typescript
2473
     * const headers = this.grid.headerCellList;
2474
     * ```
211,950✔
2475
     */
2476
    public get headerCellList(): IgxGridHeaderComponent[] {
2477
        return this.headerGroupsList.map(headerGroup => headerGroup.header).filter(header => header);
2478
    }
2479

2480
    /**
2481
     * A list of all `IgxGridFilteringCellComponent`.
653✔
2482
     *
2483
     * @example
2484
     * ```typescript
2485
     * const filterCells = this.grid.filterCellList;
2486
     * ```
2487
     */
2488
    public get filterCellList(): IgxGridFilteringCellComponent[] {
×
2489
        return this.headerGroupsList.map(group => group.filter).filter(cell => cell);
2490
    }
2491

2492
    /**
2493
     * @hidden @internal
2494
     */
2495
    public get summariesRowList() {
55,284✔
2496
        const res = new QueryList<any>();
55,284!
2497
        if (!this._summaryRowList) {
×
2498
            return res;
2499
        }
55,284!
2500
        const sumList = this._summaryRowList.filter((item) => item.element.nativeElement.parentElement !== null);
×
2501
        res.reset(sumList);
2502
        return res;
55,284✔
2503
    }
2504

2505
    /**
2506
     * A list of `IgxGridRowComponent`.
2507
     *
2508
     * @example
2509
     * ```typescript
23✔
2510
     * const rowList = this.grid.rowList;
23!
2511
     * ```
×
2512
     */
2513
    public get rowList() {
23✔
2514
        const res = new QueryList<IgxRowDirective>();
2515
        if (!this._rowList) {
2516
            return res;
2517
        }
2518
        const rList = this._rowList
2519
            .filter((item) => item.element.nativeElement.parentElement !== null)
2520
            .sort((a, b) => a.index - b.index);
2521
        res.reset(rList);
2522
        return res;
2523
    }
50✔
2524

2525
    /**
149✔
2526
     * A list of currently rendered `IgxGridRowComponent`'s.
149✔
2527
     *
149!
UNCOV
2528
     * @example
×
2529
     * ```typescript
2530
     * const dataList = this.grid.dataRowList;
149✔
2531
     * ```
2532
     */
21✔
2533
    public get dataRowList(): QueryList<IgxRowDirective> {
2534
        const res = new QueryList<IgxRowDirective>();
128✔
2535
        if (!this._dataRowList) {
16✔
2536
            return res;
2537
        }
2538
        const rList = this._dataRowList.filter(item => item.element.nativeElement.parentElement !== null).sort((a, b) => a.index - b.index);
2539
        res.reset(rList);
2540
        return res;
128✔
2541
    }
8✔
2542

8✔
2543
    /**
8✔
2544
     * Gets the header row selector template.
2545
     */
128✔
2546
    @Input()
3✔
2547
    public get headSelectorTemplate(): TemplateRef<IgxHeadSelectorTemplateContext> {
3✔
2548
        return this._headSelectorTemplate || this.headSelectorsTemplates?.first;
3✔
2549
    }
2550

2551
    /**
2552
     * Sets the header row selector template.
2553
     * ```html
2554
     * <ng-template #template igxHeadSelector let-headContext>
2555
     * {{ headContext.selectedCount }} / {{ headContext.totalCount  }}
2556
     * </ng-template>
128✔
2557
     * ```
128✔
2558
     * ```typescript
2559
     * @ViewChild("'template'", {read: TemplateRef })
2560
     * public template: TemplateRef<any>;
2561
     * this.grid.headSelectorTemplate = this.template;
2562
     * ```
2563
     */
2564
    public set headSelectorTemplate(template: TemplateRef<IgxHeadSelectorTemplateContext>) {
2565
        this._headSelectorTemplate = template;
2566
    }
2567

2568
    /**
2569
     * @hidden
2570
     * @internal
2571
     */
2572
    public get isPinningToStart() {
2573
        return this.pinning.columns !== ColumnPinningPosition.End;
2574
    }
2575

20✔
2576
    /**
2577
     * @hidden
2578
     * @internal
2579
     */
2580
    public get isRowPinningToTop() {
2581
        return this.pinning.rows !== RowPinningPosition.Bottom;
2582
    }
2583

2584
    /**
2585
     * Gets the row selector template.
2586
     */
2587
    @Input()
2588
    public get rowSelectorTemplate(): TemplateRef<IgxRowSelectorTemplateContext> {
2589
        return this._rowSelectorTemplate || this.rowSelectorsTemplates?.first;
2590
    }
2591

2592
    /**
2593
         * Sets a custom template for the row selectors.
2594
         * ```html
2595
         * <ng-template #template igxRowSelector let-rowContext>
2596
         *    <igx-checkbox [checked]="rowContext.selected"></igx-checkbox>
2597
         * </ng-template>
2598
         * ```
2599
         * ```typescript
2600
         * @ViewChild("'template'", {read: TemplateRef })
2601
         * public template: TemplateRef<any>;
2602
         * this.grid.rowSelectorTemplate = this.template;
2603
         * ```
2604
         */
3,157✔
2605
    public set rowSelectorTemplate(template: TemplateRef<IgxRowSelectorTemplateContext>) {
3,157✔
2606
        this._rowSelectorTemplate = template;
2607
    }
2608

2609
    /**
2610
     * @hidden @internal
2611
     */
2612
    public get rowOutletDirective() {
2613
        return this.rowEditingOutletDirective;
2614
    }
2615

2616
    /**
2617
     * @hidden @internal
2618
     */
161✔
2619
    public get parentRowOutletDirective() {
161✔
2620
        return this.outlet;
161✔
2621
    }
161✔
2622

161✔
2623
    /**
2624
     * @hidden @internal
2625
     */
2626
    public get rowEditCustom(): TemplateRef<IgxGridRowEditTemplateContext> {
2627
        if (this.rowEditCustomDirectives && this.rowEditCustomDirectives.first) {
2628
            return this.rowEditCustomDirectives.first;
2629
        }
2630
        return null;
2631
    }
2632

2633
    /**
2634

2635
    /**
2636
     * @hidden @internal
2637
     */
82!
2638
    public get rowEditContainer(): TemplateRef<IgxGridRowEditTemplateContext> {
82✔
2639
        return this.rowEditCustom ? this.rowEditCustom : this.defaultRowEditTemplate;
2640
    }
2641

2642
    /**
2643
     * The custom template, if any, that should be used when rendering the row drag indicator icon
79✔
2644
     */
2645
    @Input()
2646
    public get dragIndicatorIconTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
2647
        return this._customDragIndicatorIconTemplate || this.dragIndicatorIconTemplates?.first;
2648
    }
2649

2650
    /**
2651
     * Sets a custom template that should be used when rendering the row drag indicator icon.
79✔
2652
     *```html
79!
2653
     * <ng-template #template igxDragIndicatorIcon>
×
2654
     *    <igx-icon>expand_less</igx-icon>
2655
     * </ng-template>
79✔
2656
     * ```
79✔
2657
     * ```typescript
77✔
2658
     * @ViewChild("'template'", {read: TemplateRef })
77✔
2659
     * public template: TemplateRef<any>;
2660
     * this.grid.dragIndicatorIconTemplate = this.template;
79✔
2661
     * ```
2662
     */
2663
    public set dragIndicatorIconTemplate(val: TemplateRef<IgxGridEmptyTemplateContext>) {
2664
        this._customDragIndicatorIconTemplate = val;
2665
    }
2666

2667
    /**
2668
     * @hidden @internal
2669
     */
2670
    public get firstEditableColumnIndex(): number {
2671
        const index = this.visibleColumns.filter(col => col.editable)
2672
            .map(c => c.visibleIndex).sort((a, b) => a - b);
2673
        return index.length ? index[0] : null;
2674
    }
2675

2676
    /**
19!
2677
     * @hidden @internal
47✔
2678
     */
19!
2679
    public get lastEditableColumnIndex(): number {
2680
        const index = this.visibleColumns.filter(col => col.editable)
19✔
2681
            .map(c => c.visibleIndex).sort((a, b) => a > b ? -1 : 1);
19✔
2682
        return index.length ? index[0] : null;
2683
    }
19✔
2684

1✔
2685
    /**
2686
     * @hidden @internal
18✔
2687
     * TODO: Nav service logic doesn't handle 0 results from this querylist
2688
     */
2689
    public get rowEditTabs(): QueryList<IgxRowEditTabStopDirective> {
2690
        return this.rowEditTabsCUSTOM.length ? this.rowEditTabsCUSTOM : this.rowEditTabsDEFAULT;
2691
    }
18✔
2692

18✔
2693
    public get activeDescendant() {
18✔
2694
        const activeElem = this.navigation.activeNode;
18✔
2695

18✔
2696
        if (!activeElem || !Object.keys(activeElem).length) {
2697
            return this.id;
2698
        }
2699

2700
        return activeElem.row < 0 ?
2701
            `${this.id}_${activeElem.row}_${activeElem.mchCache.level}_${activeElem.column}` :
2702
            `${this.id}_${activeElem.row}_${activeElem.column}`;
2703
    }
2704

2705
    public get bannerClass(): string {
2706
        const position = this.rowEditPositioningStrategy.isTop ? 'igx-banner__border-top' : 'igx-banner__border-bottom';
2707
        return `${this.getComponentDensityClass('igx-banner')} ${position}`;
2708
    }
2709

2710
    /**
2711
     * Gets/Sets the sorting state.
2712
     *
2713
     * @remarks
2714
     * Supports two-way data binding.
2715
     * @example
2716
     * ```html
2717
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(sortingExpressions)]="model.sortingExpressions"></igx-grid>
31!
2718
     * ```
31✔
2719
     */
31!
2720
    @WatchChanges()
×
2721
    @Input()
2722
    public get sortingExpressions(): ISortingExpression[] {
31✔
2723
        return this._sortingExpressions;
31✔
2724
    }
2725

2726
    public set sortingExpressions(value: ISortingExpression[]) {
2727
        this._sortingExpressions = cloneArray(value);
2728
        this.sortingExpressionsChange.emit(this._sortingExpressions);
2729
        this.notifyChanges();
31✔
2730
    }
2731

2732
    /**
2733
     * @hidden @internal
2734
     */
2735
    public get maxLevelHeaderDepth() {
2736
        if (this._maxLevelHeaderDepth === null) {
2737
            this._maxLevelHeaderDepth = this.hasColumnLayouts ?
2738
                this._columns.reduce((acc, col) => Math.max(acc, col.rowStart), 0) :
2739
                this._columns.reduce((acc, col) => Math.max(acc, col.level), 0);
2740
        }
2741
        return this._maxLevelHeaderDepth;
2742
    }
2743

2744
    /**
200✔
2745
     * Gets the number of hidden columns.
7✔
2746
     *
2747
     * @example
193✔
2748
     * ```typescript
193✔
2749
     * const hiddenCol = this.grid.hiddenColumnsCount;
193✔
2750
     * ``
2751
     */
2752
    public get hiddenColumnsCount() {
2753
        return this._columns.filter((col) => col.columnGroup === false && col.hidden === true).length;
2754
    }
2755

2756
    /**
2757
     * Gets the number of pinned columns.
2758
     */
2759
    public get pinnedColumnsCount() {
2760
        return this.pinnedColumns.filter(col => !col.columnLayout).length;
2761
    }
2762

155✔
2763
    /**
155✔
2764
     * Gets/Sets whether the grid has batch editing enabled.
3✔
2765
     * When batch editing is enabled, changes are not made directly to the underlying data.
6✔
2766
     * Instead, they are stored as transactions, which can later be committed w/ the `commit` method.
2767
     *
2768
     * @example
2769
     * ```html
152✔
2770
     * <igx-grid [batchEditing]="true" [data]="someData">
4✔
2771
     * </igx-grid>
12✔
2772
     * ```
8✔
2773
     */
2774
    @Input()
2775
    public get batchEditing(): boolean {
2776
        return this._batchEditing;
152✔
2777
    }
2778

155✔
2779
    public set batchEditing(val: boolean) {
155✔
2780
        if (val !== this._batchEditing) {
155!
2781
            delete this._transactions;
×
2782
            this._batchEditing = val;
2783
            this.switchTransactionService(val);
155✔
2784
            this.subscribeToTransactions();
155✔
2785
        }
3✔
2786
    }
2787

2788
    /**
152✔
2789
     * Get transactions service for the grid.
2790
     */
155✔
2791
    public get transactions(): TransactionService<Transaction, State> {
2792
        if (this._diTransactions && !this.batchEditing) {
2793
            return this._diTransactions;
2794
        }
2795
        return this._transactions;
2796
    }
2797

2798
    /**
2799
     * @hidden @internal
2800
     */
2801
    public get currentRowState(): any {
2802
        return this._currentRowState;
2803
    }
2804

2805
    /**
2806
     * @hidden @internal
2807
     */
272✔
2808
    public get currencyPositionLeft(): boolean {
2809
        if (this._currencyPositionLeft !== undefined) {
2810
            return this._currencyPositionLeft;
2811
        }
2812
        const format = getLocaleNumberFormat(this.locale, NumberFormatStyle.Currency);
2813
        const formatParts = format.split(',');
2814
        const i = formatParts.indexOf(formatParts.find(c => c.includes('¤')));
2815
        return this._currencyPositionLeft = i < 1;
2816
    }
2817

2818
    /**
2819
     * Gets/Sets cell selection mode.
2820
     *
2821
     * @remarks
3✔
2822
     * By default the cell selection mode is multiple
2823
     * @param selectionMode: GridSelectionMode
2824
     */
2825
    @WatchChanges()
2826
    @Input()
2827
    public get cellSelection() {
2828
        return this._cellSelectionMode;
2829
    }
2830

2831
    public set cellSelection(selectionMode: GridSelectionMode) {
2832
        this._cellSelectionMode = selectionMode;
2833
        // if (this.gridAPI.grid) {
2834
        this.selectionService.clear(true);
2835
        this.notifyChanges();
2836
        // }
2837
    }
2838

2839
    /**
2840
     * Gets/Sets row selection mode
7✔
2841
     *
4✔
2842
     * @remarks
2843
     * By default the row selection mode is 'none'
2844
     * Note that in IgxGrid and IgxHierarchicalGrid 'multipleCascade' behaves like 'multiple'
3✔
2845
     */
2846
    @WatchChanges()
2847
    @Input()
2848
    public get rowSelection() {
2849
        return this._rowSelectionMode;
2850
    }
2851

2852
    public set rowSelection(selectionMode: GridSelectionMode) {
2853
        this._rowSelectionMode = selectionMode;
2854
        if (!this._init) {
2855
            this.selectionService.clearAllSelectedRows();
2856
            this.notifyChanges(true);
2857
        }
2858
    }
2859

2860
    /**
2861
     * Gets/Sets column selection mode
2862
     *
7✔
2863
     * @remarks
5✔
2864
     * By default the row selection mode is none
2865
     * @param selectionMode: GridSelectionMode
2866
     */
2✔
2867
    @WatchChanges()
2868
    @Input()
2869
    public get columnSelection() {
2870
        return this._columnSelectionMode;
2871
    }
2872

2873
    public set columnSelection(selectionMode: GridSelectionMode) {
2874
        this._columnSelectionMode = selectionMode;
2875
        // if (this.gridAPI.grid) {
2876
        this.selectionService.clearAllSelectedColumns();
2877
        this.notifyChanges(true);
2878
        // }
2879
    }
2880

2881
    /**
145✔
2882
     * @hidden @internal
2883
     */
2884
    public set pagingState(value) {
2885
        this._pagingState = value;
2886
        if (this.paginator && !this._init) {
2887
            this.paginator.totalRecords = value.metadata.countRecords;
2888
        }
2889
    }
2890

2891
    public get pagingState() {
2892
        return this._pagingState;
2893
    }
2894

2895
    /**
34✔
2896
     * @hidden @internal
21✔
2897
     */
21✔
2898
    public rowEditMessage;
2899

13!
2900
    /**
×
2901
     * @hidden @internal
2902
     */
13✔
2903
    public snackbarActionText = this.resourceStrings.igx_grid_snackbar_addrow_actiontext;
2904

2905
    /**
2906
     * @hidden @internal
2907
     */
2908
    public calcWidth: number;
274✔
2909
    /**
274✔
2910
     * @hidden @internal
274✔
2911
     */
274✔
2912
    public calcHeight = 0;
274✔
2913
    /**
2914
     * @hidden @internal
2915
     */
2916
    public tfootHeight: number;
2917

2918
    /**
2919
     * @hidden @internal
2920
     */
2921
    public disableTransitions = false;
2922

2923
    /**
2924
     * @hidden @internal
2925
     */
2926
    public lastSearchInfo: ISearchInfo = {
2927
        searchText: '',
2928
        caseSensitive: false,
37✔
2929
        exactMatch: false,
37✔
2930
        activeMatchIndex: 0,
2931
        matchInfoCache: []
2932
    };
2933

2934
    /**
2935
     * @hidden @internal
2936
     */
2937
    public columnWidthSetByUser = false;
2938

2939
    /**
2940
     * @hidden @internal
2941
     */
2942
    public pinnedRecords: any[];
19✔
2943

19✔
2944
    /**
2945
     * @hidden @internal
2946
     */
2947
    public unpinnedRecords: any[];
2948

2949
    /**
2950
     * @hidden @internal
2951
     */
2952
    public rendered$ = this.rendered.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true }));
2953

2954
    /** @hidden @internal */
2955
    public resizeNotify = new Subject<void>();
2956

2957
    /** @hidden @internal */
2958
    public rowAddedNotifier = new Subject<IRowDataEventArgs>();
134✔
2959

1✔
2960
    /** @hidden @internal */
2961
    public rowDeletedNotifier = new Subject<IRowDataEventArgs>();
133✔
2962

133✔
2963
    /** @hidden @internal */
133✔
2964
    public pipeTriggerNotifier = new Subject();
1✔
2965

2966
    /** @hidden @internal */
132✔
2967
    public _filteredSortedPinnedData: any[];
132✔
2968

132✔
2969
    /** @hidden @internal */
132✔
2970
    public _filteredSortedUnpinnedData: any[];
132✔
2971

130✔
2972
    /** @hidden @internal */
130✔
2973
    public _filteredPinnedData: any[];
2974

132✔
2975
    /**
2976
     * @hidden
2977
     */
2978
    public _filteredUnpinnedData;
2979
    /**
2980
     * @hidden @internal
2981
     */
2982
    public _destroyed = false;
2983
    /**
2984
     * @hidden @internal
2985
     */
2986
    public _totalRecords = -1;
2987
    /**
2988
     * @hidden @internal
24✔
2989
     */
24!
2990
    public columnsWithNoSetWidths = null;
×
2991
    /**
2992
     * @hidden @internal
24✔
2993
     */
24✔
2994
    public pipeTrigger = 0;
24✔
2995
    /**
1✔
2996
     * @hidden @internal
2997
     */
23✔
2998
    public filteringPipeTrigger = 0;
23✔
2999
    /**
23✔
3000
     * @hidden @internal
23!
3001
     */
23✔
3002
    public summaryPipeTrigger = 0;
23✔
3003
    /**
3004
     * @hidden @internal
23✔
3005
     */
3006
    public groupablePipeTrigger = 0;
3007

64,275✔
3008
    /**
64,275✔
3009
    * @hidden @internal
3010
    */
3011
    public EMPTY_DATA = [];
35,081✔
3012

3013
    public isPivot = false;
3014

3015
    /** @hidden @internal */
3016
    public _baseFontSize: number;
3017

3018
    /**
3019
     * @hidden
3020
     */
3021
    public destroy$ = new Subject<any>();
3022

3023
    /**
3024
     * @hidden
269✔
3025
     */
3026
    protected _perPage = DEFAULT_ITEMS_PER_PAGE;
3027
    /**
3028
     * @hidden
3029
     */
3030
    protected _paging = false;
3031
    /**
3032
     * @hidden
3033
     */
3034
    protected _pagingMode = GridPagingMode.Local;
3035
    /**
3036
     * @hidden
3037
     */
3038
    protected _pagingState;
3039
    /**
3040
     * @hidden
155✔
3041
     */
3042
    protected _hideRowSelectors = false;
3043
    /**
3044
     * @hidden
3045
     */
3046
    protected _rowDrag = false;
3047
    /**
3048
     * @hidden
3049
     */
3050
    protected _columns: IgxColumnComponent[] = [];
3051
    /**
3052
     * @hidden
3053
     */
3054
    protected _pinnedColumns: IgxColumnComponent[] = [];
3055
    /**
3056
     * @hidden
40✔
3057
     */
3058
    protected _unpinnedColumns: IgxColumnComponent[] = [];
3059
    /**
3060
     * @hidden
3061
     */
3062
    protected _filteringExpressionsTree: IFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And);
3063
    /**
3064
     * @hidden
3065
     */
3066
    protected _advancedFilteringExpressionsTree: IFilteringExpressionsTree;
3067
    /**
3068
     * @hidden
3069
     */
3,760✔
3070
    protected _sortingExpressions: Array<ISortingExpression> = [];
9,506✔
3071
    /**
121✔
3072
     * @hidden
121✔
3073
     */
120✔
3074
    protected _maxLevelHeaderDepth = null;
120✔
3075
    /**
1,299✔
3076
     * @hidden
3077
     */
3078
    protected _columnHiding = false;
3079
    /**
105✔
3080
     * @hidden
3081
     */
3082
    protected _columnPinning = false;
3083

121✔
3084
    protected _pinnedRecordIDs = [];
3085

3086
    /**
9,385✔
3087
     * @hidden
3088
     */
3089
    protected _hasVisibleColumns;
3090
    protected _allowFiltering = false;
3091
    protected _allowAdvancedFiltering = false;
3092
    protected _filterMode: FilterMode = FilterMode.quickFilter;
3093

3094

3095
    protected _defaultTargetRecordNumber = 10;
3096
    protected _expansionStates: Map<any, boolean> = new Map<any, boolean>();
3097
    protected _defaultExpandState = false;
3098
    protected _headerFeaturesWidth = NaN;
1✔
3099
    protected _init = true;
3100
    protected _cdrRequestRepaint = false;
3101
    protected _userOutletDirective: IgxOverlayOutletDirective;
3102
    protected _transactions: TransactionService<Transaction, State>;
3103
    protected _batchEditing = false;
3104
    protected _sortingOptions: ISortingOptions = { mode: 'multiple' };
3105
    protected _filterStrategy: IFilteringStrategy = new FilteringStrategy();
1✔
3106
    protected _autoGeneratedCols = [];
10!
3107
    protected _dataView = [];
10✔
3108

40✔
3109
    /** @hidden @internal */
3110
    public get paginator() {
3111
        return this.paginationComponents?.first;
3112
    }
3113

3114
    /**
3115
     * @hidden @internal
3116
     */
3117
    public get scrollSize() {
3118
        return this.verticalScrollContainer.getScrollNativeSize();
3119
    }
3120

3121
    private _rowEditable = false;
3122
    private _currentRowState: any;
×
3123
    private _filteredSortedData = null;
3124
    private _filteredData = null;
3125

3126
    private _customDragIndicatorIconTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3127
    private _excelStyleHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext>;
3128
    private _rowSelectorTemplate: TemplateRef<IgxRowSelectorTemplateContext>;
3129
    private _headSelectorTemplate: TemplateRef<IgxHeadSelectorTemplateContext>;
3130
    private _rowEditTextTemplate: TemplateRef<IgxGridRowEditTextTemplateContext>;
3131
    private _rowAddTextTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3132
    private _rowEditActionsTemplate: TemplateRef<IgxGridRowEditActionsTemplateContext>;
3133
    private _dragGhostCustomTemplate: TemplateRef<IgxGridRowDragGhostContext>;
6✔
3134

3135
    private _cdrRequests = false;
3136
    private _resourceStrings;
3137
    private _emptyGridMessage = null;
3138
    private _emptyFilteredGridMessage = null;
3139
    private _isLoading = false;
3140
    private _locale: string;
3141
    private overlayIDs = [];
3142
    private _sortingStrategy: IGridSortingStrategy;
3143
    private _pinning: IPinningConfig = { columns: ColumnPinningPosition.Start };
3144

×
3145
    private _hostWidth;
3146
    private _advancedFilteringOverlayId: string;
3147
    private _advancedFilteringPositionSettings: PositionSettings = {
3148
        verticalDirection: VerticalAlignment.Middle,
3149
        horizontalDirection: HorizontalAlignment.Center,
3150
        horizontalStartPoint: HorizontalAlignment.Center,
3151
        verticalStartPoint: VerticalAlignment.Middle
3152
    };
3153

3154
    private _advancedFilteringOverlaySettings: OverlaySettings = {
3155
        closeOnOutsideClick: false,
1,679,453✔
3156
        modal: false,
264,191✔
3157
        positionStrategy: new ConnectedPositioningStrategy(this._advancedFilteringPositionSettings),
3158
    };
3159

3160
    private columnListDiffer;
3161
    private rowListDiffer;
3162
    private _height: string | null = '100%';
199,006✔
3163
    private _width: string | null = '100%';
3164
    private _rowHeight: number | undefined;
3165
    private _horizontalForOfs: Array<IgxGridForOfDirective<any>> = [];
3166
    private _multiRowLayoutRowSize = 1;
3167
    // Caches
3168
    private _totalWidth = NaN;
61,727!
3169
    private _pinnedVisible = [];
62,693!
3170
    private _unpinnedVisible = [];
3171
    private _pinnedWidth = NaN;
×
3172
    private _unpinnedWidth = NaN;
3173
    private _visibleColumns = [];
3174
    private _columnGroups = false;
32,028✔
3175

3176
    private _columnWidth: string;
3177

3178
    private _summaryPosition: GridSummaryPosition = GridSummaryPosition.bottom;
3179
    private _summaryCalculationMode: GridSummaryCalculationMode = GridSummaryCalculationMode.rootAndChildLevels;
3180
    private _showSummaryOnCollapse = false;
3181
    private _summaryRowHeight = 0;
3182
    private _cellSelectionMode: GridSelectionMode = GridSelectionMode.multiple;
3183
    private _rowSelectionMode: GridSelectionMode = GridSelectionMode.none;
3184
    private _selectRowOnClick = true;
3185
    private _columnSelectionMode: GridSelectionMode = GridSelectionMode.none;
3186

3187
    private lastAddedRowIndex;
3188
    private _currencyPositionLeft: boolean;
×
3189

3190
    private rowEditPositioningStrategy = new RowEditPositionStrategy({
3191
        horizontalDirection: HorizontalAlignment.Right,
3192
        verticalDirection: VerticalAlignment.Bottom,
3193
        horizontalStartPoint: HorizontalAlignment.Left,
3194
        verticalStartPoint: VerticalAlignment.Bottom,
3195
        closeAnimation: null
3196
    });
3197

3198
    private rowEditSettings: OverlaySettings = {
3199
        scrollStrategy: new AbsoluteScrollStrategy(),
179,817✔
3200
        modal: false,
3201
        closeOnOutsideClick: false,
3202
        outlet: this.rowOutletDirective,
3203
        positionStrategy: this.rowEditPositioningStrategy
3204
    };
3205

3206
    private transactionChange$ = new Subject<void>();
3207
    private _rendered = false;
3208
    private readonly DRAG_SCROLL_DELTA = 10;
3209
    private _dataCloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy();
3210
    private _autoSize = false;
18,471,923✔
3211
    private _sortHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
3212
    private _sortAscendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
3213
    private _sortDescendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
3214

3215
    /**
3216
     * @hidden @internal
16,005✔
3217
     */
3218
    protected get minColumnWidth() {
3219
        return MINIMUM_COLUMN_WIDTH;
3220
    }
3221

3222
    /**
×
3223
     * @hidden @internal
3224
     */
3225
    public abstract id: string;
3226
    public abstract data: any[] | null;
3227

3228
    /**
39,117✔
3229
     * Returns an array of objects containing the filtered data.
3230
     *
3231
     * @example
3232
     * ```typescript
3233
     * let filteredData = this.grid.filteredData;
3234
     * ```
12,403✔
3235
     */
3236
    public get filteredData() {
3237
        return this._filteredData;
3238
    }
3239

3240
    /**
225✔
3241
     * Returns an array containing the filtered sorted data.
225✔
3242
     *
3243
     * @example
3244
     * ```typescript
3245
     * const filteredSortedData = this.grid1.filteredSortedData;
3246
     * ```
3247
     */
3248
    public get filteredSortedData(): any[] {
67,942✔
3249
        return this._filteredSortedData;
3250
    }
3251

3252
    /**
3253
     * @hidden @internal
3254
     */
3,024✔
3255
    public get rowChangesCount() {
3256
        if (!this.crudService.row) {
3257
            return 0;
3258
        }
3259
        const f = (obj: any) => {
3260
            let changes = 0;
3261
            Object.keys(obj).forEach(key => isObject(obj[key]) ? changes += f(obj[key]) : changes++);
49✔
3262
            return changes;
49✔
3263
        };
5✔
3264
        if (this.transactions.getState(this.crudService.row.id)?.type === TransactionType.ADD) {
3265
            return this._columns.filter(c => c.field).length;
49✔
3266
        }
3267
        const rowChanges = this.transactions.getAggregatedValue(this.crudService.row.id, false);
3268
        return rowChanges ? f(rowChanges) : 0;
3269
    }
3270

69,085✔
3271
    /**
3272
     * @hidden @internal
69,085!
3273
     */
×
3274
    public get dataWithAddedInTransactionRows() {
3275
        const result = cloneArray(this.gridAPI.get_all_data());
3276
        if (this.transactions.enabled) {
69,085✔
3277
            result.push(...this.transactions.getAggregatedChanges(true)
3278
                .filter(t => t.type === TransactionType.ADD)
3279
                .map(t => t.newValue));
697,348✔
3280
        }
3281

69,085✔
3282
        if (this.crudService.row && this.crudService.row.getClassName() === IgxAddRow.name) {
697,348✔
3283
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
113,105✔
3284
        }
113,105✔
3285

113,105✔
3286
        return result;
3287
    }
69,085✔
3288

142,739✔
3289
    /**
356,277✔
3290
     * @hidden @internal
69,085✔
3291
     */
3292
    public get dataLength() {
3293
        return this.transactions.enabled ? this.dataWithAddedInTransactionRows.length : this.gridAPI.get_all_data().length;
69,085✔
3294
    }
3295

37,099✔
3296
    /**
37,099✔
3297
     * @hidden @internal
37,099!
3298
     */
×
3299
    public get template(): TemplateRef<any> {
3300
        if (this.isLoading && (this.hasZeroResultFilter || this.hasNoData)) {
37,099✔
3301
            return this.loadingGridTemplate ? this.loadingGridTemplate : this.loadingGridDefaultTemplate;
3302
        }
3303

37,099✔
3304
        if (this.hasZeroResultFilter) {
3305
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
3306
        }
69,085✔
3307

1,425✔
3308
        if (this.hasNoData) {
3309
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate;
67,660✔
3310
        }
67,660!
3311
    }
3312

3313
    /**
67,660✔
3314
     * @hidden @internal
3315
     */
3316
    private get hasZeroResultFilter(): boolean {
3317
        return this.filteredData && this.filteredData.length === 0;
3318
    }
3319

157,797✔
3320
    /**
66,297✔
3321
     * @hidden @internal
3322
     */
91,500✔
3323
    private get hasNoData(): boolean {
91,500✔
3324
        return !this.data || this.dataLength === 0;
3325
    }
3326

3327
    /**
3328
     * @hidden @internal
3329
     */
3330
    public get shouldOverlayLoading(): boolean {
3331
        return this.isLoading && !this.hasNoData && !this.hasZeroResultFilter;
3332
    }
3333

3334
    /**
19,445✔
3335
     * @hidden @internal
41,619!
3336
     */
41,619✔
3337
    public get isMultiRowSelectionEnabled(): boolean {
41,619✔
3338
        return this.rowSelection === GridSelectionMode.multiple
5,686✔
3339
            || this.rowSelection === GridSelectionMode.multipleCascade;
3,635✔
3340
    }
3341

3342
    /**
41,619✔
3343
     * @hidden @internal
41,501✔
3344
     */
3345
    public get isRowSelectable(): boolean {
41,619✔
3346
        return this.rowSelection !== GridSelectionMode.none;
3347
    }
3348

3349
    /**
3350
     * @hidden @internal
3351
     */
×
3352
    public get isCellSelectable() {
3353
        return this.cellSelection !== GridSelectionMode.none;
3354
    }
3355

3356
    /**
3357
     * @hidden @internal
3358
     */
7!
3359
    public get columnInDrag() {
×
3360
        return this.gridAPI.cms.column;
3361
    }
7✔
3362

1✔
3363
    constructor(
3364
        public validation: IgxGridValidationService,
3365
        public selectionService: IgxGridSelectionService,
6✔
3366
        public colResizingService: IgxColumnResizingService,
3367
        @Inject(IGX_GRID_SERVICE_BASE) public gridAPI: GridServiceType,
3368
        protected transactionFactory: IgxFlatTransactionFactory,
3369
        private elementRef: ElementRef<HTMLElement>,
3370
        protected zone: NgZone,
3371
        @Inject(DOCUMENT) public document: any,
3372
        public cdr: ChangeDetectorRef,
2,538✔
3373
        protected differs: IterableDiffers,
100✔
3374
        protected viewRef: ViewContainerRef,
3375
        private appRef: ApplicationRef,
2,438✔
3376
        protected injector: Injector,
3377
        protected envInjector: EnvironmentInjector,
3378
        public navigation: IgxGridNavigationService,
3379
        public filteringService: IgxFilteringService,
3380
        @Inject(IgxOverlayService) protected overlayService: IgxOverlayService,
3381
        public summaryService: IgxGridSummaryService,
3382
        @Optional() @Inject(DisplayDensityToken) protected _displayDensityOptions: IDisplayDensityOptions,
2,662!
3383
        @Inject(LOCALE_ID) private localeId: string,
2,662✔
3384
        protected platform: PlatformUtil,
3385
        @Optional() @Inject(IgxGridTransaction) protected _diTransactions?: TransactionService<Transaction, State>
×
3386
    ) {
3387
        super(_displayDensityOptions);
3388
        this.locale = this.locale || this.localeId;
3389
        this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
3390
        this._transactions.cloneStrategy = this.dataCloneStrategy;
3391
        this.cdr.detach();
3392
    }
3393

3394
    /**
3395
     * @hidden
3396
     * @internal
202,907✔
3397
     */
3398
    @HostListener('mouseleave')
3399
    public hideActionStrip() {
3400
        this.actionStrip?.hide();
3401
    }
3402

3403
    /**
3404
     * @hidden
3405
     * @internal
3406
     */
3407
    public get headerFeaturesWidth() {
15,149✔
3408
        return this._headerFeaturesWidth;
3409
    }
3410

3411
    /**
3412
     * @hidden
3413
     * @internal
3414
     */
3415
    public isDetailRecord(_rec) {
3416
        return false;
3417
    }
3418

360,499✔
3419
    /**
3420
     * @hidden
3421
     * @internal
105✔
3422
     */
3423
    public isGroupByRecord(_rec) {
3424
        return false;
13✔
3425
    }
3426

3427
    /**
3428
     * @hidden @internal
3429
     */
3430
    public isGhostRecord(record: any): boolean {
3431
        return record.ghostRecord !== undefined;
3432
    }
3433
    /**
3434
     * @hidden @internal
3435
     */
3436
    public isAddRowRecord(record: any): boolean {
3437
        return record.addRow !== undefined;
258✔
3438
    }
258✔
3439

3440
    /**
3441
     * @hidden
3442
     * Returns the row index of a row that takes into account the full view data like pinning.
3443
     */
3444
    public getDataViewIndex(rowIndex, pinned) {
3445
        if (pinned && !this.isRowPinningToTop) {
3446
            rowIndex = rowIndex + this.unpinnedDataView.length;
3447
        } else if (!pinned && this.isRowPinningToTop) {
3448
            rowIndex = rowIndex + this.pinnedDataView.length;
3449
        }
3450
        return rowIndex;
18✔
3451
    }
18✔
3452

3453
    /**
3454
     * @hidden
3455
     * @internal
3456
     */
3457
    public get hasDetails() {
3458
        return false;
3459
    }
3460

3461
    /**
3462
     * Returns the state of the grid virtualization.
3463
     *
3464
     * @remarks
3465
     * Includes the start index and how many records are rendered.
3466
     * @example
29✔
3467
     * ```typescript
30✔
3468
     * const gridVirtState = this.grid1.virtualizationState;
429✔
3469
     * ```
30✔
3470
     */
3471
    public get virtualizationState() {
3472
        return this.verticalScrollContainer.state;
3473
    }
3474

3475
    /**
3476
     * @hidden
3477
     * @internal
3478
     */
3479
    public hideOverlays() {
3480
        this.overlayIDs.forEach(overlayID => {
3481
            const overlay = this.overlayService.getOverlayById(overlayID);
3482

3483
            if (overlay?.visible && !overlay.closeAnimationPlayer?.hasStarted()) {
5✔
3484
                this.overlayService.hide(overlayID);
6✔
3485

1✔
3486
                this.nativeElement.focus();
3487
            }
3488
        });
5✔
3489
    }
5✔
3490

3491
    /**
3492
     * Returns whether the record is pinned or not.
3493
     *
3494
     * @param rowIndex Index of the record in the `dataView` collection.
3495
     *
3496
     * @hidden
3497
     * @internal
3498
     */
3499
    public isRecordPinnedByViewIndex(rowIndex: number) {
3500
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this.pinnedDataView.length) ||
7✔
3501
            (!this.isRowPinningToTop && rowIndex >= this.unpinnedDataView.length);
7✔
3502
    }
3503

3504
    /**
3505
     * Returns whether the record is pinned or not.
3506
     *
3507
     * @param rowIndex Index of the record in the `filteredSortedData` collection.
1✔
3508
     */
1✔
3509
    public isRecordPinnedByIndex(rowIndex: number) {
1✔
3510
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this._filteredSortedPinnedData.length) ||
1✔
3511
            (!this.isRowPinningToTop && rowIndex >= this._filteredSortedUnpinnedData.length);
1✔
3512
    }
3513

3514
    /**
3515
     * @hidden
3516
     * @internal
3517
     */
64,996✔
3518
    public isRecordPinned(rec) {
3519
        return this.getInitialPinnedIndex(rec) !== -1;
3520
    }
3521

3522
    /**
3523
     * @hidden
172✔
3524
     * @internal
6✔
3525
     * Returns the record index in order of pinning by the user. Does not consider sorting/filtering.
6✔
3526
     */
3527
    public getInitialPinnedIndex(rec) {
166✔
3528
        const id = this.gridAPI.get_row_id(rec);
6✔
3529
        return this._pinnedRecordIDs.indexOf(id);
3530
    }
3531

163✔
3532
    /**
3533
     * @hidden
163✔
3534
     * @internal
3535
     */
3536
    public get hasPinnedRecords() {
3537
        return this._pinnedRecordIDs.length > 0;
3538
    }
3539

345✔
3540
    /**
345✔
3541
     * @hidden
212✔
3542
     * @internal
3543
     */
322✔
3544
    public get pinnedRecordsCount() {
3545
        return this._pinnedRecordIDs.length;
3546
    }
3547

3548
    /**
3549
     * @hidden
171✔
3550
     * @internal
169✔
3551
     */
168✔
3552
    public get crudService() {
168✔
3553
        return this.gridAPI.crudService;
168✔
3554
    }
168✔
3555

3556
    /**
3557
     * @hidden
3558
     * @internal
3559
     */
3560
    public _setupServices() {
348✔
3561
        this.gridAPI.grid = this as any;
3562
        this.crudService.grid = this as any;
3563
        this.selectionService.grid = this as any;
3564
        this.validation.grid = this as any;
3565
        this.navigation.grid = this as any;
3566
        this.filteringService.grid = this as any;
3567
        this.summaryService.grid = this as any;
3568
    }
3569

3570
    /**
4✔
3571
     * @hidden
167✔
3572
     * @internal
167✔
3573
     */
3574
    public _setupListeners() {
3575
        const destructor = takeUntil<any>(this.destroy$);
3576
        fromEvent(this.nativeElement, 'focusout').pipe(filter(() => !!this.navigation.activeNode), destructor).subscribe((event) => {
3577
            if (!this.crudService.cell &&
3578
                !!this.navigation.activeNode &&
3579
                ((event.target === this.tbody.nativeElement && this.navigation.activeNode.row >= 0 &&
3580
                    this.navigation.activeNode.row < this.dataView.length)
3581
                    || (event.target === this.theadRow.nativeElement && this.navigation.activeNode.row === -1)
3582
                    || (event.target === this.tfoot.nativeElement && this.navigation.activeNode.row === this.dataView.length)) &&
3583
                !(this.rowEditable && this.crudService.rowEditingBlocked && this.crudService.rowInEditMode)) {
3584
                this.navigation.lastActiveNode = this.navigation.activeNode;
67✔
3585
                this.navigation.activeNode = {} as IActiveNode;
67✔
3586
                this.notifyChanges();
3587
            }
3588
        });
3589
        this.rowAddedNotifier.pipe(destructor).subscribe(args => this.refreshGridState(args));
3590
        this.rowDeletedNotifier.pipe(destructor).subscribe(args => {
3591
            this.summaryService.deleteOperation = true;
3592
            this.summaryService.clearSummaryCache(args);
3593
        });
3594

3595
        this.subscribeToTransactions();
3596

3597
        this.resizeNotify.pipe(
3598
            filter(() => !this._init),
12✔
3599
            throttleTime(0, animationFrameScheduler, { leading: true, trailing: true }),
12✔
3600
            destructor
7✔
3601
        )
3602
            .subscribe(() => {
3603
                this.zone.run(() => {
5✔
3604
                    // do not trigger reflow if element is detached.
18!
3605
                    if (this.document.contains(this.nativeElement)) {
×
3606
                        this.notifyChanges(true);
×
3607
                    }
3608
                });
3609
            });
18✔
3610

3611
        this.pipeTriggerNotifier.pipe(takeUntil(this.destroy$)).subscribe(() => this.pipeTrigger++);
3612
        this.columnMovingEnd.pipe(destructor).subscribe(() => this.crudService.endEdit(false));
3613

12✔
3614
        this.overlayService.opening.pipe(destructor).subscribe((event) => {
12✔
3615
            if (this._advancedFilteringOverlayId === event.id) {
3616
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
3617
                if (instance) {
3618
                    instance.initialize(this as any, this.overlayService, event.id);
3619
                }
3620
            }
3621
        });
3622

3623
        this.overlayService.opened.pipe(destructor).subscribe((event) => {
3624
            const overlaySettings = this.overlayService.getOverlayById(event.id)?.settings;
3625

3626
            // do not hide the advanced filtering overlay on scroll
3✔
3627
            if (this._advancedFilteringOverlayId === event.id) {
3✔
3628
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
2✔
3629
                if (instance) {
3630
                    instance.lastActiveNode = this.navigation.activeNode;
3631
                    instance.queryBuilder.setAddButtonFocus();
1✔
3632
                }
2!
3633
                return;
×
3634
            }
×
3635

3636
            // do not hide the overlay if it's attached to a row
3637
            if (this.rowEditingOverlay?.overlayId === event.id) {
2✔
3638
                return;
3639
            }
3640

3641
            if (overlaySettings?.outlet === this.outlet && this.overlayIDs.indexOf(event.id) === -1) {
3✔
3642
                this.overlayIDs.push(event.id);
3✔
3643
            }
3644
        });
3645

3646
        this.overlayService.closed.pipe(filter(() => !this._init), destructor).subscribe((event) => {
3647
            if (this._advancedFilteringOverlayId === event.id) {
3648
                this.overlayService.detach(this._advancedFilteringOverlayId);
3649
                this._advancedFilteringOverlayId = null;
3650
                return;
3651
            }
3652

3653
            const ind = this.overlayIDs.indexOf(event.id);
3✔
3654
            if (ind !== -1) {
3✔
3655
                this.overlayIDs.splice(ind, 1);
3656
            }
3657
        });
3658

3659
        this.verticalScrollContainer.dataChanging.pipe(filter(() => !this._init), destructor).subscribe(($event) => {
3660
            const shouldRecalcSize = this.isPercentHeight &&
3661
                (!this.calcHeight || this.calcHeight === this.getDataBasedBodyHeight() ||
3662
                    this.calcHeight === this.renderedRowHeight * this._defaultTargetRecordNumber);
3663
            if (shouldRecalcSize) {
3664
                this.calculateGridHeight();
3665
                $event.containerSize = this.calcHeight;
15✔
3666
            }
3667
            this.evaluateLoadingState();
3668
        });
3669

3670
        this.verticalScrollContainer.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
3671
            // called to recalc all widths that may have changes as a result of
3672
            // the vert. scrollbar showing/hiding
3673
            this.notifyChanges(true);
3674
            this.cdr.detectChanges();
3675
        });
16✔
3676

20!
3677
        this.verticalScrollContainer.contentSizeChange.pipe(filter(() => !this._init), throttleTime(30), destructor).subscribe(() => {
20✔
3678
            this.notifyChanges(true);
3679
        });
×
3680

×
3681
        this.densityChanged.pipe(destructor).subscribe(() => {
×
3682
            this._autoSize = this.isPercentHeight && this.calcHeight !== this.getDataBasedBodyHeight();
3683
            this.crudService.endEdit(false);
3684
            if (this._summaryRowHeight === 0) {
3685
                this.summaryService.summaryHeight = 0;
3686
            }
3687
            this.notifyChanges(true);
3688
        });
97✔
3689
    }
13✔
3690

3691
    /**
1✔
3692
     * @hidden
3693
     */
12✔
3694
    public override ngOnInit() {
12✔
3695
        super.ngOnInit();
3696
        this._setupServices();
12!
3697
        this._setupListeners();
12✔
3698
        this.rowListDiffer = this.differs.find([]).create(null);
3699
        // compare based on field, not on object ref.
3700
        this.columnListDiffer = this.differs.find([]).create((index, col: ColumnType) => col.field);
12✔
3701
        this.calcWidth = this.width && this.width.indexOf('%') === -1 ? parseInt(this.width, 10) : 0;
3702
        this.shouldGenerate = this.autoGenerate;
12!
3703
    }
×
3704

×
3705
    /**
×
3706
     * @hidden
3707
     * @internal
3708
     */
×
3709
    public resetColumnsCaches() {
×
3710
        this._columns.forEach(column => column.resetCaches());
3711
    }
3712

3713
    /**
×
3714
     * @hidden @internal
×
3715
     */
3716
    public generateRowID(): string | number {
×
3717
        const primaryColumn = this._columns.find(col => col.field === this.primaryKey);
3718
        const idType = this.data.length ?
12✔
3719
            this.resolveDataTypes(this.data[0][this.primaryKey]) : primaryColumn ? primaryColumn.dataType : 'string';
2✔
3720
        return idType === 'string' ? uuidv4() : FAKE_ROW_ID--;
3721
    }
3722

10!
3723
    /**
×
3724
     * @hidden
×
3725
     * @internal
3726
     */
3727
    public resetForOfCache() {
10✔
3728
        const firstVirtRow = this.dataRowList.first;
10✔
3729
        if (firstVirtRow) {
3730
            if (this._cdrRequests) {
10✔
3731
                firstVirtRow.virtDirRow.cdr.detectChanges();
3732
            }
3733
            firstVirtRow.virtDirRow.assumeMaster();
3734
        }
3735
    }
3736

3737
    /**
10✔
3738
     * @hidden
10✔
3739
     * @internal
10✔
3740
     */
1✔
3741
    public setFilteredData(data, pinned: boolean) {
3742
        if (this.hasPinnedRecords && pinned) {
9✔
3743
            this._filteredPinnedData = data || [];
9!
3744
            const filteredUnpinned = this._filteredUnpinnedData || [];
9✔
3745
            const filteredData = [... this._filteredPinnedData, ...filteredUnpinned];
2✔
3746
            this._filteredData = filteredData.length > 0 ? filteredData : this._filteredUnpinnedData;
3747
        } else if (this.hasPinnedRecords && !pinned) {
9✔
3748
            this._filteredUnpinnedData = data;
4✔
3749
        } else {
3750
            this._filteredData = data;
9✔
3751
        }
3752
    }
3753

3754
    /**
9✔
3755
     * @hidden
9✔
3756
     * @internal
3757
     */
3758
    public resetColumnCollections() {
3759
        this._visibleColumns.length = 0;
3760
        this._pinnedVisible.length = 0;
3761
        this._unpinnedVisible.length = 0;
27✔
3762
    }
27✔
3763

27✔
3764
    /**
3765
     * @hidden
3766
     * @internal
3767
     */
3768
    public resetCachedWidths() {
3769
        this._unpinnedWidth = NaN;
3770
        this._pinnedWidth = NaN;
3771
        this._totalWidth = NaN;
3772
    }
3773

3774
    /**
3775
     * @hidden
3776
     * @internal
401✔
3777
     */
828✔
3778
    public resetCaches(recalcFeatureWidth = true) {
828!
3779
        if (recalcFeatureWidth) {
5,046✔
3780
            this._headerFeaturesWidth = NaN;
×
3781
        }
3782
        this.resetForOfCache();
828✔
3783
        this.resetColumnsCaches();
43✔
3784
        this.resetColumnCollections();
3785
        this.resetCachedWidths();
3786
        this.hasVisibleColumns = undefined;
828✔
3787
        this._columnGroups = this._columns.some(col => col.columnGroup);
828✔
3788
    }
828✔
3789

142✔
3790
    /**
139✔
3791
     * @hidden
16✔
3792
     */
3793
    public ngAfterContentInit() {
3794
        if (this.sortHeaderIconDirectiveTemplate) {
123✔
3795
            this.sortHeaderIconTemplate = this.sortHeaderIconDirectiveTemplate;
3796
        }
3797

3798
        if (this.sortAscendingHeaderIconDirectiveTemplate) {
686✔
3799
            this.sortAscendingHeaderIconTemplate = this.sortAscendingHeaderIconDirectiveTemplate;
93✔
3800
        }
93!
3801

×
3802
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
3803
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
3804
        }
93✔
3805

3806
        this.setupColumns();
3807
        this.toolbar.changes.pipe(filter(() => !this._init), takeUntil(this.destroy$)).subscribe(() => this.notifyChanges(true));
3808
        this.setUpPaginator();
3809
        this.paginationComponents.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
593✔
3810
            this.setUpPaginator();
3811
        });
3812
        if (this.actionStrip) {
3813
            this.actionStrip.menuOverlaySettings.outlet = this.outlet;
3814
        }
3815
    }
3816

3817
    /**
3818
     * @hidden @internal
3819
     */
3820
    public dataRebinding(event: IForOfDataChangingEventArgs) {
3821
        this.dataChanging.emit(event);
3822
    }
3823

3824
    /**
×
3825
     * @hidden @internal
477✔
3826
     */
56✔
3827
    public dataRebound(event) {
56✔
3828
        this.selectionService.clearHeaderCBState();
2✔
3829
        this.dataChanged.emit(event);
3830
    }
460!
3831

×
3832
    /** @hidden @internal */
147✔
3833
    public createFilterDropdown(column: ColumnType, options: OverlaySettings) {
54✔
3834
        options.outlet = this.outlet;
54✔
3835
        if (this.excelStyleFilteringComponent) {
39✔
3836
            this.excelStyleFilteringComponent.initialize(column, this.overlayService);
3837
            const id = this.overlayService.attach(this.excelStyleFilteringComponent.element, options);
3838
            this.excelStyleFilteringComponent.overlayComponentId = id;
15✔
3839
            return { id, ref: undefined };
15!
3840
        }
×
3841
        const ref = this.createComponentInstance(IgxGridExcelStyleFilteringComponent);
3842
        ref.instance.initialize(column, this.overlayService);
3843
        const id = this.overlayService.attach(ref.instance.element, options);
15✔
3844
        ref.instance.overlayComponentId = id;
3845
        return { ref, id };
3846
    }
3847

3848
    private createComponentInstance(component: any) {
3849
        const dynamicComponent: ComponentRef<any> = createComponent(component, { environmentInjector: this.envInjector, elementInjector: this.injector } );
3850
        this.appRef.attachView(dynamicComponent.hostView);
3851
        return dynamicComponent;
3852
    }
3853

3854
    /** @hidden @internal */
3855
    public setUpPaginator() {
3856
        if (this.paginator) {
3857
            this.paginator.pageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
3858
                .subscribe((page: number) => {
3859
                    this.pageChange.emit(page);
1✔
3860
                });
300✔
3861
            this.paginator.pagingDone.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
41✔
3862
                .subscribe((args: IPageEventArgs) => {
41✔
3863
                    this.selectionService.clear(true);
2✔
3864
                    this.pagingDone.emit({ previous: args.previous, current: args.current });
3865
                    this.crudService.endEdit(false);
237✔
3866
                    this.pipeTrigger++;
4✔
3867
                    this.navigateTo(0);
141✔
3868
                    this.notifyChanges();
39✔
3869
                });
39✔
3870
            this.paginator.perPageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
24✔
3871
                .subscribe((perPage: number) => {
3872
                    this.selectionService.clear(true);
3873
                    this.perPageChange.emit(perPage);
15✔
3874
                    this.paginator.page = 0;
15✔
3875
                    this.crudService.endEdit(false);
7✔
3876
                    this.notifyChanges();
3877
                });
3878
        } else {
8✔
3879
            this.markForCheck();
3880
        }
3881
    }
3882

3883
    /**
3884
     * @hidden
3885
     * @internal
3886
     */
×
3887
    public setFilteredSortedData(data, pinned: boolean) {
14✔
3888
        data = data || [];
14✔
3889
        if (this.pinnedRecordsCount > 0) {
3✔
3890
            if (pinned) {
3891
                this._filteredSortedPinnedData = data;
11✔
3892
                this.pinnedRecords = data;
11!
3893
                this._filteredSortedData = this.isRowPinningToTop ? [... this._filteredSortedPinnedData, ... this._filteredSortedUnpinnedData] :
11✔
3894
                    [... this._filteredSortedUnpinnedData, ... this._filteredSortedPinnedData];
3895
                this.refreshSearch(true, false);
3896
            } else {
3897
                this._filteredSortedUnpinnedData = data;
3898
            }
3899
        } else {
3900
            this._filteredSortedData = data;
1,345,187✔
3901
            this.refreshSearch(true, false);
3902
        }
3903
        this.buildDataView(data);
3904
    }
3905

3906
    /**
×
3907
     * @hidden @internal
3908
     */
3909
    public resetHorizontalVirtualization() {
3910
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
3911
        this._horizontalForOfs = [
3912
            ...this._dataRowList.filter(elementFilter).map(item => item.virtDirRow),
3913
            ...this._summaryRowList.filter(elementFilter).map(item => item.virtDirRow)
205✔
3914
        ];
205✔
3915
    }
205✔
3916

3917
    /**
3918
     * @hidden @internal
3919
     */
3920
    public _setupRowObservers() {
3921
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
128✔
3922
        const extractForOfs = pipe(map((collection: any[]) => collection.filter(elementFilter).map(item => item.virtDirRow)));
128✔
3923
        const rowListObserver = extractForOfs(this._dataRowList.changes);
128✔
3924
        const summaryRowObserver = extractForOfs(this._summaryRowList.changes);
128✔
3925
        rowListObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3926
            this.resetHorizontalVirtualization();
3927
        });
3928
        summaryRowObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
3929
            this.resetHorizontalVirtualization();
3930
        });
255✔
3931
        this.resetHorizontalVirtualization();
255!
3932
    }
255✔
3933

3934
    /**
3935
     * @hidden @internal
×
3936
     */
3937
    public _zoneBegoneListeners() {
3938
        this.zone.runOutsideAngular(() => {
3939
            this.verticalScrollContainer.getScroll().addEventListener('scroll', this.verticalScrollHandler.bind(this));
3940
            this.headerContainer?.getScroll().addEventListener('scroll', this.horizontalScrollHandler.bind(this));
3941
            if (this.hasColumnsToAutosize) {
3942
                this.headerContainer?.dataChanged.pipe(takeUntil(this.destroy$)).subscribe(() => {
815✔
3943
                    this.cdr.detectChanges();
50✔
3944
                    this.zone.onStable.pipe(first()).subscribe(() => {
50!
3945
                        this.autoSizeColumnsInView();
50✔
3946
                    });
50✔
3947
                });
50✔
3948
            }
3949
            fromEvent(window, 'resize').pipe(takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
3950
            resizeObservable(this.nativeElement).pipe(takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
×
3951
        });
3952
    }
3953

3954
    /**
3955
     * @hidden
3956
     */
3957
    public ngAfterViewInit() {
3958
        this.initPinning();
607✔
3959
        this.calculateGridSizes();
429✔
3960
        this._init = false;
429✔
3961
        this.cdr.reattach();
429✔
3962
        this._setupRowObservers();
429✔
3963
        this._zoneBegoneListeners();
429✔
3964

429✔
3965
        const vertScrDC = this.verticalScrollContainer.displayContainer;
429✔
3966
        vertScrDC.addEventListener('scroll', this.preventContainerScroll.bind(this));
260✔
3967

3968
        this._pinnedRowList.changes
169✔
3969
            .pipe(takeUntil(this.destroy$))
43✔
3970
            .subscribe((change: QueryList<IgxGridRowComponent>) => {
3971
                this.onPinnedRowsChanged(change);
3972
            });
3973

3974
        this.addRowSnackbar?.clicked.subscribe(() => {
3975
            const rec = this.filteredSortedData[this.lastAddedRowIndex];
3976
            this.scrollTo(rec, 0);
3977
            this.addRowSnackbar.close();
3978
        });
82!
3979

82✔
3980
        // Keep the stream open for future subscribers
79✔
3981
        this.rendered$.pipe(takeUntil(this.destroy$)).subscribe(() => {
79!
3982
            if (this.paginator) {
79✔
3983
                this.paginator.perPage = this._perPage !== DEFAULT_ITEMS_PER_PAGE ? this._perPage : this.paginator.perPage;
79✔
3984
                this.paginator.totalRecords = this.totalRecords ? this.totalRecords : this.paginator.totalRecords;
79✔
3985
                this.paginator.overlaySettings = { outlet: this.outlet };
3986
            }
3987
            if (this.hasColumnsToAutosize) {
3988
                this.autoSizeColumnsInView();
3989
            }
3990
            this._rendered = true;
3991
        });
3992
        Promise.resolve().then(() => this.rendered.next(true));
3993
    }
4!
3994

4✔
3995
    /**
4✔
3996
     * @hidden @internal
4✔
3997
     */
2✔
3998
    public notifyChanges(repaint = false) {
3999
        this._cdrRequests = true;
4✔
4000
        this._cdrRequestRepaint = repaint;
4001
        this.cdr.markForCheck();
4002
    }
4003

4004
    /**
4005
     * @hidden @internal
4006
     */
48✔
4007
    public override ngDoCheck() {
213✔
4008
        super.ngDoCheck();
48✔
4009
        if (this._init) {
48✔
4010
            return;
48✔
4011
        }
4012

4013
        if (this._cdrRequestRepaint) {
4014
            this.resetNotifyChanges();
4015
            this.calculateGridSizes();
4016
            this.refreshSearch(true);
616✔
4017
            return;
4018
        }
4019

4020
        if (this._cdrRequests) {
4021
            this.resetNotifyChanges();
4022
            this.cdr.detectChanges();
336,687✔
4023
        }
4024
    }
4025

4026
    /**
4027
     * @hidden
4028
     * @internal
122✔
4029
     */
122✔
4030
    public getDragGhostCustomTemplate() {
4031

4032
        return this.dragGhostCustomTemplate;
4033
    }
4034

4035
    /**
×
4036
     * @hidden @internal
×
4037
     */
4038
    public ngOnDestroy() {
4039
        this.tmpOutlets.forEach((tmplOutlet) => {
×
4040
            tmplOutlet.cleanCache();
4041
        });
4042

4043
        this.destroy$.next(true);
4044
        this.destroy$.complete();
4045
        this.transactionChange$.next();
4046
        this.transactionChange$.complete();
92✔
4047
        this._destroyed = true;
4048

4049
        if (this._advancedFilteringOverlayId) {
4050
            this.overlayService.detach(this._advancedFilteringOverlayId);
4051
            delete this._advancedFilteringOverlayId;
4052
        }
4053

4054
        this.overlayIDs.forEach(overlayID => {
4055
            const overlay = this.overlayService.getOverlayById(overlayID);
4056

4057
            if (overlay && !overlay.detached) {
4058
                this.overlayService.detach(overlayID);
4059
            }
4060
        });
4061

1✔
4062
        this.zone.runOutsideAngular(() => {
5✔
4063
            this.verticalScrollContainer?.getScroll()?.removeEventListener('scroll', this.verticalScrollHandler);
4064
            this.headerContainer?.getScroll()?.removeEventListener('scroll', this.horizontalScrollHandler);
4065
            const vertScrDC = this.verticalScrollContainer?.displayContainer;
4066
            vertScrDC?.removeEventListener('scroll', this.preventContainerScroll);
4067
        });
4068
    }
4069

4070
    /**
4071
     * Toggles the specified column's visibility.
4072
     *
4073
     * @example
4074
     * ```typescript
4075
     * this.grid1.toggleColumnVisibility({
4076
     *       column: this.grid1.columns[0],
4077
     *       newValue: true
4078
     * });
4079
     * ```
4080
     */
4081
    public toggleColumnVisibility(args: IColumnVisibilityChangedEventArgs) {
3✔
4082
        const col = args.column ? this._columns.find((c) => c === args.column) : undefined;
3✔
4083

2!
4084
        if (!col) {
×
4085
            return;
×
4086
        }
4087
        col.toggleVisibility(args.newValue);
2✔
4088
    }
4089

4090
    /**
4091
     * Gets/Sets a list of key-value pairs [row ID, expansion state].
1✔
4092
     *
1!
4093
     * @remarks
×
4094
     * Includes only states that differ from the default one.
×
4095
     * Supports two-way binding.
4096
     * @example
4097
     * ```html
3✔
4098
     * <igx-grid #grid [data]="data" [(expansionStates)]="model.expansionStates">
4099
     * </igx-grid>
4100
     * ```
3!
4101
     */
×
4102
    @Input()
×
4103
    public get expansionStates() {
4104
        return this._expansionStates;
4105
    }
3!
4106

4107
    public set expansionStates(value) {
3✔
4108
        this._expansionStates = new Map<any, boolean>(value);
4109
        this.expansionStatesChange.emit(this._expansionStates);
4110
        this.notifyChanges(true);
1✔
4111
        if (this.gridAPI.grid) {
4112
            this.cdr.detectChanges();
4113
        }
1✔
4114
    }
4115

1✔
4116
    /**
1✔
4117
     * Expands all rows.
1✔
4118
     *
4119
     * @example
2✔
4120
     * ```typescript
4121
     * this.grid.expandAll();
4122
     * ```
×
4123
     */
4124
    public expandAll() {
4125
        this._defaultExpandState = true;
4126
        this.expansionStates = new Map<any, boolean>();
4127
    }
4128

4129
    /**
4130
     * Collapses all rows.
4131
     *
4132
     * @example
4133
     * ```typescript
4134
     * this.grid.collapseAll();
4135
     * ```
4136
     */
4137
    public collapseAll() {
1!
4138
        this._defaultExpandState = false;
1✔
4139
        this.expansionStates = new Map<any, boolean>();
4140
    }
×
4141

4142
    /**
4143
     * Expands the row by its id.
4144
     *
4145
     * @remarks
4146
     * ID is either the primaryKey value or the data record instance.
×
4147
     * @example
×
4148
     * ```typescript
×
4149
     * this.grid.expandRow(rowID);
4150
     * ```
4151
     * @param rowID The row id - primaryKey value or the data record instance.
2✔
4152
     */
4153
    public expandRow(rowID: any) {
3✔
4154
        this.gridAPI.set_row_expansion_state(rowID, true);
2✔
4155
    }
3!
4156

3✔
4157
    /**
4158
     * Collapses the row by its id.
4159
     *
×
4160
     * @remarks
4161
     * ID is either the primaryKey value or the data record instance.
4162
     * @example
4163
     * ```typescript
237✔
4164
     * this.grid.collapseRow(rowID);
236✔
4165
     * ```
4166
     * @param rowID The row id - primaryKey value or the data record instance.
4167
     */
1✔
4168
    public collapseRow(rowID: any) {
4169
        this.gridAPI.set_row_expansion_state(rowID, false);
237!
4170
    }
237✔
4171

4172

4173
    /**
4174
     * Toggles the row by its id.
3,644✔
4175
     *
3,644✔
4176
     * @remarks
4177
     * ID is either the primaryKey value or the data record instance.
4178
     * @example
4179
     * ```typescript
318✔
4180
     * this.grid.toggleRow(rowID);
318✔
4181
     * ```
44!
4182
     * @param rowID The row id - primaryKey value or the data record instance.
4183
     */
279✔
4184
    public toggleRow(rowID: any) {
54!
4185
        const rec = this.gridAPI.get_rec_by_id(rowID);
4186
        const state = this.gridAPI.get_row_expansion_state(rec);
318✔
4187
        this.gridAPI.set_row_expansion_state(rowID, !state);
22✔
4188
    }
27!
4189

×
4190
    /**
4191
     * @hidden
4192
     * @internal
4193
     */
318✔
4194
    public getDefaultExpandState(_rec: any) {
88✔
4195
        return this._defaultExpandState;
98✔
4196
    }
43✔
4197

43✔
4198
    /**
4199
     * Gets the native element.
55!
4200
     *
55✔
4201
     * @example
55✔
4202
     * ```typescript
29!
4203
     * const nativeEl = this.grid.nativeElement.
29!
4204
     * ```
29✔
4205
     */
4206
    public get nativeElement() {
4207
        return this.elementRef.nativeElement;
26✔
4208
    }
4209

4210
    /**
4211
     * Gets/Sets the outlet used to attach the grid's overlays to.
4212
     *
318✔
4213
     * @remark
318✔
4214
     * If set, returns the outlet defined outside the grid. Otherwise returns the grid's internal outlet directive.
318✔
4215
     */
318✔
4216
    @Input()
4217
    public get outlet() {
4218
        return this.resolveOutlet();
4219
    }
×
4220

4221
    public set outlet(val: IgxOverlayOutletDirective) {
4222
        this._userOutletDirective = val;
4223
    }
4224

303✔
4225

303✔
4226
    /**
303✔
4227
     * Gets the default row height.
303✔
4228
     *
303!
4229
     * @example
×
4230
     * ```typescript
×
4231
     * const rowHeigh = this.grid.defaultRowHeight;
4232
     * ```
4233
     */
4234
    public get defaultRowHeight(): number {
14✔
4235
        switch (this.displayDensity) {
12✔
4236
            case DisplayDensity.cosy:
4237
                return 40;
2!
4238
            case DisplayDensity.compact:
×
4239
                return 32;
4240
            default:
4241
                return 50;
2✔
4242
        }
4243
    }
4244

4245
    /**
4246
     * @hidden @internal
4247
     */
4248
    public get defaultSummaryHeight(): number {
4249
        switch (this.displayDensity) {
3,119✔
4250
            case DisplayDensity.cosy:
4251
                return 30;
12✔
4252
            case DisplayDensity.compact:
4253
                return 24;
4254
            default:
4255
                return 36;
4256
        }
12✔
4257
    }
4258

4259
    /**
3,107✔
4260
     * Returns the `IgxGridHeaderGroupComponent`'s minimum allowed width.
4261
     *
4262
     * @remarks
4263
     * Used internally for restricting header group component width.
4264
     * The values below depend on the header cell default right/left padding values.
4265
     */
4266
    public get defaultHeaderGroupMinWidth(): number {
4267
        switch (this.displayDensity) {
4268
            case DisplayDensity.cosy:
11,174✔
4269
                return 32;
4270
            case DisplayDensity.compact:
5,112✔
4271
                return 24;
5,112✔
4272
            default:
4273
                return 48;
4274
        }
6,062✔
4275
    }
4276

11,174✔
4277
    /**
512✔
4278
     * Gets the current width of the container for the pinned `IgxColumnComponent`s.
4279
     *
11,174✔
4280
     * @example
197✔
4281
     * ```typescript
4282
     * const pinnedWidth = this.grid.getPinnedWidth;
11,174✔
4283
     * ```
2,267✔
4284
     */
4285
    public get pinnedWidth() {
11,174✔
4286
        if (!isNaN(this._pinnedWidth)) {
3,265✔
4287
            return this._pinnedWidth;
4288
        }
11,174✔
4289
        this._pinnedWidth = this.getPinnedWidth();
4290
        return this._pinnedWidth;
4291
    }
4292

4293
    /**
4294
     * Gets the current width of the container for the unpinned `IgxColumnComponent`s.
4295
     *
11,174✔
4296
     * @example
10,461✔
4297
     * ```typescript
4298
     * const unpinnedWidth = this.grid.getUnpinnedWidth;
11,174✔
4299
     * ```
69,419✔
4300
     */
3,370✔
4301
    public get unpinnedWidth() {
3,370✔
4302
        if (!isNaN(this._unpinnedWidth)) {
4303
            return this._unpinnedWidth;
4304
        }
4305
        this._unpinnedWidth = this.getUnpinnedWidth();
66,049✔
4306
        return this._unpinnedWidth;
66,049✔
4307
    }
4308

4309
    /**
11,174✔
4310
     * @hidden @internal
4311
     */
4312
    public get isHorizontalScrollHidden() {
4313
        const diff = this.unpinnedWidth - this.totalWidth;
4314
        return this.width === null || diff >= 0;
4315
    }
4316

66,049✔
4317
    /**
66,049!
4318
     * @hidden @internal
×
4319
     * Gets the header cell inner width for auto-sizing.
4320
     */
66,049✔
4321
    public getHeaderCellWidth(element: HTMLElement): ISizeInfo {
66,049✔
4322
        const range = this.document.createRange();
66,049✔
4323
        const headerWidth = this.platform.getNodeSizeViaRange(range,
1,283✔
4324
            element,
4325
            element.parentElement);
64,766!
4326

×
4327
        const headerStyle = this.document.defaultView.getComputedStyle(element);
4328
        const headerPadding = parseFloat(headerStyle.paddingLeft) + parseFloat(headerStyle.paddingRight) +
64,766✔
4329
            parseFloat(headerStyle.borderRightWidth);
1,521✔
4330

4331
        // Take into consideration the header group element, since column pinning applies borders to it if its not a columnGroup.
4332
        const headerGroupStyle = this.document.defaultView.getComputedStyle(element.parentElement);
64,766✔
4333
        const borderSize = parseFloat(headerGroupStyle.borderRightWidth) + parseFloat(headerGroupStyle.borderLeftWidth);
248✔
4334
        return { width: Math.ceil(headerWidth), padding: Math.ceil(headerPadding + borderSize) };
4335
    }
64,766✔
4336

4337
    /**
4338
     * @hidden @internal
7,620✔
4339
     * Gets the combined width of the columns that are specific to the enabled grid features. They are fixed.
7,620✔
4340
     */
4341
    public featureColumnsWidth(expander?: ElementRef) {
4342
        if (Number.isNaN(this._headerFeaturesWidth)) {
4343
            // TODO: platformUtil.isBrowser check
200,661!
4344
            const rowSelectArea = this.headerSelectorContainer?.nativeElement?.getBoundingClientRect ?
4345
                this.headerSelectorContainer.nativeElement.getBoundingClientRect().width : 0;
4346
            const rowDragArea = this.rowDraggable && this.headerDragContainer?.nativeElement?.getBoundingClientRect ?
4347
                this.headerDragContainer.nativeElement.getBoundingClientRect().width : 0;
4348
            const groupableArea = this.headerGroupContainer?.nativeElement?.getBoundingClientRect ?
4349
                this.headerGroupContainer.nativeElement.getBoundingClientRect().width : 0;
4350
            const expanderWidth = expander?.nativeElement?.getBoundingClientRect ? expander.nativeElement.getBoundingClientRect().width : 0;
4351
            this._headerFeaturesWidth = rowSelectArea + rowDragArea + groupableArea + expanderWidth;
138✔
4352
        }
138✔
4353
        return this._headerFeaturesWidth;
138✔
4354
    }
138✔
4355

138✔
4356
    /**
4357
     * @hidden @internal
4358
     */
4359
    public get summariesMargin() {
4360
        return this.featureColumnsWidth();
4361
    }
4362

4363
    /**
5,343✔
4364
     * Gets an array of `IgxColumnComponent`s.
32,469✔
4365
     *
32,469✔
4366
     * @example
5,343✔
4367
     * ```typescript
5,343✔
4368
     * const colums = this.grid.columns.
4369
     * ```
4370
     */
4371
    public get columns(): IgxColumnComponent[] {
4372
        return this._columns || [];
4373
    }
138!
4374

×
4375
    /**
4376
     * Gets an array of the pinned `IgxColumnComponent`s.
138✔
4377
     *
1,189✔
4378
     * @example
814✔
4379
     * ```typescript
814✔
4380
     * const pinnedColumns = this.grid.pinnedColumns.
138✔
4381
     * ```
4382
     */
4383
    public get pinnedColumns(): IgxColumnComponent[] {
138✔
4384
        if (this._pinnedVisible.length) {
4385
            return this._pinnedVisible;
4386
        }
4387
        this._pinnedVisible = this._pinnedColumns.filter(col => !col.hidden);
4388
        return this._pinnedVisible;
4389
    }
4390

4391
    /**
4392
     * Gets an array of the pinned `IgxRowComponent`s.
138✔
4393
     *
154✔
4394
     * @example
154✔
4395
     * ```typescript
154✔
4396
     * const pinnedRow = this.grid.pinnedRows;
154✔
4397
     * ```
154✔
4398
     */
87✔
4399
    public get pinnedRows(): IgxGridRowComponent[] {
87✔
4400
        return this._pinnedRowList.toArray().sort((a, b) => a.index - b.index);
21✔
4401
    }
4402

4403
    /**
154✔
4404
     * Gets an array of unpinned `IgxColumnComponent`s.
4405
     *
4406
     * @example
4407
     * ```typescript
4408
     * const unpinnedColumns = this.grid.unpinnedColumns.
4409
     * ```
4410
     */
4411
    public get unpinnedColumns(): IgxColumnComponent[] {
16✔
4412
        if (this._unpinnedVisible.length) {
16✔
4413
            return this._unpinnedVisible;
16✔
4414
        }
4415
        this._unpinnedVisible = this._unpinnedColumns.filter((col) => !col.hidden);
4416
        return this._unpinnedVisible;
4417
    }
4418

4419
    /**
3,209✔
4420
     * Gets the `width` to be set on `IgxGridHeaderGroupComponent`.
820✔
4421
     */
4422
    public getHeaderGroupWidth(column: IgxColumnComponent): string {
4423
        return this.hasColumnLayouts
2,389✔
4424
            ? ''
4425
            : `${Math.max(parseFloat(column.calcWidth), this.defaultHeaderGroupMinWidth)}px`;
20,623✔
4426
    }
3,209✔
4427

3,209✔
4428
    /**
4429
     * Returns the `IgxColumnComponent` by field name.
4430
     *
74✔
4431
     * @example
4432
     * ```typescript
4433
     * const myCol = this.grid1.getColumnByName("ID");
4434
     * ```
2,214✔
4435
     * @param name
4436
     */
4437
    public getColumnByName(name: string): IgxColumnComponent {
4438
        return this._columns.find((col) => col.field === name);
4439
    }
4440

4441
    public getColumnByVisibleIndex(index: number): IgxColumnComponent {
4442
        return this.visibleColumns.find((col) =>
×
4443
            !col.columnGroup && !col.columnLayout &&
×
4444
            col.visibleIndex === index
×
4445
        );
×
4446
    }
4447

4448
    /**
×
4449
     * Recalculates all widths of columns that have size set to `auto`.
4450
     *
4451
     * @example
4452
     * ```typescript
×
4453
     * this.grid1.recalculateAutoSizes();
×
4454
     * ```
4455
     */
4456
    public recalculateAutoSizes() {
4457
        // reset auto-size and calculate it again.
4458
        this._columns.forEach(x => x.autoSize = undefined);
4459
        this.resetCaches();
4460
        this.zone.onStable.pipe(first()).subscribe(() => {
838✔
4461
            this.cdr.detectChanges();
4462
            this.autoSizeColumnsInView();
4463
        });
4464
    }
4465

4466
    /**
4467
     * Returns an array of visible `IgxColumnComponent`s.
182✔
4468
     *
182!
4469
     * @example
182✔
4470
     * ```typescript
4471
     * const visibleColumns = this.grid.visibleColumns.
4472
     * ```
4473
     */
4474
    public get visibleColumns(): IgxColumnComponent[] {
4475
        if (this._visibleColumns.length) {
4476
            return this._visibleColumns;
65✔
4477
        }
65!
4478
        this._visibleColumns = this._columns.filter(c => !c.hidden);
4479
        return this._visibleColumns;
4480
    }
×
4481

×
4482
    /**
4483
     * @deprecated in version 12.1.0. Use the corresponding property exposed by the `igx-paginator`
65!
4484
     *
65✔
4485
     * Gets the total number of pages.
65✔
4486
     *
65✔
4487
     *
1,018✔
4488
     * @example
1,018✔
4489
     * ```typescript
1✔
4490
     * const totalPages = this.grid.totalPages;
4491
     * ```
4492
     */
1,017✔
4493
    public get totalPages(): number {
4494
        return this.paginator?.totalPages;
4495
    }
1,279✔
4496

65✔
4497
    /**
129✔
4498
     * @deprecated in version 12.1.0. Use the corresponding property exposed by the `igx-paginator`
129✔
4499
     *
4500
     * Gets if the current page is the first page.
126✔
4501
     *
4502
     *
126✔
4503
     * @example
4504
     * ```typescript
126!
4505
     * const firstPage = this.grid.isFirstPage;
4506
     * ```
4507
     */
×
4508
    public get isFirstPage(): boolean {
4509
        return this.paginator.isLastPage;
4510
    }
126✔
4511

4512
    /**
126✔
4513
     * @deprecated in version 12.1.0. Use the corresponding method `nextPage()` exposed by the `igx-paginator` instance.
4514
     *
129✔
4515
     * Goes to the next page, if the grid is not already at the last page.
4516
     *
65✔
4517
     *
65✔
4518
     * @example
61✔
4519
     * ```typescript
4520
     * this.grid1.nextPage();
4521
     * ```
4522
     */
4523
    // eslint-disable-next-line @typescript-eslint/member-ordering
4524
    public nextPage(): void {
4525
        this.paginator?.nextPage();
4526
    }
61✔
4527

61✔
4528
    /**
4529
     * @deprecated in version 12.1.0. Use the corresponding method `nextPage()` exposed by the `igx-paginator` instance.
4530
     *
61✔
4531
     * Goes to the previous page, if the grid is not already at the first page.
4532
     *
4533
     * @example
4534
     * ```
4535
     */
4536
    // eslint-disable-next-line @typescript-eslint/member-ordering
7,334✔
4537
    public previousPage(): void {
4538
        this.paginator?.previousPage();
4539
    }
4540

4541
    /**
4542
     * Returns the total number of records.
4543
     *
4544
     * @remarks
4545
     * Only functions when paging is enabled.
7,336✔
4546
     * @example
7,336✔
4547
     * ```typescript
7,336✔
4548
     * const totalRecords = this.grid.totalRecords;
7,336✔
4549
     * ```
7,336✔
4550
     */
7,336✔
4551
    @Input()
7,336✔
4552
    public get totalRecords(): number {
7,336✔
4553
        return this._totalRecords >= 0 ? this._totalRecords : this.pagingState?.metadata.countRecords;
806✔
4554
    }
4555

7,336✔
4556
    public set totalRecords(total: number) {
106✔
4557
        if (total >= 0) {
4558
            if (this.paginator) {
7,336✔
4559
                this.paginator.totalRecords = total;
4560
            }
7,336✔
4561
            this._totalRecords = total;
83✔
4562
            this.pipeTrigger++;
83✔
4563
            this.notifyChanges();
4564
        }
7,336✔
4565
    }
207✔
4566

207✔
4567
    /**
207✔
4568
     * @deprecated in version 12.1.0. Use the corresponding property exposed by the `igx-paginator`
4569
     *
4570
     * Returns if the current page is the last page.
4571
     *
7,129✔
4572
     *
7,129✔
4573
     * @example
7,129✔
4574
     * ```typescript
4575
     * const lastPage = this.grid.isLastPage;
4576
     * ```
4577
     */
7,336✔
4578
    public get isLastPage(): boolean {
7,336✔
4579
        return this.paginator.isLastPage;
17✔
4580
    }
17✔
4581

17✔
4582
    /**
4583
     * Returns the total width of the `IgxGridComponent`.
4584
     *
4585
     * @example
4586
     * ```typescript
4587
     * const gridWidth = this.grid.totalWidth;
4588
     * ```
4589
     */
4590
    public get totalWidth(): number {
6,914✔
4591
        if (!isNaN(this._totalWidth)) {
583✔
4592
            return this._totalWidth;
583✔
4593
        }
583✔
4594
        // Take only top level columns
583✔
4595
        const cols = this.visibleColumns.filter(col => col.level === 0 && !col.pinned);
4596
        let totalWidth = 0;
4597
        let i = 0;
4598
        for (i; i < cols.length; i++) {
4599
            totalWidth += parseInt(cols[i].calcWidth, 10) || 0;
4600
        }
4601
        this._totalWidth = totalWidth;
4602
        return totalWidth;
7,276✔
4603
    }
7,276✔
4604

7,276✔
4605
    /**
106✔
4606
     * @hidden
4607
     * @internal
4608
     */
4609
    public get showRowSelectors(): boolean {
4610
        return this.isRowSelectable && this.hasVisibleColumns && !this.hideRowSelectors;
4611
    }
4612

1,982✔
4613
    /**
4614
     * @hidden
4615
     * @internal
4616
     */
4617
    public get showAddButton() {
4618
        return this.rowEditable && this.dataView.length === 0 && this._columns.length > 0;
25,790✔
4619
    }
4620

4621
    /**
4622
     * @hidden
4623
     * @internal
4624
     */
6,674✔
4625
    public get showDragIcons(): boolean {
4626
        return this.rowDraggable && this._columns.length > this.hiddenColumnsCount;
4627
    }
4628

4629
    /**
4630
     * @hidden
6,674✔
4631
     * @internal
6,674✔
4632
     */
4633
    protected _getDataViewIndex(index: number): number {
4634
        let newIndex = index;
4635
        if ((index < 0 || index >= this.dataView.length) && this.pagingMode === 1 && this.paginator.page !== 0) {
4636
            newIndex = index - this.paginator.perPage * this.paginator.page;
4637
        } else if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
4638
            newIndex = index - this.gridAPI.grid.virtualizationState.startIndex;
4639
        }
6,674✔
4640
        return newIndex;
6,674✔
4641
    }
251✔
4642

4643
    /**
6,674✔
4644
     * @hidden
4645
     * @internal
4646
     */
4647
    protected getDataIndex(dataViewIndex: number): number {
4648
        let newIndex = dataViewIndex;
4649
        if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
6,674✔
4650
            newIndex = dataViewIndex + this.gridAPI.grid.virtualizationState.startIndex;
6,674!
4651
        }
6,674✔
4652
        return newIndex;
6,674✔
4653
    }
4654

4655
    /**
6,674✔
4656
     * Places a column before or after the specified target column.
4657
     *
4658
     * @example
4659
     * ```typescript
4660
     * grid.moveColumn(column, target);
4661
     * ```
5,626✔
4662
     */
4663
    public moveColumn(column: IgxColumnComponent, target: IgxColumnComponent, pos: DropPosition = DropPosition.AfterDropTarget) {
5,626✔
4664
        // M.A. May 11th, 2021 #9508 Make the event cancelable
4665
        const eventArgs: IColumnMovingEndEventArgs = { source: column, target, cancel: false };
5,626✔
4666

4667
        this.columnMovingEnd.emit(eventArgs);
4668

4669
        if (eventArgs.cancel) {
4670
            return;
4671
        }
7,276✔
4672

602✔
4673
        if (column === target || (column.level !== target.level) ||
4674
            (column.topLevelParent !== target.topLevelParent)) {
6,674✔
4675
            return;
6,674✔
4676
        }
6,674✔
4677

6,674✔
4678
        if (column.level) {
6,674✔
4679
            this._moveChildColumns(column.parent, column, target, pos);
6,674✔
4680
        }
6,674✔
4681

4682
        // let columnPinStateChanged;
4683
        // pinning and unpinning will work correctly even without passing index
6,674✔
4684
        // but is easier to calclulate the index here, and later use it in the pinning event args
6,674✔
4685
        if (target.pinned && !column.pinned) {
1,323✔
4686
            const pinnedIndex = this._pinnedColumns.indexOf(target);
1,323✔
4687
            const index = pos === DropPosition.AfterDropTarget ? pinnedIndex + 1 : pinnedIndex;
1,323✔
4688
            column.pin(index);
630✔
4689
        }
630✔
4690

4691
        if (!target.pinned && column.pinned) {
693✔
4692
            const unpinnedIndex = this._unpinnedColumns.indexOf(target);
4693
            const index = pos === DropPosition.AfterDropTarget ? unpinnedIndex + 1 : unpinnedIndex;
4694
            column.unpin(index);
5,351✔
4695
        }
4696

6,044✔
4697
        // if (target.pinned && column.pinned && !columnPinStateChanged) {
6,044✔
4698
        //     this._reorderColumns(column, target, pos, this._pinnedColumns);
4✔
4699
        // }
4!
4700

4701
        // if (!target.pinned && !column.pinned && !columnPinStateChanged) {
6,040✔
4702
        //     this._reorderColumns(column, target, pos, this._unpinnedColumns);
4703
        // }
4704

4!
4705
        this._moveColumns(column, target, pos);
4✔
4706
        this._columnsReordered(column);
4✔
4707
    }
4✔
4708

4✔
4709
    /**
4✔
4710
     * @deprecated in version 12.1.0. Use the corresponding method `paginate()` exposed by the `igx-paginator` instance.
4711
     *
4712
     * Goes to the desired page index.
784✔
4713
     *
784✔
4714
     *
784✔
4715
     * @example
4716
     * ```typescript
4717
     * // old
784!
4718
     * this.grid1.paginate(1);
4719
     * // new
4720
     * this.paginator1.paginate(1);
4✔
4721
     * ```
4✔
4722
     * @param val
4723
     */
784✔
4724
    public paginate(val: number): void {
784✔
4725
        this.paginator?.paginate(val);
4726
    }
4727

4728
    /**
4729
     * Triggers change detection for the `IgxGridComponent`.
4730
     * Calling markForCheck also triggers the grid pipes explicitly, resulting in all updates being processed.
4731
     * May degrade performance if used when not needed, or if misused:
21,510✔
4732
     * ```typescript
21,510✔
4733
     * // DON'Ts:
4734
     * // don't call markForCheck from inside a loop
12,375✔
4735
     * // don't call markForCheck when a primitive has changed
21,510✔
4736
     * grid.data.forEach(rec => {
3,977✔
4737
     *  rec = newValue;
4738
     *  grid.markForCheck();
21,510✔
4739
     * });
59✔
4740
     *
4741
     * // DOs
21,510✔
4742
     * // call markForCheck after updating a nested property
4743
     * grid.data.forEach(rec => {
4744
     *  rec.nestedProp1.nestedProp2 = newValue;
4745
     * });
4746
     * grid.markForCheck();
4747
     * ```
24✔
4748
     *
24!
4749
     * @example
24✔
4750
     * ```typescript
24✔
4751
     * grid.markForCheck();
2!
4752
     * ```
2✔
4753
     */
4754
    public markForCheck() {
2✔
4755
        this.pipeTrigger++;
4756
        this.cdr.detectChanges();
4757
    }
4758

4759
    /**
4760
     * Creates a new `IgxGridRowComponent` and adds the data record to the end of the data source.
4761
     *
4762
     * @example
4✔
4763
     * ```typescript
6✔
4764
     * this.grid1.addRow(record);
4765
     * ```
4766
     * @param data
4767
     */
4768
    public addRow(data: any): void {
4769
        // commit pending states prior to adding a row
4770
        this.crudService.endEdit(true);
5✔
4771
        this.gridAPI.addRowToData(data);
13✔
4772

13✔
4773
        this.pipeTrigger++;
4774
        this.rowAddedNotifier.next({ data: data, owner: this, primaryKey: data[this.primaryKey] });
4775
        this.notifyChanges();
4776
    }
4777

4778
    /**
4779
     * Removes the `IgxGridRowComponent` and the corresponding data record by primary key.
5,880✔
4780
     *
3,416✔
4781
     * @remarks
4782
     * Requires that the `primaryKey` property is set.
2,464✔
4783
     * The method accept rowSelector as a parameter, which is the rowID.
152✔
4784
     * @example
4785
     * ```typescript
2,312✔
4786
     * this.grid1.deleteRow(0);
142✔
4787
     * ```
4788
     * @param rowSelector
2,170✔
4789
     */
1✔
4790
    public deleteRow(rowSelector: any): any {
4791
        if (this.primaryKey !== undefined && this.primaryKey !== null) {
2,169✔
4792
            return this.deleteRowById(rowSelector);
4793
        }
4794
    }
4795

4796
    /** @hidden */
4797
    public deleteRowById(rowId: any): any {
614✔
4798
        const args = {
614✔
4799
            rowID: rowId,
614✔
4800
            primaryKey: rowId,
614✔
4801
            cancel: false,
4,007✔
4802
            rowData: this.getRowData(rowId),
4,007✔
4803
            oldValue: null,
4,007✔
4804
            owner: this
4,007✔
4805
        };
4,007✔
4806
        this.rowDelete.emit(args);
4807
        if (args.cancel) {
614✔
4808
            return;
614✔
4809
        }
614✔
4810

589✔
4811
        const record = this.gridAPI.deleteRowById(rowId);
4812
        if (record !== null && record !== undefined) {
4813
            const rowDeletedEventArgs: IRowDataEventArgs = { data: record, owner: this, primaryKey: record[this.primaryKey] };
4814
            this.rowDeleted.emit(rowDeletedEventArgs);
618✔
4815
        }
4,253✔
4816
        return record;
4817
    }
4818

4819
    /**
4820
     * Updates the `IgxGridRowComponent` and the corresponding data record by primary key.
×
4821
     *
16,439✔
4822
     * @remarks
3,274✔
4823
     * Requires that the `primaryKey` property is set.
4824
     * @example
144✔
4825
     * ```typescript
1,374✔
4826
     * this.gridWithPK.updateCell('Updated', 1, 'ProductName');
285!
4827
     * ```
1,079✔
4828
     * @param value the new value which is to be set.
4829
     * @param rowSelector corresponds to rowID.
285✔
4830
     * @param column corresponds to column field.
4831
     */
4832
    public updateCell(value: any, rowSelector: any, column: string): void {
4833
        if (this.isDefined(this.primaryKey)) {
3,274✔
4834
            const col = this._columns.find(c => c.field === column);
4835
            if (col) {
4836
                // Simplify
1,374✔
4837
                const rowData = this.gridAPI.getRowData(rowSelector);
144✔
4838
                const index = this.gridAPI.get_row_index_in_data(rowSelector);
4839
                // If row passed is invalid
3,274✔
4840
                if (index < 0) {
3,274✔
4841
                    return;
21,902✔
4842
                }
21,902!
4843

21,902✔
4844
                const id = {
4845
                    rowID: rowSelector,
4846
                    columnID: col.index,
3,274✔
4847
                    rowIndex: index
3,274✔
4848
                };
144✔
4849

1,364✔
4850
                const cell = new IgxCell(id, index, col, rowData[col.field], value, rowData, this as any);
4851
                const formControl = this.validation.getFormControl(cell.id.rowID, cell.column.field);
4852
                formControl.setValue(value);
4853
                this.gridAPI.update_cell(cell);
4854
                this.cdr.detectChanges();
4855
            }
4856
        }
4857
    }
182✔
4858

2,372✔
4859
    /**
2,107✔
4860
     * Updates the `IgxGridRowComponent`
265✔
4861
     *
187✔
4862
     * @remarks
4863
     * The row is specified by
×
4864
     * rowSelector parameter and the data source record with the passed value.
4865
     * This method will apply requested update only if primary key is specified in the grid.
246✔
4866
     * @example
246✔
4867
     * ```typescript
246✔
4868
     * grid.updateRow({
246✔
4869
     *       ProductID: 1, ProductName: 'Spearmint', InStock: true, UnitsInStock: 1, OrderDate: new Date('2005-03-21')
246✔
4870
     *   }, 1);
246✔
4871
     * ```
246✔
4872
     * @param value–
2✔
4873
     * @param rowSelector correspond to rowID
2✔
4874
     */
×
4875
    // TODO: prevent event invocation
×
4876
    public updateRow(value: any, rowSelector: any): void {
×
4877
        if (this.isDefined(this.primaryKey)) {
4878
            const editableCell = this.crudService.cell;
4879
            if (editableCell && editableCell.id.rowID === rowSelector) {
2!
4880
                this.crudService.endCellEdit();
2!
4881
            }
×
4882
            const row = new IgxEditRow(rowSelector, -1, this.gridAPI.getRowData(rowSelector), this as any);
×
4883
            this.gridAPI.update_row(row, value);
×
4884

×
4885
            // TODO: fix for #5934 and probably break for #5763
×
4886
            // consider adding of third optional boolean parameter in updateRow.
×
4887
            // If developer set this parameter to true we should call notifyChanges(true), and
4888
            // vise-versa if developer set it to false we should call notifyChanges(false).
4889
            // The parameter should default to false
×
4890
            this.notifyChanges();
4891
        }
4892
    }
4893

×
4894
    /**
×
4895
     * Returns the data that is contained in the row component.
×
4896
     *
×
4897
     * @remarks
×
4898
     * If the primary key is not specified the row selector match the row data.
×
4899
     * @example
4900
     * ```typescript
4901
     * const data = grid.getRowData(94741);
×
4902
     * ```
4903
     * @param rowSelector correspond to rowID
4904
     */
4905
    public getRowData(rowSelector: any) {
246✔
4906
        if (!this.primaryKey) {
246!
4907
            return rowSelector;
4908
        }
246!
4909
        const data = this.gridAPI.get_all_data(this.transactions.enabled);
×
4910
        const index = this.gridAPI.get_row_index_in_data(rowSelector);
×
4911
        return index < 0 ? {} : data[index];
4912
    }
4913

246!
4914
    /**
883✔
4915
     * Sort a single `IgxColumnComponent`.
4916
     *
246✔
4917
     * @remarks
10✔
4918
     * Sort the `IgxGridComponent`'s `IgxColumnComponent` based on the provided array of sorting expressions.
4919
     * @example
246✔
4920
     * ```typescript
5✔
4921
     * this.grid.sort({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false });
4922
     * ```
246!
4923
     */
×
4924
    public sort(expression: ISortingExpression | Array<ISortingExpression>): void {
4925
        const sortingState = cloneArray(this.sortingExpressions);
4926

246✔
4927
        if (expression instanceof Array) {
788✔
4928
            for (const each of expression) {
788!
4929
                this.gridAPI.prepare_sorting_expression([sortingState], each);
788✔
4930
            }
40✔
4931
        } else {
4932
            if (this._sortingOptions.mode === 'single') {
748✔
4933
                this._columns.forEach((col) => {
748✔
4934
                    if (!(col.field === expression.fieldName)) {
2,512✔
4935
                        this.clearSort(col.field);
2,512✔
4936
                    }
2,518✔
4937
                });
2,316!
4938
            }
2,316!
4939
            this.gridAPI.prepare_sorting_expression([sortingState], expression);
2,316!
4940
        }
4941

2,316✔
4942
        const eventArgs: ISortingEventArgs = { owner: this, sortingExpressions: sortingState, cancel: false };
2,316!
4943
        this.sorting.emit(eventArgs);
×
4944

×
4945
        if (eventArgs.cancel) {
4946
            return;
×
4947
        }
4948

4949
        this.crudService.endEdit(false);
4950
        if (expression instanceof Array) {
4951
            this.gridAPI.sort_multiple(expression);
748✔
4952
        } else {
746!
4953
            this.gridAPI.sort(expression);
×
4954
        }
×
4955
        requestAnimationFrame(() => this.sortingDone.emit(expression));
4956
    }
×
4957

×
4958
    /**
×
4959
     * Filters a single `IgxColumnComponent`.
4960
     *
×
4961
     * @example
×
4962
     * ```typescript
×
4963
     * public filter(term) {
×
4964
     *      this.grid.filter("ProductName", term, IgxStringFilteringOperand.instance().condition("contains"));
×
4965
     * }
×
4966
     * ```
4967
     * @param name
4968
     * @param value
4969
     * @param conditionOrExpressionTree
746✔
4970
     * @param ignoreCase
4971
     */
4972
    public filter(name: string, value: any, conditionOrExpressionTree?: IFilteringOperation | IFilteringExpressionsTree,
748✔
4973
        ignoreCase?: boolean) {
4974
        this.filteringService.filter(name, value, conditionOrExpressionTree, ignoreCase);
246!
4975
    }
×
4976

×
4977
    /**
×
4978
     * Filters all the `IgxColumnComponent` in the `IgxGridComponent` with the same condition.
4979
     *
4980
     * @example
246✔
4981
     * ```typescript
4982
     * grid.filterGlobal('some', IgxStringFilteringOperand.instance().condition('contains'));
4983
     * ```
4984
     * @param value
2,512✔
4985
     * @param condition
2✔
4986
     * @param ignoreCase
20✔
4987
     */
2✔
4988
    public filterGlobal(value: any, condition, ignoreCase?) {
2✔
4989
        this.filteringService.filterGlobal(value, condition, ignoreCase);
2!
4990
    }
4991

4992
    /**
2,510✔
4993
     * Enables summaries for the specified column and applies your customSummary.
14,120✔
4994
     *
11,790✔
4995
     * @remarks
2,510✔
4996
     * If you do not provide the customSummary, then the default summary for the column data type will be applied.
4997
     * @example
4998
     * ```typescript
4999
     * grid.enableSummaries([{ fieldName: 'ProductName' }, { fieldName: 'ID' }]);
365✔
5000
     * ```
336✔
5001
     * Enable summaries for the listed columns.
29✔
5002
     * @example
29✔
5003
     * ```typescript
119!
5004
     * grid.enableSummaries('ProductName');
29✔
5005
     * ```
29✔
5006
     * @param rest
120✔
5007
     */
75✔
5008
    public enableSummaries(...rest) {
75✔
5009
        if (rest.length === 1 && Array.isArray(rest[0])) {
11✔
5010
            this._multipleSummaries(rest[0], true);
5011
        } else {
2,345✔
5012
            this._summaries(rest[0], true, rest[1]);
579✔
5013
        }
293✔
5014
    }
75✔
5015

75✔
5016
    /**
75✔
5017
     * Disable summaries for the specified column.
5018
     *
8✔
5019
     * @example
5020
     * ```typescript
67✔
5021
     * grid.disableSummaries('ProductName');
67✔
5022
     * ```
3✔
5023
     * @remarks
5024
     * Disable summaries for the listed columns.
64✔
5025
     * @example
6✔
5026
     * ```typescript
5027
     * grid.disableSummaries([{ fieldName: 'ProductName' }]);
67✔
5028
     * ```
67✔
5029
     */
67✔
5030
    public disableSummaries(...rest) {
5031
        if (rest.length === 1 && Array.isArray(rest[0])) {
5032
            this._disableMultipleSummaries(rest[0]);
29✔
5033
        } else {
17✔
5034
            this._summaries(rest[0], false);
17✔
5035
        }
5036
    }
5037

×
5038
    /**
20✔
5039
     * If name is provided, clears the filtering state of the corresponding `IgxColumnComponent`.
20✔
5040
     *
20✔
5041
     * @remarks
20✔
5042
     * Otherwise clears the filtering state of all `IgxColumnComponent`s.
12✔
5043
     * @example
5044
     * ```typescript
8✔
5045
     * this.grid.clearFilter();
71✔
5046
     * ```
142!
5047
     * @param name
142!
5048
     */
5049
    public clearFilter(name?: string) {
5050
        this.filteringService.clearFilter(name);
71!
5051
    }
71✔
5052

5053
    /**
71✔
5054
     * If name is provided, clears the sorting state of the corresponding `IgxColumnComponent`.
5055
     *
8✔
5056
     * @remarks
5057
     * otherwise clears the sorting state of all `IgxColumnComponent`.
5058
     * @example
5059
     * ```typescript
5060
     * this.grid.clearSort();
5061
     * ```
3,755✔
5062
     * @param name
3,755✔
5063
     */
3,755✔
5064
    public clearSort(name?: string) {
3,755✔
5065
        if (!name) {
5066
            this.sortingExpressions = [];
3,755✔
5067
            return;
354✔
5068
        }
5✔
5069
        if (!this.gridAPI.get_column_by_name(name)) {
5070
            return;
354✔
5071
        }
106✔
5072
        this.gridAPI.clear_sort(name);
5073
    }
5074

5075
    /**
5076
     * @hidden @internal
5077
     */
3,755✔
5078
    public refreshGridState(_args?) {
22,123✔
5079
        this.crudService.endEdit(true);
350✔
5080
        this.selectionService.clearHeaderCBState();
5081
        this.summaryService.clearSummaryCache();
21,773✔
5082
        this.summaryPipeTrigger++;
111!
5083
        this.cdr.detectChanges();
111✔
5084
    }
5085

5086
    // TODO: We have return values here. Move them to event args ??
×
5087

×
5088
    /**
5089
     * Pins a column by field name.
5090
     *
5091
     * @remarks
21,662✔
5092
     * Returns whether the operation is successful.
5093
     * @example
5094
     * ```typescript
5095
     * this.grid.pinColumn("ID");
3,755✔
5096
     * ```
3,755✔
5097
     * @param columnName
3,755✔
5098
     * @param index
5099
     */
5100
    public pinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
5101
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
5102
        return col.pin(index);
3✔
5103
    }
106✔
5104

106✔
5105
    /**
16✔
5106
     * Unpins a column by field name. Returns whether the operation is successful.
16✔
5107
     *
16✔
5108
     * @example
5✔
5109
     * ```typescript
5✔
5110
     * this.grid.pinColumn("ID");
5111
     * ```
5112
     * @param columnName
106✔
5113
     * @param index
5✔
5114
     */
5!
5115
    public unpinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
5116
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
5117
        return col.unpin(index);
5118
    }
101✔
5119

5120
    /**
106✔
5121
     * Pin the row by its id.
5122
     *
5123
     * @remarks
5124
     * ID is either the primaryKey value or the data record instance.
5125
     * @example
5126
     * ```typescript
191✔
5127
     * this.grid.pinRow(rowID);
233✔
5128
     * ```
191✔
5129
     * @param rowID The row id - primaryKey value or the data record instance.
191✔
5130
     * @param index The index at which to insert the row in the pinned collection.
1!
5131
     */
×
5132
    public pinRow(rowID: any, index?: number, row?: RowType): boolean {
×
5133
        if (this._pinnedRecordIDs.indexOf(rowID) !== -1) {
5134
            return false;
5135
        }
5136
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, index, row, true);
190✔
5137
        this.rowPinning.emit(eventArgs);
5138

5139
        if (eventArgs.cancel) {
5140
            return;
5141
        }
5142
        this.crudService.endEdit(false);
5143

381✔
5144
        const insertIndex = typeof eventArgs.insertAtIndex === 'number' ? eventArgs.insertAtIndex : this._pinnedRecordIDs.length;
1✔
5145
        this._pinnedRecordIDs.splice(insertIndex, 0, rowID);
5146
        this.pipeTrigger++;
380✔
5147
        if (this.gridAPI.grid) {
5148
            this.cdr.detectChanges();
5149
            this.rowPinned.emit(eventArgs);
5150
        }
5151

5152
        return true;
197✔
5153
    }
197!
5154

426✔
5155
    /**
197✔
5156
     * Unpin the row by its id.
395!
5157
     *
5158
     * @remarks
197✔
5159
     * ID is either the primaryKey value or the data record instance.
102✔
5160
     * @example
5161
     * ```typescript
95✔
5162
     * this.grid.unpinRow(rowID);
95✔
5163
     * ```
95✔
5164
     * @param rowID The row id - primaryKey value or the data record instance.
5165
     */
5166
    public unpinRow(rowID: any, row?: RowType): boolean {
5167
        const index = this._pinnedRecordIDs.indexOf(rowID);
5168
        if (index === -1) {
5169
            return false;
5170
        }
5171

128✔
5172
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, null, row, false);
128✔
5173
        this.rowPinning.emit(eventArgs);
10✔
5174

5175
        if (eventArgs.cancel) {
5176
            return;
128✔
5177
        }
128✔
5178

5179
        this.crudService.endEdit(false);
5180
        this._pinnedRecordIDs.splice(index, 1);
13,183✔
5181
        this.pipeTrigger++;
5182
        if (this.gridAPI.grid) {
5183
            this.cdr.detectChanges();
5184
            this.rowPinned.emit(eventArgs);
5185
        }
7,336✔
5186

7,336✔
5187
        return true;
2✔
5188
    }
2!
5189

×
5190
    public get pinnedRowHeight() {
5191
        const containerHeight = this.pinContainer ? this.pinContainer.nativeElement.offsetHeight : 0;
2✔
5192
        return this.hasPinnedRecords ? containerHeight : 0;
2✔
5193
    }
5194

7,336✔
5195
    public get totalHeight() {
7,336✔
5196
        return this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
5197
    }
5198

329✔
5199
    /**
329✔
5200
     * Recalculates grid width/height dimensions.
329✔
5201
     *
329✔
5202
     * @remarks
329✔
5203
     * Should be run when changing DOM elements dimentions manually that affect the grid's size.
329✔
5204
     * @example
14✔
5205
     * ```typescript
5206
     * this.grid.reflow();
5207
     * ```
5208
     */
329✔
5209
    public reflow() {
329✔
5210
        this.calculateGridSizes();
329✔
5211
    }
329✔
5212

9✔
5213
    /**
5214
     * Finds the next occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
329✔
5215
     *
5216
     * @remarks
5217
     * Returns how many times the grid contains the string.
5218
     * @example
5219
     * ```typescript
329✔
5220
     * this.grid.findNext("financial");
5221
     * ```
5222
     * @param text the string to search.
317✔
5223
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
317✔
5224
     * @param exactMatch optionally, if the text should match the entire value  (defaults to false).
2,009✔
5225
     */
317✔
5226
    public findNext(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
317✔
5227
        return this.find(text, 1, caseSensitive, exactMatch);
317✔
5228
    }
316✔
5229

316✔
5230
    /**
316✔
5231
     * Finds the previous occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
5232
     *
5233
     * @remarks
5234
     * Returns how many times the grid contains the string.
317✔
5235
     * @example
206✔
5236
     * ```typescript
5237
     * this.grid.findPrev("financial");
317✔
5238
     * ```
317✔
5239
     * @param text the string to search.
5240
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
×
5241
     * @param exactMatch optionally, if the text should match the entire value (defaults to false).
825✔
5242
     */
274✔
5243
    public findPrev(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
5244
        return this.find(text, -1, caseSensitive, exactMatch);
1,622✔
5245
    }
551✔
5246

8!
5247
    /**
×
5248
     * Reapplies the existing search.
×
5249
     *
×
5250
     * @remarks
×
5251
     * Returns how many times the grid contains the last search.
×
5252
     * @example
5253
     * ```typescript
5254
     * this.grid.refreshSearch();
8✔
5255
     * ```
8!
5256
     * @param updateActiveInfo
8✔
5257
     */
8✔
5258
    public refreshSearch(updateActiveInfo?: boolean, endEdit = true): number {
5259
        if (this.lastSearchInfo.searchText) {
8✔
5260
            this.rebuildMatchCache();
5261

543✔
5262
            if (updateActiveInfo) {
543✔
5263
                const activeInfo = IgxTextHighlightDirective.highlightGroupsMap.get(this.id);
5264
                this.lastSearchInfo.matchInfoCache.forEach((match, i) => {
5265
                    if (match.column === activeInfo.column &&
5266
                        match.row === activeInfo.row &&
5267
                        match.index === activeInfo.index &&
543!
5268
                        compareMaps(match.metadata, activeInfo.metadata)) {
5269
                        this.lastSearchInfo.activeMatchIndex = i;
30✔
5270
                    }
30✔
5271
                });
30✔
5272
            }
5273

50✔
5274
            return this.find(this.lastSearchInfo.searchText,
50!
5275
                0,
139✔
5276
                this.lastSearchInfo.caseSensitive,
50✔
5277
                this.lastSearchInfo.exactMatch,
5278
                false,
×
5279
                endEdit);
×
5280
        } else {
×
5281
            return 0;
5282
        }
463✔
5283
    }
1,286!
5284

463✔
5285
    /**
5286
     * Removes all the highlights in the cell.
543✔
5287
     *
5288
     * @example
15✔
5289
     * ```typescript
30✔
5290
     * this.grid.clearSearch();
30!
5291
     * ```
7✔
5292
     */
5293
    public clearSearch() {
5294
        this.lastSearchInfo = {
23✔
5295
            searchText: '',
56✔
5296
            caseSensitive: false,
23✔
5297
            exactMatch: false,
23!
5298
            activeMatchIndex: 0,
5299
            matchInfoCache: []
5300
        };
5301

5302
        this.rowList.forEach((row) => {
5303
            if (row.cells) {
5304
                row.cells.forEach((c: IgxGridCellComponent) => {
5305
                    c.clearHighlight();
8✔
5306
                });
8✔
5307
            }
8✔
5308
        });
178✔
5309
    }
8✔
5310

5311
    /**
5312
     * Returns if the `IgxGridComponent` has sortable columns.
×
5313
     *
5314
     * @example
5315
     * ```typescript
5316
     * const sortableGrid = this.grid.hasSortableColumns;
5317
     * ```
5318
     */
5319
    public get hasSortableColumns(): boolean {
5320
        return this._columns.some((col) => col.sortable);
5321
    }
5322

5323
    /**
29✔
5324
     * Returns if the `IgxGridComponent` has editable columns.
29✔
5325
     *
5326
     * @example
5327
     * ```typescript
5328
     * const editableGrid = this.grid.hasEditableColumns;
5329
     * ```
5330
     */
5331
    public get hasEditableColumns(): boolean {
5332
        return this._columns.some((col) => col.editable);
5333
    }
5334

27✔
5335
    /**
27✔
5336
     * Returns if the `IgxGridComponent` has filterable columns.
27✔
5337
     *
5338
     * @example
5339
     * ```typescript
97✔
5340
     * const filterableGrid = this.grid.hasFilterableColumns;
777✔
5341
     * ```
97✔
5342
     */
2✔
5343
    public get hasFilterableColumns(): boolean {
5344
        return this._columns.some((col) => col.filterable);
95✔
5345
    }
694✔
5346

93✔
5347
    /**
5348
     * Returns if the `IgxGridComponent` has summarized columns.
2✔
5349
     *
5350
     * @example
195✔
5351
     * ```typescript
316!
5352
     * const summarizedGrid = this.grid.hasSummarizedColumns;
×
5353
     * ```
5354
     */
316✔
5355
    public get hasSummarizedColumns(): boolean {
277✔
5356
        const summarizedColumns = this._columns.filter(col => col.hasSummary && !col.hidden);
5357
        return summarizedColumns.length > 0;
316!
5358
    }
×
5359

×
5360
    /**
5361
     * @hidden @internal
316✔
5362
     */
316✔
5363
    public get rootSummariesEnabled(): boolean {
316✔
5364
        return this.summaryCalculationMode !== GridSummaryCalculationMode.childLevelsOnly;
316✔
5365
    }
5366

5367
    /**
84✔
5368
     * @hidden @internal
5369
     */
5370
    public get hasVisibleColumns(): boolean {
5371
        if (this._hasVisibleColumns === undefined) {
5372
            return this._columns ? this._columns.some(c => !c.hidden) : false;
5373
        }
5374
        return this._hasVisibleColumns;
84✔
5375
    }
5376

5377
    public set hasVisibleColumns(value) {
232✔
5378
        this._hasVisibleColumns = value;
5379
    }
316✔
5380
    /**
84✔
5381
     * Returns if the `IgxGridComponent` has moveable columns.
783✔
5382
     *
739✔
5383
     * @deprecated
3,091✔
5384
     * Use `IgxGridComponent.moving` instead.
5385
     *
5386
     * @example
5387
     * ```typescript
84✔
5388
     * const movableGrid = this.grid.hasMovableColumns;
5389
     * ```
316✔
5390
     */
28✔
5391
    public get hasMovableColumns(): boolean {
5392
        return this.moving;
288✔
5393
    }
8✔
5394

5395
    /**
316✔
5396
     * Returns if the `IgxGridComponent` has column groups.
298✔
5397
     *
298✔
5398
     * @example
298✔
5399
     * ```typescript
182✔
5400
     * const groupGrid = this.grid.hasColumnGroups;
5401
     * ```
298✔
5402
     */
5403
    public get hasColumnGroups(): boolean {
5404
        return this._columnGroups;
5405
    }
5406
    /**
5407
     * Returns if the `IgxGridComponent` has column layouts for multi-row layout definition.
5408
     *
5409
     * @example
18✔
5410
     * ```typescript
5411
     * const layoutGrid = this.grid.hasColumnLayouts;
316✔
5412
     * ```
5413
     */
5414
    public get hasColumnLayouts() {
205✔
5415
        return !!this._columns.some(col => col.columnLayout);
205✔
5416
    }
205✔
5417

205✔
5418

205✔
5419
    /**
886✔
5420
     * @hidden @internal
205✔
5421
     */
3,357✔
5422
    public get multiRowLayoutRowSize() {
14,377✔
5423
        return this._multiRowLayoutRowSize;
14,377!
5424
    }
14,377✔
5425

10,701✔
5426
    /**
5427
     * @hidden
5428
     */
14,377✔
5429
    protected get rowBasedHeight() {
14,162✔
5430
        return this.dataLength * this.rowHeight;
14,162✔
5431
    }
536✔
5432

9✔
5433
    /**
9✔
5434
     * @hidden
9✔
5435
     */
5436
    protected get isPercentWidth() {
5437
        return this.width && this.width.indexOf('%') !== -1;
5438
    }
5439

5440
    /**
5441
     * @hidden @internal
5442
     */
5443
    public get isPercentHeight() {
13,626✔
5444
        return this._height && this._height.indexOf('%') !== -1;
13,626✔
5445
    }
13,626✔
5446

2,157✔
5447
    /**
2,157✔
5448
     * @hidden
2,157✔
5449
     */
5450
    protected get defaultTargetBodyHeight(): number {
5451
        const allItems = this.dataLength;
5452
        return this.renderedRowHeight * Math.min(this._defaultTargetRecordNumber,
5453
            this.paginator ? Math.min(allItems, this.paginator.perPage) : allItems);
5454
    }
2,157✔
5455

2,157✔
5456
    /**
5457
     * @hidden @internal
5458
     * The rowHeight input is bound to min-height css prop of rows that adds a 1px border in all cases
5459
     */
5460
    public get renderedRowHeight(): number {
5461
        return this.rowHeight + 1;
5462
    }
5463

50✔
5464
    /**
255✔
5465
     * @hidden @internal
255✔
5466
     */
255✔
5467
    public get outerWidth() {
51✔
5468
        return this.hasVerticalScroll() ? this.calcWidth + this.scrollSize : this.calcWidth;
5469
    }
255✔
5470

255✔
5471
    /**
255✔
5472
     * @hidden @internal
255✔
5473
     * Gets the visible content height that includes header + tbody + footer.
5474
     */
8✔
5475
    public getVisibleContentHeight() {
255!
5476
        let height = this.theadRow.nativeElement.clientHeight + this.tbody.nativeElement.clientHeight;
×
5477
        if (this.hasSummarizedColumns) {
5478
            height += this.tfoot.nativeElement.clientHeight;
255✔
5479
        }
255✔
5480
        return height;
5481
    }
2✔
5482

5483
    /**
5484
     * @hidden @internal
5485
     */
5486
    public getPossibleColumnWidth(baseWidth: number = null) {
5487
        let computedWidth;
5488
        if (baseWidth !== null) {
5489
            computedWidth = baseWidth;
5490
        } else {
5491
            computedWidth = this.calcWidth ||
5492
                parseInt(this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width'), 10);
5493
        }
5494

5495
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
5496

5497

5498
        // Column layouts related
5499
        let visibleCols = [];
5500
        const columnBlocks = this.visibleColumns.filter(c => c.columnGroup);
5501
        const colsPerBlock = columnBlocks.map(block => block.getInitialChildColumnSizes(block.children));
5502
        const combinedBlocksSize = colsPerBlock.reduce((acc, item) => acc + item.length, 0);
5503
        colsPerBlock.forEach(blockCols => visibleCols = visibleCols.concat(blockCols));
5504
        //
5505

2✔
5506
        const columnsWithSetWidths = this.hasColumnLayouts ?
5507
            visibleCols.filter(c => c.widthSetByUser) :
5508
            visibleChildColumns.filter(c => c.widthSetByUser && c.width !== 'fit-content');
5509

5510
        const columnsToSize = this.hasColumnLayouts ?
5511
            combinedBlocksSize - columnsWithSetWidths.length :
5512
            visibleChildColumns.length - columnsWithSetWidths.length;
5513
        const sumExistingWidths = columnsWithSetWidths
5514
            .reduce((prev, curr) => {
5515
                const colWidth = curr.width;
5516
                let widthValue = parseInt(colWidth, 10);
5517
                if (isNaN(widthValue)) {
5518
                    widthValue = MINIMUM_COLUMN_WIDTH;
5519
                }
5520
                const currWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1 ?
5521
                    widthValue / 100 * computedWidth :
5522
                    widthValue;
5523
                return prev + currWidth;
5524
            }, 0);
5525

5526
        // When all columns are hidden, return 0px width
5527
        if (!sumExistingWidths && !columnsToSize) {
5528
            return '0px';
5529
        }
5530
        computedWidth -= this.featureColumnsWidth();
5531

5532
        const columnWidth = Math.floor(!Number.isFinite(sumExistingWidths) ?
5533
            Math.max(computedWidth / columnsToSize, this.minColumnWidth) :
5534
            Math.max((computedWidth - sumExistingWidths) / columnsToSize, this.minColumnWidth));
5535

5536
        return columnWidth + 'px';
5537
    }
5538

5539
    /**
5540
     * @hidden @internal
5541
     */
5542
    public hasVerticalScroll() {
5543
        if (this._init) {
5544
            return false;
5545
        }
5546
        const isScrollable = this.verticalScrollContainer ? this.verticalScrollContainer.isScrollable() : false;
5547
        return !!(this.calcWidth && this.dataView && this.dataView.length > 0 && isScrollable);
5548
    }
5549

5550
    /**
5551
     * Gets calculated width of the pinned area.
5552
     *
5553
     * @example
5554
     * ```typescript
5555
     * const pinnedWidth = this.grid.getPinnedWidth();
5556
     * ```
5557
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
5558
     */
5559
    public getPinnedWidth(takeHidden = false) {
5560
        const fc = takeHidden ? this._pinnedColumns : this.pinnedColumns;
5561
        let sum = 0;
5562
        for (const col of fc) {
5563
            if (col.level === 0) {
5564
                sum += parseInt(col.calcWidth, 10);
5565
            }
5566
        }
5567
        if (this.isPinningToStart) {
5568
            sum += this.featureColumnsWidth();
5569
        }
5570

5571
        return sum;
5572
    }
5573

5574
    /**
5575
     * @hidden @internal
5576
     */
5577
    public isColumnGrouped(_fieldName: string): boolean {
5578
        return false;
5579
    }
5580

5581
    /**
5582
     * @hidden @internal
5583
     * TODO: REMOVE
5584
     */
5585
    public onHeaderSelectorClick(event) {
5586
        if (!this.isMultiRowSelectionEnabled) {
5587
            return;
5588
        }
5589
        if (this.selectionService.areAllRowSelected()) {
5590
            this.selectionService.clearRowSelection(event);
5591
        } else {
5592
            this.selectionService.selectAllRows(event);
5593
        }
5594
    }
5595

5596
    /**
5597
     * @hidden @internal
5598
     */
5599
    public get headSelectorBaseAriaLabel() {
5600
        if (this._filteringExpressionsTree.filteringOperands.length > 0) {
5601
            return this.selectionService.areAllRowSelected() ? 'Deselect all filtered' : 'Select all filtered';
5602
        }
5603

5604
        return this.selectionService.areAllRowSelected() ? 'Deselect all' : 'Select all';
5605
    }
5606

5607
    /**
5608
     * @hidden
5609
     * @internal
5610
     */
5611
    public get totalRowsCountAfterFilter() {
5612
        if (this.data) {
5613
            return this.selectionService.allData.length;
5614
        }
5615

5616
        return 0;
5617
    }
5618

5619
    /**
5620
     * Returns the currently transformed paged/filtered/sorted/grouped pinned row data, displayed in the grid.
5621
     *
5622
     * @example
5623
     * ```typescript
5624
     *      const pinnedDataView = this.grid.pinnedDataView;
5625
     * ```
5626
     */
5627
    public get pinnedDataView(): any[] {
5628
        return this.pinnedRecords ? this.pinnedRecords : [];
5629
    }
5630

5631
    /**
5632
     * Returns currently transformed paged/filtered/sorted/grouped unpinned row data, displayed in the grid.
5633
     *
5634
     * @example
5635
     * ```typescript
5636
     *      const pinnedDataView = this.grid.pinnedDataView;
5637
     * ```
5638
     */
5639
    public get unpinnedDataView(): any[] {
5640
        return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer?.igxForOf || [];
5641
    }
5642

5643
    /**
5644
     * Returns the currently transformed paged/filtered/sorted/grouped/pinned/unpinned row data, displayed in the grid.
5645
     *
5646
     * @example
5647
     * ```typescript
5648
     *      const dataView = this.grid.dataView;
5649
     * ```
5650
     */
5651
    public get dataView() {
5652
        return this._dataView;
5653
    }
5654

5655
    /**
5656
     * Gets/Sets whether clicking over a row should select/deselect it
5657
     *
5658
     * @remarks
5659
     * By default it is set to true
5660
     * @param enabled: boolean
5661
     */
5662
    @WatchChanges()
5663
    @Input()
5664
    public get selectRowOnClick() {
5665
        return this._selectRowOnClick;
5666
    }
5667

5668
    public set selectRowOnClick(enabled: boolean) {
5669
        this._selectRowOnClick = enabled;
5670
    }
5671

5672
    /**
5673
     * Select specified rows by ID.
5674
     *
5675
     * @example
5676
     * ```typescript
5677
     * this.grid.selectRows([1,2,5], true);
5678
     * ```
5679
     * @param rowIDs
5680
     * @param clearCurrentSelection if true clears the current selection
5681
     */
5682
    public selectRows(rowIDs: any[], clearCurrentSelection?: boolean) {
5683
        this.selectionService.selectRowsWithNoEvent(rowIDs, clearCurrentSelection);
5684
        this.notifyChanges();
5685
    }
5686

5687
    /**
5688
     * Deselect specified rows by ID.
5689
     *
5690
     * @example
2✔
5691
     * ```typescript
5692
     * this.grid.deselectRows([1,2,5]);
5693
     * ```
2✔
5694
     * @param rowIDs
5695
     */
5696
    public deselectRows(rowIDs: any[]) {
2✔
5697
        this.selectionService.deselectRowsWithNoEvent(rowIDs);
5698
        this.notifyChanges();
5699
    }
2✔
5700

5701
    /**
5702
     * Selects all rows
2✔
5703
     *
5704
     * @remarks
5705
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
2✔
5706
     * If you set the parameter onlyFilterData to false that will select all rows in the grid exept deleted rows.
5707
     * @example
5708
     * ```typescript
2✔
5709
     * this.grid.selectAllRows();
5710
     * this.grid.selectAllRows(false);
5711
     * ```
2✔
5712
     * @param onlyFilterData
5713
     */
5714
    public selectAllRows(onlyFilterData = true) {
2✔
5715
        const data = onlyFilterData && this.filteredData ? this.filteredData : this.gridAPI.get_all_data(true);
5716
        const rowIDs = this.selectionService.getRowIDs(data).filter(rID => !this.gridAPI.row_deleted_transaction(rID));
5717
        this.selectRows(rowIDs);
2✔
5718
    }
5719

5720
    /**
2✔
5721
     * Deselects all rows
5722
     *
5723
     * @remarks
2✔
5724
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
5725
     * If you set the parameter onlyFilterData to false that will deselect all rows in the grid exept deleted rows.
5726
     * @example
2✔
5727
     * ```typescript
5728
     * this.grid.deselectAllRows();
5729
     * ```
2✔
5730
     * @param onlyFilterData
5731
     */
5732
    public deselectAllRows(onlyFilterData = true) {
2✔
5733
        if (onlyFilterData && this.filteredData && this.filteredData.length > 0) {
5734
            this.deselectRows(this.selectionService.getRowIDs(this.filteredData));
5735
        } else {
2✔
5736
            this.selectionService.clearAllSelectedRows();
5737
            this.notifyChanges();
5738
        }
2✔
5739
    }
5740

5741
    /**
5742
     * Deselect the selected cells.
5743
     * @example
5744
     * ```typescript
5745
     * this.grid.clearCellSelection();
5746
     * ```
5747
     */
5748
    public clearCellSelection(): void {
5749
        this.selectionService.clear(true);
5750
        this.notifyChanges();
5751
    }
5752

5753
    /**
5754
     * @hidden @internal
5755
     */
5756
    public dragScroll(delta: { left: number; top: number }): void {
5757
        const horizontal = this.headerContainer.getScroll();
5758
        const vertical = this.verticalScrollContainer.getScroll();
5759
        const { left, top } = delta;
5760

5761
        horizontal.scrollLeft += left * this.DRAG_SCROLL_DELTA;
5762
        vertical.scrollTop += top * this.DRAG_SCROLL_DELTA;
5763
    }
5764

5765
    /**
5766
     * @hidden @internal
5767
     */
5768
    public isDefined(arg: any): boolean {
5769
        return arg !== undefined && arg !== null;
5770
    }
5771

5772
    /**
5773
     * Select range(s) of cells between certain rows and columns of the grid.
5774
     */
5775
    public selectRange(arg: GridSelectionRange | GridSelectionRange[] | null | undefined): void {
5776
        if (!this.isDefined(arg)) {
5777
            this.clearCellSelection();
5778
            return;
5779
        }
5780
        if (arg instanceof Array) {
5781
            arg.forEach(range => this.setSelection(range));
5782
        } else {
5783
            this.setSelection(arg);
5784
        }
5785
        this.notifyChanges();
5786
    }
5787

5788
    /**
5789
     * @hidden @internal
5790
     */
5791
    public columnToVisibleIndex(field: string | number): number {
5792
        const visibleColumns = this.visibleColumns;
5793
        if (typeof field === 'number') {
5794
            return field;
5795
        }
5796
        return visibleColumns.find(column => column.field === field).visibleIndex;
5797
    }
5798

5799
    /**
5800
     * @hidden @internal
5801
     */
5802
    public setSelection(range: GridSelectionRange): void {
5803
        const startNode = { row: range.rowStart, column: this.columnToVisibleIndex(range.columnStart) };
5804
        const endNode = { row: range.rowEnd, column: this.columnToVisibleIndex(range.columnEnd) };
5805

5806
        this.selectionService.pointerState.node = startNode;
5807
        this.selectionService.selectRange(endNode, this.selectionService.pointerState);
5808
        this.selectionService.addRangeMeta(endNode, this.selectionService.pointerState);
5809
        this.selectionService.initPointerState();
5810
    }
5811

5812
    /**
5813
     * Get the currently selected ranges in the grid.
5814
     */
5815
    public getSelectedRanges(): GridSelectionRange[] {
5816
        return this.selectionService.ranges;
5817
    }
5818

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

5832
    /**
5833
     * Get current selected columns.
5834
     *
5835
     * @example
5836
     * Returns an array with selected columns
5837
     * ```typescript
5838
     * const selectedColumns = this.grid.selectedColumns();
5839
     * ```
5840
     */
5841
    public selectedColumns(): ColumnType[] {
5842
        const fields = this.selectionService.getSelectedColumns();
5843
        return fields.map(field => this.getColumnByName(field)).filter(field => field);
5844
    }
5845

5846
    /**
5847
     * Select specified columns.
5848
     *
5849
     * @example
5850
     * ```typescript
5851
     * this.grid.selectColumns(['ID','Name'], true);
5852
     * ```
5853
     * @param columns
5854
     * @param clearCurrentSelection if true clears the current selection
5855
     */
5856
    public selectColumns(columns: string[] | ColumnType[], clearCurrentSelection?: boolean) {
5857
        let fieldToSelect: string[] = [];
5858
        if (columns.length === 0 || typeof columns[0] === 'string') {
5859
            fieldToSelect = columns as string[];
5860
        } else {
5861
            (columns as ColumnType[]).forEach(col => {
5862
                if (col.columnGroup) {
5863
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
5864
                    fieldToSelect = [...fieldToSelect, ...children];
5865
                } else {
5866
                    fieldToSelect.push(col.field);
5867
                }
5868
            });
5869
        }
5870

5871
        this.selectionService.selectColumnsWithNoEvent(fieldToSelect, clearCurrentSelection);
5872
        this.notifyChanges();
5873
    }
5874

5875
    /**
5876
     * Deselect specified columns by field.
5877
     *
5878
     * @example
5879
     * ```typescript
5880
     * this.grid.deselectColumns(['ID','Name']);
5881
     * ```
5882
     * @param columns
5883
     */
5884
    public deselectColumns(columns: string[] | ColumnType[]) {
5885
        let fieldToDeselect: string[] = [];
5886
        if (columns.length === 0 || typeof columns[0] === 'string') {
5887
            fieldToDeselect = columns as string[];
5888
        } else {
5889
            (columns as ColumnType[]).forEach(col => {
5890
                if (col.columnGroup) {
5891
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
5892
                    fieldToDeselect = [...fieldToDeselect, ...children];
5893
                } else {
5894
                    fieldToDeselect.push(col.field);
5895
                }
5896
            });
5897
        }
5898
        this.selectionService.deselectColumnsWithNoEvent(fieldToDeselect);
5899
        this.notifyChanges();
5900
    }
5901

5902
    /**
5903
     * Deselects all columns
5904
     *
5905
     * @example
5906
     * ```typescript
5907
     * this.grid.deselectAllColumns();
5908
     * ```
5909
     */
5910
    public deselectAllColumns() {
5911
        this.selectionService.clearAllSelectedColumns();
5912
        this.notifyChanges();
5913
    }
5914

5915
    /**
5916
     * Selects all columns
5917
     *
5918
     * @example
5919
     * ```typescript
5920
     * this.grid.deselectAllColumns();
5921
     * ```
5922
     */
5923
    public selectAllColumns() {
5924
        this.selectColumns(this._columns.filter(c => !c.columnGroup));
5925
    }
5926

5927
    /**
5928
     *
5929
     * Returns an array of the current columns selection in the form of `[{ column.field: cell.value }, ...]`.
5930
     *
5931
     * @remarks
5932
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5933
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5934
     */
5935
    public getSelectedColumnsData(formatters = false, headers = false) {
5936
        const source = this.filteredSortedData ? this.filteredSortedData : this.data;
5937
        return this.extractDataFromColumnsSelection(source, formatters, headers);
5938
    }
5939

5940

5941
    public combineSelectedCellAndColumnData(columnData: any[], formatters = false, headers = false) {
5942
        const source = this.filteredSortedData;
5943
        return this.extractDataFromSelection(source, formatters, headers, columnData);
5944
    }
5945

5946
    /**
5947
     * @hidden @internal
5948
     */
5949
    public preventContainerScroll = (evt) => {
5950
        if (evt.target.scrollTop !== 0) {
5951
            this.verticalScrollContainer.addScrollTop(evt.target.scrollTop);
5952
            evt.target.scrollTop = 0;
5953
        }
5954
        if (evt.target.scrollLeft !== 0) {
5955
            this.headerContainer.scrollPosition += evt.target.scrollLeft;
5956
            evt.target.scrollLeft = 0;
5957
        }
5958
    };
5959

5960
    /**
5961
     * @hidden
5962
     * @internal
5963
     */
5964
    public copyHandler(event) {
5965
        const eventPathElements = event.composedPath().map(el => el.tagName?.toLowerCase());
5966
        if (eventPathElements.includes('igx-grid-filtering-row') ||
5967
            eventPathElements.includes('igx-grid-filtering-cell')) {
5968
            return;
5969
        }
5970

5971
        const selectedColumns = this.gridAPI.grid.selectedColumns();
5972
        const columnData = this.getSelectedColumnsData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
5973
        let selectedData;
5974
        if (event.type === 'copy') {
5975
            selectedData = this.getSelectedData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
5976
        };
5977

5978
        let data = [];
5979
        let result;
5980

5981
        if (event.code === 'KeyC' && (event.ctrlKey || event.metaKey) && event.currentTarget.className === 'igx-grid-thead__wrapper') {
5982
            if (selectedData.length) {
5983
                if (columnData.length === 0) {
5984
                    result = this.prepareCopyData(event, selectedData);
5985
                } else {
5986
                    data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
5987
                        this.clipboardOptions.copyHeaders);
5988
                    result = this.prepareCopyData(event, data[0], data[1]);
5989
                }
5990
            } else {
5991
                data = columnData;
5992
                result = this.prepareCopyData(event, data);
5993
            }
5994

5995
            navigator.clipboard.writeText(result).then().catch(e => console.error(e));
5996
        } else if (!this.clipboardOptions.enabled || this.crudService.cellInEditMode || event.type === 'keydown') {
5997
            return;
5998
        } else {
5999
            if (selectedColumns.length) {
6000
                data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
6001
                    this.clipboardOptions.copyHeaders);
6002
                result = this.prepareCopyData(event, data[0], data[1]);
6003
            } else {
6004
                data = selectedData;
6005
                result = this.prepareCopyData(event, data);
6006
            }
6007
            event.clipboardData.setData('text/plain', result);
6008
        }
6009
    }
6010

6011
    /**
6012
     * @hidden @internal
6013
     */
6014
    public prepareCopyData(event, data, keys?) {
6015
        const ev = { data, cancel: false } as IGridClipboardEvent;
6016
        this.gridCopy.emit(ev);
6017

6018
        if (ev.cancel) {
6019
            return;
6020
        }
6021

6022
        const transformer = new CharSeparatedValueData(ev.data, this.clipboardOptions.separator);
6023
        let result = keys ? transformer.prepareData(keys) : transformer.prepareData();
6024

6025
        if (!this.clipboardOptions.copyHeaders) {
6026
            result = result.substring(result.indexOf('\n') + 1);
6027
        }
6028

6029
        if (data && data.length > 0 && Object.values(data[0]).length === 1) {
6030
            result = result.slice(0, -2);
6031
        }
6032

6033
        event.preventDefault();
6034

6035
        /* Necessary for the hiearachical case but will probably have to
6036
           change how getSelectedData is propagated in the hiearachical grid
6037
        */
6038
        event.stopPropagation();
6039

6040
        return result;
6041
    }
6042

6043
    /**
6044
     * @hidden @internal
6045
     */
6046
    public showSnackbarFor(index: number) {
6047
        this.addRowSnackbar.actionText = index === -1 ? '' : this.snackbarActionText;
6048
        this.lastAddedRowIndex = index;
6049
        this.addRowSnackbar.open();
6050
    }
6051

6052
    /**
6053
     * Navigates to a position in the grid based on provided `rowindex` and `visibleColumnIndex`.
6054
     *
6055
     * @remarks
6056
     * Also can execute a custom logic over the target element,
6057
     * through a callback function that accepts { targetType: GridKeydownTargetType, target: Object }
6058
     * @example
6059
     * ```typescript
6060
     *  this.grid.navigateTo(10, 3, (args) => { args.target.nativeElement.focus(); });
6061
     * ```
6062
     */
6063
    public navigateTo(rowIndex: number, visibleColIndex = -1, cb: (args: any) => void = null) {
6064
        const totalItems = (this as any).totalItemCount ?? this.dataView.length - 1;
6065
        if (rowIndex < 0 || rowIndex > totalItems || (visibleColIndex !== -1
6066
            && this._columns.map(col => col.visibleIndex).indexOf(visibleColIndex) === -1)) {
6067
            return;
6068
        }
6069
        if (this.dataView.slice(rowIndex, rowIndex + 1).find(rec => rec.expression || rec.childGridsData)) {
6070
            visibleColIndex = -1;
6071
        }
6072
        // If the target row is pinned no need to scroll as well.
6073
        const shouldScrollVertically = this.navigation.shouldPerformVerticalScroll(rowIndex, visibleColIndex);
6074
        const shouldScrollHorizontally = this.navigation.shouldPerformHorizontalScroll(visibleColIndex, rowIndex);
6075
        if (shouldScrollVertically) {
6076
            this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () => {
6077
                if (shouldScrollHorizontally) {
6078
                    this.navigation.performHorizontalScrollToCell(visibleColIndex, () =>
6079
                        this.executeCallback(rowIndex, visibleColIndex, cb));
6080
                } else {
6081
                    this.executeCallback(rowIndex, visibleColIndex, cb);
6082
                }
6083
            });
6084
        } else if (shouldScrollHorizontally) {
6085
            this.navigation.performHorizontalScrollToCell(visibleColIndex, () => {
6086
                if (shouldScrollVertically) {
6087
                    this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () =>
6088
                        this.executeCallback(rowIndex, visibleColIndex, cb));
6089
                } else {
6090
                    this.executeCallback(rowIndex, visibleColIndex, cb);
6091
                }
6092
            });
6093
        } else {
6094
            this.executeCallback(rowIndex, visibleColIndex, cb);
6095
        }
6096
    }
6097

6098
    /**
6099
     * Returns `ICellPosition` which defines the next cell,
6100
     * according to the current position, that match specific criteria.
6101
     *
6102
     * @remarks
6103
     * You can pass callback function as a third parameter of `getPreviousCell` method.
6104
     * The callback function accepts IgxColumnComponent as a param
6105
     * @example
6106
     * ```typescript
6107
     *  const nextEditableCellPosition = this.grid.getNextCell(0, 3, (column) => column.editable);
6108
     * ```
6109
     */
6110
    public getNextCell(currRowIndex: number, curVisibleColIndex: number,
6111
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
6112
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
6113
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
6114
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
6115
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
6116
        }
6117
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => a - b) :
6118
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => a - b);
6119
        const nextCellIndex = colIndexes.find(index => index > curVisibleColIndex);
6120
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
6121
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && nextCellIndex !== undefined) {
6122
            return { rowIndex: currRowIndex, visibleColumnIndex: nextCellIndex };
6123
        } else {
6124
            const nextIndex = this.getNextDataRowIndex(currRowIndex)
6125
            if (colIndexes.length === 0 || nextIndex === currRowIndex) {
6126
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
6127
            } else {
6128
                return { rowIndex: nextIndex, visibleColumnIndex: colIndexes[0] };
6129
            }
6130
        }
6131
    }
6132

6133
    /**
6134
     * Returns `ICellPosition` which defines the previous cell,
6135
     * according to the current position, that match specific criteria.
6136
     *
6137
     * @remarks
6138
     * You can pass callback function as a third parameter of `getPreviousCell` method.
6139
     * The callback function accepts IgxColumnComponent as a param
6140
     * @example
6141
     * ```typescript
6142
     *  const previousEditableCellPosition = this.grid.getPreviousCell(0, 3, (column) => column.editable);
6143
     * ```
6144
     */
6145
    public getPreviousCell(currRowIndex: number, curVisibleColIndex: number,
6146
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
6147
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
6148
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
6149
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
6150
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
6151
        }
6152
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => b - a) :
6153
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => b - a);
6154
        const prevCellIndex = colIndexes.find(index => index < curVisibleColIndex);
6155
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
6156
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && prevCellIndex !== undefined) {
6157
            return { rowIndex: currRowIndex, visibleColumnIndex: prevCellIndex };
6158
        } else {
6159
            const prevIndex = this.getNextDataRowIndex(currRowIndex, true);
6160
            if (colIndexes.length === 0 || prevIndex === currRowIndex) {
6161
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
6162
            } else {
6163
                return { rowIndex: prevIndex, visibleColumnIndex: colIndexes[0] };
6164
            }
6165
        }
6166
    }
6167

6168
    /**
6169
     * @hidden
6170
     * @internal
6171
     */
6172
    public endRowEditTabStop(commit = true, event?: Event) {
6173
        const canceled = this.crudService.endEdit(commit, event);
6174

6175
        if (canceled) {
6176
            return true;
6177
        }
6178

6179
        const activeCell = this.gridAPI.grid.navigation.activeNode;
6180
        if (activeCell && activeCell.row !== -1) {
6181
            this.tbody.nativeElement.focus();
6182
        }
6183
    }
6184

6185
    /**
6186
     * @hidden @internal
6187
     */
6188
    public trackColumnChanges(index, col) {
6189
        return col.field + col._calcWidth;
6190
    }
6191

6192
    /**
6193
     * @hidden
6194
     */
6195
    public isExpandedGroup(_group: IGroupByRecord): boolean {
6196
        return undefined;
6197
    }
6198

6199
    /**
6200
     * @hidden @internal
6201
     * TODO: MOVE to CRUD
6202
     */
6203
    public openRowOverlay(id) {
6204
        this.configureRowEditingOverlay(id, this.rowList.length <= MIN_ROW_EDITING_COUNT_THRESHOLD);
6205

6206
        this.rowEditingOverlay.open(this.rowEditSettings);
6207
        this.rowEditingOverlay.element.addEventListener('wheel', this.rowEditingWheelHandler.bind(this));
6208
    }
6209

6210
    /**
6211
     * @hidden @internal
6212
     */
6213
    public closeRowEditingOverlay() {
6214
        this.rowEditingOverlay.element.removeEventListener('wheel', this.rowEditingWheelHandler);
6215
        this.rowEditPositioningStrategy.isTopInitialPosition = null;
6216
        this.rowEditingOverlay.close();
6217
        this.rowEditingOverlay.element.parentElement.style.display = '';
6218
    }
6219

6220
    /**
6221
     * @hidden @internal
6222
     */
6223
    public toggleRowEditingOverlay(show) {
6224
        const rowStyle = this.rowEditingOverlay.element.style;
6225
        if (show) {
6226
            rowStyle.display = 'block';
6227
        } else {
6228
            rowStyle.display = 'none';
6229
        }
6230
    }
6231

6232
    /**
6233
     * @hidden @internal
6234
     */
6235
    public repositionRowEditingOverlay(row: RowType) {
6236
        if (row && !this.rowEditingOverlay.collapsed) {
6237
            const rowStyle = this.rowEditingOverlay.element.parentElement.style;
6238
            if (row) {
6239
                rowStyle.display = '';
6240
                this.configureRowEditingOverlay(row.key);
6241
                this.rowEditingOverlay.reposition();
6242
            } else {
6243
                rowStyle.display = 'none';
6244
            }
6245
        }
6246
    }
6247

6248
    /**
6249
     * @hidden @internal
6250
     */
6251
    public cachedViewLoaded(args: ICachedViewLoadedEventArgs) {
6252
        if (this.hasHorizontalScroll()) {
6253
            const tmplId = args.context.templateID.type;
6254
            const index = args.context.index;
6255
            args.view.detectChanges();
6256
            this.zone.onStable.pipe(first()).subscribe(() => {
6257
                const row = tmplId === 'dataRow' ? this.gridAPI.get_row_by_index(index) : null;
6258
                const summaryRow = tmplId === 'summaryRow' ? this.summariesRowList.find((sr) => sr.dataRowIndex === index) : null;
6259
                if (row && row instanceof IgxRowDirective) {
6260
                    this._restoreVirtState(row);
6261
                } else if (summaryRow) {
6262
                    this._restoreVirtState(summaryRow);
6263
                }
6264
            });
6265
        }
6266
    }
6267

6268
    /**
6269
     * Opens the advanced filtering dialog.
6270
     */
6271
    public openAdvancedFilteringDialog(overlaySettings?: OverlaySettings) {
6272
        const settings = overlaySettings ? overlaySettings : this._advancedFilteringOverlaySettings;
6273
        if (!this._advancedFilteringOverlayId) {
6274
            this._advancedFilteringOverlaySettings.target =
6275
                (this as any).rootGrid ? (this as any).rootGrid.nativeElement : this.nativeElement;
6276
            this._advancedFilteringOverlaySettings.outlet = this.outlet;
6277

6278
            this._advancedFilteringOverlayId = this.overlayService.attach(
6279
                IgxAdvancedFilteringDialogComponent,
6280
                this.viewRef,
6281
                settings);
6282
            this.overlayService.show(this._advancedFilteringOverlayId);
6283
        }
6284
    }
6285

6286
    /**
6287
     * Closes the advanced filtering dialog.
6288
     *
6289
     * @param applyChanges indicates whether the changes should be applied
6290
     */
6291
    public closeAdvancedFilteringDialog(applyChanges: boolean) {
6292
        if (this._advancedFilteringOverlayId) {
6293
            const advancedFilteringOverlay = this.overlayService.getOverlayById(this._advancedFilteringOverlayId);
6294
            const advancedFilteringDialog = advancedFilteringOverlay.componentRef.instance as IgxAdvancedFilteringDialogComponent;
6295

6296
            if (applyChanges) {
6297
                advancedFilteringDialog.applyChanges();
6298
            }
6299
            advancedFilteringDialog.closeDialog();
6300
        }
6301
    }
6302

6303
    /**
6304
     * @hidden @internal
6305
     */
6306
    public getEmptyRecordObjectFor(inRow: RowType) {
6307
        const row = { ...inRow?.data };
6308
        Object.keys(row).forEach(key => row[key] = undefined);
6309
        const id = this.generateRowID();
6310
        row[this.primaryKey] = id;
6311
        return { rowID: id, data: row, recordRef: row };
6312
    }
6313

6314
    /**
6315
     * @hidden @internal
6316
     */
6317
    public hasHorizontalScroll() {
6318
        return this.totalWidth - this.unpinnedWidth > 0;
6319
    }
6320

6321
    /**
6322
     * @hidden @internal
6323
     */
6324
    public isSummaryRow(rowData): boolean {
6325
        return rowData && rowData.summaries && (rowData.summaries instanceof Map);
6326
    }
6327

6328
    /**
6329
     * @hidden @internal
6330
     */
6331
    public triggerPipes() {
6332
        this.pipeTrigger++;
6333
        this.cdr.detectChanges();
6334
    }
6335

6336
    /**
6337
     * @hidden
6338
     */
6339
    public rowEditingWheelHandler(event: WheelEvent) {
6340
        if (event.deltaY > 0) {
6341
            this.verticalScrollContainer.scrollNext();
6342
        } else {
6343
            this.verticalScrollContainer.scrollPrev();
6344
        }
6345
    }
6346

6347
    /**
6348
     * @hidden
6349
     */
6350
    public getUnpinnedIndexById(id) {
6351
        return this.unpinnedRecords.findIndex(x => x[this.primaryKey] === id);
6352
    }
6353

6354
    /**
6355
     * Finishes the row transactions on the current row and returns whether the grid editing was canceled.
6356
     *
6357
     * @remarks
6358
     * If `commit === true`, passes them from the pending state to the data (or transaction service)
6359
     * @example
6360
     * ```html
6361
     * <button type="button" igxButton (click)="grid.endEdit(true)">Commit Row</button>
6362
     * ```
6363
     * @param commit
6364
     */
6365
    // TODO: Facade for crud service refactoring. To be removed
6366
    // TODO: do not remove this, as it is used in rowEditTemplate, but mark is as internal and hidden
6367
    public endEdit(commit = true, event?: Event): boolean {
6368
        return this.crudService.endEdit(commit, event);
6369
    }
6370

6371
    /**
6372
     * Enters add mode by spawning the UI under the specified row by rowID.
6373
     *
6374
     * @remarks
6375
     * If null is passed as rowID, the row adding UI is spawned as the first record in the data view
6376
     * @remarks
6377
     * Spawning the UI to add a child for a record only works if you provide a rowID
6378
     * @example
6379
     * ```typescript
6380
     * this.grid.beginAddRowById('ALFKI');
6381
     * this.grid.beginAddRowById('ALFKI', true);
6382
     * this.grid.beginAddRowById(null);
6383
     * ```
6384
     * @param rowID - The rowID to spawn the add row UI for, or null to spawn it as the first record in the data view
6385
     * @param asChild - Whether the record should be added as a child. Only applicable to igxTreeGrid.
6386
     */
6387
    public beginAddRowById(rowID: any, asChild?: boolean): void {
6388
        let index = rowID;
6389
        if (rowID == null) {
6390
            if (asChild) {
6391
                console.warn('The record cannot be added as a child to an unspecified record.');
6392
                return;
6393
            }
6394
            index = null;
6395
        } else {
6396
            // find the index of the record with that PK
6397
            index = this.gridAPI.get_rec_index_by_id(rowID, this.dataView);
6398
            if (index === -1) {
6399
                console.warn('No row with the specified ID was found.');
6400
                return;
6401
            }
6402
        }
6403

6404
        this._addRowForIndex(index, asChild);
6405
    }
6406

6407
    protected _addRowForIndex(index: number, asChild?: boolean) {
6408
        if (!this.dataView.length) {
6409
            this.beginAddRowForIndex(index, asChild);
6410
            return;
6411
        }
6412
        // check if the index is valid - won't support anything outside the data view
6413
        if (index >= 0 && index < this.dataView.length) {
6414
            // check if the index is in the view port
6415
            if ((index < this.virtualizationState.startIndex ||
6416
                index >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) &&
6417
                !this.isRecordPinnedByViewIndex(index)) {
6418
                this.verticalScrollContainer.chunkLoad
6419
                    .pipe(first(), takeUntil(this.destroy$))
6420
                    .subscribe(() => {
6421
                        this.beginAddRowForIndex(index, asChild);
6422
                    });
6423
                this.navigateTo(index);
6424
                this.notifyChanges(true);
6425
                return;
6426
            }
6427
            this.beginAddRowForIndex(index, asChild);
6428
        } else {
6429
            console.warn('The row with the specified PK or index is outside of the current data view.');
6430
        }
6431
    }
6432

6433
    /**
6434
     * Enters add mode by spawning the UI at the specified index.
6435
     *
6436
     * @remarks
6437
     * Accepted values for index are integers from 0 to this.grid.dataView.length
6438
     * @example
6439
     * ```typescript
6440
     * this.grid.beginAddRowByIndex(0);
6441
     * ```
6442
     * @param index - The index to spawn the UI at. Accepts integers from 0 to this.grid.dataView.length
6443
     */
6444
    public beginAddRowByIndex(index: number): void {
6445
        if (index === 0) {
6446
            return this.beginAddRowById(null);
6447
        }
6448
        return this._addRowForIndex(index - 1);
6449
    }
6450

6451
    /**
6452
     * @hidden
6453
     */
6454
    public preventHeaderScroll(args) {
6455
        if (args.target.scrollLeft !== 0) {
6456
            (this.navigation as any).forOfDir().getScroll().scrollLeft = args.target.scrollLeft;
6457
            args.target.scrollLeft = 0;
6458
        }
6459
    }
6460

6461
    protected beginAddRowForIndex(index: number, asChild: boolean = false) {
6462
        // TODO is row from rowList suitable for enterAddRowMode
6463
        const row = index == null ?
6464
            null : this.rowList.find(r => r.index === index);
6465
        if (row !== undefined) {
6466
            this.crudService.enterAddRowMode(row, asChild);
6467
        } else {
6468
            console.warn('No row with the specified PK or index was found.');
6469
        }
6470
    }
6471

6472
    protected switchTransactionService(val: boolean) {
6473
        if (val) {
6474
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);
6475
        } else {
6476
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
6477
        }
6478

6479
        if (this.dataCloneStrategy) {
6480
            this._transactions.cloneStrategy = this.dataCloneStrategy;
6481
        }
6482
    }
6483

6484
    protected subscribeToTransactions(): void {
6485
        this.transactionChange$.next();
6486
        this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$)))
6487
            .subscribe(this.transactionStatusUpdate.bind(this));
6488
    }
6489

6490
    protected transactionStatusUpdate(event: StateUpdateEvent) {
6491
        let actions: Action<Transaction>[] = [];
6492
        if (event.origin === TransactionEventOrigin.REDO) {
6493
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.DELETE) : [];
6494
        } else if (event.origin === TransactionEventOrigin.UNDO) {
6495
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.ADD) : [];
6496
        }
6497
        if (actions.length > 0) {
6498
            for (const action of actions) {
6499
                if (this.selectionService.isRowSelected(action.transaction.id)) {
6500
                    this.selectionService.deselectRow(action.transaction.id);
6501
                }
6502
            }
6503
        }
6504
        if (event.origin === TransactionEventOrigin.REDO || event.origin === TransactionEventOrigin.UNDO) {
6505
            event.actions.forEach(x => {
6506
                if (x.transaction.type === TransactionType.UPDATE) {
6507
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
6508
                    this.validation.update(x.transaction.id, value ?? x.recordRef);
6509
                } else if (x.transaction.type === TransactionType.DELETE || x.transaction.type === TransactionType.ADD) {
6510
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
6511
                    if (value) {
6512
                        this.validation.create(x.transaction.id, value ?? x.recordRef);
6513
                        this.validation.update(x.transaction.id, value ?? x.recordRef);
6514
                        this.validation.markAsTouched(x.transaction.id);
6515
                    } else {
6516
                        this.validation.clear(x.transaction.id);
6517
                    }
6518
                }
6519

6520
            });
6521
        }
6522

6523
        this.selectionService.clearHeaderCBState();
6524
        this.summaryService.clearSummaryCache();
6525
        this.pipeTrigger++;
6526
        this.notifyChanges();
6527
    };
6528

6529
    protected writeToData(rowIndex: number, value: any) {
6530
        mergeObjects(this.gridAPI.get_all_data()[rowIndex], value);
6531
    }
6532

6533
    protected _restoreVirtState(row) {
6534
        // check virtualization state of data record added from cache
6535
        // in case state is no longer valid - update it.
6536
        const rowForOf = row.virtDirRow;
6537
        const gridScrLeft = rowForOf.getScroll().scrollLeft;
6538
        const left = -parseInt(rowForOf.dc.instance._viewContainer.element.nativeElement.style.left, 10);
6539
        const actualScrollLeft = left + rowForOf.getColumnScrollLeft(rowForOf.state.startIndex);
6540
        if (gridScrLeft !== actualScrollLeft) {
6541
            rowForOf.onHScroll(gridScrLeft);
6542
            rowForOf.cdr.detectChanges();
6543
        }
6544
    }
6545

6546
    protected changeRowEditingOverlayStateOnScroll(row: RowType) {
6547
        if (!this.rowEditable || !this.rowEditingOverlay || this.rowEditingOverlay.collapsed) {
6548
            return;
6549
        }
6550
        if (!row) {
6551
            this.toggleRowEditingOverlay(false);
6552
        } else {
6553
            this.repositionRowEditingOverlay(row);
6554
        }
6555
    }
6556

6557
    /**
6558
     * Should be called when data and/or isLoading input changes so that the overlay can be
6559
     * hidden/shown based on the current value of shouldOverlayLoading
6560
     */
6561
    protected evaluateLoadingState() {
6562
        if (this.shouldOverlayLoading) {
6563
            // a new overlay should be shown
6564
            const overlaySettings: OverlaySettings = {
6565
                outlet: this.loadingOutlet,
6566
                closeOnOutsideClick: false,
6567
                positionStrategy: new ContainerPositionStrategy()
6568
            };
6569
            this.loadingOverlay.open(overlaySettings);
6570
        } else {
6571
            this.loadingOverlay.close();
6572
        }
6573
    }
6574

6575
    /**
6576
     * @hidden
6577
     * Sets grid width i.e. this.calcWidth
6578
     */
6579
    protected calculateGridWidth() {
6580
        let width;
6581

6582
        if (this.isPercentWidth) {
6583
            /* width in %*/
6584
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width');
6585
            width = computed.indexOf('%') === -1 ? parseInt(computed, 10) : null;
6586
        } else {
6587
            width = parseInt(this.width, 10);
6588
        }
6589

6590
        if (!width && this.nativeElement) {
6591
            width = this.nativeElement.offsetWidth;
6592
        }
6593

6594

6595
        if (this.width === null || !width) {
6596
            width = this.getColumnWidthSum();
6597
        }
6598

6599
        if (this.hasVerticalScroll() && this.width !== null) {
6600
            width -= this.scrollSize;
6601
        }
6602
        if ((Number.isFinite(width) || width === null) && width !== this.calcWidth) {
6603
            this.calcWidth = width;
6604
        }
6605
        this._derivePossibleWidth();
6606
    }
6607

6608
    /**
6609
     * @hidden
6610
     * Sets columns defaultWidth property
6611
     */
6612
    protected _derivePossibleWidth() {
6613
        if (!this.columnWidthSetByUser) {
6614
            this._columnWidth = this.width !== null ? this.getPossibleColumnWidth() : this.minColumnWidth + 'px';
6615
        }
6616
        this._columns.forEach((column: IgxColumnComponent) => {
6617
            if (this.hasColumnLayouts && parseInt(this._columnWidth, 10)) {
6618
                const columnWidthCombined = parseInt(this._columnWidth, 10) * (column.colEnd ? column.colEnd - column.colStart : 1);
6619
                column.defaultWidth = columnWidthCombined + 'px';
6620
            } else {
6621
                // D.K. March 29th, 2021 #9145 Consider min/max width when setting defaultWidth property
6622
                column.defaultWidth = this.getExtremumBasedColWidth(column);
6623
                column.resetCaches();
6624
            }
6625
        });
6626
        this.resetCachedWidths();
6627
    }
6628

6629
    /**
6630
     * @hidden
6631
     * @internal
6632
     */
6633
    protected getExtremumBasedColWidth(column: IgxColumnComponent): string {
6634
        let width = this._columnWidth;
6635
        if (width && typeof width !== 'string') {
6636
            width = String(width);
6637
        }
6638
        const minWidth = width.indexOf('%') === -1 ? column.minWidthPx : column.minWidthPercent;
6639
        const maxWidth = width.indexOf('%') === -1 ? column.maxWidthPx : column.maxWidthPercent;
6640
        if (column.hidden) {
6641
            return width;
6642
        }
6643

6644
        if (minWidth > parseFloat(width)) {
6645
            width = String(column.minWidth);
6646
        } else if (maxWidth < parseFloat(width)) {
6647
            width = String(column.maxWidth);
6648
        }
6649

6650
        // if no px or % are defined in maxWidth/minWidth consider it px
6651
        if (width.indexOf('%') === -1 && width.indexOf('px') === -1) {
6652
            width += 'px';
6653
        }
6654
        return width;
6655
    }
6656

6657
    protected resetNotifyChanges() {
6658
        this._cdrRequestRepaint = false;
6659
        this._cdrRequests = false;
6660
    }
6661

6662
    /** @hidden @internal */
6663
    public resolveOutlet() {
6664
        return this._userOutletDirective ? this._userOutletDirective : this._outletDirective;
6665
    }
6666

6667
    /**
6668
     * Reorder columns in the main columnList and _columns collections.
6669
     *
6670
     * @hidden
6671
     */
6672
    protected _moveColumns(from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6673
        const orderedList = this._pinnedColumns.concat(this._unpinnedColumns);
6674
        const list = orderedList;
6675
        this._reorderColumns(from, to, pos, list);
6676
        const newList = this._resetColumnList(list);
6677
        this.updateColumns(newList);
6678
    }
6679

6680

6681
    /**
6682
     * Update internal column's collection.
6683
     * @hidden
6684
     */
6685
    public updateColumns(newColumns: IgxColumnComponent[]) {
6686
        // update internal collections to retain order.
6687
        this._pinnedColumns = newColumns
6688
            .filter((c) => c.pinned);
6689
        this._unpinnedColumns = newColumns.filter((c) => !c.pinned);
6690
        this._columns = newColumns;
6691
        this.resetCaches();
6692
    }
6693

6694
    /**
6695
     * @hidden
6696
     */
6697
    protected _resetColumnList(list?) {
6698
        if (!list) {
6699
            list = this._columns;
6700
        }
6701
        let newList = [];
6702
        list.filter(c => c.level === 0).forEach(p => {
6703
            newList.push(p);
6704
            if (p.columnGroup) {
6705
                newList = newList.concat(p.allChildren);
6706
            }
6707
        });
6708
        return newList;
6709
    }
6710

6711
    /**
6712
     * Reorders columns inside the passed column collection.
6713
     * When reordering column group collection, the collection is not flattened.
6714
     * In all other cases, the columns collection is flattened, this is why adittional calculations on the dropIndex are done.
6715
     *
6716
     * @hidden
6717
     */
6718
    protected _reorderColumns(from: IgxColumnComponent, to: IgxColumnComponent, position: DropPosition, columnCollection: any[],
6719
        inGroup = false) {
6720
        const fromIndex = columnCollection.indexOf(from);
6721
        const childColumnsCount = inGroup ? 1 : from.allChildren.length + 1;
6722
        columnCollection.splice(fromIndex, childColumnsCount);
6723
        let dropIndex = columnCollection.indexOf(to);
6724
        if (position === DropPosition.AfterDropTarget) {
6725
            dropIndex++;
6726
            if (!inGroup && to.columnGroup) {
6727
                dropIndex += to.allChildren.length;
6728
            }
6729
        }
6730
        columnCollection.splice(dropIndex, 0, from);
6731
    }
6732

6733
    /**
6734
     * Reorder column group collection.
6735
     *
6736
     * @hidden
6737
     */
6738
    protected _moveChildColumns(parent: IgxColumnComponent, from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6739
        const buffer = parent.children.toArray();
6740
        this._reorderColumns(from, to, pos, buffer, true);
6741
        parent.children.reset(buffer);
6742
    }
6743

6744
    /**
6745
     * @hidden @internal
6746
     */
6747
    protected setupColumns() {
6748
        if (this.autoGenerate) {
6749
            this.autogenerateColumns();
6750
        } else {
6751
            this._columns = this.getColumnList();
6752
        }
6753

6754
        this.initColumns(this._columns, (col: IgxColumnComponent) => this.columnInit.emit(col));
6755
        this.columnListDiffer.diff(this.columnList);
6756

6757
        this.columnList.changes
6758
            .pipe(takeUntil(this.destroy$))
6759
            .subscribe((change: QueryList<IgxColumnComponent>) => {
6760
                this.onColumnsChanged(change);
6761
            });
6762
    }
6763

6764
    protected getColumnList() {
6765
        return this.columnList.toArray();
6766
    }
6767

6768
    /**
6769
     * @hidden
6770
     */
6771
    protected deleteRowFromData(rowID: any, index: number) {
6772
        //  if there is a row (index !== 0) delete it
6773
        //  if there is a row in ADD or UPDATE state change it's state to DELETE
6774
        if (index !== -1) {
6775
            if (this.transactions.enabled) {
6776
                const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null };
6777
                this.transactions.add(transaction, this.data[index]);
6778
            } else {
6779
                this.data.splice(index, 1);
6780
            }
6781
        } else {
6782
            const state: State = this.transactions.getState(rowID);
6783
            this.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef);
6784
        }
6785
    }
6786

6787

6788
    /**
6789
     * @hidden @internal
6790
     */
6791
    protected getDataBasedBodyHeight(): number {
6792
        return !this.data || (this.data.length < this._defaultTargetRecordNumber) ?
6793
            0 : this.defaultTargetBodyHeight;
6794
    }
6795

6796
    /**
6797
     * @hidden @internal
6798
     */
6799
    protected onPinnedRowsChanged(change: QueryList<IgxGridRowComponent>) {
6800
        const diff = this.rowListDiffer.diff(change);
6801
        if (diff) {
6802
            this.notifyChanges(true);
6803
        }
6804
    }
6805

6806
    /**
6807
     * @hidden
6808
     */
6809
    protected onColumnsChanged(change: QueryList<IgxColumnComponent>) {
6810
        const diff = this.columnListDiffer.diff(change);
6811

6812
        if (this.autoGenerate && this._columns.length === 0 && this._autoGeneratedCols.length > 0) {
6813
            // In Ivy if there are nested conditional templates the content children are re-evaluated
6814
            // hence autogenerated columns are cleared and need to be reset.
6815
            this.updateColumns(this._autoGeneratedCols);
6816
            return;
6817
        }
6818
        if (diff) {
6819
            let added = false;
6820
            let removed = false;
6821
            diff.forEachAddedItem((record: IterableChangeRecord<IgxColumnComponent>) => {
6822
                added = true;
6823
                if (record.item.pinned) {
6824
                    this._pinnedColumns.push(record.item);
6825
                } else {
6826
                    this._unpinnedColumns.push(record.item);
6827
                }
6828
            });
6829

6830
            this.initColumns(this.columnList.toArray(), (col: IgxColumnComponent) => this.columnInit.emit(col));
6831

6832
            diff.forEachRemovedItem((record: IterableChangeRecord<IgxColumnComponent | IgxColumnGroupComponent>) => {
6833
                const isColumnGroup = record.item instanceof IgxColumnGroupComponent;
6834
                if (!isColumnGroup) {
6835
                    // Clear Grouping
6836
                    this.gridAPI.clear_groupby(record.item.field);
6837

6838
                    // Clear Filtering
6839
                    this.filteringService.clear_filter(record.item.field);
6840

6841
                    // Close filter row
6842
                    if (this.filteringService.isFilterRowVisible
6843
                        && this.filteringService.filteredColumn
6844
                        && this.filteringService.filteredColumn.field === record.item.field) {
6845
                        this.filteringRow.close();
6846
                    }
6847

6848
                    // Clear Sorting
6849
                    this.gridAPI.clear_sort(record.item.field);
6850

6851
                    // Remove column selection
6852
                    this.selectionService.deselectColumnsWithNoEvent([record.item.field]);
6853
                }
6854
                removed = true;
6855
            });
6856

6857
            this.resetCaches();
6858

6859
            if (added || removed) {
6860
                this.onColumnsAddedOrRemoved();
6861
            }
6862
        }
6863
    }
6864

6865
    /**
6866
     * @hidden @internal
6867
     */
6868
    protected onColumnsAddedOrRemoved() {
6869
        this.summaryService.clearSummaryCache();
6870
        Promise.resolve().then(() => {
6871
            // `onColumnsChanged` can be executed midway a current detectChange cycle and markForCheck will be ignored then.
6872
            // This ensures that we will wait for the current cycle to end so we can trigger a new one and ngDoCheck to fire.
6873
            this.notifyChanges(true);
6874
        });
6875
    }
6876

6877
    /**
6878
     * @hidden
6879
     */
6880
    protected calculateGridSizes(recalcFeatureWidth = true) {
6881
        /*
6882
            TODO: (R.K.) This layered lasagne should be refactored
6883
            ASAP. The reason I have to reset the caches so many times is because
6884
            after teach `detectChanges` call they are filled with invalid
6885
            state. Of course all of this happens midway through the grid
6886
            sizing process which of course, uses values from the caches, thus resulting
6887
            in a broken layout.
6888
        */
6889
        this.cdr.detectChanges();
6890
        this.resetCaches(recalcFeatureWidth);
6891

6892
        const hasScroll = this.hasVerticalScroll();
6893
        this.calculateGridWidth();
6894
        this.resetCaches(recalcFeatureWidth);
6895
        this.cdr.detectChanges();
6896
        this.calculateGridHeight();
6897

6898
        if (this.rowEditable) {
6899
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
6900
        }
6901

6902
        if (this.filteringService.isFilterRowVisible) {
6903
            this.filteringRow.resetChipsArea();
6904
        }
6905

6906
        this.cdr.detectChanges();
6907
        // in case scrollbar has appeared recalc to size correctly.
6908
        if (hasScroll !== this.hasVerticalScroll()) {
6909
            this.calculateGridWidth();
6910
            this.cdr.detectChanges();
6911
        }
6912
        if (this.zone.isStable) {
6913
            this.zone.run(() => {
6914
                this._applyWidthHostBinding();
6915
                this.cdr.detectChanges();
6916
            });
6917
        } else {
6918
            this.zone.onStable.pipe(first()).subscribe(() => {
6919
                this.zone.run(() => {
6920
                    this._applyWidthHostBinding();
6921
                });
6922
            });
6923
        }
6924
        this.resetCaches(recalcFeatureWidth);
6925
        if (this.hasColumnsToAutosize) {
6926
            this.cdr.detectChanges();
6927
            this.zone.onStable.pipe(first()).subscribe(() => {
6928
                this.autoSizeColumnsInView();
6929
            });
6930
        }
6931
    }
6932

6933
    /**
6934
     * @hidden
6935
     * @internal
6936
     */
6937
    protected calcGridHeadRow() {
6938
        if (this.maxLevelHeaderDepth) {
6939
            this._baseFontSize = parseFloat(getComputedStyle(this.document.documentElement).getPropertyValue('font-size'));
6940
            const hasFilterRow = this._allowFiltering && this._filterMode === FilterMode.quickFilter;
6941
            const minSize = (this.maxLevelHeaderDepth + 1 + (hasFilterRow ? 1 : 0)) * this.defaultRowHeight / this._baseFontSize;
6942
            this.theadRow.nativeElement.style.minHeight = `${minSize}rem`;
6943
        }
6944
    }
6945

6946
    /**
6947
     * @hidden
6948
     * Sets TBODY height i.e. this.calcHeight
6949
     */
6950
    protected calculateGridHeight() {
6951
        this.calcGridHeadRow();
6952

6953
        this.calcHeight = this._calculateGridBodyHeight();
6954
        if (this.pinnedRowHeight && this.calcHeight) {
6955
            this.calcHeight -= this.pinnedRowHeight;
6956
        }
6957
    }
6958

6959
    /**
6960
     * @hidden
6961
     */
6962
    protected getGroupAreaHeight(): number {
6963
        return 0;
6964
    }
6965

6966
    /**
6967
     * @hidden
6968
     */
6969
    protected getComputedHeight(elem) {
6970
        return elem.offsetHeight ? parseFloat(this.document.defaultView.getComputedStyle(elem).getPropertyValue('height')) : 0;
6971
    }
6972
    /**
6973
     * @hidden
6974
     */
6975
    protected getFooterHeight(): number {
6976
        return this.summaryRowHeight || this.getComputedHeight(this.tfoot.nativeElement);
6977
    }
6978
    /**
6979
     * @hidden
6980
     */
6981
    protected getTheadRowHeight(): number {
6982
        const height = this.getComputedHeight(this.theadRow.nativeElement);
6983
        return (!this.allowFiltering || (this.allowFiltering && this.filterMode !== FilterMode.quickFilter)) ?
6984
            height - this.getFilterCellHeight() :
6985
            height;
6986
    }
6987

6988
    /**
6989
     * @hidden
6990
     */
6991
    protected getToolbarHeight(): number {
6992
        let toolbarHeight = 0;
6993
        if (this.toolbar.first) {
6994
            toolbarHeight = this.getComputedHeight(this.toolbar.first.nativeElement);
6995
        }
6996
        return toolbarHeight;
6997
    }
6998

6999
    /**
7000
     * @hidden
7001
     */
7002
    protected getPagingFooterHeight(): number {
7003
        let pagingHeight = 0;
7004
        if (this.footer) {
7005
            const height = this.getComputedHeight(this.footer.nativeElement);
7006
            pagingHeight = this.footer.nativeElement.firstElementChild ?
7007
                height : 0;
7008
        }
7009
        return pagingHeight;
7010
    }
7011

7012
    /**
7013
     * @hidden
7014
     */
7015
    protected getFilterCellHeight(): number {
7016
        const headerGroupNativeEl = (this.headerGroupsList.length !== 0) ?
7017
            this.headerGroupsList[0].nativeElement : null;
7018
        const filterCellNativeEl = (headerGroupNativeEl) ?
7019
            headerGroupNativeEl.querySelector('igx-grid-filtering-cell') as HTMLElement : null;
7020
        return (filterCellNativeEl) ? filterCellNativeEl.offsetHeight : 0;
7021
    }
7022

7023
    /**
7024
     * @hidden
7025
     */
7026
    protected _calculateGridBodyHeight(): number {
7027
        if (!this._height) {
7028
            return null;
7029
        }
7030
        const actualTheadRow = this.getTheadRowHeight();
7031
        const footerHeight = this.getFooterHeight();
7032
        const toolbarHeight = this.getToolbarHeight();
7033
        const pagingHeight = this.getPagingFooterHeight();
7034
        const groupAreaHeight = this.getGroupAreaHeight();
7035
        const scrHeight = this.getComputedHeight(this.scr.nativeElement);
7036
        const renderedHeight = toolbarHeight + actualTheadRow +
7037
            footerHeight + pagingHeight + groupAreaHeight +
7038
            scrHeight;
7039

7040
        let gridHeight = 0;
7041

7042
        if (this.isPercentHeight) {
7043
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('height');
7044
            const autoSize = this._shouldAutoSize(renderedHeight);
7045
            if (autoSize || computed.indexOf('%') !== -1) {
7046
                const bodyHeight = this.getDataBasedBodyHeight();
7047
                return bodyHeight > 0 ? bodyHeight : null;
7048
            }
7049
            gridHeight = parseFloat(computed);
7050
        } else {
7051
            gridHeight = parseInt(this._height, 10);
7052
        }
7053
        const height = Math.abs(gridHeight - renderedHeight);
7054

7055
        if (Math.round(height) === 0 || isNaN(gridHeight)) {
7056
            const bodyHeight = this.defaultTargetBodyHeight;
7057
            return bodyHeight > 0 ? bodyHeight : null;
7058
        }
7059
        return height;
7060
    }
7061

7062
    protected checkContainerSizeChange() {
7063
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
7064
        const origHeight = parentElement.offsetHeight;
7065
        this.nativeElement.style.display = 'none';
7066
        const height = parentElement.offsetHeight;
7067
        this.nativeElement.style.display = '';
7068
        return origHeight !== height;
7069
    }
7070

7071
    protected _shouldAutoSize(renderedHeight) {
7072
        this.tbody.nativeElement.style.display = 'none';
7073
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
7074
        let res = !parentElement ||
7075
            parentElement.clientHeight === 0 ||
7076
            parentElement.clientHeight === renderedHeight;
7077
        if ((!this.platform.isChromium && !this.platform.isFirefox) || this._autoSize) {
7078
            // If grid causes the parent container to extend (for example when container is flex)
7079
            // we should always auto-size since the actual size of the container will continuously change as the grid renders elements.
7080
            this._autoSize = false;
7081
            res = this.checkContainerSizeChange();
7082
        }
7083
        this.tbody.nativeElement.style.display = '';
7084
        return res;
7085
    }
7086

7087
    /**
7088
     * @hidden
7089
     * Gets calculated width of the unpinned area
7090
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
7091
     */
7092
    protected getUnpinnedWidth(takeHidden = false) {
7093
        let width = this.isPercentWidth ?
7094
            this.calcWidth :
7095
            parseInt(this.width, 10) || parseInt(this.hostWidth, 10) || this.calcWidth;
7096
        if (this.hasVerticalScroll() && !this.isPercentWidth) {
7097
            width -= this.scrollSize;
7098
        }
7099
        if (!this.isPinningToStart) {
7100
            width -= this.featureColumnsWidth();
7101
        }
7102

7103
        return width - this.getPinnedWidth(takeHidden);
7104
    }
7105

7106
    /**
7107
     * @hidden
7108
     */
7109
    protected _summaries(fieldName: string, hasSummary: boolean, summaryOperand?: any) {
7110
        const column = this.gridAPI.get_column_by_name(fieldName);
7111
        if (column) {
7112
            column.hasSummary = hasSummary;
7113
            if (summaryOperand) {
7114
                if (this.rootSummariesEnabled) {
7115
                    this.summaryService.retriggerRootPipe++;
7116
                }
7117
                column.summaries = summaryOperand;
7118
            }
7119
        }
7120
    }
7121

7122
    /**
7123
     * @hidden
7124
     */
7125
    protected _multipleSummaries(expressions: ISummaryExpression[], hasSummary: boolean) {
7126
        expressions.forEach((element) => {
7127
            this._summaries(element.fieldName, hasSummary, element.customSummary);
7128
        });
7129
    }
7130
    /**
7131
     * @hidden
7132
     */
7133
    protected _disableMultipleSummaries(expressions) {
7134
        expressions.forEach((column) => {
7135
            const columnName = column && column.fieldName ? column.fieldName : column;
7136
            this._summaries(columnName, false);
7137
        });
7138
    }
7139

7140
    /**
7141
     * @hidden
7142
     */
7143
    public resolveDataTypes(rec) {
7144
        if (typeof rec === 'number') {
7145
            return GridColumnDataType.Number;
7146
        } else if (typeof rec === 'boolean') {
7147
            return GridColumnDataType.Boolean;
7148
        } else if (typeof rec === 'object' && rec instanceof Date) {
7149
            return GridColumnDataType.Date;
7150
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
7151
            return GridColumnDataType.Image;
7152
        }
7153
        return GridColumnDataType.String;
7154
    }
7155

7156
    /**
7157
     * @hidden
7158
     */
7159
    protected autogenerateColumns() {
7160
        const data = this.gridAPI.get_data();
7161
        const fields = this.generateDataFields(data);
7162
        const columns = [];
7163

7164
        fields.forEach((field) => {
7165
            const ref = createComponent(IgxColumnComponent, { environmentInjector:  this.envInjector, elementInjector: this.injector});
7166
            ref.instance.field = field;
7167
            ref.instance.dataType = this.resolveDataTypes(data[0][field]);
7168
            ref.changeDetectorRef.detectChanges();
7169
            columns.push(ref.instance);
7170
        });
7171
        this._autoGeneratedCols = columns;
7172

7173
        this.updateColumns(columns);
7174
        if (data && data.length > 0) {
7175
            this.shouldGenerate = false;
7176
        }
7177
    }
7178

7179
    protected generateDataFields(data: any[]): string[] {
7180
        return Object.keys(data && data.length !== 0 ? data[0] : [])
7181
            .filter(key => !this.autoGenerateExclude.includes(key));
7182
    }
7183

7184
    /**
7185
     * @hidden
7186
     */
7187
    protected initColumns(collection: IgxColumnComponent[], cb: (args: any) => void = null) {
7188
        this._columnGroups = collection.some(col => col.columnGroup);
7189
        if (this.hasColumnLayouts) {
7190
            // Set overall row layout size
7191
            collection.forEach((col) => {
7192
                if (col.columnLayout) {
7193
                    const layoutSize = col.children ?
7194
                        col.children.reduce((acc, val) => Math.max(val.rowStart + val.gridRowSpan - 1, acc), 1) :
7195
                        1;
7196
                    this._multiRowLayoutRowSize = Math.max(layoutSize, this._multiRowLayoutRowSize);
7197
                }
7198
            });
7199
        }
7200
        if (this.hasColumnLayouts && this.hasColumnGroups) {
7201
            // invalid configuration - multi-row and column groups
7202
            // remove column groups
7203
            const columnLayoutColumns = collection.filter((col) => col.columnLayout || col.columnLayoutChild);
7204
            collection = columnLayoutColumns;
7205
        }
7206
        this._maxLevelHeaderDepth = null;
7207
        collection.forEach((column: IgxColumnComponent) => {
7208
            column.defaultWidth = this.columnWidthSetByUser ? this._columnWidth : column.defaultWidth ? column.defaultWidth : '';
7209

7210
            if (cb) {
7211
                cb(column);
7212
            }
7213
        });
7214

7215
        this.updateColumns(collection);
7216

7217
        if (this.hasColumnLayouts) {
7218
            collection.forEach((column: IgxColumnComponent) => {
7219
                column.populateVisibleIndexes();
7220
            });
7221
        }
7222
    }
7223

7224
    /**
7225
     * @hidden
7226
     */
7227
    protected reinitPinStates() {
7228
        this._pinnedColumns = this._columns
7229
            .filter((c) => c.pinned).sort((a, b) => this._pinnedColumns.indexOf(a) - this._pinnedColumns.indexOf(b));
7230
        this._unpinnedColumns = this.hasColumnGroups ? this._columns.filter((c) => !c.pinned) :
7231
            this._columns.filter((c) => !c.pinned)
7232
                .sort((a, b) => this._unpinnedColumns.indexOf(a) - this._unpinnedColumns.indexOf(b));
7233
    }
7234

7235
    protected extractDataFromSelection(source: any[], formatters = false, headers = false, columnData?: any[]): any[] {
7236
        let columnsArray: IgxColumnComponent[];
7237
        let record = {};
7238
        let selectedData = [];
7239
        let keys = [];
7240
        const selectionCollection = new Map();
7241
        const keysAndData = [];
7242
        const activeEl = this.selectionService.activeElement;
7243

7244
        if (this.nativeElement.tagName.toLowerCase() === 'igx-hierarchical-grid') {
7245
            const expansionRowIndexes = [];
7246
            for (const [key, value] of this.expansionStates.entries()) {
7247
                if (value) {
7248
                    const rowIndex = this.gridAPI.get_rec_index_by_id(key, this.dataView);
7249
                    expansionRowIndexes.push(rowIndex);
7250
                }
7251
            }
7252
            if (this.selectionService.selection.size > 0) {
7253
                if (expansionRowIndexes.length > 0) {
7254
                    for (const [key, value] of this.selectionService.selection.entries()) {
7255
                        let updatedKey = key;
7256
                        let subtract = 0;
7257
                        expansionRowIndexes.forEach((row) => {
7258
                            if (updatedKey > Number(row)) {
7259
                                subtract++;
7260
                            }
7261
                        });
7262
                        selectionCollection.set(updatedKey - subtract, value);
7263
                    }
7264
                }
7265
            } else if (activeEl) {
7266
                let subtract = 0;
7267
                if (expansionRowIndexes.length > 0) {
7268
                    expansionRowIndexes.forEach(row => {
7269
                        if (activeEl.row > Number(row)) {
7270
                            subtract++;
7271
                        }
7272
                    });
7273
                    activeEl.row -= subtract;
7274
                }
7275
            }
7276
        }
7277

7278
        const totalItems = (this as any).totalItemCount ?? 0;
7279
        const isRemote = totalItems && totalItems > this.dataView.length;
7280
        let selectionMap;
7281
        if (this.nativeElement.tagName.toLowerCase() === 'igx-hierarchical-grid' && selectionCollection.size > 0) {
7282
            selectionMap = isRemote ? Array.from(selectionCollection) :
7283
                Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length);
7284
        } else {
7285
            selectionMap = isRemote ? Array.from(this.selectionService.selection) :
7286
                Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length);
7287
        }
7288

7289
        if (this.cellSelection === GridSelectionMode.single && activeEl) {
7290
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
7291
        }
7292

7293
        if (this.cellSelection === GridSelectionMode.none && activeEl) {
7294
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
7295
        }
7296

7297
        if (columnData) {
7298
            selectedData = columnData;
7299
        }
7300

7301
        // eslint-disable-next-line prefer-const
7302
        for (let [row, set] of selectionMap) {
7303
            row = this.paginator && (this.pagingMode === GridPagingMode.Local && source === this.filteredSortedData) ? row + (this.paginator.perPage * this.paginator.page) : row;
7304
            row = isRemote ? row - this.virtualizationState.startIndex : row;
7305
            if (!source[row] || source[row].detailsData !== undefined) {
7306
                continue;
7307
            }
7308
            const temp = Array.from(set);
7309
            for (const each of temp) {
7310
                columnsArray = this.getSelectableColumnsAt(each);
7311
                columnsArray.forEach((col) => {
7312
                    if (col) {
7313
                        const key = !this.isPivot && headers ? col.header || col.field : col.field;
7314
                        const rowData = source[row].ghostRecord ? source[row].recordRef : source[row];
7315
                        const value = this.isPivot ? rowData.aggregationValues.get(col.field)
7316
                        : resolveNestedPath(rowData, col.field);
7317
                        record[key] = formatters && col.formatter ? col.formatter(value, rowData) : value;
7318
                        if (columnData) {
7319
                            if (!record[key]) {
7320
                                record[key] = '';
7321
                            }
7322
                            record[key] = record[key].toString().concat('recordRow-' + row);
7323
                        }
7324
                    }
7325
                });
7326
            }
7327
            if (Object.keys(record).length) {
7328
                if (columnData) {
7329
                    if (!keys.length) {
7330
                        keys = Object.keys(columnData[0]);
7331
                    }
7332
                    for (const [key, value] of Object.entries(record)) {
7333
                        if (!keys.includes(key)) {
7334
                            keys.push(key);
7335
                        }
7336
                        let c: any = value;
7337
                        const rowNumber = +c.split('recordRow-')[1];
7338
                        c = c.split('recordRow-')[0];
7339
                        record[key] = c;
7340
                        const mergedObj = Object.assign(selectedData[rowNumber], record);
7341
                        selectedData[rowNumber] = mergedObj;
7342
                    }
7343
                } else {
7344
                    selectedData.push(record);
7345
                }
7346
            }
7347
            record = {};
7348
        }
7349

7350
        if (keys.length) {
7351
            keysAndData.push(selectedData);
7352
            keysAndData.push(keys);
7353
            return keysAndData;
7354
        } else {
7355
            return selectedData;
7356
        }
7357
    }
7358

7359
    protected getSelectableColumnsAt(index) {
7360
        if (this.hasColumnLayouts) {
7361
            const visibleLayoutColumns = this.visibleColumns
7362
                .filter(col => col.columnLayout)
7363
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
7364
            const colLayout = visibleLayoutColumns[index];
7365
            return colLayout ? colLayout.children.toArray() : [];
7366
        } else {
7367
            const visibleColumns = this.visibleColumns
7368
                .filter(col => !col.columnGroup)
7369
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
7370
            return [visibleColumns[index]];
7371
        }
7372
    }
7373

7374
    protected autoSizeColumnsInView() {
7375
        if (!this.hasColumnsToAutosize) return;
7376
        const vState = this.headerContainer.state;
7377
        let colResized = false;
7378
        const unpinnedInView = this.headerContainer.igxGridForOf.slice(vState.startIndex, vState.startIndex + vState.chunkSize).flatMap(x => x.columnGroup ? x.allChildren : x);
7379
        const columnsInView = this.pinnedColumns.concat(unpinnedInView);
7380
        for (const col of columnsInView) {
7381
            if (!col.autoSize && col.headerCell) {
7382
                const cellsContentWidths = [];
7383
                if (col._cells.length !== this.rowList.length) {
7384
                    this.rowList.forEach(x => x.cdr.detectChanges());
7385
                }
7386
                const cells = this._dataRowList.map(x => x.cells.find(c => c.column === col));
7387
                cells.forEach((cell) => cellsContentWidths.push(cell?.nativeElement?.offsetWidth || 0));
7388
                const header = this.headerCellList.find(x => x.column === col);
7389
                cellsContentWidths.push(header.nativeElement.offsetWidth);
7390
                const max = Math.max(...cellsContentWidths);
7391
                if (max === 0) {
7392
                    // cells not in DOM yet...
7393
                    continue;
7394
                }
7395
                let maxSize = Math.ceil(Math.max(...cellsContentWidths)) + 1;
7396
                if (col.maxWidth && maxSize > col.maxWidthPx) {
7397
                    maxSize = col.maxWidthPx;
7398
                } else if (maxSize < col.minWidthPx) {
7399
                    maxSize = col.minWidthPx;
7400
                }
7401
                col.autoSize = maxSize;
7402
                col.resetCaches();
7403
                colResized = true;
7404
            }
7405
        }
7406
        if (colResized) {
7407
            this.resetCachedWidths();
7408
            this.cdr.detectChanges();
7409
        }
7410
    }
7411

7412
    protected extractDataFromColumnsSelection(source: any[], formatters = false, headers = false): any[] {
7413
        let record = {};
7414
        const selectedData = [];
7415
        const selectedColumns = this.selectedColumns();
7416
        if (selectedColumns.length === 0) {
7417
            return [];
7418
        }
7419

7420
        for (const data of source) {
7421
            selectedColumns.forEach((col) => {
7422
                const key = headers ? col.header || col.field : col.field;
7423
                record[key] = formatters && col.formatter ? col.formatter(data[col.field], data)
7424
                    : data[col.field];
7425
            });
7426

7427
            if (Object.keys(record).length) {
7428
                selectedData.push(record);
7429
            }
7430
            record = {};
7431
        }
7432
        return selectedData;
7433
    }
7434

7435
    /**
7436
     * @hidden
7437
     */
7438
    protected initPinning() {
7439
        const pinnedColumns = [];
7440
        const unpinnedColumns = [];
7441

7442
        this.calculateGridWidth();
7443
        this.resetCaches();
7444
        // When a column is a group or is inside a group, pin all related.
7445
        this._pinnedColumns.forEach(col => {
7446
            if (col.parent) {
7447
                col.parent.pinned = true;
7448
            }
7449
            if (col.columnGroup) {
7450
                col.children.forEach(child => child.pinned = true);
7451
            }
7452
        });
7453

7454
        // Make sure we don't exceed unpinned area min width and get pinned and unpinned col collections.
7455
        // We take into account top level columns (top level groups and non groups).
7456
        // If top level is unpinned the pinning handles all children to be unpinned as well.
7457
        for (const column of this._columns) {
7458
            if (column.pinned && !column.parent) {
7459
                pinnedColumns.push(column);
7460
            } else if (column.pinned && column.parent) {
7461
                if (column.topLevelParent.pinned) {
7462
                    pinnedColumns.push(column);
7463
                } else {
7464
                    column.pinned = false;
7465
                    unpinnedColumns.push(column);
7466
                }
7467
            } else {
7468
                unpinnedColumns.push(column);
7469
            }
7470
        }
7471

7472
        // Assign the applicable collections.
7473
        this._pinnedColumns = pinnedColumns;
7474
        this._unpinnedColumns = unpinnedColumns;
7475
        this.notifyChanges();
7476
    }
7477

7478
    /**
7479
     * @hidden
7480
     */
7481
    protected scrollTo(row: any | number, column: any | number, inCollection = this._filteredSortedUnpinnedData): void {
7482
        let delayScrolling = false;
7483

7484
        if (this.paginator && typeof (row) !== 'number') {
7485
            const rowIndex = inCollection.indexOf(row);
7486
            const page = Math.floor(rowIndex / this.paginator.perPage);
7487

7488
            if (this.paginator.page !== page) {
7489
                delayScrolling = true;
7490
                this.paginator.page = page;
7491
            }
7492
        }
7493

7494
        if (delayScrolling) {
7495
            this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
7496
                this.scrollDirective(this.verticalScrollContainer,
7497
                    typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
7498
            });
7499
        } else {
7500
            this.scrollDirective(this.verticalScrollContainer,
7501
                typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
7502
        }
7503

7504
        this.scrollToHorizontally(column);
7505
    }
7506

7507
    /**
7508
     * @hidden
7509
     */
7510
    protected scrollToHorizontally(column: any | number) {
7511
        let columnIndex = typeof column === 'number' ? column : this.getColumnByName(column).visibleIndex;
7512
        const scrollRow = this.rowList.find(r => !!r.virtDirRow);
7513
        const virtDir = scrollRow ? scrollRow.virtDirRow : null;
7514
        if (this.isPinningToStart && this.pinnedColumns.length) {
7515
            if (columnIndex >= this.pinnedColumns.length) {
7516
                columnIndex -= this.pinnedColumns.length;
7517
                this.scrollDirective(virtDir, columnIndex);
7518
            }
7519
        } else {
7520
            this.scrollDirective(virtDir, columnIndex);
7521
        }
7522
    }
7523

7524
    /**
7525
     * @hidden
7526
     */
7527
    protected scrollDirective(directive: IgxGridForOfDirective<any>, goal: number): void {
7528
        if (!directive) {
7529
            return;
7530
        }
7531
        directive.scrollTo(goal);
7532
    }
7533

7534

7535
    /**
7536
     * @hidden
7537
     */
7538
    protected getColumnWidthSum(): number {
7539
        let colSum = 0;
7540
        const cols = this.hasColumnLayouts ?
7541
            this.visibleColumns.filter(x => x.columnLayout) : this.visibleColumns.filter(x => !x.columnGroup);
7542
        cols.forEach((item) => {
7543
            colSum += parseInt((item.calcWidth || item.defaultWidth), 10) || this.minColumnWidth;
7544
        });
7545
        if (!colSum) {
7546
            return null;
7547
        }
7548
        this.cdr.detectChanges();
7549
        colSum += this.featureColumnsWidth();
7550
        return colSum;
7551
    }
7552

7553
    /**
7554
     * Notify changes, reset cache and populateVisibleIndexes.
7555
     *
7556
     * @hidden
7557
     */
7558
    private _columnsReordered(column: IgxColumnComponent) {
7559
        this.notifyChanges();
7560
        if (this.hasColumnLayouts) {
7561
            this._columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
7562
        }
7563
        // after reordering is done reset cached column collections.
7564
        this.resetColumnCollections();
7565
        column.resetCaches();
7566
    }
7567

7568
    protected buildDataView(_data: any[]) {
7569
        this._dataView = this.isRowPinningToTop ?
7570
            [...this.pinnedDataView, ...this.unpinnedDataView] :
7571
            [...this.unpinnedDataView, ...this.pinnedDataView];
7572
    }
7573

7574
    private _applyWidthHostBinding() {
7575
        let width = this._width;
7576
        if (width === null) {
7577
            let currentWidth = this.calcWidth;
7578
            if (this.hasVerticalScroll()) {
7579
                currentWidth += this.scrollSize;
7580
            }
7581
            width = currentWidth + 'px';
7582
            this.resetCaches();
7583
        }
7584
        this._hostWidth = width;
7585
        this.cdr.markForCheck();
7586
    }
7587

7588
    protected verticalScrollHandler(event) {
7589
        this.verticalScrollContainer.onScroll(event);
7590
        this.disableTransitions = true;
7591

7592
        this.zone.run(() => {
7593
            this.zone.onStable.pipe(first()).subscribe(() => {
7594
                this.verticalScrollContainer.chunkLoad.emit(this.verticalScrollContainer.state);
7595
                if (this.rowEditable) {
7596
                    this.changeRowEditingOverlayStateOnScroll(this.crudService.rowInEditMode);
7597
                }
7598
            });
7599
        });
7600
        this.disableTransitions = false;
7601

7602
        this.hideOverlays();
7603
        this.actionStrip?.hide();
7604
        if (this.actionStrip) {
7605
            this.actionStrip.context = null;
7606
        }
7607
        const args: IGridScrollEventArgs = {
7608
            direction: 'vertical',
7609
            event,
7610
            scrollPosition: this.verticalScrollContainer.scrollPosition
7611
        };
7612
        this.gridScroll.emit(args);
7613
    }
7614

7615
    protected horizontalScrollHandler(event) {
7616
        const scrollLeft = event.target.scrollLeft;
7617
        this.headerContainer.onHScroll(scrollLeft);
7618
        this._horizontalForOfs.forEach(vfor => vfor.onHScroll(scrollLeft));
7619
        this.cdr.markForCheck();
7620

7621
        this.zone.run(() => {
7622
            this.zone.onStable.pipe(first()).subscribe(() => {
7623
                this.parentVirtDir.chunkLoad.emit(this.headerContainer.state);
7624
                requestAnimationFrame(() => {
7625
                    this.autoSizeColumnsInView();
7626
                });
7627
            });
7628
        });
7629
        if(!this.navigation.isColumnFullyVisible(this.navigation.lastColumnIndex)) {
7630
            this.hideOverlays();
7631
        }
7632
        const args: IGridScrollEventArgs = { direction: 'horizontal', event, scrollPosition: this.headerContainer.scrollPosition };
7633
        this.gridScroll.emit(args);
7634
    }
7635

7636
    private executeCallback(rowIndex, visibleColIndex = -1, cb: (args: any) => void = null) {
7637
        if (!cb) {
7638
            return;
7639
        }
7640
        let row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
7641
        if (!row) {
7642
            if ((this as any).totalItemCount) {
7643
                this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
7644
                    this.cdr.detectChanges();
7645
                    row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
7646
                    const cbArgs = this.getNavigationArguments(row, visibleColIndex);
7647
                    cb(cbArgs);
7648
                });
7649
            }
7650
            const dataViewIndex = this._getDataViewIndex(rowIndex);
7651
            if (this.dataView[dataViewIndex].detailsData) {
7652
                this.navigation.setActiveNode({ row: rowIndex });
7653
                this.cdr.detectChanges();
7654
            }
7655

7656
            return;
7657
        }
7658
        const args = this.getNavigationArguments(row, visibleColIndex);
7659
        cb(args);
7660
    }
7661

7662
    private getNavigationArguments(row, visibleColIndex) {
7663
        let targetType: GridKeydownTargetType; let target;
7664
        switch (row.nativeElement.tagName.toLowerCase()) {
7665
            case 'igx-grid-groupby-row':
7666
                targetType = 'groupRow';
7667
                target = row;
7668
                break;
7669
            case 'igx-grid-summary-row':
7670
                targetType = 'summaryCell';
7671
                target = visibleColIndex !== -1 ?
7672
                    row.summaryCells.find(c => c.visibleColumnIndex === visibleColIndex) : row.summaryCells.first;
7673
                break;
7674
            case 'igx-child-grid-row':
7675
                targetType = 'hierarchicalRow';
7676
                target = row;
7677
                break;
7678
            default:
7679
                targetType = 'dataCell';
7680
                target = visibleColIndex !== -1 ? row.cells.find(c => c.visibleColumnIndex === visibleColIndex) : row.cells.first;
7681
                break;
7682
        }
7683
        return { targetType, target };
7684
    }
7685

7686
    private getNextDataRowIndex(currentRowIndex, previous = false): number {
7687
        const resolvedIndex = this._getDataViewIndex(currentRowIndex);
7688
        if (currentRowIndex < 0 || (currentRowIndex === 0 && previous) || (resolvedIndex >= this.dataView.length - 1 && !previous)) {
7689
            return currentRowIndex;
7690
        }
7691
        // find next/prev record that is editable.
7692
        const nextRowIndex = previous ? this.findPrevEditableDataRowIndex(currentRowIndex) :
7693
            this.dataView.findIndex((rec, index) =>
7694
                index > resolvedIndex && this.isEditableDataRecordAtIndex(index));
7695
        const nextDataIndex = this.getDataIndex(nextRowIndex);
7696
        return nextDataIndex !== -1 ? nextDataIndex : currentRowIndex;
7697
    }
7698

7699
    /**
7700
     * Returns the previous editable row index or -1 if no such row is found.
7701
     *
7702
     * @param currentIndex The index of the current editable record.
7703
     */
7704
    private findPrevEditableDataRowIndex(currentIndex): number {
7705
        let i = this.dataView.length;
7706
        const resolvedIndex = this._getDataViewIndex(currentIndex);
7707
        while (i--) {
7708
            if (i < resolvedIndex && this.isEditableDataRecordAtIndex(i)) {
7709
                return i;
7710
            }
7711
        }
7712
        return -1;
7713
    }
7714

7715

7716
    /**
7717
     * Returns if the record at the specified data view index is a an editable data record.
7718
     * If record is group rec, summary rec, child rec, ghost rec. etc. it is not editable.
7719
     *
7720
     * @param dataViewIndex The index of that record in the data view.
7721
     *
7722
     */
7723
    // TODO: Consider moving it into CRUD
7724
    private isEditableDataRecordAtIndex(dataViewIndex) {
7725
        const rec = this.dataView[dataViewIndex];
7726
        return !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData &&
7727
            !this.isGhostRecordAtIndex(dataViewIndex);
7728
    }
7729

7730
    /**
7731
     * Returns if the record at the specified data view index is a ghost.
7732
     * If record is pinned but is not in pinned area then it is a ghost record.
7733
     *
7734
     * @param dataViewIndex The index of that record in the data view.
7735
     */
7736
    private isGhostRecordAtIndex(dataViewIndex) {
7737
        const isPinned = this.isRecordPinned(this.dataView[dataViewIndex]);
7738
        const isInPinnedArea = this.isRecordPinnedByViewIndex(dataViewIndex);
7739
        return isPinned && !isInPinnedArea;
7740
    }
7741

7742
    private isValidPosition(rowIndex, colIndex): boolean {
7743
        const rows = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).length;
7744
        const cols = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0 && !col.hidden).length;
7745
        if (rows < 1 || cols < 1) {
7746
            return false;
7747
        }
7748
        if (rowIndex > -1 && rowIndex < this.dataView.length &&
7749
            colIndex > - 1 && colIndex <= Math.max(...this.visibleColumns.map(c => c.visibleIndex))) {
7750
            return true;
7751
        }
7752
        return false;
7753
    }
7754

7755
    private find(text: string, increment: number, caseSensitive?: boolean, exactMatch?: boolean, scroll?: boolean, endEdit = true) {
7756
        if (!this.rowList) {
7757
            return 0;
7758
        }
7759

7760
        if (endEdit) {
7761
            this.crudService.endEdit(false);
7762
        }
7763

7764
        if (!text) {
7765
            this.clearSearch();
7766
            return 0;
7767
        }
7768

7769
        const caseSensitiveResolved = caseSensitive ? true : false;
7770
        const exactMatchResolved = exactMatch ? true : false;
7771
        let rebuildCache = false;
7772

7773
        if (this.lastSearchInfo.searchText !== text ||
7774
            this.lastSearchInfo.caseSensitive !== caseSensitiveResolved ||
7775
            this.lastSearchInfo.exactMatch !== exactMatchResolved) {
7776
            this.lastSearchInfo = {
7777
                searchText: text,
7778
                activeMatchIndex: 0,
7779
                caseSensitive: caseSensitiveResolved,
7780
                exactMatch: exactMatchResolved,
7781
                matchInfoCache: []
7782
            };
7783

7784
            rebuildCache = true;
7785
        } else {
7786
            this.lastSearchInfo.activeMatchIndex += increment;
7787
        }
7788

7789
        if (rebuildCache) {
7790
            this.rowList.forEach((row) => {
7791
                if (row.cells) {
7792
                    row.cells.forEach((c: IgxGridCellComponent) => {
7793
                        c.highlightText(text, caseSensitiveResolved, exactMatchResolved);
7794
                    });
7795
                }
7796
            });
7797

7798
            this.rebuildMatchCache();
7799
        }
7800

7801
        if (this.lastSearchInfo.activeMatchIndex >= this.lastSearchInfo.matchInfoCache.length) {
7802
            this.lastSearchInfo.activeMatchIndex = 0;
7803
        } else if (this.lastSearchInfo.activeMatchIndex < 0) {
7804
            this.lastSearchInfo.activeMatchIndex = this.lastSearchInfo.matchInfoCache.length - 1;
7805
        }
7806

7807
        if (this.lastSearchInfo.matchInfoCache.length) {
7808
            const matchInfo = this.lastSearchInfo.matchInfoCache[this.lastSearchInfo.activeMatchIndex];
7809
            this.lastSearchInfo = { ...this.lastSearchInfo };
7810

7811
            if (scroll !== false) {
7812
                this.scrollTo(matchInfo.row, matchInfo.column);
7813
            }
7814

7815
            IgxTextHighlightDirective.setActiveHighlight(this.id, {
7816
                column: matchInfo.column,
7817
                row: matchInfo.row,
7818
                index: matchInfo.index,
7819
                metadata: matchInfo.metadata,
7820
            });
7821

7822
        } else {
7823
            IgxTextHighlightDirective.clearActiveHighlight(this.id);
7824
        }
7825

7826
        return this.lastSearchInfo.matchInfoCache.length;
7827
    }
7828

7829
    private rebuildMatchCache() {
7830
        this.lastSearchInfo.matchInfoCache = [];
7831

7832
        const caseSensitive = this.lastSearchInfo.caseSensitive;
7833
        const exactMatch = this.lastSearchInfo.exactMatch;
7834
        const searchText = caseSensitive ? this.lastSearchInfo.searchText : this.lastSearchInfo.searchText.toLowerCase();
7835
        const data = this.filteredSortedData;
7836
        const columnItems = this.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex);
7837
        data.forEach((dataRow, rowIndex) => {
7838
            columnItems.forEach((c) => {
7839
                const pipeArgs = this.getColumnByName(c.field).pipeArgs;
7840
                const value = c.formatter ? c.formatter(resolveNestedPath(dataRow, c.field), dataRow) :
7841
                    c.dataType === 'number' ? formatNumber(resolveNestedPath(dataRow, c.field), this.locale, pipeArgs.digitsInfo) :
7842
                        c.dataType === 'date'
7843
                            ? formatDate(resolveNestedPath(dataRow, c.field), pipeArgs.format, this.locale, pipeArgs.timezone)
7844
                            : resolveNestedPath(dataRow, c.field);
7845
                if (value !== undefined && value !== null && c.searchable) {
7846
                    let searchValue = caseSensitive ? String(value) : String(value).toLowerCase();
7847

7848
                    if (exactMatch) {
7849
                        if (searchValue === searchText) {
7850
                            const metadata = new Map<string, any>();
7851
                            metadata.set('pinned', this.isRecordPinnedByIndex(rowIndex));
7852
                            this.lastSearchInfo.matchInfoCache.push({
7853
                                row: dataRow,
7854
                                column: c.field,
7855
                                index: 0,
7856
                                metadata,
7857
                            });
7858
                        }
7859
                    } else {
7860
                        let occurenceIndex = 0;
7861
                        let searchIndex = searchValue.indexOf(searchText);
7862

7863
                        while (searchIndex !== -1) {
7864
                            const metadata = new Map<string, any>();
7865
                            metadata.set('pinned', this.isRecordPinnedByIndex(rowIndex));
7866
                            this.lastSearchInfo.matchInfoCache.push({
7867
                                row: dataRow,
7868
                                column: c.field,
7869
                                index: occurenceIndex++,
7870
                                metadata,
7871
                            });
7872

7873
                            searchValue = searchValue.substring(searchIndex + searchText.length);
7874
                            searchIndex = searchValue.indexOf(searchText);
7875
                        }
7876
                    }
7877
                }
7878
            });
7879
        });
7880
    }
7881

7882
    // TODO: About to Move to CRUD
7883
    private configureRowEditingOverlay(rowID: any, useOuter = false) {
7884
        let settings = this.rowEditSettings;
7885
        const overlay = this.overlayService.getOverlayById(this.rowEditingOverlay.overlayId);
7886
        if (overlay) {
7887
            settings = overlay.settings;
7888
        }
7889
        settings.outlet = useOuter ? this.parentRowOutletDirective : this.rowOutletDirective;
7890
        this.rowEditPositioningStrategy.settings.container = this.tbody.nativeElement;
7891
        const pinned = this._pinnedRecordIDs.indexOf(rowID) !== -1;
7892
        const targetRow = !pinned ?
7893
            this.gridAPI.get_row_by_key(rowID) as IgxRowDirective
7894
            : this.pinnedRows.find(x => x.key === rowID) as IgxRowDirective;
7895
        if (!targetRow) {
7896
            return;
7897
        }
7898
        settings.target = targetRow.element.nativeElement;
7899
        this.toggleRowEditingOverlay(true);
7900
    }
7901
}
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