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

IgniteUI / igniteui-angular / 6405949148

04 Oct 2023 12:27PM CUT coverage: 92.26% (+0.02%) from 92.238%
6405949148

push

github

web-flow
chore(grids): bump watermark version (#13435)

Co-authored-by: Konstantin Dinev <kdinev@mail.bw.edu>

15308 of 17987 branches covered (0.0%)

26844 of 29096 relevant lines covered (92.26%)

29550.57 hits per line

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

92.56
/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 } 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/density';
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,351✔
81
    IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandedIndicatorDirective,
134,213✔
82
    IgxHeaderCollapsedIndicatorDirective, IgxExcelStyleHeaderIconDirective, IgxSortAscendingHeaderIconDirective,
83
    IgxSortDescendingHeaderIconDirective, IgxSortHeaderIconDirective
68,138✔
84
} from './grid.directives';
85
import {
86
    GridKeydownTargetType,
87
    GridSelectionMode,
83,983✔
88
    GridSummaryPosition,
89
    GridSummaryCalculationMode,
90
    FilterMode,
48,022✔
91
    ColumnPinningPosition,
92
    RowPinningPosition,
93
    GridPagingMode,
1!
94
    GridValidationTrigger
1✔
95
} from './common/enums';
1✔
96
import {
97
    IGridCellEventArgs,
98
    IRowSelectionEventArgs,
99
    IPinColumnEventArgs,
100
    IGridEditEventArgs,
247✔
101
    IRowDataEventArgs,
102
    IColumnResizeEventArgs,
103
    IColumnMovingStartEventArgs,
104
    IColumnMovingEventArgs,
3✔
105
    IColumnMovingEndEventArgs,
106
    IGridKeydownEventArgs,
107
    IRowDragStartEventArgs,
1,229✔
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,
122
    IColumnVisibilityChangingEventArgs,
123
    IPinColumnCancellableEventArgs
1✔
124
} from './common/events';
125
import { IgxAdvancedFilteringDialogComponent } from './filtering/advanced-filtering/advanced-filtering-dialog.component';
126
import {
127
    ColumnType,
10,392✔
128
    GridServiceType,
129
    GridType,
130
    IGridFormGroupCreatedEventArgs,
131
    IGridValidationStatusEventArgs,
28,683✔
132
    IgxGridEmptyTemplateContext,
133
    IgxGridHeaderTemplateContext,
134
    IgxGridRowDragGhostContext,
135
    IgxGridRowEditActionsTemplateContext,
426✔
136
    IgxGridRowEditTemplateContext,
137
    IgxGridRowEditTextTemplateContext,
138
    IgxGridRowTemplateContext,
139
    IgxGridTemplateContext,
27,944✔
140
    IgxHeadSelectorTemplateContext,
141
    IgxRowSelectorTemplateContext,
142
    IGX_GRID_SERVICE_BASE,
143
    ISizeInfo,
1,364✔
144
    RowType,
145
    IPinningConfig
146
} from './common/grid.interface';
4,357✔
147
import { DropPosition } from './moving/moving.service';
148
import { IgxHeadSelectorDirective, IgxRowSelectorDirective } from './selection/row-selectors';
149
import { IgxColumnComponent } from './columns/column.component';
150
import { IgxColumnGroupComponent } from './columns/column-group.component';
151
import { IgxRowDragGhostDirective, IgxDragIndicatorIconDirective } from './row-drag.directive';
152
import { IgxSnackbarComponent } from '../snackbar/snackbar.component';
153
import { v4 as uuidv4 } from 'uuid';
154
import { IgxActionStripComponent } from '../action-strip/action-strip.component';
155
import { IgxGridRowComponent } from './grid/grid-row.component';
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';
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';
1✔
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';
346✔
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/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';
175
import { IgxGridValidationService } from './grid/grid-validation.service';
176

177
IgcTrialWatermark.register();
178

179
interface IMatchInfoCache {
180
    row: any;
181
    index: number;
1✔
182
    column: string;
183
    metadata: Map<string, boolean>;
184
}
4,707✔
185

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

196
@Directive()
197
export abstract class IgxGridBaseDirective extends DisplayDensityBase implements GridType,
198
    OnInit, DoCheck, OnDestroy, AfterContentInit, AfterViewInit {
199

200
    /**
201
     * Gets/Sets the display time for the row adding snackbar notification.
1✔
202
     *
203
     * @remarks
204
     * By default it is 6000ms.
11,469✔
205
     */
206
    @Input()
207
    public snackbarDisplayTime = 6000;
208

209
    /**
210
     * Gets/Sets whether to auto-generate the columns.
211
     *
212
     * @remarks
213
     * The default value is false. When set to true, it will override all columns declared through code or in markup.
214
     * @example
215
     * ```html
216
     * <igx-grid [data]="Data" [autoGenerate]="true"></igx-grid>
217
     * ```
218
     */
219
    @Input()
220
    public autoGenerate = false;
821✔
221

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

239
    /**
821✔
240
     * Controls whether columns moving is enabled in the grid.
241
     *
242
     */
11,475✔
243
    @Input()
244
    public moving = false;
245

246
    /**
247
     * Gets/Sets a custom template when empty.
248
     *
249
     * @example
250
     * ```html
251
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [emptyGridTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
252
     * ```
253
     */
254
    @Input()
255
    public emptyGridTemplate: TemplateRef<void>;
256

257
    /**
258
     * Gets/Sets a custom template for adding row UI when grid is empty.
821✔
259
     *
260
     * @example
261
     * ```html
926✔
262
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [addRowEmptyTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
263
     * ```
264
     */
265
    @Input()
266
    public addRowEmptyTemplate: TemplateRef<void>;
267

268
    /**
269
     * Gets/Sets a custom template when loading.
270
     *
271
     * @example
272
     * ```html
273
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [loadingGridTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
274
     * ```
275
     */
276
    @Input()
277
    public loadingGridTemplate: TemplateRef<void>;
821✔
278

279
    /**
280
     * Get/Set IgxSummaryRow height
9,107✔
281
     */
282
    @Input()
283
    public set summaryRowHeight(value: number) {
284
        this._summaryRowHeight = value | 0;
285
        this.summaryService.summaryHeight = value;
286
        if (!this._init) {
287
            this.reflow();
288
        }
289
    }
290

291
    public get summaryRowHeight(): number {
292
        if (this.hasSummarizedColumns && this.rootSummariesEnabled) {
293
            return this._summaryRowHeight || this.summaryService.calcMaxSummaryHeight();
294
        }
295
        return 0;
296
    }
822✔
297

298
    /** @hidden @internal */
299
    public get hasColumnsToAutosize() {
956✔
300
        return this._columns.some(x => x.width === 'fit-content');
301
    }
302

303
    /**
304
     * Gets/Sets the data clone strategy of the grid when in edit mode.
305
     *
306
     * @example
307
     * ```html
308
     *  <igx-grid #grid [data]="localData" [dataCloneStrategy]="customCloneStrategy"></igx-grid>
309
     * ```
310
     */
311
    @Input()
312
    public get dataCloneStrategy(): IDataCloneStrategy {
313
        return this._dataCloneStrategy;
314
    }
315

827✔
316
    public set dataCloneStrategy(strategy: IDataCloneStrategy) {
317
        if (strategy) {
318
            this._dataCloneStrategy = strategy;
879✔
319
            this._transactions.cloneStrategy = strategy;
320
        }
321
    }
322

323
    /**
324
     * Controls the copy behavior of the grid.
325
     */
326
    @Input()
327
    public clipboardOptions = {
328
        /**
329
         * Enables/disables the copy behavior
330
         */
331
        enabled: true,
332
        /**
333
         * Include the columns headers in the clipboard output.
334
         */
827✔
335
        copyHeaders: true,
336
        /**
337
         * Apply the columns formatters (if any) on the data in the clipboard output.
23,154✔
338
         */
339
        copyFormatters: true,
340
        /**
341
         * The separator used for formatting the copy output. Defaults to `\t`.
342
         */
343
        separator: '\t'
344
    };
345

346
    /**
347
     * Emitted after filtering is performed.
348
     *
349
     * @remarks
350
     * Returns the filtering expressions tree of the column for which filtering was performed.
351
     * @example
352
     * ```html
353
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
827✔
354
     *              (filteringExpressionsTreeChange)="filteringExprTreeChange($event)"></igx-grid>
355
     * ```
356
     */
31,300✔
357
    @Output()
358
    public filteringExpressionsTreeChange = new EventEmitter<IFilteringExpressionsTree>();
359

2✔
360
    /**
361
     * Emitted after advanced filtering is performed.
362
     *
205,973✔
363
     * @remarks
3,880✔
364
     * Returns the advanced filtering expressions tree.
365
     * @example
205,973✔
366
     * ```html
367
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
368
     *           (advancedFilteringExpressionsTreeChange)="advancedFilteringExprTreeChange($event)"></igx-grid>
304✔
369
     * ```
370
     */
371
    @Output()
3✔
372
    public advancedFilteringExpressionsTreeChange = new EventEmitter<IFilteringExpressionsTree>();
373

374
    /**
106,343✔
375
     * Emitted when grid is scrolled horizontally/vertically.
376
     *
377
     * @example
899!
378
     * ```html
899✔
379
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
899✔
380
     *              (gridScroll)="onScroll($event)"></igx-grid>
492✔
381
     * ```
7✔
382
     */
7✔
383
    @Output()
7✔
384
    public gridScroll = new EventEmitter<IGridScrollEventArgs>();
385

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

404
    /**
94✔
405
     * Sets conditional style properties on the grid row element.
78✔
406
     * It accepts an object literal where the keys are
78✔
407
     * the style properties and the value is an expression to be evaluated.
78✔
408
     * ```typescript
409
     * styles = {
410
     *  background: 'yellow',
16✔
411
     *  color: (row: RowType) => row.selected : 'red': 'white'
412
     * }
94✔
413
     * ```
94✔
414
     * ```html
415
     * <igx-grid #grid [data]="Data" [rowStyles]="styles" [autoGenerate]="true"></igx-grid>
18✔
416
     * ```
417
     *
94✔
418
     * @memberof IgxColumnComponent
94✔
419
     */
94✔
420
    @Input()
421
    public rowStyles = null;
94✔
422

423
    /**
424
     * Gets/Sets the primary key.
1,206,915✔
425
     *
426
     * @example
427
     * ```html
3,895✔
428
     * <igx-grid #grid [data]="localData" [primaryKey]="'ProductID'" [autoGenerate]="true"></igx-grid>
3,890✔
429
     * ```
3,890✔
430
     */
3,890✔
431
    @WatchChanges()
3,890✔
432
    @Input()
3,890✔
433
    public primaryKey: any;
3,890✔
434

435
    /**
436
     * Gets/Sets a unique values strategy used by the Excel Style Filtering
437
     *
8,687✔
438
     * @remarks
439
     * Provides a callback for loading unique column values on demand.
440
     * If this property is provided, the unique values it generates will be used by the Excel Style Filtering.
2✔
441
     * @example
2✔
442
     * ```html
2✔
443
     * <igx-grid [data]="localData" [filterMode]="'excelStyleFilter'" [uniqueColumnValuesStrategy]="columnValuesStrategy"></igx-grid>
444
     * ```
445
     */
446
    @Input()
1,665,150✔
447
    public uniqueColumnValuesStrategy: (column: ColumnType,
448
        filteringExpressionsTree: IFilteringExpressionsTree,
449
        done: (values: any[]) => void) => void;
732✔
450

330✔
451
    /** @hidden @internal */
452
    @ContentChildren(IgxGridExcelStyleFilteringComponent, { read: IgxGridExcelStyleFilteringComponent, descendants: false })
453
    public excelStyleFilteringComponents: QueryList<IgxGridExcelStyleFilteringComponent>;
454

455
    /** @hidden @internal */
1,665,065✔
456
    public get excelStyleFilteringComponent() {
457
        return this.excelStyleFilteringComponents?.first;
458
    }
20!
459

20✔
460
    /** @hidden @internal */
461
    public get headerGroups() {
462
        return this.theadRow.groups;
463
    }
23,974✔
464

465
    /**
466
     * Emitted when a cell is clicked.
20✔
467
     *
20✔
468
     * @remarks
469
     * Returns the `IgxGridCell`.
470
     * @example
252,505✔
471
     * ```html
472
     * <igx-grid #grid (cellClick)="cellClick($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
473
     * ```
46✔
474
     */
46✔
475
    @Output()
476
    public cellClick = new EventEmitter<IGridCellEventArgs>();
477

10,067,367✔
478

479
    /**
480
     * Emitted when formGroup is created on edit of row/cell.
521✔
481
     *
34✔
482
     * @example
483
     * ```html
521✔
484
     * <igx-grid #grid (formGroupCreated)="formGroupCreated($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
521✔
485
     * ```
486
     */
487
    @Output()
34,697✔
488
    public formGroupCreated = new EventEmitter<IGridFormGroupCreatedEventArgs>();
489

490
    /**
2,955✔
491
     * Emitted when grid's validation status changes.
2,953✔
492
     *
2,953✔
493
     * @example
2,953✔
494
     * ```html
495
     * <igx-grid #grid (validationStatusChange)="validationStatusChange($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
496
     * ```
497
     */
31,306✔
498
    @Output()
499
    public validationStatusChange = new EventEmitter<IGridValidationStatusEventArgs>();
500

166,808✔
501
    /**
502
     * Emitted when a cell is selected.
503
     *
2,001!
504
     * @remarks
2,001✔
505
     *  Returns the `IgxGridCell`.
2,001✔
506
     * @example
2,001✔
507
     * ```html
508
     * <igx-grid #grid (selected)="onCellSelect($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
509
     * ```
510
     */
511
    @Output()
×
512
    public selected = new EventEmitter<IGridCellEventArgs>();
513

514
    /**
1,160,123!
515
     *  Emitted when `IgxGridRowComponent` is selected.
516
     *
517
     * @example
×
518
     * ```html
×
519
     * <igx-grid #grid (rowSelectionChanging)="rowSelectionChanging($event)" [data]="localData" [autoGenerate]="true"></igx-grid>
520
     * ```
×
521
     */
522
    @Output()
523
    public rowSelectionChanging = new EventEmitter<IRowSelectionEventArgs>();
609✔
524

525
    /**
526
     *  Emitted when `IgxColumnComponent` is selected.
229✔
527
     *
229✔
528
     * @example
229✔
529
     * ```html
530
     * <igx-grid #grid (columnSelectionChanging)="columnSelectionChanging($event)" [data]="localData" [autoGenerate]="true"></igx-grid>
531
     * ```
×
532
     */
533
    @Output()
534
    public columnSelectionChanging = new EventEmitter<IColumnSelectionEventArgs>();
457✔
535

536
    /**
537
     * Emitted before `IgxColumnComponent` is pinned.
12!
538
     *
12✔
539
     * @remarks
12✔
540
     * The index at which to insert the column may be changed through the `insertAtIndex` property.
6✔
541
     * @example
542
     * ```typescript
543
     * public columnPinning(event) {
12✔
544
     *     if (event.column.field === "Name") {
545
     *       event.insertAtIndex = 0;
12✔
546
     *     }
547
     * }
548
     * ```
549
     */
75,908✔
550
    @Output()
551
    public columnPin = new EventEmitter<IPinColumnCancellableEventArgs>();
552

×
553
    /**
554
     * Emitted after `IgxColumnComponent` is pinned.
555
     *
195✔
556
     * @remarks
557
     * The index that the column is inserted at may be changed through the `insertAtIndex` property.
558
     * @example
2,291,143✔
559
     * ```typescript
560
     * public columnPinning(event) {
561
     *     if (event.column.field === "Name") {
123!
562
     *       event.insertAtIndex = 0;
123✔
563
     *     }
564
     * }
123✔
565
     * ```
566
     */
567
    @Output()
329,472✔
568
    public columnPinned = new EventEmitter<IPinColumnEventArgs>();
569

570
    /**
689✔
571
     * Emitted when cell enters edit mode.
678✔
572
     *
678✔
573
     * @remarks
678✔
574
     * This event is cancelable.
18✔
575
     * @example
576
     * ```html
678✔
577
     * <igx-grid #grid3 (cellEditEnter)="editStart($event)" [data]="data" [primaryKey]="'ProductID'">
678✔
578
     * </igx-grid>
678✔
579
     * ```
580
     */
581
    @Output()
582
    public cellEditEnter = new EventEmitter<IGridEditEventArgs>();
1,026✔
583

584
    /**
585
     * Emitted when cell exits edit mode.
71!
586
     *
71✔
587
     * @example
71✔
588
     * ```html
71✔
589
     * <igx-grid #grid3 (cellEditExit)="editExit($event)" [data]="data" [primaryKey]="'ProductID'">
5✔
590
     * </igx-grid>
591
     * ```
592
     */
593
    @Output()
594
    public cellEditExit = new EventEmitter<IGridEditDoneEventArgs>();
67,959✔
595

596
    /**
597
     * Emitted when cell has been edited.
168!
598
     *
599
     * @remarks
600
     * Event is fired after editing is completed, when the cell is exiting edit mode.
168✔
601
     * This event is cancelable.
168✔
602
     * @example
603
     * ```html
×
604
     * <igx-grid #grid3 (cellEdit)="editDone($event)" [data]="data" [primaryKey]="'ProductID'">
605
     * </igx-grid>
168✔
606
     * ```
1✔
607
     */
608
    @Output()
168✔
609
    public cellEdit = new EventEmitter<IGridEditEventArgs>();
610

611
    /**
25,742✔
612
     * Emitted after cell has been edited and editing has been committed.
613
     *
614
     * @example
18✔
615
     * ```html
18✔
616
     * <igx-grid #grid3 (cellEditDone)="editDone($event)" [data]="data" [primaryKey]="'ProductID'">
617
     * </igx-grid>
618
     * ```
224,513✔
619
     */
620
    @Output()
621
    public cellEditDone = new EventEmitter<IGridEditDoneEventArgs>();
63✔
622

63✔
623
    /**
25✔
624
     * Emitted when a row enters edit mode.
25✔
625
     *
25✔
626
     * @remarks
627
     * Emitted when [rowEditable]="true".
628
     * This event is cancelable.
629
     * @example
25,734✔
630
     * ```html
631
     * <igx-grid #grid3 (rowEditEnter)="editStart($event)" [primaryKey]="'ProductID'" [rowEditable]="true">
632
     * </igx-grid>
8✔
633
     * ```
8✔
634
     */
635
    @Output()
636
    public rowEditEnter = new EventEmitter<IGridEditEventArgs>();
37,290✔
637

638
    /**
639
     * Emitted when exiting edit mode for a row.
37✔
640
     *
641
     * @remarks
642
     * Emitted when [rowEditable]="true" & `endEdit(true)` is called.
38,795✔
643
     * Emitted when changing rows during edit mode, selecting an un-editable cell in the edited row,
644
     * performing paging operation, column resizing, pinning, moving or hitting `Done`
645
     * button inside of the rowEditingOverlay, or hitting the `Enter` key while editing a cell.
14✔
646
     * This event is cancelable.
647
     * @example
648
     * ```html
2✔
649
     * <igx-grid #grid3 (rowEdit)="editDone($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
2✔
650
     * </igx-grid>
651
     * ```
652
     */
22,433✔
653
    @Output()
654
    public rowEdit = new EventEmitter<IGridEditEventArgs>();
655

134!
656
    /**
657
     * Emitted after exiting edit mode for a row and editing has been committed.
658
     *
724✔
659
     * @remarks
660
     * Emitted when [rowEditable]="true" & `endEdit(true)` is called.
661
     * Emitted when changing rows during edit mode, selecting an un-editable cell in the edited row,
662
     * performing paging operation, column resizing, pinning, moving or hitting `Done`
16,883✔
663
     * button inside of the rowEditingOverlay, or hitting the `Enter` key while editing a cell.
664
     * @example
665
     * ```html
666
     * <igx-grid #grid3 (rowEditDone)="editDone($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
4,822✔
667
     * </igx-grid>
668
     * ```
669
     */
670
    @Output()
14,658✔
671
    public rowEditDone = new EventEmitter<IGridEditDoneEventArgs>();
672

673
    /**
674
     * Emitted when row editing is canceled.
675
     *
676
     * @remarks
2,720✔
677
     * Emits when [rowEditable]="true" & `endEdit(false)` is called.
2,720!
678
     * Emitted when changing hitting `Esc` key during cell editing and when click on the `Cancel` button
×
679
     * in the row editing overlay.
680
     * @example
2,720✔
681
     * ```html
2,720✔
682
     * <igx-grid #grid3 (rowEditExit)="editExit($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
2,720✔
683
     * </igx-grid>
684
     * ```
685
     */
686
    @Output()
687
    public rowEditExit = new EventEmitter<IGridEditDoneEventArgs>();
688

689
    /**
690
     * Emitted when a column is initialized.
691
     *
692
     * @remarks
693
     * Returns the column object.
10,679✔
694
     * @example
10,679!
695
     * ```html
×
696
     * <igx-grid #grid [data]="localData" (columnInit)="initColumns($event)" [autoGenerate]="true"></igx-grid>
697
     * ```
10,679✔
698
     */
83,410✔
699
    @Output()
92,973✔
700
    public columnInit = new EventEmitter<IgxColumnComponent>();
10,679✔
701

10,679✔
702
    /**
703
     * Emitted before sorting expressions are applied.
704
     *
705
     * @remarks
706
     * Returns an `ISortingEventArgs` object. `sortingExpressions` key holds the sorting expressions.
707
     * @example
708
     * ```html
709
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sorting)="sorting($event)"></igx-grid>
710
     * ```
711
     */
712
    @Output()
51,173✔
713
    public sorting = new EventEmitter<ISortingEventArgs>();
51,173✔
714

5,536✔
715
    /**
716
     * Emitted after sorting is completed.
224,727✔
717
     *
45,637✔
718
     * @remarks
45,637✔
719
     * Returns the sorting expression.
720
     * @example
721
     * ```html
2,653✔
722
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingDone)="sortingDone($event)"></igx-grid>
723
     * ```
724
     */
725
    @Output()
726
    public sortingDone = new EventEmitter<ISortingExpression | ISortingExpression[]>();
727

728
    /**
729
     * Emitted before filtering expressions are applied.
730
     *
731
     * @remarks
732
     * Returns an `IFilteringEventArgs` object. `filteringExpressions` key holds the filtering expressions for the column.
733
     * @example
734
     * ```html
735
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true" (filtering)="filtering($event)"></igx-grid>
736
     * ```
737
     */
1✔
738
    @Output()
739
    public filtering = new EventEmitter<IFilteringEventArgs>();
740

741
    /**
742
     * Emitted after filtering is performed through the UI.
743
     *
744
     * @remarks
1,859,867✔
745
     * Returns the filtering expressions tree of the column for which filtering was performed.
746
     * @example
747
     * ```html
748
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true" (filteringDone)="filteringDone($event)"></igx-grid>
749
     * ```
750
     */
751
    @Output()
565,219✔
752
    public filteringDone = new EventEmitter<IFilteringExpressionsTree>();
753

754
    /**
14,968✔
755
     * Emitted when a row is added.
756
     *
757
     * @remarks
758
     * Returns the data for the new `IgxGridRowComponent` object.
759
     * @example
760
     * ```html
761
     * <igx-grid #grid [data]="localData" (rowAdded)="rowAdded($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
762
     * ```
763
     */
764
    @Output()
765
    public rowAdded = new EventEmitter<IRowDataEventArgs>();
766

767
    /**
768
     * Emitted when a row is deleted.
769
     *
770
     * @remarks
1✔
771
     * Returns an `IRowDataEventArgs` object.
772
     * @example
773
     * ```html
774
     * <igx-grid #grid [data]="localData" (rowDeleted)="rowDeleted($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
775
     * ```
776
     */
4,132✔
777
    @Output()
778
    public rowDeleted = new EventEmitter<IRowDataEventArgs>();
779

780
    /**
781
     * Emmited when deleting a row.
782
     *
1✔
783
     * @remarks
784
     * This event is cancelable.
785
     * Returns an `IGridEditEventArgs` object.
786
     * @example
787
     * ```html
788
     * <igx-grid #grid [data]="localData" (rowDelete)="rowDelete($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
5,117✔
789
     * ```
40✔
790
     */
791
    @Output()
5,077✔
792
    public rowDelete = new EventEmitter<IGridEditEventArgs>();
793

794
    /**
795
     * Emmited just before the newly added row is commited.
796
     *
797
     * @remarks
798
     * This event is cancelable.
799
     * Returns an `IGridEditEventArgs` object.
4,720✔
800
     * @example
801
     * ```html
802
     * <igx-grid #grid [data]="localData" (rowAdd)="rowAdd($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2,395✔
803
     * ```
804
     */
805
    @Output()
806
    public rowAdd = new EventEmitter<IGridEditEventArgs>();
807

808
    /**
809
     * Emitted after column is resized.
810
     *
811
     * @remarks
812
     * Returns the `IgxColumnComponent` object's old and new width.
813
     * @example
814
     * ```html
815
     * <igx-grid #grid [data]="localData" (columnResized)="resizing($event)" [autoGenerate]="true"></igx-grid>
816
     * ```
817
     */
818
    @Output()
822✔
819
    public columnResized = new EventEmitter<IColumnResizeEventArgs>();
820

821
    /**
822
     * Emitted when a cell is right clicked.
823
     *
824
     * @remarks
26✔
825
     * Returns the `IgxGridCell` object.
21✔
826
     * ```html
4!
827
     * <igx-grid #grid [data]="localData" (contextMenu)="contextMenu($event)" [autoGenerate]="true"></igx-grid>
828
     * ```
829
     */
830
    @Output()
831
    public contextMenu = new EventEmitter<IGridCellEventArgs>();
832

47✔
833
    /**
55✔
834
     * Emitted when a cell is double clicked.
6!
835
     *
836
     * @remarks
837
     * Returns the `IgxGridCell` object.
838
     * @example
839
     * ```html
840
     * <igx-grid #grid [data]="localData" (doubleClick)="dblClick($event)" [autoGenerate]="true"></igx-grid>
841
     * ```
141✔
842
     */
843
    @Output()
844
    public doubleClick = new EventEmitter<IGridCellEventArgs>();
845

107,662✔
846
    /**
107,662✔
847
     * Emitted before column visibility is changed.
92,969✔
848
     *
849
     * @remarks
14,693✔
850
     * Args: { column: any, newValue: boolean }
851
     * @example
852
     * ```html
853
     * <igx-grid (columnVisibilityChanging)="visibilityChanging($event)"></igx-grid>
854
     * ```
855
     */
4,720✔
856
    @Output()
4,720✔
857
    public columnVisibilityChanging = new EventEmitter<IColumnVisibilityChangingEventArgs>();
858

859
    /**
250,115✔
860
     * Emitted after column visibility is changed.
861
     *
862
     * @remarks
434✔
863
     * Args: { column: IgxColumnComponent, newValue: boolean }
434✔
864
     * @example
434✔
865
     * ```html
866
     * <igx-grid (columnVisibilityChanged)="visibilityChanged($event)"></igx-grid>
867
     * ```
868
     */
869
    @Output()
870
    public columnVisibilityChanged = new EventEmitter<IColumnVisibilityChangedEventArgs>();
26,614✔
871

2,549✔
872
    /**
1,364✔
873
     * Emitted when column moving starts.
15,270✔
874
     *
875
     * @remarks
26,614✔
876
     * Returns the moved `IgxColumnComponent` object.
877
     * @example
878
     * ```html
879
     * <igx-grid (columnMovingStart)="movingStart($event)"></igx-grid>
880
     * ```
881
     */
882
    @Output()
883
    public columnMovingStart = new EventEmitter<IColumnMovingStartEventArgs>();
884

885
    /**
886
     * Emitted during the column moving operation.
23,188✔
887
     *
888
     * @remarks
889
     * Returns the source and target `IgxColumnComponent` objects. This event is cancelable.
890
     * @example
891
     * ```html
892
     * <igx-grid (columnMoving)="moving($event)"></igx-grid>
100✔
893
     * ```
894
     */
895
    @Output()
406,444✔
896
    public columnMoving = new EventEmitter<IColumnMovingEventArgs>();
897

898
    /**
156✔
899
     * Emitted when column moving ends.
145✔
900
     *
145✔
901
     * @remarks
145✔
902
     * Returns the source and target `IgxColumnComponent` objects.
145✔
903
     * @example
904
     * ```html
905
     * <igx-grid (columnMovingEnd)="movingEnds($event)"></igx-grid>
906
     * ```
907
     */
908
    @Output()
909
    public columnMovingEnd = new EventEmitter<IColumnMovingEndEventArgs>();
5,562,552✔
910

1,923✔
911
    /**
912
     * Emitted when keydown is triggered over element inside grid's body.
5,560,629✔
913
     *
914
     * @remarks
915
     * This event is fired only if the key combination is supported in the grid.
916
     * Return the target type, target object and the original event. This event is cancelable.
917
     * @example
918
     * ```html
×
919
     *  <igx-grid (gridKeydown)="customKeydown($event)"></igx-grid>
920
     * ```
921
     */
922
    @Output()
923
    public gridKeydown = new EventEmitter<IGridKeydownEventArgs>();
924

8✔
925
    /**
6✔
926
     * Emitted when start dragging a row.
927
     *
2✔
928
     * @remarks
2✔
929
     * Return the dragged row.
3✔
930
     */
2✔
931
    @Output()
932
    public rowDragStart = new EventEmitter<IRowDragStartEventArgs>();
933

907,033✔
934
    /**
935
     * Emitted when dropping a row.
936
     *
33✔
937
     * @remarks
938
     * Return the dropped row.
33✔
939
     */
33✔
940
    @Output()
941
    public rowDragEnd = new EventEmitter<IRowDragEndEventArgs>();
942

943
    /**
227,750✔
944
     * Emitted when a copy operation is executed.
945
     *
946
     * @remarks
494✔
947
     * Fired only if copy behavior is enabled through the [`clipboardOptions`]{@link IgxGridBaseDirective#clipboardOptions}.
494✔
948
     */
72✔
949
    @Output()
72✔
950
    public gridCopy = new EventEmitter<IGridClipboardEvent>();
951

952
    /**
953
     * @hidden @internal
179,637✔
954
     */
955
    @Output()
956
    public expansionStatesChange = new EventEmitter<Map<any, boolean>>();
135✔
957

958
    /**
135✔
959
     * Emitted when the expanded state of a row gets changed.
135✔
960
     *
961
     * @example
962
     * ```html
963
     * <igx-grid [data]="employeeData" (rowToggle)="rowToggle($event)" [autoGenerate]="true"></igx-grid>
964
     * ```
965
     */
966
    @Output()
578✔
967
    public rowToggle = new EventEmitter<IRowToggleEventArgs>();
578✔
968

427✔
969
    /**
970
     * Emitted when the pinned state of a row is changed.
971
     *
972
     * @example
175✔
973
     * ```html
974
     * <igx-grid [data]="employeeData" (rowPinning)="rowPin($event)" [autoGenerate]="true"></igx-grid>
975
     * ```
976
     */
2,723,097✔
977
    @Output()
978
    public rowPinning = new EventEmitter<IPinRowEventArgs>();
979

980
    /**
981
     * Emitted when the pinned state of a row is changed.
982
     *
150,619✔
983
     * @example
984
     * ```html
985
     * <igx-grid [data]="employeeData" (rowPinned)="rowPin($event)" [autoGenerate]="true"></igx-grid>
986
     * ```
987
     */
988
    @Output()
62,237✔
989
    public rowPinned = new EventEmitter<IPinRowEventArgs>();
990

991
    /**
992
     * Emmited when the active node is changed.
993
     *
994
     * @example
995
     * ```
996
     * <igx-grid [data]="data" [autoGenerate]="true" (activeNodeChange)="activeNodeChange($event)"></igx-grid>
997
     * ```
998
     */
999
    @Output()
39,590✔
1000
    public activeNodeChange = new EventEmitter<IActiveNodeChangeEventArgs>();
1001

1002
    /**
1003
     * Emitted before sorting is performed.
1004
     *
1005
     * @remarks
1006
     * Returns the sorting expressions.
1007
     * @example
1008
     * ```html
1009
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingExpressionsChange)="sortingExprChange($event)"></igx-grid>
1010
     * ```
100,471✔
1011
     */
1012
    @Output()
1013
    public sortingExpressionsChange = new EventEmitter<ISortingExpression[]>();
1014

1015

1016
    /**
13,419✔
1017
     * Emitted when an export process is initiated by the user.
10,939✔
1018
     *
1019
     * @example
2,480✔
1020
     * ```typescript
422✔
1021
     * toolbarExporting(event: IGridToolbarExportEventArgs){
441✔
1022
     *     const toolbarExporting = event;
422✔
1023
     * }
1024
     * ```
2,480✔
1025
     */
77✔
1026
    @Output()
1027
    public toolbarExporting = new EventEmitter<IGridToolbarExportEventArgs>();
2,461✔
1028

2,461✔
1029
    /* End of toolbar related definitions */
1030

1031
    /**
1032
     * Emitted when making a range selection.
1033
     *
1034
     * @remarks
9,763✔
1035
     * Range selection can be made either through drag selection or through keyboard selection.
9,763✔
1036
     */
5,706✔
1037
    @Output()
1,064✔
1038
    public rangeSelected = new EventEmitter<GridSelectionRange>();
329✔
1039

1040
    /** Emitted after the ngAfterViewInit hook. At this point the grid exists in the DOM */
9,763✔
1041
    @Output()
359✔
1042
    public rendered = new EventEmitter<boolean>();
1043

9,763✔
1044
    /**
1045
     * @hidden @internal
1046
     */
1047
    @Output()
1048
    public localeChange = new EventEmitter<boolean>();
1049

36,672✔
1050
    /**
1051
     * Emitted before the grid's data view is changed because of a data operation, rebinding, etc.
1052
     *
1053
     * @example
1054
     * ```typescript
1055
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanging)='handleDataChangingEvent()'></igx-grid>
36,374✔
1056
     * ```
55✔
1057
     */
1058
    @Output()
36,319✔
1059
    public dataChanging = new EventEmitter<IForOfDataChangingEventArgs>();
183!
1060

1061
    /**
36,136✔
1062
     * Emitted after the grid's data view is changed because of a data operation, rebinding, etc.
462!
1063
     *
1064
     * @example
1065
     * ```typescript
1066
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanged)='handleDataChangedEvent()'></igx-grid>
1067
     * ```
1068
     */
1069
    @Output()
36,504✔
1070
    public dataChanged = new EventEmitter<any>();
1071

1072

1073
    /**
1074
     * @hidden @internal
1075
     */
36,367✔
1076
    @ViewChild(IgxSnackbarComponent)
1077
    public addRowSnackbar: IgxSnackbarComponent;
1078

1079
    /**
1080
     * @hidden @internal
1081
     */
39,522✔
1082
    @ViewChild(IgxGridColumnResizerComponent)
1083
    public resizeLine: IgxGridColumnResizerComponent;
1084

1085
    /**
1086
     * @hidden @internal
1087
     */
2,738✔
1088
    @ViewChild('loadingOverlay', { read: IgxToggleDirective, static: true })
1089
    public loadingOverlay: IgxToggleDirective;
1090

1091
    /**
1092
     * @hidden @internal
1093
     */
1094
    @ViewChild('igxLoadingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
219,736✔
1095
    public loadingOutlet: IgxOverlayOutletDirective;
1096

1097
    /**
1098
     * @hidden @internal
1099
     */
1100
    @ContentChildren(IgxColumnComponent, { read: IgxColumnComponent, descendants: true })
8,049✔
1101
    public columnList: QueryList<IgxColumnComponent> = new QueryList<IgxColumnComponent>();
1102

1103
    /** @hidden @internal */
1104
    @ContentChild(IgxActionStripComponent)
1105
    public actionStrip: IgxActionStripComponent;
1106

216,723✔
1107
    /**
1108
     * @hidden @internal
1109
     */
1110
    @ContentChild(IgxExcelStyleLoadingValuesTemplateDirective, { read: IgxExcelStyleLoadingValuesTemplateDirective, static: true })
1111
    public excelStyleLoadingValuesTemplateDirective: IgxExcelStyleLoadingValuesTemplateDirective;
1112

1113
    /** @hidden @internal */
1114
    @ViewChild('emptyFilteredGrid', { read: TemplateRef, static: true })
1115
    public emptyFilteredGridTemplate: TemplateRef<any>;
1116

1117
    /** @hidden @internal */
3,880✔
1118
    @ViewChild('defaultEmptyGrid', { read: TemplateRef, static: true })
3,880✔
1119
    public emptyGridDefaultTemplate: TemplateRef<any>;
3,880✔
1120

3,880✔
1121
    /**
3,880✔
1122
     * @hidden @internal
3,880✔
1123
     */
3,880✔
1124
    @ViewChild('defaultLoadingGrid', { read: TemplateRef, static: true })
3,880✔
1125
    public loadingGridDefaultTemplate: TemplateRef<any>;
3,880✔
1126

3,880✔
1127
    /**
3,880✔
1128
     * @hidden @internal
3,880✔
1129
     */
3,880✔
1130
    @ViewChild('scrollContainer', { read: IgxGridForOfDirective, static: true })
3,880✔
1131
    public parentVirtDir: IgxGridForOfDirective<any, any[]>;
3,880✔
1132

3,880✔
1133
    /**
3,880✔
1134
     * @hidden
3,880✔
1135
     * @internal
3,880✔
1136
     */
3,880✔
1137
    @ContentChildren(IgxHeadSelectorDirective, { read: TemplateRef, descendants: false })
3,880✔
1138
    public headSelectorsTemplates: QueryList<TemplateRef<IgxHeadSelectorTemplateContext>>;
3,880✔
1139

3,880✔
1140
    /**
3,880✔
1141
     * @hidden
3,880✔
1142
     * @internal
3,880✔
1143
     */
3,880✔
1144
    @ContentChildren(IgxRowSelectorDirective, { read: TemplateRef, descendants: false })
3,880✔
1145
    public rowSelectorsTemplates: QueryList<TemplateRef<IgxRowSelectorTemplateContext>>;
1146

1147
    /**
1148
     * @hidden
1149
     * @internal
1150
     */
1151
    @ContentChildren(IgxRowDragGhostDirective, { read: TemplateRef, descendants: false })
1152
    public dragGhostCustomTemplates: QueryList<TemplateRef<IgxGridRowDragGhostContext>>;
1153

1154

1155
    /**
1156
     * Gets the custom template, if any, used for row drag ghost.
1157
     */
1158
    @Input()
1159
    public get dragGhostCustomTemplate() {
1160
        return this._dragGhostCustomTemplate || this.dragGhostCustomTemplates?.first;
1161
    }
1162

3,880✔
1163
    /**
3,880✔
1164
     * Sets a custom template for the row drag ghost.
3,880✔
1165
     *```html
3,880✔
1166
     * <ng-template #template igxRowDragGhost>
3,880✔
1167
     *    <igx-icon>menu</igx-icon>
3,880✔
1168
     * </ng-template>
3,880✔
1169
     * ```
3,880✔
1170
     * ```typescript
3,880✔
1171
     * @ViewChild("'template'", {read: TemplateRef })
3,880✔
1172
     * public template: TemplateRef<any>;
3,880✔
1173
     * this.grid.dragGhostCustomTemplate = this.template;
3,880✔
1174
     * ```
3,880✔
1175
     */
3,880✔
1176
    public set dragGhostCustomTemplate(template: TemplateRef<IgxGridRowDragGhostContext>) {
3,880✔
1177
        this._dragGhostCustomTemplate = template;
3,880✔
1178
    }
3,880✔
1179

3,880✔
1180

3,880✔
1181
    /**
3,880✔
1182
     * @hidden @internal
3,880✔
1183
     */
3,880✔
1184
    @ViewChild('verticalScrollContainer', { read: IgxGridForOfDirective, static: true })
3,880✔
1185
    public verticalScrollContainer: IgxGridForOfDirective<any, any[]>;
3,880✔
1186

3,880✔
1187
    /**
3,880✔
1188
     * @hidden @internal
3,880✔
1189
     */
3,880✔
1190
    @ViewChild('verticalScrollHolder', { read: IgxGridForOfDirective, static: true })
3,880✔
1191
    public verticalScroll: IgxGridForOfDirective<any, any[]>;
3,880✔
1192

3,880✔
1193
    /**
3,880✔
1194
     * @hidden @internal
3,880✔
1195
     */
3,880✔
1196
    @ViewChild('scr', { read: ElementRef, static: true })
3,880✔
1197
    public scr: ElementRef;
3,880✔
1198

3,880✔
1199
    /** @hidden @internal */
3,880✔
1200
    @ViewChild('headSelectorBaseTemplate', { read: TemplateRef, static: true })
3,880✔
1201
    public headerSelectorBaseTemplate: TemplateRef<any>;
3,880✔
1202

3,880✔
1203
    /**
3,880✔
1204
     * @hidden @internal
3,880✔
1205
     */
3,880✔
1206
    @ViewChild('footer', { read: ElementRef })
3,880✔
1207
    public footer: ElementRef;
3,880✔
1208

3,880✔
1209
    /** @hidden @internal */
3,880✔
1210
    public get headerContainer() {
3,880✔
1211
        return this.theadRow?.headerForOf;
3,880✔
1212
    }
3,880✔
1213

3,880✔
1214
    /** @hidden @internal */
3,880✔
1215
    public get headerSelectorContainer() {
3,880✔
1216
        return this.theadRow?.headerSelectorContainer;
3,880✔
1217
    }
3,880✔
1218

3,880✔
1219
    /** @hidden @internal */
3,880✔
1220
    public get headerDragContainer() {
3,880✔
1221
        return this.theadRow?.headerDragContainer;
3,880✔
1222
    }
3,880✔
1223

3,880✔
1224
    /** @hidden @internal */
3,880✔
1225
    public get headerGroupContainer() {
3,880✔
1226
        return this.theadRow?.headerGroupContainer;
3,880✔
1227
    }
3,880✔
1228

1229
    /** @hidden @internal */
1230
    public get filteringRow(): IgxGridFilteringRowComponent {
1231
        return this.theadRow?.filterRow;
1232
    }
3,880✔
1233

1234
    /** @hidden @internal */
3,880✔
1235
    @ViewChild(IgxGridHeaderRowComponent, { static: true })
1236
    public theadRow: IgxGridHeaderRowComponent;
1237

1238
    /** @hidden @internal */
3,880✔
1239
    @ViewChild(IgxGridGroupByAreaComponent)
1240
    public groupArea: IgxGridGroupByAreaComponent;
1241

1242
    /**
3,880✔
1243
     * @hidden @internal
1244
     */
1245
    @ViewChild('tbody', { static: true })
1246
    public tbody: ElementRef;
3,880✔
1247

1248
    /**
1249
     * @hidden @internal
1250
     */
3,880✔
1251
    @ViewChild('pinContainer', { read: ElementRef })
1252
    public pinContainer: ElementRef;
1253

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

1260
    /**
1261
     * @hidden @internal
1262
     */
3,880✔
1263
    @ViewChild('igxRowEditingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1264
    public rowEditingOutletDirective: IgxOverlayOutletDirective;
1265

1266
    /**
3,880✔
1267
     * @hidden @internal
1268
     */
3,880✔
1269
    @ViewChildren(IgxTemplateOutletDirective, { read: IgxTemplateOutletDirective })
1270
    public tmpOutlets: QueryList<any> = new QueryList<any>();
3,880✔
1271

1272
    /**
3,880✔
1273
     * @hidden
1274
     * @internal
3,880✔
1275
     */
1276
    @ViewChild('dragIndicatorIconBase', { read: TemplateRef, static: true })
1277
    public dragIndicatorIconBase: TemplateRef<any>;
1278

3,880✔
1279
    /**
1280
     * @hidden @internal
1281
     */
1282
    @ContentChildren(IgxRowEditTemplateDirective, { descendants: false, read: TemplateRef })
3,880✔
1283
    public rowEditCustomDirectives: QueryList<TemplateRef<IgxGridRowEditTemplateContext>>;
1284

1285
    /**
1286
     * @hidden @internal
3,880✔
1287
     */
1288
    @ContentChildren(IgxRowEditTextDirective, { descendants: false, read: TemplateRef })
1289
    public rowEditTextDirectives: QueryList<TemplateRef<IgxGridRowEditTextTemplateContext>>;
1290

3,880✔
1291
    /**
1292
     * Gets the row edit text template.
1293
     */
1294
    @Input()
3,880✔
1295
    public get rowEditTextTemplate(): TemplateRef<IgxGridRowEditTextTemplateContext> {
1296
        return this._rowEditTextTemplate || this.rowEditTextDirectives?.first;
1297
    }
1298
    /**
3,880✔
1299
     * Sets the row edit text template.
1300
     *```html
1301
     * <ng-template #template igxRowEditText let-rowChangesCount>
1302
     * Changes: {{rowChangesCount}}
3,880✔
1303
     * </ng-template>
1304
     * ```
1305
     *```typescript
1306
     * @ViewChild('template', {read: TemplateRef })
3,880✔
1307
     * public template: TemplateRef<any>;
1308
     * this.grid.rowEditTextTemplate = this.template;
3,880✔
1309
     * ```
1310
     */
1311
    public set rowEditTextTemplate(template: TemplateRef<IgxGridRowEditTextTemplateContext>) {
1312
        this._rowEditTextTemplate = template;
3,880✔
1313
    }
1314

1315
    /**
1316
     * @hidden @internal
3,880✔
1317
     */
1318
    @ContentChild(IgxRowAddTextDirective, { read: TemplateRef })
1319
    public rowAddText: TemplateRef<IgxGridEmptyTemplateContext>;
1320

3,880✔
1321
    /**
1322
     * Gets the row add text template.
1323
     */
1324
    @Input()
3,880✔
1325
    public get rowAddTextTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
1326
        return this._rowAddTextTemplate || this.rowAddText;
1327
    }
1328
    /**
3,880✔
1329
     * Sets the row add text template.
1330
     *```html
1331
     * <ng-template #template igxRowAddText>
1332
     * Adding Row
3,880✔
1333
     * </ng-template>
1334
     * ```
1335
     *```typescript
1336
     * @ViewChild('template', {read: TemplateRef })
3,880✔
1337
     * public template: TemplateRef<any>;
1338
     * this.grid.rowAddTextTemplate = this.template;
1339
     * ```
1340
     */
3,880✔
1341
    public set rowAddTextTemplate(template: TemplateRef<IgxGridEmptyTemplateContext>) {
1342
        this._rowAddTextTemplate = template;
1343
    }
1344

3,880✔
1345
    /**
1346
     * @hidden @internal
1347
     */
1348
    @ContentChildren(IgxRowEditActionsDirective, { descendants: false, read: TemplateRef })
3,880✔
1349
    public rowEditActionsDirectives: QueryList<TemplateRef<IgxGridRowEditActionsTemplateContext>>;
1350

1351
    /**
1352
     * Gets the row edit actions template.
3,880✔
1353
     */
1354
    @Input()
1355
    public get rowEditActionsTemplate(): TemplateRef<IgxGridRowEditActionsTemplateContext> {
1356
        return this._rowEditActionsTemplate || this.rowEditActionsDirectives?.first;
3,880✔
1357
    }
3,880✔
1358
    /**
3,880✔
1359
     * Sets the row edit actions template.
3,880✔
1360
     *```html
3,880✔
1361
     * <ng-template #template igxRowEditActions let-endRowEdit>
3,880✔
1362
     * <button igxButton igxRowEditTabStop (click)="endRowEdit(false)">Cancel</button>
3,880✔
1363
     * <button igxButton igxRowEditTabStop (click)="endRowEdit(true)">Apply</button>
3,880✔
1364
     * </ng-template>
3,880✔
1365
     * ```
3,880✔
1366
     *```typescript
3,880✔
1367
     * @ViewChild('template', {read: TemplateRef })
3,880✔
1368
     * public template: TemplateRef<any>;
3,880✔
1369
     * this.grid.rowEditActionsTemplate = this.template;
3,880✔
1370
     * ```
3,880✔
1371
     */
3,880✔
1372
    public set rowEditActionsTemplate(template: TemplateRef<IgxGridRowEditActionsTemplateContext>) {
3,880✔
1373
        this._rowEditActionsTemplate = template;
3,880✔
1374
    }
3,880✔
1375

3,880✔
1376
    /**
3,880✔
1377
     * The custom template, if any, that should be used when rendering a row expand indicator.
3,880✔
1378
     */
3,880✔
1379
    @ContentChild(IgxRowExpandedIndicatorDirective, { read: TemplateRef })
3,880✔
1380
    protected rowExpandedIndicatorDirectiveTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
3,880✔
1381

3,880✔
1382
    /**
1383
     * Gets the row expand indicator template.
1384
    */
1385
    @Input()
1386
    public get rowExpandedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1387
        return this._rowExpandedIndicatorTemplate || this.rowExpandedIndicatorDirectiveTemplate;
3,880✔
1388
    }
1389

1390
    /**
1391
     * Sets the row expand indicator template.
1392
     *```html
3,880✔
1393
     *<ng-template igxRowExpandedIndicator>
3,880✔
1394
     *  <igx-icon role="button">remove</igx-icon>
3,880✔
1395
     *</ng-template>
3,880✔
1396
     * ```
1397
     *```typescript
3,880✔
1398
     * @ViewChild('template', {read: TemplateRef })
3,880✔
1399
     * public template: TemplateRef<any>;
3,880✔
1400
     * this.grid.rowExpandedIndicatorTemplate = this.template;
3,880✔
1401
     * ```
3,880✔
1402
    */
3,880✔
1403
    public set rowExpandedIndicatorTemplate(template: TemplateRef<IgxGridRowTemplateContext>) {
3,880✔
1404
        this._rowExpandedIndicatorTemplate = template;
3,880✔
1405
    }
3,880✔
1406

3,880✔
1407
    /**
3,880✔
1408
     * The custom template, if any, that should be used when rendering a row collapse indicator.
3,880✔
1409
     */
3,880✔
1410
    @ContentChild(IgxRowCollapsedIndicatorDirective, { read: TemplateRef })
3,880✔
1411
    protected rowCollapsedIndicatorDirectiveTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
3,880✔
1412

3,880✔
1413
    /**
1414
     * Gets the row collapse indicator template.
1415
    */
1416
    @Input()
1417
    public get rowCollapsedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1418
        return this._rowCollapsedIndicatorTemplate || this.rowCollapsedIndicatorDirectiveTemplate;
1419
    }
3,880✔
1420

1421
    /**
1422
     * Sets the row collapse indicator template.
1423
     *```html
1424
     *<ng-template igxRowCollapsedIndicator>
1425
     *  <igx-icon role="button">add</igx-icon>
1426
     *</ng-template>
3,880✔
1427
     * ```
3,880✔
1428
     *```typescript
3,880✔
1429
     * @ViewChild('template', {read: TemplateRef })
3,880✔
1430
     * public template: TemplateRef<any>;
3,880✔
1431
     * this.grid.rowCollapsedIndicatorTemplate = this.template;
3,880✔
1432
     * ```
3,880✔
1433
    */
3,880✔
1434
    public set rowCollapsedIndicatorTemplate(template: TemplateRef<IgxGridRowTemplateContext>) {
1435
        this._rowCollapsedIndicatorTemplate = template;
1436
    }
1437

3,880✔
1438
    /**
×
1439
     * The custom template, if any, that should be used when rendering a header expand indicator.
×
1440
     */
×
1441
    @ContentChild(IgxHeaderExpandedIndicatorDirective, { read: TemplateRef })
1442
    protected headerExpandedIndicatorDirectiveTemplate: TemplateRef<IgxGridTemplateContext> = null;
×
1443

×
1444
    /**
×
1445
     * Gets the header expand indicator template.
1446
    */
1447
    @Input()
3,880✔
1448
    public get headerExpandedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
3,880✔
1449
        return this._headerExpandIndicatorTemplate || this.headerExpandedIndicatorDirectiveTemplate;
3,880✔
1450
    }
3,880✔
1451

1452
    /**
1453
     * Sets the header expand indicator template.
3✔
1454
     *```html
1455
     *<ng-template igxHeaderExpandedIndicator>
1456
     *  <igx-icon role="button">remove</igx-icon>
1457
     *</ng-template>
1458
     * ```
1459
     *```typescript
1460
     * @ViewChild('template', {read: TemplateRef })
1,055✔
1461
     * public template: TemplateRef<any>;
1462
     * this.grid.headerExpandedIndicatorTemplate = this.template;
1463
     * ```
1464
    */
1465
    public set headerExpandedIndicatorTemplate(template: TemplateRef<IgxGridTemplateContext>) {
1466
        this._headerExpandIndicatorTemplate = template;
1467
    }
1,221✔
1468

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

1,221✔
1475
    /**
1476
     * Gets the row collapse indicator template.
1477
    */
1478
    @Input()
1479
    public get headerCollapsedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
1480
        return this._headerCollapseIndicatorTemplate || this.headerCollapsedIndicatorDirectiveTemplate;
408,066✔
1481
    }
1482

1483
    /**
1484
     * Sets the row collapse indicator template.
1485
     *```html
1486
     *<ng-template igxHeaderCollapsedIndicator>
×
1487
     *  <igx-icon role="button">add</igx-icon>
1488
     *</ng-template>
1489
     * ```
1490
     *```typescript
1491
     * @ViewChild('template', {read: TemplateRef })
1492
     * public template: TemplateRef<any>;
1493
     * this.grid.headerCollapsedIndicatorTemplate = this.template;
189,688✔
1494
     * ```
185✔
1495
    */
1496
    public set headerCollapsedIndicatorTemplate(template: TemplateRef<IgxGridTemplateContext>) {
189,503✔
1497
        this._headerCollapseIndicatorTemplate = template;
187,822✔
1498
    }
1499

189,688✔
1500
    /** @hidden @internal */
1501
    @ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef })
1502
    public excelStyleHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
1503

1504
    /**
1505
     * Gets the excel style header icon.
1506
    */
371✔
1507
    @Input()
1508
    public get excelStyleHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1509
        return this._excelStyleHeaderIconTemplate || this.excelStyleHeaderIconDirectiveTemplate;
1510
    }
1511

1512
    /**
1513
     * Sets the excel style header icon.
1514
     *```html
1515
     *<ng-template #template igxExcelStyleHeaderIcon>
1516
     * <igx-icon>filter_alt</igx-icon>
1517
     *</ng-template>
1518
     * ```
1519
     *```typescript
1,211✔
1520
     * @ViewChild('template', {read: TemplateRef })
1521
     * public template: TemplateRef<any>;
1522
     * this.grid.excelStyleHeaderIconTemplate = this.template;
1523
     * ```
1524
    */
1525
    public set excelStyleHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1526
        this._excelStyleHeaderIconTemplate = template;
535✔
1527
    }
1✔
1528

1!
1529

1✔
1530
    /**
1✔
1531
     * @hidden
1532
     * @internal
1533
     */
1534
    @ContentChild(IgxSortAscendingHeaderIconDirective, { read: TemplateRef })
1535
    public sortAscendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
1536

1537
    /**
1538
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1539
     */
1540
    @Input()
1541
    public get sortAscendingHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1542
        return this._sortAscendingHeaderIconTemplate;
1543
    }
276,075✔
1544

1545
    /**
1546
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1547
     *```html
1548
     * <ng-template #template igxSortAscendingHeaderIcon>
1549
     *    <igx-icon>expand_less</igx-icon>
1550
     * </ng-template>
1551
     * ```
1552
     * ```typescript
2,166!
1553
     * @ViewChild("'template'", {read: TemplateRef })
1554
     * public template: TemplateRef<any>;
1555
     * this.grid.sortAscendingHeaderIconTemplate = this.template;
1556
     * ```
1557
     */
1558
    public set sortAscendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1559
        this._sortAscendingHeaderIconTemplate = template;
1560
    }
826,911✔
1561

1562
    /** @hidden @internal */
1563
    @ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef })
1564
    public sortDescendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
1565

1566
    /**
1567
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1568
     */
827,019✔
1569
    @Input()
827,019✔
1570
    public get sortDescendingHeaderIconTemplate() {
1571
        return this._sortDescendingHeaderIconTemplate;
1572
    }
1573

1574
    /**
1575
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1576
     *```html
430,804✔
1577
     * <ng-template #template igxSortDescendingHeaderIcon>
1578
     *    <igx-icon>expand_more</igx-icon>
1579
     * </ng-template>
1580
     * ```
1581
     * ```typescript
1582
     * @ViewChild("'template'", {read: TemplateRef })
1583
     * public template: TemplateRef<any>;
6,656✔
1584
     * this.grid.sortDescendingHeaderIconTemplate = this.template;
1585
     * ```
1586
     */
1587
    public set sortDescendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1588
        this._sortDescendingHeaderIconTemplate = template;
1589
    }
1590

3,513,634✔
1591
    /**
1592
     * @hidden
1593
     * @internal
1594
     */
1595
    @ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef })
1596
    public sortHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
1597

3,402✔
1598
    /**
3,402✔
1599
     * Gets custom template, if any, that should be used when rendering a header sorting indicator when columns are not sorted.
3,402✔
1600
     */
3,402✔
1601
    @Input()
3,402✔
1602
    public get sortHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
3,402✔
1603
        return this._sortHeaderIconTemplate;
3,402✔
1604
    }
1605

1606
    /**
1607
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are not sorted.
1608
     *```html
1609
     * <ng-template #template igxSortHeaderIcon>
1610
     *    <igx-icon>unfold_more</igx-icon>
3,402✔
1611
     * </ng-template>
3,402✔
1612
     * ```
646!
1613
     * ```typescript
1614
     * @ViewChild("'template'", {read: TemplateRef })
1615
     * public template: TemplateRef<any>;
1616
     * this.grid.sortHeaderIconTemplate = this.template;
1617
     * ```
1618
     */
55!
1619
    public set sortHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
53✔
1620
        this._sortHeaderIconTemplate = template;
53✔
1621
    }
53✔
1622

1623
    /**
1624
     * @hidden
3,402✔
1625
     * @internal
3,402✔
1626
     */
185✔
1627
    @ContentChildren(IgxDragIndicatorIconDirective, { read: TemplateRef, descendants: false })
185✔
1628
    public dragIndicatorIconTemplates: QueryList<TemplateRef<IgxGridEmptyTemplateContext>>;
1629

3,402✔
1630
    /**
3,402✔
1631
     * @hidden @internal
1632
     */
1,405✔
1633
    @ViewChildren(IgxRowEditTabStopDirective)
1634
    public rowEditTabsDEFAULT: QueryList<IgxRowEditTabStopDirective>;
1,405✔
1635

1,229✔
1636
    /**
1637
     * @hidden @internal
1638
     */
1639
    @ContentChildren(IgxRowEditTabStopDirective, { descendants: true })
3,402✔
1640
    public rowEditTabsCUSTOM: QueryList<IgxRowEditTabStopDirective>;
3,402✔
1641

3,402✔
1642
    /**
826✔
1643
     * @hidden @internal
79✔
1644
     */
79!
1645
    @ViewChild('rowEditingOverlay', { read: IgxToggleDirective })
79✔
1646
    public rowEditingOverlay: IgxToggleDirective;
1647

1648
    /**
1649
     * @hidden @internal
3,402✔
1650
     */
562✔
1651
    @HostBinding('attr.tabindex')
1652
    public tabindex = 0;
562✔
1653

75✔
1654
    /**
75!
1655
     * @hidden @internal
75✔
1656
     */
75✔
1657
    @HostBinding('attr.role')
1658
    public hostRole = 'grid';
75✔
1659

1660
    /** @hidden @internal */
1661
    @ContentChildren(IgxGridToolbarComponent)
487✔
1662
    public toolbar: QueryList<IgxGridToolbarComponent>;
20✔
1663

1664
    /** @hidden @internal */
467✔
1665
    @ContentChildren(IgxPaginatorComponent)
318✔
1666
    protected paginationComponents: QueryList<IgxPaginatorComponent>;
1667

1668
    /**
3,402✔
1669
     * @hidden @internal
456✔
1670
     */
29✔
1671
    @ViewChild('igxFilteringOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
29✔
1672
    protected _outletDirective: IgxOverlayOutletDirective;
29✔
1673

1674
    /**
427✔
1675
     * @hidden @internal
427✔
1676
     */
184✔
1677
    @ViewChild('defaultExpandedTemplate', { read: TemplateRef, static: true })
1678
    protected defaultExpandedTemplate: TemplateRef<any>;
1679

6,447✔
1680
    /**
3,114✔
1681
     * @hidden @internal
1682
     */
1683
    @ViewChild('defaultCollapsedTemplate', { read: TemplateRef, static: true })
3,114✔
1684
    protected defaultCollapsedTemplate: TemplateRef<any>;
105✔
1685

105✔
1686
    /**
1687
     * @hidden @internal
3,114✔
1688
     */
1689
    @ViewChild('defaultESFHeaderIcon', { read: TemplateRef, static: true })
3,402✔
1690
    protected defaultESFHeaderIconTemplate: TemplateRef<any>;
1691

1692
    @ViewChildren('summaryRow', { read: IgxSummaryRowComponent })
705✔
1693
    protected _summaryRowList: QueryList<IgxSummaryRowComponent>;
705✔
1694

1695
    @ViewChildren('row')
3,402✔
1696
    private _rowList: QueryList<IgxGridRowComponent>;
1,246✔
1697

1698
    @ViewChildren('pinnedRow')
3,402✔
1699
    private _pinnedRowList: QueryList<IgxGridRowComponent>;
42✔
1700

42✔
1701
    /**
42!
1702
     * @hidden @internal
42✔
1703
     */
1704
    @ViewChild('defaultRowEditTemplate', { read: TemplateRef, static: true })
42✔
1705
    private defaultRowEditTemplate: TemplateRef<IgxGridRowEditTemplateContext>;
1706

1707
    @ViewChildren(IgxRowDirective, { read: IgxRowDirective })
1708
    private _dataRowList: QueryList<IgxRowDirective>;
1709

1710
    @HostBinding('class')
1711
    private get hostClass(): string {
3,402✔
1712
        return this.getComponentDensityClass('igx-grid');
3,402✔
1713
    }
3,402✔
1714

3,402✔
1715
    /**
1716
     * Gets/Sets the resource strings.
16,793✔
1717
     *
3,402✔
1718
     * @remarks
3,402✔
1719
     * By default it uses EN resources.
1720
     */
1721
    @Input()
1722
    public set resourceStrings(value: IGridResourceStrings) {
1723
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
1724
    }
1725

201,373✔
1726
    public get resourceStrings(): IGridResourceStrings {
1727
        if (!this._resourceStrings) {
1728
            this._resourceStrings = CurrentResourceStrings.GridResStrings;
1729
        }
1730
        return this._resourceStrings;
1731
    }
59✔
1732

57✔
1733
    /**
1!
1734
     * Gets/Sets the filtering logic of the `IgxGridComponent`.
57✔
1735
     *
1736
     * @remarks
1737
     * The default is AND.
1738
     * @example
1739
     * ```html
1740
     * <igx-grid [data]="Data" [autoGenerate]="true" [filteringLogic]="filtering"></igx-grid>
1741
     * ```
31,837✔
1742
     */
31,837✔
1743
    @WatchChanges()
15,412✔
1744
    @Input()
5,413✔
1745
    public get filteringLogic() {
1746
        return this._filteringExpressionsTree.operator;
15,412✔
1747
    }
1748

1749
    public set filteringLogic(value: FilteringLogic) {
1750
        this._filteringExpressionsTree.operator = value;
1751
    }
1752

1753
    /**
1754
     * Gets/Sets the filtering state.
1,533✔
1755
     *
49✔
1756
     * @example
49✔
1757
     * ```html
49✔
1758
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(filteringExpressionsTree)]="model.filteringExpressions"></igx-grid>
49✔
1759
     * ```
1760
     * @remarks
1,484✔
1761
     * Supports two-way binding.
47✔
1762
     */
1763
    @WatchChanges()
1764
    @Input()
1,437✔
1765
    public get filteringExpressionsTree() {
1766
        return this._filteringExpressionsTree;
1767
    }
1768

1769
    public set filteringExpressionsTree(value) {
1770
        if (value && value instanceof FilteringExpressionsTree) {
1771
            const val = (value as FilteringExpressionsTree);
1772
            for (let index = 0; index < val.filteringOperands.length; index++) {
31,977✔
1773
                if (!(val.filteringOperands[index] instanceof FilteringExpressionsTree)) {
31,977✔
1774
                    const newExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, val.filteringOperands[index].fieldName);
31,977✔
1775
                    newExpressionsTree.filteringOperands.push(val.filteringOperands[index] as IFilteringExpression);
1776
                    val.filteringOperands[index] = newExpressionsTree;
1777
                }
1778
            }
1779

1780
            value.type = FilteringExpressionsTreeType.Regular;
1781
            this._filteringExpressionsTree = value;
42,962✔
1782
            this.filteringPipeTrigger++;
42,962✔
1783
            this.filteringExpressionsTreeChange.emit(this._filteringExpressionsTree);
42,962✔
1784

1785
            if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
1786
                this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1787
                this._filteredData = null;
1788
            }
1789

10,009✔
1790
            this.filteringService.refreshExpressions();
31,837✔
1791
            this.selectionService.clearHeaderCBState();
31,831✔
1792
            this.summaryService.clearSummaryCache();
1793
            this.notifyChanges();
31,837✔
1794
        }
31,837✔
1795
    }
31,837✔
1796

31,837✔
1797
    /**
31,837✔
1798
     * Gets/Sets the advanced filtering state.
151,906✔
1799
     *
1800
     * @example
1801
     * ```typescript
1802
     * let advancedFilteringExpressionsTree = this.grid.advancedFilteringExpressionsTree;
1803
     * this.grid.advancedFilteringExpressionsTree = logic;
1804
     * ```
3,297✔
1805
     */
5✔
1806
    @WatchChanges()
1807
    @Input()
3,297✔
1808
    public get advancedFilteringExpressionsTree() {
5✔
1809
        return this._advancedFilteringExpressionsTree;
1810
    }
3,297✔
1811

5✔
1812
    public set advancedFilteringExpressionsTree(value) {
1813
        if (value && value instanceof FilteringExpressionsTree) {
3,297✔
1814
            value.type = FilteringExpressionsTreeType.Advanced;
3,297✔
1815
            this._advancedFilteringExpressionsTree = value;
3,297✔
1816
            this.filteringPipeTrigger++;
3,297✔
1817
        } else {
63✔
1818
            this._advancedFilteringExpressionsTree = null;
1819
        }
3,297✔
1820
        this.advancedFilteringExpressionsTreeChange.emit(this._advancedFilteringExpressionsTree);
107✔
1821

1822
        if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
1823
            this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1824
            this._filteredData = null;
1825
        }
1826

1827
        this.selectionService.clearHeaderCBState();
6,195✔
1828
        this.summaryService.clearSummaryCache();
1829
        this.notifyChanges();
1830

1831
        // Wait for the change detection to update filtered data through the pipes and then emit the event.
1832
        requestAnimationFrame(() => this.filteringDone.emit(this._advancedFilteringExpressionsTree));
1833
    }
6,195✔
1834

6,195✔
1835
    /**
1836
     * Gets/Sets the locale.
1837
     *
1838
     * @remarks
189✔
1839
     * If not set, returns browser's language.
189✔
1840
     */
19✔
1841
    @Input()
19✔
1842
    public get locale(): string {
19✔
1843
        return this._locale;
19✔
1844
    }
1845

170✔
1846
    public set locale(value: string) {
170✔
1847
        if (value !== this._locale) {
170✔
1848
            this._locale = value;
170✔
1849
            this._currencyPositionLeft = undefined;
170✔
1850
            this.summaryService.clearSummaryCache();
1851
            this.pipeTrigger++;
1852
            this.notifyChanges();
170✔
1853
            this.localeChange.next();
170✔
1854
        }
170✔
1855
    }
1856

1857
    @Input()
1858
    public get pagingMode() {
3,361✔
1859
        return this._pagingMode;
215✔
1860
    }
1861

114✔
1862
    public set pagingMode(val: GridPagingMode) {
114✔
1863
        this._pagingMode = val;
114✔
1864
        this.pipeTrigger++;
114✔
1865
        this.notifyChanges(true);
114✔
1866
    }
1867

215✔
1868
    /** @hidden @internal */
1869
    public get page(): number {
70✔
1870
        return this.paginator?.page || 0;
70✔
1871
    }
70✔
1872

70✔
1873
    public set page(val: number) {
1874
        if (this.paginator) {
1875
            this.paginator.page = val;
1876
        }
3,146✔
1877
    }
1878

1879
    /** @hidden @internal */
1880
    public get perPage(): number {
1881
        return this.paginator?.perPage || DEFAULT_ITEMS_PER_PAGE;
1882
    }
1883

1884
    public set perPage(val: number) {
6,170✔
1885
        if (this.paginator) {
6,170✔
1886
            this.paginator.perPage = val;
341✔
1887
        }
174✔
1888
    }
174✔
1889

174✔
1890
    /**
1891
     * Gets/Sets if the row selectors are hidden.
174✔
1892
     *
1893
     * @remarks
1894
     *  By default row selectors are shown
167✔
1895
     */
1896
    @WatchChanges()
1897
    @Input()
1898
    public get hideRowSelectors() {
5,829✔
1899
        return this._hideRowSelectors;
5,829✔
1900
    }
1901

6,170✔
1902
    public set hideRowSelectors(value: boolean) {
1903
        this._hideRowSelectors = value;
1904
        this.notifyChanges(true);
1905
    }
1906

1907
    /**
47,833✔
1908
     * Gets/Sets whether rows can be moved.
6,468✔
1909
     *
44,686✔
1910
     * @example
3,147✔
1911
     * ```html
1912
     * <igx-grid #grid [rowDraggable]="true"></igx-grid>
1913
     * ```
1914
     */
1915
    @Input()
1916
    public get rowDraggable(): boolean {
1917
        return this._rowDrag && this.hasVisibleColumns;
17,133✔
1918
    }
17,133✔
1919

3,415✔
1920
    public set rowDraggable(val: boolean) {
3,415✔
1921
        this._rowDrag = val;
3,415✔
1922
        this.notifyChanges(true);
2,644✔
1923
    }
1924

3,415✔
1925
    /**
313✔
1926
     * Gets/Sets the trigger for validators used when editing the grid.
1927
     *
3,415✔
1928
     * @example
1929
     * ```html
1930
     * <igx-grid #grid validationTrigger='blur'></igx-grid>
1931
     * ```
1932
     */
1933
    @Input()
3,415✔
1934
    public validationTrigger: GridValidationTrigger = 'change';
3,415✔
1935

3,415✔
1936
    /**
3,415✔
1937
     * @hidden
11✔
1938
     * @internal
18✔
1939
     */
18✔
1940
    public rowDragging = false;
17✔
1941

1942
    /** @hidden @internal */
1943
    public dragRowID = null;
1944

3,415✔
1945
    /**
3,415✔
1946
     * Gets/Sets whether the rows are editable.
1947
     *
1948
     * @remarks
1949
     * By default it is set to false.
1950
     * @example
1951
     * ```html
1952
     * <igx-grid #grid [rowEditable]="true" [primaryKey]="'ProductID'" ></igx-grid>
3,415✔
1953
     * ```
3,415✔
1954
     */
3,415✔
1955
    @WatchChanges()
3,415✔
1956
    @Input()
3,415✔
1957
    public get rowEditable(): boolean {
3,415✔
1958
        return this._rowEditable;
3,415✔
1959
    }
3,415✔
1960

3,415✔
1961
    public set rowEditable(val: boolean) {
1962
        if (!this._init) {
1963
            this.refreshGridState();
182✔
1964
        }
1965
        this._rowEditable = val;
3,415✔
1966
        this.notifyChanges();
4✔
1967
    }
4✔
1968

4✔
1969
    /**
1970
     * Gets/Sets the height.
1971
     *
3,415✔
1972
     * @example
1,877✔
1973
     * ```html
73✔
1974
     * <igx-grid #grid [data]="Data" [height]="'305px'" [autoGenerate]="true"></igx-grid>
73✔
1975
     * ```
1976
     */
1,877✔
1977
    @WatchChanges()
10✔
1978
    @HostBinding('style.height')
1979
    @Input()
1,877✔
1980
    public get height(): string | null {
1981
        return this._height;
3,415✔
1982
    }
1983

1984
    public set height(value: string | null) {
1985
        if (this._height !== value) {
1986
            this._height = value;
14,539✔
1987
            this.nativeElement.style.height = value;
450,299✔
1988
            this.notifyChanges(true);
450,299✔
1989
        }
450,299✔
1990
    }
1991

1992
    /**
1993
     * @hidden @internal
1994
     */
1995
    @HostBinding('style.width')
16,921✔
1996
    public get hostWidth() {
16,921✔
1997
        return this._width || this._hostWidth;
3,898✔
1998
    }
1999

13,023✔
2000
    /**
3,573✔
2001
     * Gets/Sets the width of the grid.
3,573✔
2002
     *
3,573✔
2003
     * @example
3,573✔
2004
     * ```typescript
2005
     * let gridWidth = this.grid.width;
9,450✔
2006
     * ```
4,010✔
2007
     */
4,010✔
2008
    @WatchChanges()
2009
    @Input()
2010
    public get width(): string | null {
2011
        return this._width;
2012
    }
2013

2014
    public set width(value: string | null) {
2015
        if (this._width !== value) {
1,229✔
2016
            this._width = value;
2017
            this.nativeElement.style.width = value;
2018
            this.notifyChanges(true);
2019
        }
2020
    }
2021

3,337✔
2022
    /** @hidden @internal */
24,731✔
2023
    public get headerWidth() {
2024
        return parseInt(this.width, 10) - 17;
3,337✔
2025
    }
3,337✔
2026

3,337✔
2027
    /**
3,337✔
2028
     * Gets/Sets the row height.
3,337✔
2029
     *
3,337!
2030
     * @example
×
2031
     * ```html
×
2032
     * <igx-grid #grid [data]="localData" [rowHeight]="100" [autoGenerate]="true"></igx-grid>
2033
     * ```
3,337✔
2034
     */
133✔
2035
    @WatchChanges()
133✔
2036
    @Input()
123✔
2037
    public get rowHeight(): number {
2038
        return this._rowHeight ? this._rowHeight : this.defaultRowHeight;
2039
    }
3,337✔
2040

3,337✔
2041
    public set rowHeight(value: number | string) {
3,337✔
2042
        if (typeof value !== 'number') {
3,337✔
2043
            value = parseInt(value, 10);
3,337✔
2044
        }
2045
        this._rowHeight = value;
2046
    }
2047

2048
    /**
2049
     * Gets/Sets the default width of the columns.
2050
     *
2051
     * @example
2052
     * ```html
2053
     * <igx-grid #grid [data]="localData" [columnWidth]="100" [autoGenerate]="true"></igx-grid>
2054
     * ```
2055
     */
2056
    @WatchChanges()
2057
    @Input()
2058
    public get columnWidth(): string {
×
2059
        return this._columnWidth;
×
2060
    }
×
2061
    public set columnWidth(value: string) {
2062
        this._columnWidth = value;
×
2063
        this.columnWidthSetByUser = true;
2064
        this.notifyChanges(true);
2065
    }
191,532✔
2066

2067
    /**
2068
     * Get/Sets the message displayed when there are no records.
656✔
2069
     *
656✔
2070
     * @example
656✔
2071
     * ```html
656✔
2072
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
566✔
2073
     * ```
2074
     */
2075
    @Input()
2076
    public set emptyGridMessage(value: string) {
2077
        this._emptyGridMessage = value;
2078
    }
2079
    public get emptyGridMessage(): string {
2080
        return this._emptyGridMessage || this.resourceStrings.igx_grid_emptyGrid_message;
2081
    }
2082

2083
    /**
2084
     * Gets/Sets whether the grid is going to show a loading indicator.
8✔
2085
     *
8✔
2086
     * @example
2087
     * ```html
2088
     * <igx-grid #grid [data]="Data" [isLoading]="true" [autoGenerate]="true"></igx-grid>
2089
     * ```
2090
     */
2091
    @WatchChanges()
2092
    @Input()
2093
    public set isLoading(value: boolean) {
2094
        if (this._isLoading !== value) {
2095
            this._isLoading = value;
2096
            if (this.data) {
2✔
2097
                this.evaluateLoadingState();
2✔
2098
            }
2099
        }
2100
        Promise.resolve().then(() => {
2101
            // wait for the current detection cycle to end before triggering a new one.
2102
            this.notifyChanges();
2103
        });
2104
    }
2105

2106
    public get isLoading(): boolean {
2107
        return this._isLoading;
2108
    }
2109

2110
    /**
2111
     * Gets/Sets whether the columns should be auto-generated once again after the initialization of the grid
50✔
2112
     *
2113
     * @remarks
2114
     * This will allow to bind the grid to remote data and having auto-generated columns at the same time.
2115
     * Note that after generating the columns, this property would be disabled to avoid re-creating
2116
     * columns each time a new data is assigned.
2117
     * @example
2118
     * ```typescript
2119
     *  this.grid.shouldGenerate = true;
2120
     * ```
2121
     */
2122
    public shouldGenerate: boolean;
2123

2124
    /**
2125
     * Gets/Sets the message displayed when there are no records and the grid is filtered.
8✔
2126
     *
2127
     * @example
2128
     * ```html
2129
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
2130
     * ```
2131
     */
2132
    @Input()
2133
    public set emptyFilteredGridMessage(value: string) {
2134
        this._emptyFilteredGridMessage = value;
2135
    }
2136

2137
    public get emptyFilteredGridMessage(): string {
2138
        return this._emptyFilteredGridMessage || this.resourceStrings.igx_grid_emptyFilteredGrid_message;
2139
    }
79✔
2140

79✔
2141
    /**
79✔
2142
     * Gets/Sets the initial pinning configuration.
2143
     *
2144
     * @remarks
2145
     * Allows to apply pinning the columns to the start or the end.
2146
     * Note that pinning to both sides at a time is not allowed.
2147
     * @example
2148
     * ```html
7,018✔
2149
     * <igx-grid [pinning]="pinningConfig"></igx-grid>
2150
     * ```
2151
     */
2152
    @Input()
2153
    public get pinning() {
2154
        return this._pinning;
2155
    }
2156
    public set pinning(value) {
2157
        if (value !== this._pinning) {
2158
            this.resetCaches();
2159
        }
41,186✔
2160
        this._pinning = value;
2161
    }
2162

166,068✔
2163
    /**
2164
     * Gets/Sets if the filtering is enabled.
2165
     *
×
2166
     * @example
2167
     * ```html
2168
     * <igx-grid #grid [data]="localData" [allowFiltering]="true" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2169
     * ```
2170
     */
2171
    @Input()
2172
    public get allowFiltering() {
2173
        return this._allowFiltering;
2174
    }
2175

2176
    public set allowFiltering(value) {
1,185,120✔
2177
        if (this._allowFiltering !== value) {
2178
            this._allowFiltering = value;
5,974✔
2179
            this.filteringService.registerSVGIcons();
2180

16,235✔
2181
            if (!this._init) {
2182
                this.calcGridHeadRow();
1,162,911✔
2183
            }
2184

2185
            this.filteringService.isFilterRowVisible = false;
2186
            this.filteringService.filteredColumn = null;
2187

2188
            this.notifyChanges(true);
2189
        }
11,898✔
2190
    }
2191

27✔
2192
    /**
2193
     * Gets/Sets a value indicating whether the advanced filtering is enabled.
74✔
2194
     *
2195
     * @example
11,797✔
2196
     * ```html
2197
     * <igx-grid #grid [data]="localData" [allowAdvancedFiltering]="true" [autoGenerate]="true"></igx-grid>
2198
     * ```
2199
     */
2200
    @Input()
2201
    public get allowAdvancedFiltering() {
2202
        return this._allowAdvancedFiltering;
2203
    }
2204

2205
    public set allowAdvancedFiltering(value) {
2206
        if (this._allowAdvancedFiltering !== value) {
364,106✔
2207
            this._allowAdvancedFiltering = value;
2208
            this.filteringService.registerSVGIcons();
1,196✔
2209

2210
            if (!this._init) {
2,748✔
2211
                this.notifyChanges(true);
2212
            }
360,162✔
2213
        }
2214
    }
2215

2216
    /**
2217
     * Gets/Sets the filter mode.
181,264✔
2218
     *
161,287✔
2219
     * @example
2220
     * ```html
19,977✔
2221
     * <igx-grid #grid [data]="localData" [filterMode]="'quickFilter'" [height]="'305px'" [autoGenerate]="true"></igx-grid>
19,977✔
2222
     * ```
2223
     * @remarks
2224
     * By default it's set to FilterMode.quickFilter.
2225
     */
309,133✔
2226
    @Input()
287,753✔
2227
    public get filterMode() {
2228
        return this._filterMode;
21,380✔
2229
    }
21,380✔
2230

2231
    public set filterMode(value: FilterMode) {
2232
        switch (value) {
2233
            case FilterMode.excelStyleFilter:
2234
            case FilterMode.quickFilter:
2235
                this._filterMode = value;
36,407✔
2236
                break;
36,407✔
2237
            default:
2238
                break;
2239
        }
2240

2241
        if (this.filteringService.isFilterRowVisible) {
2242
            this.filteringRow.close();
2243
        }
35✔
2244
        this.notifyChanges(true);
35✔
2245
    }
35✔
2246

35✔
2247
    /**
2248
     * Gets/Sets the summary position.
2249
     *
35✔
2250
     * @example
35✔
2251
     * ```html
35✔
2252
     * <igx-grid #grid [data]="localData" summaryPosition="top" [autoGenerate]="true"></igx-grid>
2253
     * ```
2254
     * @remarks
2255
     * By default it is bottom.
2256
     */
2257
    @Input()
2258
    public get summaryPosition() {
119,163✔
2259
        return this._summaryPosition;
2260
    }
27,027✔
2261

2262
    public set summaryPosition(value: GridSummaryPosition) {
27,027✔
2263
        this._summaryPosition = value;
2264
        this.notifyChanges();
27,027✔
2265
    }
2266

27,027✔
2267
    /**
27,027✔
2268
     * Gets/Sets the summary calculation mode.
2269
     *
119,163✔
2270
     * @example
2271
     * ```html
2272
     * <igx-grid #grid [data]="localData" summaryCalculationMode="rootLevelOnly" [autoGenerate]="true"></igx-grid>
2273
     * ```
2274
     * @remarks
2275
     * By default it is rootAndChildLevels which means the summaries are calculated for the root level and each child level.
17,655✔
2276
     */
2277
    @Input()
2278
    public get summaryCalculationMode() {
2279
        return this._summaryCalculationMode;
2280
    }
2281

2282
    public set summaryCalculationMode(value: GridSummaryCalculationMode) {
2283
        this._summaryCalculationMode = value;
2284
        if (!this._init) {
2285
            this.crudService.endEdit(false);
2286
            this.summaryService.resetSummaryHeight();
29,116!
2287
            this.notifyChanges(true);
2288
        }
2289
    }
2290

2291
    /**
2292
     * Controls whether the summary row is visible when groupBy/parent row is collapsed.
2293
     *
2294
     * @example
2295
     * ```html
2296
     * <igx-grid #grid [data]="localData" [showSummaryOnCollapse]="true" [autoGenerate]="true"></igx-grid>
2297
     * ```
3,110,878✔
2298
     * @remarks
251,349✔
2299
     * By default showSummaryOnCollapse is set to 'false' which means that the summary row is not visible
2300
     * when the groupBy/parent row is collapsed.
2,859,529✔
2301
     */
2,859,529✔
2302
    @Input()
2303
    public get showSummaryOnCollapse() {
2304
        return this._showSummaryOnCollapse;
2305
    }
2306

2307
    public set showSummaryOnCollapse(value: boolean) {
2308
        this._showSummaryOnCollapse = value;
2309
        this.notifyChanges();
2310
    }
2311

2312
    /**
145✔
2313
     * Gets/Sets the filtering strategy of the grid.
2314
     *
2315
     * @example
2316
     * ```html
2317
     *  <igx-grid #grid [data]="localData" [filterStrategy]="filterStrategy"></igx-grid>
2318
     * ```
2319
     */
2320
    @Input()
2321
    public get filterStrategy(): IFilteringStrategy {
2322
        return this._filterStrategy;
2323
    }
608,776✔
2324

584,590✔
2325
    public set filterStrategy(classRef: IFilteringStrategy) {
2326
        this._filterStrategy = classRef;
141,132✔
2327
    }
24,186✔
2328

2329
    /**
2330
     * Gets/Sets the sorting strategy of the grid.
2331
     *
2332
     * @example
2333
     * ```html
×
2334
     *  <igx-grid #grid [data]="localData" [sortStrategy]="sortStrategy"></igx-grid>
2335
     * ```
2336
     */
2337
    @Input()
2338
    public get sortStrategy(): IGridSortingStrategy {
2339
        return this._sortingStrategy;
2340
    }
2341

2342
    public set sortStrategy(value: IGridSortingStrategy) {
2343
        this._sortingStrategy = value;
2344
    }
2345

2346
    /**
2347
     * Gets/Sets the sorting options - single or multiple sorting.
120,552✔
2348
     * Accepts an `ISortingOptions` object with any of the `mode` properties.
2349
     *
2350
     * @example
11,891✔
2351
     * ```typescript
2352
     * const _sortingOptions: ISortingOptions = {
2353
     *      mode: 'single'
2354
     * }
2355
     * ```html
2356
     * <igx-grid [sortingOptions]="sortingOptions"><igx-grid>
2357
     * ```
2358
     */
2359
    @Input()
2360
    public set sortingOptions(value: ISortingOptions) {
2361
        this.clearSort();
2362
        this._sortingOptions = Object.assign(this._sortingOptions, value);
2363
    }
12✔
2364

2✔
2365
    public get sortingOptions() {
2✔
2366
        return this._sortingOptions;
2✔
2367
    }
2✔
2368

2369
    /**
2370
     * Gets/Sets the current selection state.
2371
     *
2372
     * @remarks
2373
     * Represents the selected rows' IDs (primary key or rowData)
2374
     * @example
2375
     * ```html
2376
     * <igx-grid [data]="localData" primaryKey="ID" rowSelection="multiple" [selectedRows]="[0, 1, 2]"><igx-grid>
2377
     * ```
2378
     */
2379
    @Input()
165,726✔
2380
    public set selectedRows(rowIDs: any[]) {
132,400✔
2381
        this.selectRows(rowIDs || [], true);
2382
    }
193,060✔
2383

33,326✔
2384
    public get selectedRows(): any[] {
2385
        return this.selectionService.getSelectedRows();
2386
    }
146✔
2387

2388

2389
    /** @hidden @internal */
1!
2390
    public get headerGroupsList(): IgxGridHeaderGroupComponent[] {
1!
2391
        return this.theadRow.groups;
1✔
2392
    }
2393

1✔
2394
    /** @hidden @internal */
1✔
2395
    public get headerCellList(): IgxGridHeaderComponent[] {
1✔
2396
        return this.headerGroupsList.map(headerGroup => headerGroup.header).filter(header => header);
2397
    }
2398

2399
    /** @hidden @internal */
2400
    public get filterCellList(): IgxGridFilteringCellComponent[] {
37,019✔
2401
        return this.headerGroupsList.map(group => group.filter).filter(cell => cell);
16,921✔
2402
    }
2403

2404
    /**
126,725✔
2405
     * @hidden @internal
20,098✔
2406
     */
20,098✔
2407
    public get summariesRowList() {
20,098✔
2408
        const res = new QueryList<any>();
102,946✔
2409
        if (!this._summaryRowList) {
2410
            return res;
20,098✔
2411
        }
20,098✔
2412
        const sumList = this._summaryRowList.filter((item) => item.element.nativeElement.parentElement !== null);
2413
        res.reset(sumList);
2414
        return res;
2415
    }
2416

2417
    /**
2418
     * A list of `IgxGridRowComponent`.
210,267✔
2419
     *
2420
     * @example
2421
     * ```typescript
2422
     * const rowList = this.grid.rowList;
2423
     * ```
2424
     */
2425
    public get rowList() {
636✔
2426
        const res = new QueryList<IgxRowDirective>();
2427
        if (!this._rowList) {
2428
            return res;
2429
        }
2430
        const rList = this._rowList
2431
            .filter((item) => item.element.nativeElement.parentElement !== null)
2432
            .sort((a, b) => a.index - b.index);
×
2433
        res.reset(rList);
2434
        return res;
2435
    }
2436

2437
    /**
2438
     * A list of currently rendered `IgxGridRowComponent`'s.
2439
     *
55,284✔
2440
     * @example
55,284!
2441
     * ```typescript
×
2442
     * const dataList = this.grid.dataRowList;
2443
     * ```
55,284!
2444
     */
×
2445
    public get dataRowList(): QueryList<IgxRowDirective> {
2446
        const res = new QueryList<IgxRowDirective>();
55,284✔
2447
        if (!this._dataRowList) {
2448
            return res;
2449
        }
2450
        const rList = this._dataRowList.filter(item => item.element.nativeElement.parentElement !== null).sort((a, b) => a.index - b.index);
2451
        res.reset(rList);
2452
        return res;
2453
    }
23✔
2454

23!
2455
    /**
×
2456
     * Gets the header row selector template.
2457
     */
23✔
2458
    @Input()
2459
    public get headSelectorTemplate(): TemplateRef<IgxHeadSelectorTemplateContext> {
2460
        return this._headSelectorTemplate || this.headSelectorsTemplates?.first;
2461
    }
2462

2463
    /**
2464
     * Sets the header row selector template.
2465
     * ```html
2466
     * <ng-template #template igxHeadSelector let-headContext>
2467
     * {{ headContext.selectedCount }} / {{ headContext.totalCount  }}
50✔
2468
     * </ng-template>
2469
     * ```
149✔
2470
     * ```typescript
149✔
2471
     * @ViewChild("'template'", {read: TemplateRef })
149!
2472
     * public template: TemplateRef<any>;
×
2473
     * this.grid.headSelectorTemplate = this.template;
2474
     * ```
149✔
2475
     */
2476
    public set headSelectorTemplate(template: TemplateRef<IgxHeadSelectorTemplateContext>) {
21✔
2477
        this._headSelectorTemplate = template;
2478
    }
128✔
2479

16✔
2480
    /**
2481
     * @hidden
2482
     * @internal
2483
     */
2484
    public get isPinningToStart() {
128✔
2485
        return this.pinning.columns !== ColumnPinningPosition.End;
8✔
2486
    }
8✔
2487

8✔
2488
    /**
2489
     * @hidden
128✔
2490
     * @internal
3✔
2491
     */
3✔
2492
    public get isRowPinningToTop() {
3✔
2493
        return this.pinning.rows !== RowPinningPosition.Bottom;
2494
    }
2495

2496
    /**
2497
     * Gets the row selector template.
2498
     */
2499
    @Input()
2500
    public get rowSelectorTemplate(): TemplateRef<IgxRowSelectorTemplateContext> {
128✔
2501
        return this._rowSelectorTemplate || this.rowSelectorsTemplates?.first;
128✔
2502
    }
2503

2504
    /**
2505
         * Sets a custom template for the row selectors.
2506
         * ```html
2507
         * <ng-template #template igxRowSelector let-rowContext>
2508
         *    <igx-checkbox [checked]="rowContext.selected"></igx-checkbox>
2509
         * </ng-template>
2510
         * ```
2511
         * ```typescript
2512
         * @ViewChild("'template'", {read: TemplateRef })
2513
         * public template: TemplateRef<any>;
2514
         * this.grid.rowSelectorTemplate = this.template;
2515
         * ```
2516
         */
2517
    public set rowSelectorTemplate(template: TemplateRef<IgxRowSelectorTemplateContext>) {
2518
        this._rowSelectorTemplate = template;
2519
    }
2520

2521
    /**
2522
     * @hidden @internal
2523
     */
2524
    public get rowOutletDirective() {
2525
        return this.rowEditingOutletDirective;
2526
    }
2527

2528
    /**
2529
     * @hidden @internal
2530
     */
3,149✔
2531
    public get parentRowOutletDirective() {
3,149✔
2532
        return this.outlet;
2533
    }
2534

2535
    /**
2536
     * @hidden @internal
2537
     */
2538
    public get rowEditCustom(): TemplateRef<IgxGridRowEditTemplateContext> {
2539
        if (this.rowEditCustomDirectives && this.rowEditCustomDirectives.first) {
2540
            return this.rowEditCustomDirectives.first;
2541
        }
2542
        return null;
2543
    }
2544

161✔
2545
    /**
161✔
2546

161✔
2547
    /**
161✔
2548
     * @hidden @internal
161✔
2549
     */
2550
    public get rowEditContainer(): TemplateRef<IgxGridRowEditTemplateContext> {
2551
        return this.rowEditCustom ? this.rowEditCustom : this.defaultRowEditTemplate;
2552
    }
2553

2554
    /**
2555
     * The custom template, if any, that should be used when rendering the row drag indicator icon
2556
     */
2557
    @Input()
2558
    public get dragIndicatorIconTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
2559
        return this._customDragIndicatorIconTemplate || this.dragIndicatorIconTemplates?.first;
2560
    }
2561

2562
    /**
2563
     * Sets a custom template that should be used when rendering the row drag indicator icon.
82!
2564
     *```html
82✔
2565
     * <ng-template #template igxDragIndicatorIcon>
2566
     *    <igx-icon>expand_less</igx-icon>
2567
     * </ng-template>
2568
     * ```
2569
     * ```typescript
79✔
2570
     * @ViewChild("'template'", {read: TemplateRef })
2571
     * public template: TemplateRef<any>;
2572
     * this.grid.dragIndicatorIconTemplate = this.template;
2573
     * ```
2574
     */
2575
    public set dragIndicatorIconTemplate(val: TemplateRef<IgxGridEmptyTemplateContext>) {
2576
        this._customDragIndicatorIconTemplate = val;
2577
    }
79✔
2578

79!
2579
    /**
×
2580
     * @hidden @internal
2581
     */
79✔
2582
    public get firstEditableColumnIndex(): number {
79✔
2583
        const index = this.visibleColumns.filter(col => col.editable)
77✔
2584
            .map(c => c.visibleIndex).sort((a, b) => a - b);
77✔
2585
        return index.length ? index[0] : null;
2586
    }
79✔
2587

2588
    /**
2589
     * @hidden @internal
2590
     */
2591
    public get lastEditableColumnIndex(): number {
2592
        const index = this.visibleColumns.filter(col => col.editable)
2593
            .map(c => c.visibleIndex).sort((a, b) => a > b ? -1 : 1);
2594
        return index.length ? index[0] : null;
2595
    }
2596

2597
    /**
2598
     * @hidden @internal
2599
     * TODO: Nav service logic doesn't handle 0 results from this querylist
2600
     */
2601
    public get rowEditTabs(): QueryList<IgxRowEditTabStopDirective> {
2602
        return this.rowEditTabsCUSTOM.length ? this.rowEditTabsCUSTOM : this.rowEditTabsDEFAULT;
19!
2603
    }
47✔
2604

19!
2605
    /** @hidden @internal */
2606
    public get activeDescendant() {
19✔
2607
        const activeElem = this.navigation.activeNode;
19✔
2608

2609
        if (!activeElem || !Object.keys(activeElem).length) {
19✔
2610
            return this.id;
1✔
2611
        }
2612

18✔
2613
        return activeElem.row < 0 ?
2614
            `${this.id}_${activeElem.row}_${activeElem.mchCache.level}_${activeElem.column}` :
2615
            `${this.id}_${activeElem.row}_${activeElem.column}`;
2616
    }
2617

18✔
2618
    /** @hidden @internal */
18✔
2619
    public get bannerClass(): string {
18✔
2620
        const position = this.rowEditPositioningStrategy.isTop ? 'igx-banner__border-top' : 'igx-banner__border-bottom';
18✔
2621
        return `${this.getComponentDensityClass('igx-banner')} ${position}`;
18✔
2622
    }
2623

2624
    /**
2625
     * Gets/Sets the sorting state.
2626
     *
2627
     * @remarks
2628
     * Supports two-way data binding.
2629
     * @example
2630
     * ```html
2631
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(sortingExpressions)]="model.sortingExpressions"></igx-grid>
2632
     * ```
2633
     */
2634
    @WatchChanges()
2635
    @Input()
2636
    public get sortingExpressions(): ISortingExpression[] {
2637
        return this._sortingExpressions;
2638
    }
2639

2640
    public set sortingExpressions(value: ISortingExpression[]) {
2641
        this._sortingExpressions = cloneArray(value);
2642
        this.sortingExpressionsChange.emit(this._sortingExpressions);
2643
        this.notifyChanges();
31!
2644
    }
31✔
2645

31!
2646
    /**
×
2647
     * @hidden @internal
2648
     */
31✔
2649
    public get maxLevelHeaderDepth() {
31✔
2650
        if (this._maxLevelHeaderDepth === null) {
2651
            this._maxLevelHeaderDepth = this.hasColumnLayouts ?
2652
                this._columns.reduce((acc, col) => Math.max(acc, col.rowStart), 0) :
2653
                this._columns.reduce((acc, col) => Math.max(acc, col.level), 0);
2654
        }
2655
        return this._maxLevelHeaderDepth;
31✔
2656
    }
2657

2658
    /**
2659
     * Gets the number of hidden columns.
2660
     *
2661
     * @example
2662
     * ```typescript
2663
     * const hiddenCol = this.grid.hiddenColumnsCount;
2664
     * ``
2665
     */
2666
    public get hiddenColumnsCount() {
2667
        return this._columns.filter((col) => col.columnGroup === false && col.hidden === true).length;
2668
    }
2669

2670
    /**
200✔
2671
     * Gets the number of pinned columns.
7✔
2672
     */
2673
    public get pinnedColumnsCount() {
193✔
2674
        return this.pinnedColumns.filter(col => !col.columnLayout).length;
193✔
2675
    }
193✔
2676

2677
    /**
2678
     * Gets/Sets whether the grid has batch editing enabled.
2679
     * When batch editing is enabled, changes are not made directly to the underlying data.
2680
     * Instead, they are stored as transactions, which can later be committed w/ the `commit` method.
2681
     *
2682
     * @example
2683
     * ```html
2684
     * <igx-grid [batchEditing]="true" [data]="someData">
2685
     * </igx-grid>
2686
     * ```
2687
     */
2688
    @Input()
155✔
2689
    public get batchEditing(): boolean {
155✔
2690
        return this._batchEditing;
3✔
2691
    }
6✔
2692

2693
    public set batchEditing(val: boolean) {
2694
        if (val !== this._batchEditing) {
2695
            delete this._transactions;
152✔
2696
            this._batchEditing = val;
4✔
2697
            this.switchTransactionService(val);
12✔
2698
            this.subscribeToTransactions();
8✔
2699
        }
2700
    }
2701

2702
    /**
152✔
2703
     * Get transactions service for the grid.
2704
     */
155✔
2705
    public get transactions(): TransactionService<Transaction, State> {
155✔
2706
        if (this._diTransactions && !this.batchEditing) {
155!
2707
            return this._diTransactions;
×
2708
        }
2709
        return this._transactions;
155✔
2710
    }
155✔
2711

3✔
2712
    /**
2713
     * @hidden @internal
2714
     */
152✔
2715
    public get currentRowState(): any {
2716
        return this._currentRowState;
155✔
2717
    }
2718

2719
    /**
2720
     * @hidden @internal
2721
     */
2722
    public get currencyPositionLeft(): boolean {
2723
        if (this._currencyPositionLeft !== undefined) {
2724
            return this._currencyPositionLeft;
2725
        }
2726
        const format = getLocaleNumberFormat(this.locale, NumberFormatStyle.Currency);
2727
        const formatParts = format.split(',');
2728
        const i = formatParts.indexOf(formatParts.find(c => c.includes('¤')));
2729
        return this._currencyPositionLeft = i < 1;
2730
    }
2731

2732
    /**
2733
     * Gets/Sets cell selection mode.
272✔
2734
     *
2735
     * @remarks
2736
     * By default the cell selection mode is multiple
2737
     * @param selectionMode: GridSelectionMode
2738
     */
2739
    @WatchChanges()
2740
    @Input()
2741
    public get cellSelection() {
2742
        return this._cellSelectionMode;
2743
    }
2744

2745
    public set cellSelection(selectionMode: GridSelectionMode) {
2746
        this._cellSelectionMode = selectionMode;
2747
        // if (this.gridAPI.grid) {
3✔
2748
        this.selectionService.clear(true);
2749
        this.notifyChanges();
2750
        // }
2751
    }
2752

2753
    /**
2754
     * Gets/Sets row selection mode
2755
     *
2756
     * @remarks
2757
     * By default the row selection mode is 'none'
2758
     * Note that in IgxGrid and IgxHierarchicalGrid 'multipleCascade' behaves like 'multiple'
2759
     */
2760
    @WatchChanges()
2761
    @Input()
2762
    public get rowSelection() {
2763
        return this._rowSelectionMode;
2764
    }
2765

2766
    public set rowSelection(selectionMode: GridSelectionMode) {
7✔
2767
        this._rowSelectionMode = selectionMode;
4✔
2768
        if (!this._init) {
2769
            this.selectionService.clearAllSelectedRows();
2770
            this.notifyChanges(true);
3✔
2771
        }
2772
    }
2773

2774
    /**
2775
     * Gets/Sets column selection mode
2776
     *
2777
     * @remarks
2778
     * By default the row selection mode is none
2779
     * @param selectionMode: GridSelectionMode
2780
     */
2781
    @WatchChanges()
2782
    @Input()
2783
    public get columnSelection() {
2784
        return this._columnSelectionMode;
2785
    }
2786

2787
    public set columnSelection(selectionMode: GridSelectionMode) {
2788
        this._columnSelectionMode = selectionMode;
7✔
2789
        // if (this.gridAPI.grid) {
5✔
2790
        this.selectionService.clearAllSelectedColumns();
2791
        this.notifyChanges(true);
2792
        // }
2✔
2793
    }
2794

2795
    /**
2796
     * @hidden @internal
2797
     */
2798
    public set pagingState(value) {
2799
        this._pagingState = value;
2800
        if (this.paginator && !this._init) {
2801
            this.paginator.totalRecords = value.metadata.countRecords;
2802
        }
2803
    }
2804

2805
    public get pagingState() {
2806
        return this._pagingState;
2807
    }
145✔
2808

2809
    /**
2810
     * @hidden @internal
2811
     */
2812
    public rowEditMessage;
2813

2814
    /**
2815
     * @hidden @internal
2816
     */
2817
    public snackbarActionText = this.resourceStrings.igx_grid_snackbar_addrow_actiontext;
2818

2819
    /**
2820
     * @hidden @internal
2821
     */
34✔
2822
    public calcWidth: number;
21✔
2823
    /**
21✔
2824
     * @hidden @internal
2825
     */
13!
2826
    public calcHeight = 0;
×
2827
    /**
2828
     * @hidden @internal
13✔
2829
     */
2830
    public tfootHeight: number;
2831

2832
    /**
2833
     * @hidden @internal
2834
     */
274✔
2835
    public disableTransitions = false;
274✔
2836

274✔
2837
    /**
274✔
2838
     * Represents the last search information.
274✔
2839
     */
2840
    public lastSearchInfo: ISearchInfo = {
2841
        searchText: '',
2842
        caseSensitive: false,
2843
        exactMatch: false,
2844
        activeMatchIndex: 0,
2845
        matchInfoCache: [],
2846
        matchCount: 0,
2847
        content: ''
2848
    };
2849

2850
    /**
2851
     * @hidden @internal
2852
     */
2853
    public columnWidthSetByUser = false;
2854

37✔
2855
    /**
37✔
2856
     * @hidden @internal
2857
     */
2858
    public pinnedRecords: any[];
2859

2860
    /**
2861
     * @hidden @internal
2862
     */
2863
    public unpinnedRecords: any[];
2864

2865
    /**
2866
     * @hidden @internal
2867
     */
2868
    public rendered$ = this.rendered.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true }));
19✔
2869

19✔
2870
    /** @hidden @internal */
2871
    public resizeNotify = new Subject<void>();
2872

2873
    /** @hidden @internal */
2874
    public rowAddedNotifier = new Subject<IRowDataEventArgs>();
2875

2876
    /** @hidden @internal */
2877
    public rowDeletedNotifier = new Subject<IRowDataEventArgs>();
2878

2879
    /** @hidden @internal */
2880
    public pipeTriggerNotifier = new Subject();
2881

2882
    /** @hidden @internal */
2883
    public _filteredSortedPinnedData: any[];
2884

134✔
2885
    /** @hidden @internal */
1✔
2886
    public _filteredSortedUnpinnedData: any[];
2887

133✔
2888
    /** @hidden @internal */
133✔
2889
    public _filteredPinnedData: any[];
133✔
2890

1✔
2891
    /**
2892
     * @hidden
132✔
2893
     */
132✔
2894
    public _filteredUnpinnedData;
132✔
2895
    /**
132✔
2896
     * @hidden @internal
132✔
2897
     */
130✔
2898
    public _destroyed = false;
130✔
2899
    /**
2900
     * @hidden @internal
132✔
2901
     */
2902
    public _totalRecords = -1;
2903
    /**
2904
     * @hidden @internal
2905
     */
2906
    public columnsWithNoSetWidths = null;
2907
    /**
2908
     * @hidden @internal
2909
     */
2910
    public pipeTrigger = 0;
2911
    /**
2912
     * @hidden @internal
2913
     */
2914
    public filteringPipeTrigger = 0;
24✔
2915
    /**
24!
2916
     * @hidden @internal
×
2917
     */
2918
    public summaryPipeTrigger = 0;
24✔
2919
    /**
24✔
2920
     * @hidden @internal
24✔
2921
     */
1✔
2922
    public groupablePipeTrigger = 0;
2923

23✔
2924
    /**
23✔
2925
    * @hidden @internal
23✔
2926
    */
23!
2927
    public EMPTY_DATA = [];
23✔
2928

23✔
2929
    /** @hidden @internal */
2930
    public isPivot = false;
23✔
2931

2932
    /** @hidden @internal */
2933
    public _baseFontSize: number;
2934

63,816✔
2935
    /**
63,816✔
2936
     * @hidden
2937
     */
2938
    public destroy$ = new Subject<any>();
2939
    /**
34,858✔
2940
     * @hidden
2941
     */
2942
    protected _pagingMode = GridPagingMode.Local;
2943
    /**
2944
     * @hidden
2945
     */
2946
    protected _pagingState;
2947
    /**
2948
     * @hidden
2949
     */
2950
    protected _hideRowSelectors = false;
2951
    /**
2952
     * @hidden
269✔
2953
     */
2954
    protected _rowDrag = false;
2955
    /**
2956
     * @hidden
2957
     */
2958
    protected _columns: IgxColumnComponent[] = [];
2959
    /**
2960
     * @hidden
2961
     */
2962
    protected _pinnedColumns: IgxColumnComponent[] = [];
2963
    /**
2964
     * @hidden
2965
     */
2966
    protected _unpinnedColumns: IgxColumnComponent[] = [];
2967
    /**
2968
     * @hidden
155✔
2969
     */
2970
    protected _filteringExpressionsTree: IFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And);
2971
    /**
2972
     * @hidden
2973
     */
2974
    protected _advancedFilteringExpressionsTree: IFilteringExpressionsTree;
2975
    /**
2976
     * @hidden
2977
     */
2978
    protected _sortingExpressions: Array<ISortingExpression> = [];
2979
    /**
2980
     * @hidden
2981
     */
2982
    protected _maxLevelHeaderDepth = null;
2983
    /**
2984
     * @hidden
40✔
2985
     */
2986
    protected _columnHiding = false;
2987
    /**
2988
     * @hidden
2989
     */
2990
    protected _columnPinning = false;
2991

2992
    protected _pinnedRecordIDs = [];
2993

2994
    /**
2995
     * @hidden
2996
     */
2997
    protected _hasVisibleColumns;
3,705✔
2998
    protected _allowFiltering = false;
9,441✔
2999
    protected _allowAdvancedFiltering = false;
121✔
3000
    protected _filterMode: FilterMode = FilterMode.quickFilter;
121✔
3001

120✔
3002

120✔
3003
    protected _defaultTargetRecordNumber = 10;
1,299✔
3004
    protected _expansionStates: Map<any, boolean> = new Map<any, boolean>();
3005
    protected _defaultExpandState = false;
3006
    protected _headerFeaturesWidth = NaN;
3007
    protected _init = true;
105✔
3008
    protected _cdrRequestRepaint = false;
3009
    protected _userOutletDirective: IgxOverlayOutletDirective;
3010
    protected _transactions: TransactionService<Transaction, State>;
3011
    protected _batchEditing = false;
121✔
3012
    protected _sortingOptions: ISortingOptions = { mode: 'multiple' };
3013
    protected _filterStrategy: IFilteringStrategy = new FilteringStrategy();
3014
    protected _autoGeneratedCols = [];
9,320✔
3015
    protected _dataView = [];
3016

3017
    /** @hidden @internal */
3018
    public get paginator() {
3019
        return this.paginationComponents?.first;
3020
    }
3021

3022
    /**
3023
     * @hidden @internal
3024
     */
3025
    public get scrollSize() {
3026
        return this.verticalScrollContainer.getScrollNativeSize();
1✔
3027
    }
3028

3029
    private _rowEditable = false;
3030
    private _currentRowState: any;
3031
    private _filteredSortedData = null;
3032
    private _filteredData = null;
3033

3034
    private _customDragIndicatorIconTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3035
    private _excelStyleHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext>;
1✔
3036
    private _rowSelectorTemplate: TemplateRef<IgxRowSelectorTemplateContext>;
10!
3037
    private _headSelectorTemplate: TemplateRef<IgxHeadSelectorTemplateContext>;
10✔
3038
    private _rowEditTextTemplate: TemplateRef<IgxGridRowEditTextTemplateContext>;
40✔
3039
    private _rowAddTextTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3040
    private _rowEditActionsTemplate: TemplateRef<IgxGridRowEditActionsTemplateContext>;
3041
    private _dragGhostCustomTemplate: TemplateRef<IgxGridRowDragGhostContext>;
3042
    private _rowExpandedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3043
    private _rowCollapsedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3044
    private _headerExpandIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
3045
    private _headerCollapseIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
6✔
3046

3047
    private _cdrRequests = false;
3048
    private _resourceStrings;
3049
    private _emptyGridMessage = null;
1,673,206✔
3050
    private _emptyFilteredGridMessage = null;
263,142✔
3051
    private _isLoading = false;
3052
    private _locale: string;
3053
    private overlayIDs = [];
3054
    private _sortingStrategy: IGridSortingStrategy;
3055
    private _pinning: IPinningConfig = { columns: ColumnPinningPosition.Start };
3056

198,752✔
3057
    private _hostWidth;
3058
    private _advancedFilteringOverlayId: string;
3059
    private _advancedFilteringPositionSettings: PositionSettings = {
3060
        verticalDirection: VerticalAlignment.Middle,
3061
        horizontalDirection: HorizontalAlignment.Center,
3062
        horizontalStartPoint: HorizontalAlignment.Center,
61,428!
3063
        verticalStartPoint: VerticalAlignment.Middle
62,395!
3064
    };
3065

×
3066
    private _advancedFilteringOverlaySettings: OverlaySettings = {
3067
        closeOnOutsideClick: false,
3068
        modal: false,
31,837✔
3069
        positionStrategy: new ConnectedPositioningStrategy(this._advancedFilteringPositionSettings),
3070
    };
3071

3072
    private columnListDiffer;
×
3073
    private rowListDiffer;
3074
    private _height: string | null = '100%';
3075
    private _width: string | null = '100%';
3076
    private _rowHeight: number | undefined;
178,638✔
3077
    private _horizontalForOfs: Array<IgxGridForOfDirective<any, any[]>> = [];
3078
    private _multiRowLayoutRowSize = 1;
3079
    // Caches
3080
    private _totalWidth = NaN;
18,411,334✔
3081
    private _pinnedVisible = [];
3082
    private _unpinnedVisible = [];
3083
    private _pinnedWidth = NaN;
3084
    private _unpinnedWidth = NaN;
3085
    private _visibleColumns = [];
3086
    private _columnGroups = false;
15,848✔
3087

3088
    private _columnWidth: string;
3089

3090
    private _summaryPosition: GridSummaryPosition = GridSummaryPosition.bottom;
3091
    private _summaryCalculationMode: GridSummaryCalculationMode = GridSummaryCalculationMode.rootAndChildLevels;
3092
    private _showSummaryOnCollapse = false;
×
3093
    private _summaryRowHeight = 0;
3094
    private _cellSelectionMode: GridSelectionMode = GridSelectionMode.multiple;
3095
    private _rowSelectionMode: GridSelectionMode = GridSelectionMode.none;
3096
    private _selectRowOnClick = true;
3097
    private _columnSelectionMode: GridSelectionMode = GridSelectionMode.none;
3098

38,840✔
3099
    private lastAddedRowIndex;
3100
    private _currencyPositionLeft: boolean;
3101

3102
    private rowEditPositioningStrategy = new RowEditPositionStrategy({
3103
        horizontalDirection: HorizontalAlignment.Right,
3104
        verticalDirection: VerticalAlignment.Bottom,
12,346✔
3105
        horizontalStartPoint: HorizontalAlignment.Left,
3106
        verticalStartPoint: VerticalAlignment.Bottom,
3107
        closeAnimation: null
3108
    });
3109

3110
    private rowEditSettings: OverlaySettings = {
225✔
3111
        scrollStrategy: new AbsoluteScrollStrategy(),
225✔
3112
        modal: false,
3113
        closeOnOutsideClick: false,
3114
        outlet: this.rowOutletDirective,
3115
        positionStrategy: this.rowEditPositioningStrategy
3116
    };
3117

3118
    private transactionChange$ = new Subject<void>();
67,761✔
3119
    private _rendered = false;
3120
    private readonly DRAG_SCROLL_DELTA = 10;
3121
    private _dataCloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy();
3122
    private _autoSize = false;
3123
    private _sortHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
3124
    private _sortAscendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
2,980✔
3125
    private _sortDescendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
3126

3127
    /**
3128
     * @hidden @internal
3129
     */
3130
    protected get minColumnWidth() {
3131
        return MINIMUM_COLUMN_WIDTH;
49✔
3132
    }
49✔
3133

5✔
3134
    /**
3135
     * @hidden @internal
49✔
3136
     */
3137
    public abstract id: string;
3138
    public abstract data: any[] | null;
3139

3140
    /**
68,453✔
3141
     * Returns an array of objects containing the filtered data.
3142
     *
68,453!
3143
     * @example
×
3144
     * ```typescript
3145
     * let filteredData = this.grid.filteredData;
3146
     * ```
68,453✔
3147
     */
3148
    public get filteredData() {
3149
        return this._filteredData;
692,797✔
3150
    }
3151

68,453✔
3152
    /**
692,797✔
3153
     * Returns an array containing the filtered sorted data.
112,326✔
3154
     *
112,326✔
3155
     * @example
112,326✔
3156
     * ```typescript
3157
     * const filteredSortedData = this.grid1.filteredSortedData;
68,453✔
3158
     * ```
140,646✔
3159
     */
355,651✔
3160
    public get filteredSortedData(): any[] {
68,453✔
3161
        return this._filteredSortedData;
3162
    }
3163

68,453✔
3164
    /**
3165
     * @hidden @internal
37,011✔
3166
     */
37,011✔
3167
    public get rowChangesCount() {
37,011!
3168
        if (!this.crudService.row) {
×
3169
            return 0;
3170
        }
37,011✔
3171
        const f = (obj: any) => {
3172
            let changes = 0;
3173
            Object.keys(obj).forEach(key => isObject(obj[key]) ? changes += f(obj[key]) : changes++);
37,011✔
3174
            return changes;
3175
        };
3176
        if (this.transactions.getState(this.crudService.row.id)?.type === TransactionType.ADD) {
68,453✔
3177
            return this._columns.filter(c => c.field).length;
1,422✔
3178
        }
3179
        const rowChanges = this.transactions.getAggregatedValue(this.crudService.row.id, false);
67,031✔
3180
        return rowChanges ? f(rowChanges) : 0;
67,031!
3181
    }
3182

3183
    /**
67,031✔
3184
     * @hidden @internal
3185
     */
3186
    public get dataWithAddedInTransactionRows() {
3187
        const result = cloneArray(this.gridAPI.get_all_data());
3188
        if (this.transactions.enabled) {
3189
            result.push(...this.transactions.getAggregatedChanges(true)
156,766✔
3190
                .filter(t => t.type === TransactionType.ADD)
66,175✔
3191
                .map(t => t.newValue));
3192
        }
90,591✔
3193

90,591✔
3194
        if (this.crudService.row && this.crudService.row.getClassName() === IgxAddRow.name) {
3195
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
3196
        }
3197

3198
        return result;
3199
    }
3200

3201
    /**
3202
     * @hidden @internal
3203
     */
3204
    public get dataLength() {
19,312✔
3205
        return this.transactions.enabled ? this.dataWithAddedInTransactionRows.length : this.gridAPI.get_all_data().length;
41,357!
3206
    }
41,357✔
3207

41,357✔
3208
    /**
5,656✔
3209
     * @hidden @internal
3,629✔
3210
     */
3211
    public get template(): TemplateRef<any> {
3212
        if (this.isLoading && (this.hasZeroResultFilter || this.hasNoData)) {
41,357✔
3213
            return this.loadingGridTemplate ? this.loadingGridTemplate : this.loadingGridDefaultTemplate;
41,239✔
3214
        }
3215

41,357✔
3216
        if (this.hasZeroResultFilter) {
3217
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
3218
        }
3219

3220
        if (this.hasNoData) {
3221
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate;
×
3222
        }
3223
    }
3224

3225
    /**
3226
     * @hidden @internal
3227
     */
3228
    private get hasZeroResultFilter(): boolean {
7!
3229
        return this.filteredData && this.filteredData.length === 0;
×
3230
    }
3231

7✔
3232
    /**
1✔
3233
     * @hidden @internal
3234
     */
3235
    private get hasNoData(): boolean {
6✔
3236
        return !this.data || this.dataLength === 0;
3237
    }
3238

3239
    /**
3240
     * @hidden @internal
3241
     */
3242
    public get shouldOverlayLoading(): boolean {
2,529✔
3243
        return this.isLoading && !this.hasNoData && !this.hasZeroResultFilter;
100✔
3244
    }
3245

2,429✔
3246
    /**
3247
     * @hidden @internal
3248
     */
3249
    public get isMultiRowSelectionEnabled(): boolean {
3250
        return this.rowSelection === GridSelectionMode.multiple
3251
            || this.rowSelection === GridSelectionMode.multipleCascade;
3252
    }
2,653!
3253

2,653✔
3254
    /**
3255
     * @hidden @internal
×
3256
     */
3257
    public get isRowSelectable(): boolean {
3258
        return this.rowSelection !== GridSelectionMode.none;
3259
    }
201,319✔
3260

3261
    /**
3262
     * @hidden @internal
3263
     */
15,129✔
3264
    public get isCellSelectable() {
3265
        return this.cellSelection !== GridSelectionMode.none;
3266
    }
3267

3268
    /**
3269
     * @hidden @internal
3270
     */
3271
    public get columnInDrag() {
3272
        return this.gridAPI.cms.column;
3273
    }
3274

358,393✔
3275
    constructor(
3276
        public readonly validation: IgxGridValidationService,
3277
        /** @hidden @internal */
105✔
3278
        public readonly selectionService: IgxGridSelectionService,
3279
        protected colResizingService: IgxColumnResizingService,
3280
        @Inject(IGX_GRID_SERVICE_BASE) public readonly gridAPI: GridServiceType,
13✔
3281
        protected transactionFactory: IgxFlatTransactionFactory,
3282
        private elementRef: ElementRef<HTMLElement>,
3283
        protected zone: NgZone,
3284
        /** @hidden @internal */
3285
        @Inject(DOCUMENT) public document: any,
3286
        public readonly cdr: ChangeDetectorRef,
3287
        protected differs: IterableDiffers,
3288
        protected viewRef: ViewContainerRef,
3289
        private appRef: ApplicationRef,
3290
        protected injector: Injector,
3291
        protected envInjector: EnvironmentInjector,
3292
        public navigation: IgxGridNavigationService,
3293
        /** @hidden @internal */
258✔
3294
        public filteringService: IgxFilteringService,
258✔
3295
        @Inject(IgxOverlayService) protected overlayService: IgxOverlayService,
3296
        /** @hidden @internal */
3297
        public summaryService: IgxGridSummaryService,
3298
        @Optional() @Inject(DisplayDensityToken) protected _displayDensityOptions: IDisplayDensityOptions,
3299
        @Inject(LOCALE_ID) private localeId: string,
3300
        protected platform: PlatformUtil,
3301
        @Optional() @Inject(IgxGridTransaction) protected _diTransactions?: TransactionService<Transaction, State>
3302
    ) {
3303
        super(_displayDensityOptions);
3304
        this.locale = this.locale || this.localeId;
3305
        this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
3306
        this._transactions.cloneStrategy = this.dataCloneStrategy;
18✔
3307
        this.cdr.detach();
18✔
3308
    }
3309

3310
    /**
3311
     * @hidden
3312
     * @internal
3313
     */
3314
    @HostListener('mouseleave')
3315
    public hideActionStrip() {
3316
        this.actionStrip?.hide();
3317
    }
3318

3319
    /**
3320
     * @hidden
3321
     * @internal
3322
     */
29✔
3323
    public get headerFeaturesWidth() {
30✔
3324
        return this._headerFeaturesWidth;
429✔
3325
    }
30✔
3326

3327
    /**
3328
     * @hidden
3329
     * @internal
3330
     */
3331
    public isDetailRecord(_rec) {
3332
        return false;
3333
    }
3334

3335
    /**
3336
     * @hidden
3337
     * @internal
3338
     */
3339
    public isGroupByRecord(_rec) {
5✔
3340
        return false;
6✔
3341
    }
1✔
3342

3343
    /**
3344
     * @hidden @internal
5✔
3345
     */
5✔
3346
    public isGhostRecord(record: any): boolean {
3347
        return record.ghostRecord !== undefined;
3348
    }
3349
    /**
3350
     * @hidden @internal
3351
     */
3352
    public isAddRowRecord(record: any): boolean {
3353
        return record.addRow !== undefined;
3354
    }
3355

3356
    /**
7✔
3357
     * @hidden
7✔
3358
     * Returns the row index of a row that takes into account the full view data like pinning.
3359
     */
3360
    public getDataViewIndex(rowIndex, pinned) {
3361
        if (pinned && !this.isRowPinningToTop) {
3362
            rowIndex = rowIndex + this.unpinnedDataView.length;
3363
        } else if (!pinned && this.isRowPinningToTop) {
1✔
3364
            rowIndex = rowIndex + this.pinnedDataView.length;
1✔
3365
        }
1✔
3366
        return rowIndex;
1✔
3367
    }
1✔
3368

3369
    /**
3370
     * @hidden
3371
     * @internal
3372
     */
3373
    public get hasDetails() {
65,188✔
3374
        return false;
3375
    }
3376

3377
    /**
3378
     * Returns the state of the grid virtualization.
3379
     *
172✔
3380
     * @remarks
6✔
3381
     * Includes the start index and how many records are rendered.
6✔
3382
     * @example
3383
     * ```typescript
166✔
3384
     * const gridVirtState = this.grid1.virtualizationState;
6✔
3385
     * ```
3386
     */
3387
    public get virtualizationState() {
163✔
3388
        return this.verticalScrollContainer.state;
3389
    }
163✔
3390

3391
    /**
3392
     * @hidden
3393
     * @internal
3394
     */
3395
    public hideOverlays() {
345✔
3396
        this.overlayIDs.forEach(overlayID => {
345✔
3397
            const overlay = this.overlayService.getOverlayById(overlayID);
212✔
3398

3399
            if (overlay?.visible && !overlay.closeAnimationPlayer?.hasStarted()) {
322✔
3400
                this.overlayService.hide(overlayID);
3401

3402
                this.nativeElement.focus();
3403
            }
3404
        });
3405
    }
171✔
3406

169✔
3407
    /**
168✔
3408
     * Returns whether the record is pinned or not.
168✔
3409
     *
168✔
3410
     * @param rowIndex Index of the record in the `dataView` collection.
168✔
3411
     *
3412
     * @hidden
3413
     * @internal
3414
     */
3415
    public isRecordPinnedByViewIndex(rowIndex: number) {
3416
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this.pinnedDataView.length) ||
348✔
3417
            (!this.isRowPinningToTop && rowIndex >= this.unpinnedDataView.length);
3418
    }
3419

3420
    /**
3421
     * Returns whether the record is pinned or not.
3422
     *
3423
     * @param rowIndex Index of the record in the `filteredSortedData` collection.
3424
     */
3425
    public isRecordPinnedByIndex(rowIndex: number) {
3426
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this._filteredSortedPinnedData.length) ||
4✔
3427
            (!this.isRowPinningToTop && rowIndex >= this._filteredSortedUnpinnedData.length);
167✔
3428
    }
167✔
3429

3430
    /**
3431
     * @hidden
3432
     * @internal
3433
     */
3434
    public isRecordPinned(rec) {
3435
        return this.getInitialPinnedIndex(rec) !== -1;
3436
    }
3437

3438
    /**
3439
     * @hidden
3440
     * @internal
67✔
3441
     * Returns the record index in order of pinning by the user. Does not consider sorting/filtering.
67✔
3442
     */
3443
    public getInitialPinnedIndex(rec) {
3444
        const id = this.gridAPI.get_row_id(rec);
3445
        return this._pinnedRecordIDs.indexOf(id);
3446
    }
3447

3448
    /**
3449
     * @hidden
3450
     * @internal
3451
     */
3452
    public get hasPinnedRecords() {
3453
        return this._pinnedRecordIDs.length > 0;
3454
    }
12✔
3455

12✔
3456
    /**
7✔
3457
     * @hidden
3458
     * @internal
3459
     */
5✔
3460
    public get pinnedRecordsCount() {
18!
3461
        return this._pinnedRecordIDs.length;
×
3462
    }
×
3463

3464
    /**
3465
     * @hidden
18✔
3466
     * @internal
3467
     */
3468
    public get crudService() {
3469
        return this.gridAPI.crudService;
12✔
3470
    }
12✔
3471

3472
    /**
3473
     * @hidden
3474
     * @internal
3475
     */
3476
    public _setupServices() {
3477
        this.gridAPI.grid = this as any;
3478
        this.crudService.grid = this as any;
3479
        this.selectionService.grid = this as any;
3480
        this.validation.grid = this as any;
3481
        this.navigation.grid = this as any;
3482
        this.filteringService.grid = this as any;
3✔
3483
        this.summaryService.grid = this as any;
3✔
3484
    }
2✔
3485

3486
    /**
3487
     * @hidden
1✔
3488
     * @internal
2!
3489
     */
×
3490
    public _setupListeners() {
×
3491
        const destructor = takeUntil<any>(this.destroy$);
3492
        fromEvent(this.nativeElement, 'focusout').pipe(filter(() => !!this.navigation.activeNode), destructor).subscribe((event) => {
3493
            if (!this.crudService.cell &&
2✔
3494
                !!this.navigation.activeNode &&
3495
                ((event.target === this.tbody.nativeElement && this.navigation.activeNode.row >= 0 &&
3496
                    this.navigation.activeNode.row < this.dataView.length)
3497
                    || (event.target === this.theadRow.nativeElement && this.navigation.activeNode.row === -1)
3✔
3498
                    || (event.target === this.tfoot.nativeElement && this.navigation.activeNode.row === this.dataView.length)) &&
3✔
3499
                !(this.rowEditable && this.crudService.rowEditingBlocked && this.crudService.rowInEditMode)) {
3500
                this.navigation.lastActiveNode = this.navigation.activeNode;
3501
                this.navigation.activeNode = {} as IActiveNode;
3502
                this.notifyChanges();
3503
            }
3504
        });
3505
        this.rowAddedNotifier.pipe(destructor).subscribe(args => this.refreshGridState(args));
3506
        this.rowDeletedNotifier.pipe(destructor).subscribe(args => {
3507
            this.summaryService.deleteOperation = true;
3508
            this.summaryService.clearSummaryCache(args);
3509
        });
3✔
3510

3✔
3511
        this.subscribeToTransactions();
3512

3513
        this.resizeNotify.pipe(
3514
            filter(() => !this._init),
3515
            throttleTime(0, animationFrameScheduler, { leading: true, trailing: true }),
3516
            destructor
3517
        )
3518
            .subscribe(() => {
3519
                this.zone.run(() => {
3520
                    // do not trigger reflow if element is detached.
3521
                    if (this.document.contains(this.nativeElement)) {
15✔
3522
                        this.notifyChanges(true);
3523
                    }
3524
                });
3525
            });
3526

3527
        this.pipeTriggerNotifier.pipe(takeUntil(this.destroy$)).subscribe(() => this.pipeTrigger++);
3528
        this.columnMovingEnd.pipe(destructor).subscribe(() => this.crudService.endEdit(false));
3529

3530
        this.overlayService.opening.pipe(destructor).subscribe((event) => {
3531
            if (this._advancedFilteringOverlayId === event.id) {
16✔
3532
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
20!
3533
                if (instance) {
20✔
3534
                    instance.initialize(this as any, this.overlayService, event.id);
3535
                }
3536
            }
×
3537
        });
×
3538

×
3539
        this.overlayService.opened.pipe(destructor).subscribe((event) => {
3540
            const overlaySettings = this.overlayService.getOverlayById(event.id)?.settings;
3541

3542
            // do not hide the advanced filtering overlay on scroll
3543
            if (this._advancedFilteringOverlayId === event.id) {
3544
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
3545
                if (instance) {
97✔
3546
                    instance.lastActiveNode = this.navigation.activeNode;
13✔
3547
                    instance.queryBuilder.setAddButtonFocus();
3548
                }
1✔
3549
                return;
3550
            }
12✔
3551

12✔
3552
            // do not hide the overlay if it's attached to a row
3553
            if (this.rowEditingOverlay?.overlayId === event.id) {
12!
3554
                return;
12✔
3555
            }
3556

12✔
3557
            if (overlaySettings?.outlet === this.outlet && this.overlayIDs.indexOf(event.id) === -1) {
3558
                this.overlayIDs.push(event.id);
12!
3559
            }
×
3560
        });
×
3561

×
3562
        this.overlayService.closed.pipe(filter(() => !this._init), destructor).subscribe((event) => {
3563
            if (this._advancedFilteringOverlayId === event.id) {
3564
                this.overlayService.detach(this._advancedFilteringOverlayId);
×
3565
                this._advancedFilteringOverlayId = null;
×
3566
                return;
3567
            }
3568

3569
            const ind = this.overlayIDs.indexOf(event.id);
×
3570
            if (ind !== -1) {
×
3571
                this.overlayIDs.splice(ind, 1);
3572
            }
×
3573
        });
3574

12✔
3575
        this.verticalScrollContainer.dataChanging.pipe(filter(() => !this._init), destructor).subscribe(($event) => {
2✔
3576
            const shouldRecalcSize = this.isPercentHeight &&
3577
                (!this.calcHeight || this.calcHeight === this.getDataBasedBodyHeight() ||
3578
                    this.calcHeight === this.renderedRowHeight * this._defaultTargetRecordNumber);
10!
3579
            if (shouldRecalcSize) {
×
3580
                this.calculateGridHeight();
×
3581
                $event.containerSize = this.calcHeight;
3582
            }
3583
            this.evaluateLoadingState();
10✔
3584
        });
10✔
3585

3586
        this.verticalScrollContainer.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
10✔
3587
            // called to recalc all widths that may have changes as a result of
3588
            // the vert. scrollbar showing/hiding
3589
            this.notifyChanges(true);
3590
            this.cdr.detectChanges();
3591
        });
3592

3593
        this.verticalScrollContainer.contentSizeChange.pipe(filter(() => !this._init), throttleTime(30), destructor).subscribe(() => {
10✔
3594
            this.notifyChanges(true);
10✔
3595
        });
10✔
3596

1✔
3597
        this.densityChanged.pipe(destructor).subscribe(() => {
3598
            this._autoSize = this.isPercentHeight && this.calcHeight !== this.getDataBasedBodyHeight();
9✔
3599
            this.crudService.endEdit(false);
9!
3600
            if (this._summaryRowHeight === 0) {
9✔
3601
                this.summaryService.summaryHeight = 0;
2✔
3602
            }
3603
            this.notifyChanges(true);
9✔
3604
        });
4✔
3605
    }
3606

9✔
3607
    /**
3608
     * @hidden
3609
     */
3610
    public override ngOnInit() {
9✔
3611
        super.ngOnInit();
9✔
3612
        this._setupServices();
3613
        this._setupListeners();
3614
        this.rowListDiffer = this.differs.find([]).create(null);
3615
        // compare based on field, not on object ref.
3616
        this.columnListDiffer = this.differs.find([]).create((index, col: ColumnType) => col.field);
3617
        this.calcWidth = this.width && this.width.indexOf('%') === -1 ? parseInt(this.width, 10) : 0;
27✔
3618
        this.shouldGenerate = this.autoGenerate;
27✔
3619
    }
27✔
3620

3621
    /**
3622
     * @hidden
3623
     * @internal
3624
     */
3625
    public resetColumnsCaches() {
3626
        this._columns.forEach(column => column.resetCaches());
3627
    }
3628

3629
    /**
3630
     * @hidden @internal
3631
     */
3632
    public generateRowID(): string | number {
405✔
3633
        const primaryColumn = this._columns.find(col => col.field === this.primaryKey);
830✔
3634
        const idType = this.data.length ?
830!
3635
            this.resolveDataTypes(this.data[0][this.primaryKey]) : primaryColumn ? primaryColumn.dataType : 'string';
5,046✔
3636
        return idType === 'string' ? uuidv4() : FAKE_ROW_ID--;
×
3637
    }
3638

830✔
3639
    /**
43✔
3640
     * @hidden
3641
     * @internal
3642
     */
830✔
3643
    public resetForOfCache() {
830✔
3644
        const firstVirtRow = this.dataRowList.first;
830✔
3645
        if (firstVirtRow) {
142✔
3646
            if (this._cdrRequests) {
139✔
3647
                firstVirtRow.virtDirRow.cdr.detectChanges();
16✔
3648
            }
3649
            firstVirtRow.virtDirRow.assumeMaster();
3650
        }
123✔
3651
    }
3652

3653
    /**
3654
     * @hidden
688✔
3655
     * @internal
93✔
3656
     */
93!
3657
    public setFilteredData(data, pinned: boolean) {
×
3658
        if (this.hasPinnedRecords && pinned) {
3659
            this._filteredPinnedData = data || [];
3660
            const filteredUnpinned = this._filteredUnpinnedData || [];
93✔
3661
            const filteredData = [... this._filteredPinnedData, ...filteredUnpinned];
3662
            this._filteredData = filteredData.length > 0 ? filteredData : this._filteredUnpinnedData;
3663
        } else if (this.hasPinnedRecords && !pinned) {
3664
            this._filteredUnpinnedData = data;
3665
        } else {
595✔
3666
            this._filteredData = data;
3667
        }
3668
    }
3669

3670
    /**
3671
     * @hidden
3672
     * @internal
3673
     */
3674
    public resetColumnCollections() {
3675
        this._visibleColumns.length = 0;
3676
        this._pinnedVisible.length = 0;
3677
        this._unpinnedVisible.length = 0;
3678
    }
3679

3680
    /**
×
3681
     * @hidden
477✔
3682
     * @internal
56✔
3683
     */
56✔
3684
    public resetCachedWidths() {
2✔
3685
        this._unpinnedWidth = NaN;
3686
        this._pinnedWidth = NaN;
460!
3687
        this._totalWidth = NaN;
×
3688
    }
147✔
3689

54✔
3690
    /**
54✔
3691
     * @hidden
39✔
3692
     * @internal
3693
     */
3694
    public resetCaches(recalcFeatureWidth = true) {
15✔
3695
        if (recalcFeatureWidth) {
15!
3696
            this._headerFeaturesWidth = NaN;
×
3697
        }
3698
        this.resetForOfCache();
3699
        this.resetColumnsCaches();
15✔
3700
        this.resetColumnCollections();
3701
        this.resetCachedWidths();
3702
        this.hasVisibleColumns = undefined;
3703
        this._columnGroups = this._columns.some(col => col.columnGroup);
3704
    }
3705

3706
    /**
3707
     * @hidden
3708
     */
3709
    public ngAfterContentInit() {
3710
        if (this.sortHeaderIconDirectiveTemplate) {
3711
            this.sortHeaderIconTemplate = this.sortHeaderIconDirectiveTemplate;
3712
        }
3713

3714
        if (this.sortAscendingHeaderIconDirectiveTemplate) {
3715
            this.sortAscendingHeaderIconTemplate = this.sortAscendingHeaderIconDirectiveTemplate;
1✔
3716
        }
300✔
3717

41✔
3718
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
41✔
3719
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
2✔
3720
        }
3721

237✔
3722
        this.setupColumns();
4✔
3723
        this.toolbar.changes.pipe(filter(() => !this._init), takeUntil(this.destroy$)).subscribe(() => this.notifyChanges(true));
141✔
3724
        this.setUpPaginator();
39✔
3725
        this.paginationComponents.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
39✔
3726
            this.setUpPaginator();
24✔
3727
        });
3728
        if (this.actionStrip) {
3729
            this.actionStrip.menuOverlaySettings.outlet = this.outlet;
15✔
3730
        }
15✔
3731
    }
7✔
3732

3733
    /**
3734
     * @hidden @internal
8✔
3735
     */
3736
    public dataRebinding(event: IForOfDataChangingEventArgs) {
3737
        this.dataChanging.emit(event);
3738
    }
3739

3740
    /**
3741
     * @hidden @internal
3742
     */
×
3743
    public dataRebound(event) {
14✔
3744
        this.selectionService.clearHeaderCBState();
14✔
3745
        this.dataChanged.emit(event);
3✔
3746
    }
3747

11✔
3748
    /** @hidden @internal */
11!
3749
    public createFilterDropdown(column: ColumnType, options: OverlaySettings) {
11✔
3750
        options.outlet = this.outlet;
3751
        if (this.excelStyleFilteringComponent) {
3752
            this.excelStyleFilteringComponent.initialize(column, this.overlayService);
3753
            const id = this.overlayService.attach(this.excelStyleFilteringComponent.element, options);
3754
            this.excelStyleFilteringComponent.overlayComponentId = id;
3755
            return { id, ref: undefined };
3756
        }
1,332,862✔
3757
        const ref = this.createComponentInstance(IgxGridExcelStyleFilteringComponent);
3758
        ref.instance.initialize(column, this.overlayService);
3759
        const id = this.overlayService.attach(ref.instance.element, options);
3760
        ref.instance.overlayComponentId = id;
3761
        return { ref, id };
3762
    }
×
3763

3764
    private createComponentInstance(component: any) {
3765
        const dynamicComponent: ComponentRef<any> = createComponent(component, { environmentInjector: this.envInjector, elementInjector: this.injector } );
3766
        this.appRef.attachView(dynamicComponent.hostView);
3767
        return dynamicComponent;
3768
    }
3769

205✔
3770
    /** @hidden @internal */
205✔
3771
    public setUpPaginator() {
205✔
3772
        if (this.paginator) {
3773
            this.paginator.pageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
3774
                .subscribe(() => {
3775
                    this.selectionService.clear(true);
3776
                    this.crudService.endEdit(false);
3777
                    this.pipeTrigger++;
128✔
3778
                    this.navigateTo(0);
128✔
3779
                    this.notifyChanges();
128✔
3780
                });
128✔
3781
            this.paginator.perPageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
3782
                .subscribe(() => {
3783
                    this.selectionService.clear(true);
3784
                    this.page = 0;
3785
                    this.crudService.endEdit(false);
3786
                    this.notifyChanges();
253✔
3787
                });
253!
3788
        } else {
253✔
3789
            this.markForCheck();
3790
        }
3791
    }
×
3792

3793
    /**
3794
     * @hidden
3795
     * @internal
3796
     */
3797
    public setFilteredSortedData(data, pinned: boolean) {
3798
        data = data || [];
811✔
3799
        if (this.pinnedRecordsCount > 0) {
48✔
3800
            if (pinned) {
48!
3801
                this._filteredSortedPinnedData = data;
48✔
3802
                this.pinnedRecords = data;
48✔
3803
                this._filteredSortedData = this.isRowPinningToTop ? [... this._filteredSortedPinnedData, ... this._filteredSortedUnpinnedData] :
48✔
3804
                    [... this._filteredSortedUnpinnedData, ... this._filteredSortedPinnedData];
3805
                this.refreshSearch(true, false);
3806
            } else {
×
3807
                this._filteredSortedUnpinnedData = data;
3808
            }
3809
        } else {
3810
            this._filteredSortedData = data;
3811
            this.refreshSearch(true, false);
3812
        }
3813
        this.buildDataView(data);
3814
    }
603✔
3815

428✔
3816
    /**
428✔
3817
     * @hidden @internal
428✔
3818
     */
428✔
3819
    public resetHorizontalVirtualization() {
428✔
3820
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
428✔
3821
        this._horizontalForOfs = [
428✔
3822
            ...this._dataRowList.filter(elementFilter).map(item => item.virtDirRow),
259✔
3823
            ...this._summaryRowList.filter(elementFilter).map(item => item.virtDirRow)
3824
        ];
169✔
3825
    }
43✔
3826

3827
    /**
3828
     * @hidden @internal
3829
     */
3830
    public _setupRowObservers() {
3831
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
3832
        const extractForOfs = pipe(map((collection: any[]) => collection.filter(elementFilter).map(item => item.virtDirRow)));
3833
        const rowListObserver = extractForOfs(this._dataRowList.changes);
3834
        const summaryRowObserver = extractForOfs(this._summaryRowList.changes);
82!
3835
        rowListObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
82✔
3836
            this.resetHorizontalVirtualization();
79✔
3837
        });
79!
3838
        summaryRowObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
79✔
3839
            this.resetHorizontalVirtualization();
79✔
3840
        });
79✔
3841
        this.resetHorizontalVirtualization();
3842
    }
3843

3844
    /**
3845
     * @hidden @internal
3846
     */
3847
    public _zoneBegoneListeners() {
3848
        this.zone.runOutsideAngular(() => {
3849
            this.verticalScrollContainer.getScroll().addEventListener('scroll', this.verticalScrollHandler.bind(this));
4!
3850
            this.headerContainer?.getScroll().addEventListener('scroll', this.horizontalScrollHandler.bind(this));
4✔
3851
            if (this.hasColumnsToAutosize) {
4✔
3852
                this.headerContainer?.dataChanged.pipe(takeUntil(this.destroy$)).subscribe(() => {
4✔
3853
                    this.cdr.detectChanges();
2✔
3854
                    this.zone.onStable.pipe(first()).subscribe(() => {
3855
                        this.autoSizeColumnsInView();
4✔
3856
                    });
3857
                });
3858
            }
3859
            fromEvent(window, 'resize').pipe(takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
3860
            resizeObservable(this.nativeElement).pipe(takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
3861
        });
3862
    }
48✔
3863

213✔
3864
    /**
48✔
3865
     * @hidden
48✔
3866
     */
48✔
3867
    public ngAfterViewInit() {
3868
        this.initPinning();
3869
        this.calculateGridSizes();
3870
        this._init = false;
3871
        this.cdr.reattach();
3872
        this._setupRowObservers();
612✔
3873
        this._zoneBegoneListeners();
3874

3875
        const vertScrDC = this.verticalScrollContainer.displayContainer;
3876
        vertScrDC.addEventListener('scroll', this.preventContainerScroll.bind(this));
3877

3878
        this._pinnedRowList.changes
334,121✔
3879
            .pipe(takeUntil(this.destroy$))
3880
            .subscribe((change: QueryList<IgxGridRowComponent>) => {
3881
                this.onPinnedRowsChanged(change);
3882
            });
3883

3884
        this.addRowSnackbar?.clicked.subscribe(() => {
122✔
3885
            const rec = this.filteredSortedData[this.lastAddedRowIndex];
122✔
3886
            this.scrollTo(rec, 0);
3887
            this.addRowSnackbar.close();
3888
        });
3889

3890
        // Keep the stream open for future subscribers
3891
        this.rendered$.pipe(takeUntil(this.destroy$)).subscribe(() => {
×
3892
            if (this.paginator) {
×
3893
                this.paginator.totalRecords = this.totalRecords ? this.totalRecords : this.paginator.totalRecords;
3894
                this.paginator.overlaySettings = { outlet: this.outlet };
3895
            }
×
3896
            if (this.hasColumnsToAutosize) {
3897
                this.autoSizeColumnsInView();
3898
            }
3899
            this._rendered = true;
3900
        });
3901
        Promise.resolve().then(() => this.rendered.next(true));
3902
    }
92✔
3903

3904
    /**
3905
     * @hidden @internal
3906
     */
3907
    public notifyChanges(repaint = false) {
3908
        this._cdrRequests = true;
3909
        this._cdrRequestRepaint = repaint;
3910
        this.cdr.markForCheck();
3911
    }
3912

3913
    /**
3914
     * @hidden @internal
3915
     */
3916
    public override ngDoCheck() {
3917
        super.ngDoCheck();
1✔
3918
        if (this._init) {
5✔
3919
            return;
3920
        }
3921

3922
        if (this._cdrRequestRepaint) {
3923
            this.resetNotifyChanges();
3924
            this.calculateGridSizes();
3925
            this.refreshSearch(true);
3926
            return;
3927
        }
3928

3929
        if (this._cdrRequests) {
3930
            this.resetNotifyChanges();
3931
            this.cdr.detectChanges();
3932
        }
3933
    }
3934

3935
    /**
3936
     * @hidden
3937
     * @internal
3✔
3938
     */
3✔
3939
    public getDragGhostCustomTemplate() {
2!
3940

×
3941
        return this.dragGhostCustomTemplate;
×
3942
    }
3943

2✔
3944
    /**
3945
     * @hidden @internal
3946
     */
3947
    public ngOnDestroy() {
1✔
3948
        this.tmpOutlets.forEach((tmplOutlet) => {
1!
3949
            tmplOutlet.cleanCache();
×
3950
        });
×
3951

3952
        this.destroy$.next(true);
3953
        this.destroy$.complete();
3✔
3954
        this.transactionChange$.next();
3955
        this.transactionChange$.complete();
3956
        this._destroyed = true;
3!
3957

×
3958
        if (this._advancedFilteringOverlayId) {
×
3959
            this.overlayService.detach(this._advancedFilteringOverlayId);
3960
            delete this._advancedFilteringOverlayId;
3961
        }
3!
3962

3963
        this.overlayIDs.forEach(overlayID => {
3✔
3964
            const overlay = this.overlayService.getOverlayById(overlayID);
3965

3966
            if (overlay && !overlay.detached) {
1✔
3967
                this.overlayService.detach(overlayID);
3968
            }
3969
        });
1✔
3970

3971
        this.zone.runOutsideAngular(() => {
1✔
3972
            this.verticalScrollContainer?.getScroll()?.removeEventListener('scroll', this.verticalScrollHandler);
1✔
3973
            this.headerContainer?.getScroll()?.removeEventListener('scroll', this.horizontalScrollHandler);
1✔
3974
            const vertScrDC = this.verticalScrollContainer?.displayContainer;
3975
            vertScrDC?.removeEventListener('scroll', this.preventContainerScroll);
2✔
3976
        });
3977
    }
3978

×
3979
    /**
3980
     * Toggles the specified column's visibility.
3981
     *
3982
     * @example
3983
     * ```typescript
3984
     * this.grid1.toggleColumnVisibility({
3985
     *       column: this.grid1.columns[0],
3986
     *       newValue: true
3987
     * });
3988
     * ```
3989
     */
3990
    public toggleColumnVisibility(args: IColumnVisibilityChangedEventArgs) {
3991
        const col = args.column ? this._columns.find((c) => c === args.column) : undefined;
3992

3993
        if (!col) {
1!
3994
            return;
1✔
3995
        }
3996
        col.toggleVisibility(args.newValue);
×
3997
    }
3998

3999
    /**
4000
     * Gets/Sets a list of key-value pairs [row ID, expansion state].
4001
     *
4002
     * @remarks
×
4003
     * Includes only states that differ from the default one.
×
4004
     * Supports two-way binding.
×
4005
     * @example
4006
     * ```html
4007
     * <igx-grid #grid [data]="data" [(expansionStates)]="model.expansionStates">
2✔
4008
     * </igx-grid>
4009
     * ```
3✔
4010
     */
2✔
4011
    @Input()
3!
4012
    public get expansionStates() {
3✔
4013
        return this._expansionStates;
4014
    }
4015

×
4016
    public set expansionStates(value) {
4017
        this._expansionStates = new Map<any, boolean>(value);
4018
        this.expansionStatesChange.emit(this._expansionStates);
4019
        this.notifyChanges(true);
237✔
4020
        if (this.gridAPI.grid) {
236✔
4021
            this.cdr.detectChanges();
4022
        }
4023
    }
1✔
4024

4025
    /**
237!
4026
     * Expands all rows.
237✔
4027
     *
4028
     * @example
4029
     * ```typescript
4030
     * this.grid.expandAll();
3,639✔
4031
     * ```
3,639✔
4032
     */
4033
    public expandAll() {
4034
        this._defaultExpandState = true;
4035
        this.expansionStates = new Map<any, boolean>();
318✔
4036
    }
318✔
4037

44!
4038
    /**
4039
     * Collapses all rows.
279✔
4040
     *
54!
4041
     * @example
4042
     * ```typescript
318✔
4043
     * this.grid.collapseAll();
22✔
4044
     * ```
27!
4045
     */
×
4046
    public collapseAll() {
4047
        this._defaultExpandState = false;
4048
        this.expansionStates = new Map<any, boolean>();
4049
    }
318✔
4050

88✔
4051
    /**
98✔
4052
     * Expands the row by its id.
43✔
4053
     *
43✔
4054
     * @remarks
4055
     * ID is either the primaryKey value or the data record instance.
55!
4056
     * @example
55✔
4057
     * ```typescript
55✔
4058
     * this.grid.expandRow(rowID);
29!
4059
     * ```
29!
4060
     * @param rowID The row id - primaryKey value or the data record instance.
29✔
4061
     */
4062
    public expandRow(rowID: any) {
4063
        this.gridAPI.set_row_expansion_state(rowID, true);
26✔
4064
    }
4065

4066
    /**
4067
     * Collapses the row by its id.
4068
     *
318✔
4069
     * @remarks
318✔
4070
     * ID is either the primaryKey value or the data record instance.
318✔
4071
     * @example
318✔
4072
     * ```typescript
4073
     * this.grid.collapseRow(rowID);
4074
     * ```
×
4075
     * @param rowID The row id - primaryKey value or the data record instance.
4076
     */
4077
    public collapseRow(rowID: any) {
4078
        this.gridAPI.set_row_expansion_state(rowID, false);
4079
    }
302✔
4080

302✔
4081

302✔
4082
    /**
302✔
4083
     * Toggles the row by its id.
302!
4084
     *
×
4085
     * @remarks
×
4086
     * ID is either the primaryKey value or the data record instance.
4087
     * @example
4088
     * ```typescript
4089
     * this.grid.toggleRow(rowID);
14✔
4090
     * ```
13✔
4091
     * @param rowID The row id - primaryKey value or the data record instance.
4092
     */
1!
4093
    public toggleRow(rowID: any) {
×
4094
        const rec = this.gridAPI.get_rec_by_id(rowID);
4095
        const state = this.gridAPI.get_row_expansion_state(rec);
4096
        this.gridAPI.set_row_expansion_state(rowID, !state);
1✔
4097
    }
4098

4099
    /**
4100
     * @hidden
4101
     * @internal
4102
     */
4103
    public getDefaultExpandState(_rec: any) {
4104
        return this._defaultExpandState;
3,120✔
4105
    }
4106

12✔
4107
    /**
4108
     * Gets the native element.
4109
     *
4110
     * @example
4111
     * ```typescript
12✔
4112
     * const nativeEl = this.grid.nativeElement.
4113
     * ```
4114
     */
3,108✔
4115
    public get nativeElement() {
4116
        return this.elementRef.nativeElement;
4117
    }
4118

4119
    /**
4120
     * Gets/Sets the outlet used to attach the grid's overlays to.
4121
     *
4122
     * @remark
4123
     * If set, returns the outlet defined outside the grid. Otherwise returns the grid's internal outlet directive.
11,109✔
4124
     */
4125
    @Input()
5,094✔
4126
    public get outlet() {
5,094✔
4127
        return this.resolveOutlet();
4128
    }
4129

6,015✔
4130
    public set outlet(val: IgxOverlayOutletDirective) {
4131
        this._userOutletDirective = val;
11,109✔
4132
    }
510✔
4133

4134

11,109✔
4135
    /**
195✔
4136
     * Gets the default row height.
4137
     *
11,109✔
4138
     * @example
2,226✔
4139
     * ```typescript
4140
     * const rowHeigh = this.grid.defaultRowHeight;
11,109✔
4141
     * ```
3,246✔
4142
     */
4143
    public get defaultRowHeight(): number {
11,109✔
4144
        switch (this.displayDensity) {
4145
            case DisplayDensity.cosy:
4146
                return 40;
4147
            case DisplayDensity.compact:
4148
                return 32;
4149
            default:
4150
                return 50;
11,109✔
4151
        }
10,395✔
4152
    }
4153

11,109✔
4154
    /**
68,995✔
4155
     * @hidden @internal
3,335✔
4156
     */
3,335✔
4157
    public get defaultSummaryHeight(): number {
4158
        switch (this.displayDensity) {
4159
            case DisplayDensity.cosy:
4160
                return 30;
65,660✔
4161
            case DisplayDensity.compact:
65,660✔
4162
                return 24;
4163
            default:
4164
                return 36;
11,109✔
4165
        }
4166
    }
4167

4168
    /**
4169
     * Returns the `IgxGridHeaderGroupComponent`'s minimum allowed width.
4170
     *
4171
     * @remarks
65,660✔
4172
     * Used internally for restricting header group component width.
65,660!
4173
     * The values below depend on the header cell default right/left padding values.
×
4174
     */
4175
    public get defaultHeaderGroupMinWidth(): number {
65,660✔
4176
        switch (this.displayDensity) {
65,660✔
4177
            case DisplayDensity.cosy:
65,660✔
4178
                return 32;
1,283✔
4179
            case DisplayDensity.compact:
4180
                return 24;
64,377!
4181
            default:
×
4182
                return 48;
4183
        }
64,377✔
4184
    }
1,520✔
4185

4186
    /** @hidden @internal */
4187
    public get pinnedWidth() {
64,377✔
4188
        if (!isNaN(this._pinnedWidth)) {
248✔
4189
            return this._pinnedWidth;
4190
        }
64,377✔
4191
        this._pinnedWidth = this.getPinnedWidth();
4192
        return this._pinnedWidth;
4193
    }
7,583✔
4194

7,583✔
4195
    /** @hidden @internal */
4196
    public get unpinnedWidth() {
4197
        if (!isNaN(this._unpinnedWidth)) {
4198
            return this._unpinnedWidth;
199,454!
4199
        }
4200
        this._unpinnedWidth = this.getUnpinnedWidth();
4201
        return this._unpinnedWidth;
4202
    }
4203

4204
    /**
4205
     * @hidden @internal
4206
     */
138✔
4207
    public get isHorizontalScrollHidden() {
138✔
4208
        const diff = this.unpinnedWidth - this.totalWidth;
138✔
4209
        return this.width === null || diff >= 0;
138✔
4210
    }
138✔
4211

4212
    /**
4213
     * @hidden @internal
4214
     * Gets the header cell inner width for auto-sizing.
4215
     */
4216
    public getHeaderCellWidth(element: HTMLElement): ISizeInfo {
4217
        const range = this.document.createRange();
4218
        const headerWidth = this.platform.getNodeSizeViaRange(range,
5,337✔
4219
            element,
32,444✔
4220
            element.parentElement);
32,444✔
4221

5,337✔
4222
        const headerStyle = this.document.defaultView.getComputedStyle(element);
5,337✔
4223
        const headerPadding = parseFloat(headerStyle.paddingLeft) + parseFloat(headerStyle.paddingRight) +
4224
            parseFloat(headerStyle.borderRightWidth);
4225

4226
        // Take into consideration the header group element, since column pinning applies borders to it if its not a columnGroup.
4227
        const headerGroupStyle = this.document.defaultView.getComputedStyle(element.parentElement);
4228
        const borderSize = parseFloat(headerGroupStyle.borderRightWidth) + parseFloat(headerGroupStyle.borderLeftWidth);
138!
4229
        return { width: Math.ceil(headerWidth), padding: Math.ceil(headerPadding + borderSize) };
×
4230
    }
4231

138✔
4232
    /**
1,189✔
4233
     * @hidden @internal
814✔
4234
     * Gets the combined width of the columns that are specific to the enabled grid features. They are fixed.
814✔
4235
     */
138✔
4236
    public featureColumnsWidth(expander?: ElementRef) {
4237
        if (Number.isNaN(this._headerFeaturesWidth)) {
4238
            // TODO: platformUtil.isBrowser check
138✔
4239
            const rowSelectArea = this.headerSelectorContainer?.nativeElement?.getBoundingClientRect ?
4240
                this.headerSelectorContainer.nativeElement.getBoundingClientRect().width : 0;
4241
            const rowDragArea = this.rowDraggable && this.headerDragContainer?.nativeElement?.getBoundingClientRect ?
4242
                this.headerDragContainer.nativeElement.getBoundingClientRect().width : 0;
4243
            const groupableArea = this.headerGroupContainer?.nativeElement?.getBoundingClientRect ?
4244
                this.headerGroupContainer.nativeElement.getBoundingClientRect().width : 0;
4245
            const expanderWidth = expander?.nativeElement?.getBoundingClientRect ? expander.nativeElement.getBoundingClientRect().width : 0;
4246
            this._headerFeaturesWidth = rowSelectArea + rowDragArea + groupableArea + expanderWidth;
4247
        }
138✔
4248
        return this._headerFeaturesWidth;
154✔
4249
    }
154✔
4250

154✔
4251
    /**
154✔
4252
     * @hidden @internal
154✔
4253
     */
87✔
4254
    public get summariesMargin() {
87✔
4255
        return this.featureColumnsWidth();
21✔
4256
    }
4257

4258
    /**
154✔
4259
     * Gets an array of `IgxColumnComponent`s.
4260
     *
4261
     * @example
4262
     * ```typescript
4263
     * const colums = this.grid.columns.
4264
     * ```
4265
     */
4266
    public get columns(): IgxColumnComponent[] {
16✔
4267
        return this._columns || [];
16✔
4268
    }
16✔
4269

4270
    /**
4271
     * Gets an array of the pinned `IgxColumnComponent`s.
4272
     *
4273
     * @example
4274
     * ```typescript
3,204✔
4275
     * const pinnedColumns = this.grid.pinnedColumns.
819✔
4276
     * ```
4277
     */
4278
    public get pinnedColumns(): IgxColumnComponent[] {
2,385✔
4279
        if (this._pinnedVisible.length) {
4280
            return this._pinnedVisible;
20,603✔
4281
        }
3,204✔
4282
        this._pinnedVisible = this._pinnedColumns.filter(col => !col.hidden);
3,204✔
4283
        return this._pinnedVisible;
4284
    }
4285

74✔
4286
    /**
4287
     * Gets an array of the pinned `IgxRowComponent`s.
4288
     *
4289
     * @example
2,210✔
4290
     * ```typescript
4291
     * const pinnedRow = this.grid.pinnedRows;
4292
     * ```
4293
     */
4294
    public get pinnedRows(): IgxGridRowComponent[] {
4295
        return this._pinnedRowList.toArray().sort((a, b) => a.index - b.index);
4296
    }
4297

×
4298
    /**
×
4299
     * Gets an array of unpinned `IgxColumnComponent`s.
×
4300
     *
×
4301
     * @example
4302
     * ```typescript
4303
     * const unpinnedColumns = this.grid.unpinnedColumns.
×
4304
     * ```
4305
     */
4306
    public get unpinnedColumns(): IgxColumnComponent[] {
4307
        if (this._unpinnedVisible.length) {
×
4308
            return this._unpinnedVisible;
×
4309
        }
4310
        this._unpinnedVisible = this._unpinnedColumns.filter((col) => !col.hidden);
4311
        return this._unpinnedVisible;
4312
    }
4313

4314
    /**
4315
     * Gets the `width` to be set on `IgxGridHeaderGroupComponent`.
836✔
4316
     */
4317
    public getHeaderGroupWidth(column: IgxColumnComponent): string {
4318
        return this.hasColumnLayouts
4319
            ? ''
4320
            : `${Math.max(parseFloat(column.calcWidth), this.defaultHeaderGroupMinWidth)}px`;
4321
    }
4322

182✔
4323
    /**
182!
4324
     * Returns the `IgxColumnComponent` by field name.
182✔
4325
     *
4326
     * @example
4327
     * ```typescript
4328
     * const myCol = this.grid1.getColumnByName("ID");
4329
     * ```
4330
     * @param name
4331
     */
65✔
4332
    public getColumnByName(name: string): IgxColumnComponent {
65!
4333
        return this._columns.find((col) => col.field === name);
4334
    }
4335

×
4336
    public getColumnByVisibleIndex(index: number): IgxColumnComponent {
×
4337
        return this.visibleColumns.find((col) =>
4338
            !col.columnGroup && !col.columnLayout &&
65!
4339
            col.visibleIndex === index
65✔
4340
        );
65✔
4341
    }
65✔
4342

1,018✔
4343
    /**
1,018✔
4344
     * Recalculates all widths of columns that have size set to `auto`.
1✔
4345
     *
4346
     * @example
4347
     * ```typescript
1,017✔
4348
     * this.grid1.recalculateAutoSizes();
4349
     * ```
4350
     */
1,279✔
4351
    public recalculateAutoSizes() {
65✔
4352
        // reset auto-size and calculate it again.
129✔
4353
        this._columns.forEach(x => x.autoSize = undefined);
129✔
4354
        this.resetCaches();
4355
        this.zone.onStable.pipe(first()).subscribe(() => {
126✔
4356
            this.cdr.detectChanges();
4357
            this.autoSizeColumnsInView();
126✔
4358
        });
4359
    }
126!
4360

4361
    /**
4362
     * Returns an array of visible `IgxColumnComponent`s.
×
4363
     *
4364
     * @example
4365
     * ```typescript
126✔
4366
     * const visibleColumns = this.grid.visibleColumns.
4367
     * ```
126✔
4368
     */
4369
    public get visibleColumns(): IgxColumnComponent[] {
129✔
4370
        if (this._visibleColumns.length) {
4371
            return this._visibleColumns;
65✔
4372
        }
65✔
4373
        this._visibleColumns = this._columns.filter(c => !c.hidden);
61✔
4374
        return this._visibleColumns;
4375
    }
4376

4377
    /**
4378
     * Returns the total number of records.
4379
     *
4380
     * @remarks
4381
     * Only functions when paging is enabled.
61✔
4382
     * @example
61✔
4383
     * ```typescript
4384
     * const totalRecords = this.grid.totalRecords;
4385
     * ```
61✔
4386
     */
4387
    @Input()
4388
    public get totalRecords(): number {
4389
        return this._totalRecords >= 0 ? this._totalRecords : this.pagingState?.metadata.countRecords;
4390
    }
4391

7,274✔
4392
    public set totalRecords(total: number) {
4393
        if (total >= 0) {
4394
            if (this.paginator) {
4395
                this.paginator.totalRecords = total;
4396
            }
4397
            this._totalRecords = total;
4398
            this.pipeTrigger++;
4399
            this.notifyChanges();
4400
        }
7,276✔
4401
    }
7,276✔
4402

7,276✔
4403
    /** @hidden @internal */
7,276✔
4404
    public get totalWidth(): number {
7,276✔
4405
        if (!isNaN(this._totalWidth)) {
7,276✔
4406
            return this._totalWidth;
7,276✔
4407
        }
7,276✔
4408
        // Take only top level columns
803✔
4409
        const cols = this.visibleColumns.filter(col => col.level === 0 && !col.pinned);
4410
        let totalWidth = 0;
7,276✔
4411
        let i = 0;
107✔
4412
        for (i; i < cols.length; i++) {
4413
            totalWidth += parseInt(cols[i].calcWidth, 10) || 0;
7,276✔
4414
        }
4415
        this._totalWidth = totalWidth;
7,276✔
4416
        return totalWidth;
83✔
4417
    }
83✔
4418

4419
    /**
7,276✔
4420
     * @hidden
200✔
4421
     * @internal
200✔
4422
     */
200✔
4423
    public get showRowSelectors(): boolean {
4424
        return this.isRowSelectable && this.hasVisibleColumns && !this.hideRowSelectors;
4425
    }
4426

7,076✔
4427
    /**
7,076✔
4428
     * @hidden
7,076✔
4429
     * @internal
4430
     */
4431
    public get showAddButton() {
4432
        return this.rowEditable && this.dataView.length === 0 && this._columns.length > 0;
7,276✔
4433
    }
7,276✔
4434

15✔
4435
    /**
15✔
4436
     * @hidden
15✔
4437
     * @internal
4438
     */
4439
    public get showDragIcons(): boolean {
4440
        return this.rowDraggable && this._columns.length > this.hiddenColumnsCount;
4441
    }
4442

4443
    /**
4444
     * @hidden
4445
     * @internal
6,853✔
4446
     */
583✔
4447
    protected _getDataViewIndex(index: number): number {
583✔
4448
        let newIndex = index;
583✔
4449
        if ((index < 0 || index >= this.dataView.length) && this.pagingMode === 1 && this.page !== 0) {
583✔
4450
            newIndex = index - this.perPage * this.page;
4451
        } else if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
4452
            newIndex = index - this.gridAPI.grid.virtualizationState.startIndex;
4453
        }
4454
        return newIndex;
4455
    }
4456

4457
    /**
7,216✔
4458
     * @hidden
7,216✔
4459
     * @internal
7,216✔
4460
     */
106✔
4461
    protected getDataIndex(dataViewIndex: number): number {
4462
        let newIndex = dataViewIndex;
4463
        if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
4464
            newIndex = dataViewIndex + this.gridAPI.grid.virtualizationState.startIndex;
4465
        }
4466
        return newIndex;
4467
    }
1,976✔
4468

4469
    /**
4470
     * Places a column before or after the specified target column.
4471
     *
4472
     * @example
4473
     * ```typescript
25,572✔
4474
     * grid.moveColumn(column, target);
4475
     * ```
4476
     */
4477
    public moveColumn(column: IgxColumnComponent, target: IgxColumnComponent, pos: DropPosition = DropPosition.AfterDropTarget) {
4478
        // M.A. May 11th, 2021 #9508 Make the event cancelable
4479
        const eventArgs: IColumnMovingEndEventArgs = { source: column, target, cancel: false };
6,620✔
4480

4481
        this.columnMovingEnd.emit(eventArgs);
4482

4483
        if (eventArgs.cancel) {
4484
            return;
4485
        }
6,620✔
4486

6,620✔
4487
        if (column === target || (column.level !== target.level) ||
4488
            (column.topLevelParent !== target.topLevelParent)) {
4489
            return;
4490
        }
4491

4492
        if (column.level) {
4493
            this._moveChildColumns(column.parent, column, target, pos);
4494
        }
6,620✔
4495

6,620✔
4496
        // let columnPinStateChanged;
251✔
4497
        // pinning and unpinning will work correctly even without passing index
4498
        // but is easier to calclulate the index here, and later use it in the pinning event args
6,620✔
4499
        if (target.pinned && !column.pinned) {
4500
            const pinnedIndex = this._pinnedColumns.indexOf(target);
4501
            const index = pos === DropPosition.AfterDropTarget ? pinnedIndex + 1 : pinnedIndex;
4502
            column.pin(index);
4503
        }
4504

6,620✔
4505
        if (!target.pinned && column.pinned) {
6,620!
4506
            const unpinnedIndex = this._unpinnedColumns.indexOf(target);
6,620✔
4507
            const index = pos === DropPosition.AfterDropTarget ? unpinnedIndex + 1 : unpinnedIndex;
6,620✔
4508
            column.unpin(index);
4509
        }
4510

6,620✔
4511
        // if (target.pinned && column.pinned && !columnPinStateChanged) {
4512
        //     this._reorderColumns(column, target, pos, this._pinnedColumns);
4513
        // }
4514

4515
        // if (!target.pinned && !column.pinned && !columnPinStateChanged) {
4516
        //     this._reorderColumns(column, target, pos, this._unpinnedColumns);
5,593✔
4517
        // }
4518

5,593✔
4519
        this._moveColumns(column, target, pos);
4520
        this._columnsReordered(column);
5,593✔
4521
    }
4522

4523
    /**
4524
     * Triggers change detection for the `IgxGridComponent`.
4525
     * Calling markForCheck also triggers the grid pipes explicitly, resulting in all updates being processed.
4526
     * May degrade performance if used when not needed, or if misused:
7,216✔
4527
     * ```typescript
596✔
4528
     * // DON'Ts:
4529
     * // don't call markForCheck from inside a loop
6,620✔
4530
     * // don't call markForCheck when a primitive has changed
6,620✔
4531
     * grid.data.forEach(rec => {
6,620✔
4532
     *  rec = newValue;
6,620✔
4533
     *  grid.markForCheck();
6,620✔
4534
     * });
6,620✔
4535
     *
6,620✔
4536
     * // DOs
4537
     * // call markForCheck after updating a nested property
4538
     * grid.data.forEach(rec => {
6,620✔
4539
     *  rec.nestedProp1.nestedProp2 = newValue;
6,620✔
4540
     * });
1,317✔
4541
     * grid.markForCheck();
1,317✔
4542
     * ```
1,317✔
4543
     *
628✔
4544
     * @example
628✔
4545
     * ```typescript
4546
     * grid.markForCheck();
689✔
4547
     * ```
4548
     */
4549
    public markForCheck() {
5,303✔
4550
        this.pipeTrigger++;
4551
        this.cdr.detectChanges();
5,992✔
4552
    }
5,992✔
4553

4✔
4554
    /**
4!
4555
     * Creates a new `IgxGridRowComponent` and adds the data record to the end of the data source.
4556
     *
5,988✔
4557
     * @example
4558
     * ```typescript
4559
     * this.grid1.addRow(record);
4!
4560
     * ```
4✔
4561
     * @param data
4✔
4562
     */
4✔
4563
    public addRow(data: any): void {
4✔
4564
        // commit pending states prior to adding a row
4✔
4565
        this.crudService.endEdit(true);
4566
        this.gridAPI.addRowToData(data);
4567

778✔
4568
        this.pipeTrigger++;
778✔
4569
        this.rowAddedNotifier.next({ data: data, owner: this, primaryKey: data[this.primaryKey] });
778✔
4570
        this.notifyChanges();
4571
    }
4572

778!
4573
    /**
4574
     * Removes the `IgxGridRowComponent` and the corresponding data record by primary key.
4575
     *
4✔
4576
     * @remarks
4✔
4577
     * Requires that the `primaryKey` property is set.
4578
     * The method accept rowSelector as a parameter, which is the rowID.
778✔
4579
     * @example
778✔
4580
     * ```typescript
4581
     * this.grid1.deleteRow(0);
4582
     * ```
4583
     * @param rowSelector
4584
     */
4585
    public deleteRow(rowSelector: any): any {
4586
        if (this.primaryKey !== undefined && this.primaryKey !== null) {
21,380✔
4587
            return this.deleteRowById(rowSelector);
21,380✔
4588
        }
4589
    }
12,275✔
4590

21,380✔
4591
    /** @hidden */
3,917✔
4592
    public deleteRowById(rowId: any): any {
4593
        const args = {
21,380✔
4594
            rowID: rowId,
59✔
4595
            primaryKey: rowId,
4596
            cancel: false,
21,380✔
4597
            rowData: this.getRowData(rowId),
4598
            oldValue: null,
4599
            owner: this
4600
        };
4601
        this.rowDelete.emit(args);
4602
        if (args.cancel) {
24✔
4603
            return;
24!
4604
        }
24✔
4605

24✔
4606
        const record = this.gridAPI.deleteRowById(rowId);
2!
4607
        if (record !== null && record !== undefined) {
2✔
4608
            const rowDeletedEventArgs: IRowDataEventArgs = { data: record, owner: this, primaryKey: record[this.primaryKey] };
4609
            this.rowDeleted.emit(rowDeletedEventArgs);
2✔
4610
        }
4611
        return record;
4612
    }
4613

4614
    /**
4615
     * Updates the `IgxGridRowComponent` and the corresponding data record by primary key.
4616
     *
4617
     * @remarks
4✔
4618
     * Requires that the `primaryKey` property is set.
6✔
4619
     * @example
4620
     * ```typescript
4621
     * this.gridWithPK.updateCell('Updated', 1, 'ProductName');
4622
     * ```
4623
     * @param value the new value which is to be set.
4624
     * @param rowSelector corresponds to rowID.
4625
     * @param column corresponds to column field.
5✔
4626
     */
13✔
4627
    public updateCell(value: any, rowSelector: any, column: string): void {
13✔
4628
        if (this.isDefined(this.primaryKey)) {
4629
            const col = this._columns.find(c => c.field === column);
4630
            if (col) {
4631
                // Simplify
4632
                const rowData = this.gridAPI.getRowData(rowSelector);
4633
                const index = this.gridAPI.get_row_index_in_data(rowSelector);
4634
                // If row passed is invalid
5,875✔
4635
                if (index < 0) {
3,414✔
4636
                    return;
4637
                }
2,461✔
4638

151✔
4639
                const id = {
4640
                    rowID: rowSelector,
2,310✔
4641
                    columnID: col.index,
141✔
4642
                    rowIndex: index
4643
                };
2,169✔
4644

1✔
4645
                const cell = new IgxCell(id, index, col, rowData[col.field], value, rowData, this as any);
4646
                const formControl = this.validation.getFormControl(cell.id.rowID, cell.column.field);
2,168✔
4647
                formControl.setValue(value);
4648
                this.gridAPI.update_cell(cell);
4649
                this.cdr.detectChanges();
4650
            }
4651
        }
4652
    }
613✔
4653

613✔
4654
    /**
613✔
4655
     * Updates the `IgxGridRowComponent`
613✔
4656
     *
4,002✔
4657
     * @remarks
4,002✔
4658
     * The row is specified by
4,002✔
4659
     * rowSelector parameter and the data source record with the passed value.
4,002✔
4660
     * This method will apply requested update only if primary key is specified in the grid.
4,002✔
4661
     * @example
4662
     * ```typescript
613✔
4663
     * grid.updateRow({
613✔
4664
     *       ProductID: 1, ProductName: 'Spearmint', InStock: true, UnitsInStock: 1, OrderDate: new Date('2005-03-21')
613✔
4665
     *   }, 1);
588✔
4666
     * ```
4667
     * @param value–
4668
     * @param rowSelector correspond to rowID
4669
     */
617✔
4670
    // TODO: prevent event invocation
4,248✔
4671
    public updateRow(value: any, rowSelector: any): void {
4672
        if (this.isDefined(this.primaryKey)) {
4673
            const editableCell = this.crudService.cell;
4674
            if (editableCell && editableCell.id.rowID === rowSelector) {
4675
                this.crudService.endCellEdit();
×
4676
            }
16,419✔
4677
            const row = new IgxEditRow(rowSelector, -1, this.gridAPI.getRowData(rowSelector), this as any);
3,269✔
4678
            this.gridAPI.update_row(row, value);
4679

144✔
4680
            // TODO: fix for #5934 and probably break for #5763
1,374✔
4681
            // consider adding of third optional boolean parameter in updateRow.
285!
4682
            // If developer set this parameter to true we should call notifyChanges(true), and
1,079✔
4683
            // vise-versa if developer set it to false we should call notifyChanges(false).
4684
            // The parameter should default to false
285✔
4685
            this.notifyChanges();
4686
        }
4687
    }
4688

3,269✔
4689
    /**
4690
     * Returns the data that is contained in the row component.
4691
     *
1,374✔
4692
     * @remarks
144✔
4693
     * If the primary key is not specified the row selector match the row data.
4694
     * @example
3,269✔
4695
     * ```typescript
3,269✔
4696
     * const data = grid.getRowData(94741);
21,882✔
4697
     * ```
21,882!
4698
     * @param rowSelector correspond to rowID
21,882✔
4699
     */
4700
    public getRowData(rowSelector: any) {
4701
        if (!this.primaryKey) {
3,269✔
4702
            return rowSelector;
3,269✔
4703
        }
144✔
4704
        const data = this.gridAPI.get_all_data(this.transactions.enabled);
1,364✔
4705
        const index = this.gridAPI.get_row_index_in_data(rowSelector);
4706
        return index < 0 ? {} : data[index];
4707
    }
4708

4709
    /**
4710
     * Sort a single `IgxColumnComponent`.
4711
     *
4712
     * @remarks
182✔
4713
     * Sort the `IgxGridComponent`'s `IgxColumnComponent` based on the provided array of sorting expressions.
2,372✔
4714
     * @example
2,107✔
4715
     * ```typescript
265✔
4716
     * this.grid.sort({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false });
187✔
4717
     * ```
4718
     */
×
4719
    public sort(expression: ISortingExpression | Array<ISortingExpression>): void {
4720
        const sortingState = cloneArray(this.sortingExpressions);
246✔
4721

246✔
4722
        if (expression instanceof Array) {
246✔
4723
            for (const each of expression) {
246✔
4724
                this.gridAPI.prepare_sorting_expression([sortingState], each);
246✔
4725
            }
246✔
4726
        } else {
246✔
4727
            if (this._sortingOptions.mode === 'single') {
2✔
4728
                this._columns.forEach((col) => {
2✔
4729
                    if (!(col.field === expression.fieldName)) {
×
4730
                        this.clearSort(col.field);
×
4731
                    }
×
4732
                });
4733
            }
4734
            this.gridAPI.prepare_sorting_expression([sortingState], expression);
2!
4735
        }
2!
4736

×
4737
        const eventArgs: ISortingEventArgs = { owner: this, sortingExpressions: sortingState, cancel: false };
×
4738
        this.sorting.emit(eventArgs);
×
4739

×
4740
        if (eventArgs.cancel) {
×
4741
            return;
×
4742
        }
4743

4744
        this.crudService.endEdit(false);
×
4745
        if (expression instanceof Array) {
4746
            this.gridAPI.sort_multiple(expression);
4747
        } else {
4748
            this.gridAPI.sort(expression);
×
4749
        }
×
4750
        requestAnimationFrame(() => this.sortingDone.emit(expression));
×
4751
    }
×
4752

×
4753
    /**
×
4754
     * Filters a single `IgxColumnComponent`.
4755
     *
4756
     * @example
×
4757
     * ```typescript
4758
     * public filter(term) {
4759
     *      this.grid.filter("ProductName", term, IgxStringFilteringOperand.instance().condition("contains"));
4760
     * }
246✔
4761
     * ```
246!
4762
     * @param name
4763
     * @param value
246!
4764
     * @param conditionOrExpressionTree
×
4765
     * @param ignoreCase
×
4766
     */
4767
    public filter(name: string, value: any, conditionOrExpressionTree?: IFilteringOperation | IFilteringExpressionsTree,
4768
        ignoreCase?: boolean) {
246!
4769
        this.filteringService.filter(name, value, conditionOrExpressionTree, ignoreCase);
883✔
4770
    }
4771

246✔
4772
    /**
10✔
4773
     * Filters all the `IgxColumnComponent` in the `IgxGridComponent` with the same condition.
4774
     *
246✔
4775
     * @example
5✔
4776
     * ```typescript
4777
     * grid.filterGlobal('some', IgxStringFilteringOperand.instance().condition('contains'));
246!
4778
     * ```
×
4779
     * @param value
4780
     * @param condition
4781
     * @param ignoreCase
246✔
4782
     */
788✔
4783
    public filterGlobal(value: any, condition, ignoreCase?) {
788!
4784
        this.filteringService.filterGlobal(value, condition, ignoreCase);
788✔
4785
    }
40✔
4786

4787
    /**
748✔
4788
     * Enables summaries for the specified column and applies your customSummary.
748✔
4789
     *
2,512✔
4790
     * @remarks
2,512✔
4791
     * If you do not provide the customSummary, then the default summary for the column data type will be applied.
2,518✔
4792
     * @example
2,316!
4793
     * ```typescript
2,316!
4794
     * grid.enableSummaries([{ fieldName: 'ProductName' }, { fieldName: 'ID' }]);
2,316!
4795
     * ```
4796
     * Enable summaries for the listed columns.
2,316✔
4797
     * @example
2,316!
4798
     * ```typescript
×
4799
     * grid.enableSummaries('ProductName');
×
4800
     * ```
4801
     * @param rest
×
4802
     */
4803
    public enableSummaries(...rest) {
4804
        if (rest.length === 1 && Array.isArray(rest[0])) {
4805
            this._multipleSummaries(rest[0], true);
4806
        } else {
748✔
4807
            this._summaries(rest[0], true, rest[1]);
746!
4808
        }
×
4809
    }
×
4810

4811
    /**
×
4812
     * Disable summaries for the specified column.
×
4813
     *
×
4814
     * @example
4815
     * ```typescript
×
4816
     * grid.disableSummaries('ProductName');
×
4817
     * ```
×
4818
     * @remarks
×
4819
     * Disable summaries for the listed columns.
×
4820
     * @example
×
4821
     * ```typescript
4822
     * grid.disableSummaries([{ fieldName: 'ProductName' }]);
4823
     * ```
4824
     */
746✔
4825
    public disableSummaries(...rest) {
4826
        if (rest.length === 1 && Array.isArray(rest[0])) {
4827
            this._disableMultipleSummaries(rest[0]);
748✔
4828
        } else {
4829
            this._summaries(rest[0], false);
246!
4830
        }
×
4831
    }
×
4832

×
4833
    /**
4834
     * If name is provided, clears the filtering state of the corresponding `IgxColumnComponent`.
4835
     *
246✔
4836
     * @remarks
4837
     * Otherwise clears the filtering state of all `IgxColumnComponent`s.
4838
     * @example
4839
     * ```typescript
2,512✔
4840
     * this.grid.clearFilter();
2✔
4841
     * ```
20✔
4842
     * @param name
2✔
4843
     */
2✔
4844
    public clearFilter(name?: string) {
2!
4845
        this.filteringService.clearFilter(name);
4846
    }
4847

2,510✔
4848
    /**
14,120✔
4849
     * If name is provided, clears the sorting state of the corresponding `IgxColumnComponent`.
11,790✔
4850
     *
2,510✔
4851
     * @remarks
4852
     * otherwise clears the sorting state of all `IgxColumnComponent`.
4853
     * @example
4854
     * ```typescript
360✔
4855
     * this.grid.clearSort();
334✔
4856
     * ```
26✔
4857
     * @param name
26✔
4858
     */
110!
4859
    public clearSort(name?: string) {
26✔
4860
        if (!name) {
26✔
4861
            this.sortingExpressions = [];
111✔
4862
            return;
66✔
4863
        }
66✔
4864
        if (!this.gridAPI.get_column_by_name(name)) {
11✔
4865
            return;
4866
        }
2,345✔
4867
        this.gridAPI.clear_sort(name);
579✔
4868
    }
66✔
4869

66✔
4870
    /**
4871
     * @hidden @internal
2✔
4872
     */
4873
    public refreshGridState(_args?) {
272✔
4874
        this.crudService.endEdit(true);
64✔
4875
        this.selectionService.clearHeaderCBState();
64✔
4876
        this.summaryService.clearSummaryCache();
64✔
4877
        this.summaryPipeTrigger++;
3✔
4878
        this.cdr.detectChanges();
4879
    }
61✔
4880

6✔
4881
    // TODO: We have return values here. Move them to event args ??
4882

64✔
4883
    /**
64✔
4884
     * Pins a column by field name.
64✔
4885
     *
4886
     * @remarks
4887
     * Returns whether the operation is successful.
26✔
4888
     * @example
16✔
4889
     * ```typescript
16✔
4890
     * this.grid.pinColumn("ID");
4891
     * ```
4892
     * @param columnName
×
4893
     * @param index
20✔
4894
     */
20✔
4895
    public pinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
20✔
4896
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
20✔
4897
        return col.pin(index);
12✔
4898
    }
4899

8✔
4900
    /**
71✔
4901
     * Unpins a column by field name. Returns whether the operation is successful.
142!
4902
     *
142!
4903
     * @example
4904
     * ```typescript
4905
     * this.grid.pinColumn("ID");
71!
4906
     * ```
71✔
4907
     * @param columnName
4908
     * @param index
71✔
4909
     */
4910
    public unpinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
8✔
4911
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
4912
        return col.unpin(index);
4913
    }
4914

4915
    /**
4916
     * Pin the row by its id.
3,750✔
4917
     *
3,750✔
4918
     * @remarks
3,750✔
4919
     * ID is either the primaryKey value or the data record instance.
3,750✔
4920
     * @example
4921
     * ```typescript
3,750✔
4922
     * this.grid.pinRow(rowID);
354✔
4923
     * ```
5✔
4924
     * @param rowID The row id - primaryKey value or the data record instance.
4925
     * @param index The index at which to insert the row in the pinned collection.
354✔
4926
     */
106✔
4927
    public pinRow(rowID: any, index?: number, row?: RowType): boolean {
4928
        if (this._pinnedRecordIDs.indexOf(rowID) !== -1) {
4929
            return false;
4930
        }
4931
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, index, row, true);
4932
        this.rowPinning.emit(eventArgs);
3,750✔
4933

22,103✔
4934
        if (eventArgs.cancel) {
350✔
4935
            return;
4936
        }
21,753✔
4937
        this.crudService.endEdit(false);
111!
4938

111✔
4939
        const insertIndex = typeof eventArgs.insertAtIndex === 'number' ? eventArgs.insertAtIndex : this._pinnedRecordIDs.length;
4940
        this._pinnedRecordIDs.splice(insertIndex, 0, rowID);
4941
        this.pipeTrigger++;
×
4942
        if (this.gridAPI.grid) {
×
4943
            this.cdr.detectChanges();
4944
            this.rowPinned.emit(eventArgs);
4945
        }
4946

21,642✔
4947
        return true;
4948
    }
4949

4950
    /**
3,750✔
4951
     * Unpin the row by its id.
3,750✔
4952
     *
3,750✔
4953
     * @remarks
4954
     * ID is either the primaryKey value or the data record instance.
4955
     * @example
4956
     * ```typescript
4957
     * this.grid.unpinRow(rowID);
3✔
4958
     * ```
106✔
4959
     * @param rowID The row id - primaryKey value or the data record instance.
106✔
4960
     */
16✔
4961
    public unpinRow(rowID: any, row?: RowType): boolean {
16✔
4962
        const index = this._pinnedRecordIDs.indexOf(rowID);
16✔
4963
        if (index === -1) {
5✔
4964
            return false;
5✔
4965
        }
4966

4967
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, null, row, false);
106✔
4968
        this.rowPinning.emit(eventArgs);
5✔
4969

5!
4970
        if (eventArgs.cancel) {
4971
            return;
4972
        }
4973

101✔
4974
        this.crudService.endEdit(false);
4975
        this._pinnedRecordIDs.splice(index, 1);
106✔
4976
        this.pipeTrigger++;
4977
        if (this.gridAPI.grid) {
4978
            this.cdr.detectChanges();
4979
            this.rowPinned.emit(eventArgs);
4980
        }
4981

191✔
4982
        return true;
233✔
4983
    }
191✔
4984

191✔
4985
    /** @hidden @internal */
1!
4986
    public get pinnedRowHeight() {
×
4987
        const containerHeight = this.pinContainer ? this.pinContainer.nativeElement.offsetHeight : 0;
×
4988
        return this.hasPinnedRecords ? containerHeight : 0;
4989
    }
4990

4991
    /** @hidden @internal */
190✔
4992
    public get totalHeight() {
4993
        return this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
4994
    }
4995

4996
    /**
4997
     * Recalculates grid width/height dimensions.
4998
     *
381✔
4999
     * @remarks
1✔
5000
     * Should be run when changing DOM elements dimentions manually that affect the grid's size.
5001
     * @example
380✔
5002
     * ```typescript
5003
     * this.grid.reflow();
5004
     * ```
5005
     */
5006
    public reflow() {
5007
        this.calculateGridSizes();
195✔
5008
    }
195!
5009

420✔
5010
    /**
195✔
5011
     * Finds the next occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
389!
5012
     *
5013
     * @remarks
195✔
5014
     * Returns how many times the grid contains the string.
102✔
5015
     * @example
5016
     * ```typescript
93✔
5017
     * this.grid.findNext("financial");
93✔
5018
     * ```
93✔
5019
     * @param text the string to search.
5020
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
5021
     * @param exactMatch optionally, if the text should match the entire value  (defaults to false).
5022
     */
5023
    public findNext(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
5024
        return this.find(text, 1, caseSensitive, exactMatch);
5025
    }
5026

128✔
5027
    /**
128✔
5028
     * Finds the previous occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
10✔
5029
     *
5030
     * @remarks
5031
     * Returns how many times the grid contains the string.
128✔
5032
     * @example
128✔
5033
     * ```typescript
5034
     * this.grid.findPrev("financial");
5035
     * ```
13,165✔
5036
     * @param text the string to search.
5037
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
5038
     * @param exactMatch optionally, if the text should match the entire value (defaults to false).
5039
     */
5040
    public findPrev(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
7,276✔
5041
        return this.find(text, -1, caseSensitive, exactMatch);
7,276✔
5042
    }
2✔
5043

2!
5044
    /**
×
5045
     * Reapplies the existing search.
5046
     *
2✔
5047
     * @remarks
2✔
5048
     * Returns how many times the grid contains the last search.
5049
     * @example
7,276✔
5050
     * ```typescript
7,276✔
5051
     * this.grid.refreshSearch();
5052
     * ```
5053
     * @param updateActiveInfo
329✔
5054
     */
329✔
5055
    public refreshSearch(updateActiveInfo?: boolean, endEdit = true): number {
329✔
5056
        if (this.lastSearchInfo.searchText) {
329✔
5057
            this.rebuildMatchCache();
329✔
5058

329✔
5059
            if (updateActiveInfo) {
14✔
5060
                const activeInfo = IgxTextHighlightDirective.highlightGroupsMap.get(this.id);
5061
                this.lastSearchInfo.matchInfoCache.forEach((match, i) => {
5062
                    if (match.column === activeInfo.column &&
5063
                        match.row === activeInfo.row &&
329✔
5064
                        match.index === activeInfo.index &&
329✔
5065
                        compareMaps(match.metadata, activeInfo.metadata)) {
329✔
5066
                        this.lastSearchInfo.activeMatchIndex = i;
329✔
5067
                    }
9✔
5068
                });
5069
            }
329✔
5070

5071
            return this.find(this.lastSearchInfo.searchText,
5072
                0,
5073
                this.lastSearchInfo.caseSensitive,
5074
                this.lastSearchInfo.exactMatch,
329✔
5075
                false,
5076
                endEdit);
5077
        } else {
317✔
5078
            return 0;
317✔
5079
        }
2,009✔
5080
    }
317✔
5081

317✔
5082
    /**
317✔
5083
     * Removes all the highlights in the cell.
316✔
5084
     *
316✔
5085
     * @example
316✔
5086
     * ```typescript
5087
     * this.grid.clearSearch();
5088
     * ```
5089
     */
317✔
5090
    public clearSearch() {
206✔
5091
        this.lastSearchInfo = {
5092
            searchText: '',
317✔
5093
            caseSensitive: false,
317✔
5094
            exactMatch: false,
5095
            activeMatchIndex: 0,
×
5096
            matchInfoCache: [],
827✔
5097
            matchCount: 0,
276✔
5098
            content: ''
5099
        };
1,622✔
5100

551✔
5101
        this.rowList.forEach((row) => {
8!
5102
            if (row.cells) {
×
5103
                row.cells.forEach((c: IgxGridCellComponent) => {
×
5104
                    c.clearHighlight();
×
5105
                });
×
5106
            }
×
5107
        });
5108
    }
5109

8✔
5110
    /** @hidden @internal */
8!
5111
    public get hasEditableColumns(): boolean {
8✔
5112
        return this._columns.some((col) => col.editable);
8✔
5113
    }
5114

8✔
5115
    /** @hidden @internal */
5116
    public get hasSummarizedColumns(): boolean {
543✔
5117
        const summarizedColumns = this._columns.filter(col => col.hasSummary && !col.hidden);
543✔
5118
        return summarizedColumns.length > 0;
5119
    }
5120

5121
    /**
5122
     * @hidden @internal
543!
5123
     */
5124
    public get rootSummariesEnabled(): boolean {
30✔
5125
        return this.summaryCalculationMode !== GridSummaryCalculationMode.childLevelsOnly;
30✔
5126
    }
30✔
5127

5128
    /**
50✔
5129
     * @hidden @internal
50!
5130
     */
139✔
5131
    public get hasVisibleColumns(): boolean {
50✔
5132
        if (this._hasVisibleColumns === undefined) {
5133
            return this._columns ? this._columns.some(c => !c.hidden) : false;
×
5134
        }
×
5135
        return this._hasVisibleColumns;
×
5136
    }
5137

463✔
5138
    public set hasVisibleColumns(value) {
1,286!
5139
        this._hasVisibleColumns = value;
463✔
5140
    }
5141

543✔
5142
    /** @hidden @internal */
5143
    public get hasMovableColumns(): boolean {
15✔
5144
        return this.moving;
30✔
5145
    }
30!
5146

7✔
5147
    /** @hidden @internal */
5148
    public get hasColumnGroups(): boolean {
5149
        return this._columnGroups;
23✔
5150
    }
56✔
5151

23✔
5152
    /** @hidden @internal */
23!
5153
    public get hasColumnLayouts() {
5154
        return !!this._columns.some(col => col.columnLayout);
5155
    }
5156

5157

5158
    /**
5159
     * @hidden @internal
5160
     */
8✔
5161
    public get multiRowLayoutRowSize() {
8✔
5162
        return this._multiRowLayoutRowSize;
8✔
5163
    }
178✔
5164

8✔
5165
    /**
5166
     * @hidden
5167
     */
×
5168
    protected get rowBasedHeight() {
5169
        return this.dataLength * this.rowHeight;
5170
    }
5171

5172
    /**
5173
     * @hidden
5174
     */
5175
    protected get isPercentWidth() {
5176
        return this.width && this.width.indexOf('%') !== -1;
5177
    }
5178

29✔
5179
    /**
29✔
5180
     * @hidden @internal
5181
     */
5182
    public get isPercentHeight() {
5183
        return this._height && this._height.indexOf('%') !== -1;
5184
    }
5185

5186
    /**
5187
     * @hidden
5188
     */
5189
    protected get defaultTargetBodyHeight(): number {
27✔
5190
        const allItems = this.dataLength;
27✔
5191
        return this.renderedRowHeight * Math.min(this._defaultTargetRecordNumber,
27✔
5192
            this.paginator ? Math.min(allItems, this.paginator.perPage) : allItems);
5193
    }
5194

97✔
5195
    /**
777✔
5196
     * @hidden @internal
97✔
5197
     * The rowHeight input is bound to min-height css prop of rows that adds a 1px border in all cases
2✔
5198
     */
5199
    public get renderedRowHeight(): number {
95✔
5200
        return this.rowHeight + 1;
694✔
5201
    }
93✔
5202

5203
    /**
2✔
5204
     * @hidden @internal
5205
     */
195✔
5206
    public get outerWidth() {
316!
5207
        return this.hasVerticalScroll() ? this.calcWidth + this.scrollSize : this.calcWidth;
×
5208
    }
5209

316✔
5210
    /**
277✔
5211
     * @hidden @internal
5212
     * Gets the visible content height that includes header + tbody + footer.
316!
5213
     */
×
5214
    public getVisibleContentHeight() {
×
5215
        let height = this.theadRow.nativeElement.clientHeight + this.tbody.nativeElement.clientHeight;
5216
        if (this.hasSummarizedColumns) {
316✔
5217
            height += this.tfoot.nativeElement.clientHeight;
316✔
5218
        }
316✔
5219
        return height;
316✔
5220
    }
5221

5222
    /**
84✔
5223
     * @hidden @internal
5224
     */
5225
    public getPossibleColumnWidth(baseWidth: number = null) {
5226
        let computedWidth;
5227
        if (baseWidth !== null) {
5228
            computedWidth = baseWidth;
5229
        } else {
5230
            computedWidth = this.calcWidth ||
5231
                parseInt(this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width'), 10);
84✔
5232
        }
5233

5234
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
232✔
5235

5236

316✔
5237
        // Column layouts related
84✔
5238
        let visibleCols = [];
783✔
5239
        const columnBlocks = this.visibleColumns.filter(c => c.columnGroup);
739✔
5240
        const colsPerBlock = columnBlocks.map(block => block.getInitialChildColumnSizes(block.children));
3,091✔
5241
        const combinedBlocksSize = colsPerBlock.reduce((acc, item) => acc + item.length, 0);
5242
        colsPerBlock.forEach(blockCols => visibleCols = visibleCols.concat(blockCols));
5243
        //
5244

84✔
5245
        const columnsWithSetWidths = this.hasColumnLayouts ?
5246
            visibleCols.filter(c => c.widthSetByUser) :
316✔
5247
            visibleChildColumns.filter(c => c.widthSetByUser && c.width !== 'fit-content');
28✔
5248

5249
        const columnsToSize = this.hasColumnLayouts ?
288✔
5250
            combinedBlocksSize - columnsWithSetWidths.length :
8✔
5251
            visibleChildColumns.length - columnsWithSetWidths.length;
5252
        const sumExistingWidths = columnsWithSetWidths
316✔
5253
            .reduce((prev, curr) => {
298✔
5254
                const colWidth = curr.width;
298✔
5255
                let widthValue = parseInt(colWidth, 10);
298✔
5256
                if (isNaN(widthValue)) {
182✔
5257
                    widthValue = MINIMUM_COLUMN_WIDTH;
5258
                }
298✔
5259
                const currWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1 ?
5260
                    widthValue / 100 * computedWidth :
5261
                    widthValue;
5262
                return prev + currWidth;
5263
            }, 0);
5264

5265
        // When all columns are hidden, return 0px width
5266
        if (!sumExistingWidths && !columnsToSize) {
18✔
5267
            return '0px';
5268
        }
316✔
5269
        computedWidth -= this.featureColumnsWidth();
5270

5271
        const columnWidth = Math.floor(!Number.isFinite(sumExistingWidths) ?
205✔
5272
            Math.max(computedWidth / columnsToSize, this.minColumnWidth) :
205✔
5273
            Math.max((computedWidth - sumExistingWidths) / columnsToSize, this.minColumnWidth));
205✔
5274

205✔
5275
        return columnWidth + 'px';
205✔
5276
    }
886✔
5277

205✔
5278
    /**
3,357✔
5279
     * @hidden @internal
14,377✔
5280
     */
14,377!
5281
    public hasVerticalScroll() {
14,377✔
5282
        if (this._init) {
10,701✔
5283
            return false;
5284
        }
5285
        const isScrollable = this.verticalScrollContainer ? this.verticalScrollContainer.isScrollable() : false;
14,377✔
5286
        return !!(this.calcWidth && this.dataView && this.dataView.length > 0 && isScrollable);
14,162✔
5287
    }
14,162✔
5288

536✔
5289
    /**
9✔
5290
     * Gets calculated width of the pinned area.
5291
     *
5292
     * @example
5293
     * ```typescript
5294
     * const pinnedWidth = this.grid.getPinnedWidth();
5295
     * ```
9✔
5296
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
5297
     */
5298
    public getPinnedWidth(takeHidden = false) {
5299
        const fc = takeHidden ? this._pinnedColumns : this.pinnedColumns;
13,626✔
5300
        let sum = 0;
13,626✔
5301
        for (const col of fc) {
13,626✔
5302
            if (col.level === 0) {
2,157✔
5303
                sum += parseInt(col.calcWidth, 10);
5304
            }
5305
        }
5306
        if (this.isPinningToStart) {
5307
            sum += this.featureColumnsWidth();
5308
        }
2,157✔
5309

2,157✔
5310
        return sum;
2,157✔
5311
    }
5312

5313
    /**
5314
     * @hidden @internal
5315
     */
5316
    public isColumnGrouped(_fieldName: string): boolean {
205✔
5317
        return false;
5318
    }
5319

48✔
5320
    /**
253✔
5321
     * @hidden @internal
253✔
5322
     * TODO: REMOVE
253✔
5323
     */
49✔
5324
    public onHeaderSelectorClick(event) {
5325
        if (!this.isMultiRowSelectionEnabled) {
253✔
5326
            return;
253✔
5327
        }
253✔
5328
        if (this.selectionService.areAllRowSelected()) {
253✔
5329
            this.selectionService.clearRowSelection(event);
5330
        } else {
8✔
5331
            this.selectionService.selectAllRows(event);
253!
5332
        }
×
5333
    }
5334

253✔
5335
    /**
253✔
5336
     * @hidden @internal
5337
     */
2✔
5338
    public get headSelectorBaseAriaLabel() {
5339
        if (this._filteringExpressionsTree.filteringOperands.length > 0) {
5340
            return this.selectionService.areAllRowSelected() ? 'Deselect all filtered' : 'Select all filtered';
5341
        }
5342

5343
        return this.selectionService.areAllRowSelected() ? 'Deselect all' : 'Select all';
5344
    }
5345

5346
    /**
5347
     * @hidden
5348
     * @internal
5349
     */
5350
    public get totalRowsCountAfterFilter() {
5351
        if (this.data) {
5352
            return this.selectionService.allData.length;
5353
        }
5354

5355
        return 0;
5356
    }
5357

5358
    /** @hidden @internal */
5359
    public get pinnedDataView(): any[] {
5360
        return this.pinnedRecords ? this.pinnedRecords : [];
5361
    }
2✔
5362

5363
    /** @hidden @internal */
5364
    public get unpinnedDataView(): any[] {
5365
        return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer?.igxForOf || [];
5366
    }
5367

5368
    /**
5369
     * Returns the currently transformed paged/filtered/sorted/grouped/pinned/unpinned row data, displayed in the grid.
5370
     *
5371
     * @example
5372
     * ```typescript
5373
     *      const dataView = this.grid.dataView;
5374
     * ```
5375
     */
5376
    public get dataView() {
5377
        return this._dataView;
5378
    }
5379

5380
    /**
5381
     * Gets/Sets whether clicking over a row should select/deselect it
5382
     *
5383
     * @remarks
5384
     * By default it is set to true
5385
     * @param enabled: boolean
5386
     */
5387
    @WatchChanges()
5388
    @Input()
5389
    public get selectRowOnClick() {
5390
        return this._selectRowOnClick;
5391
    }
5392

5393
    public set selectRowOnClick(enabled: boolean) {
5394
        this._selectRowOnClick = enabled;
5395
    }
5396

5397
    /**
5398
     * Select specified rows by ID.
5399
     *
5400
     * @example
5401
     * ```typescript
5402
     * this.grid.selectRows([1,2,5], true);
5403
     * ```
5404
     * @param rowIDs
5405
     * @param clearCurrentSelection if true clears the current selection
5406
     */
5407
    public selectRows(rowIDs: any[], clearCurrentSelection?: boolean) {
5408
        this.selectionService.selectRowsWithNoEvent(rowIDs, clearCurrentSelection);
5409
        this.notifyChanges();
5410
    }
5411

5412
    /**
5413
     * Deselect specified rows by ID.
5414
     *
5415
     * @example
5416
     * ```typescript
5417
     * this.grid.deselectRows([1,2,5]);
5418
     * ```
5419
     * @param rowIDs
5420
     */
5421
    public deselectRows(rowIDs: any[]) {
5422
        this.selectionService.deselectRowsWithNoEvent(rowIDs);
5423
        this.notifyChanges();
5424
    }
5425

5426
    /**
5427
     * Selects all rows
5428
     *
5429
     * @remarks
5430
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
5431
     * If you set the parameter onlyFilterData to false that will select all rows in the grid exept deleted rows.
5432
     * @example
5433
     * ```typescript
5434
     * this.grid.selectAllRows();
5435
     * this.grid.selectAllRows(false);
5436
     * ```
5437
     * @param onlyFilterData
5438
     */
5439
    public selectAllRows(onlyFilterData = true) {
5440
        const data = onlyFilterData && this.filteredData ? this.filteredData : this.gridAPI.get_all_data(true);
5441
        const rowIDs = this.selectionService.getRowIDs(data).filter(rID => !this.gridAPI.row_deleted_transaction(rID));
5442
        this.selectRows(rowIDs);
5443
    }
5444

5445
    /**
5446
     * Deselects all rows
5447
     *
5448
     * @remarks
5449
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
5450
     * If you set the parameter onlyFilterData to false that will deselect all rows in the grid exept deleted rows.
5451
     * @example
5452
     * ```typescript
5453
     * this.grid.deselectAllRows();
5454
     * ```
5455
     * @param onlyFilterData
5456
     */
5457
    public deselectAllRows(onlyFilterData = true) {
5458
        if (onlyFilterData && this.filteredData && this.filteredData.length > 0) {
5459
            this.deselectRows(this.selectionService.getRowIDs(this.filteredData));
5460
        } else {
5461
            this.selectionService.clearAllSelectedRows();
5462
            this.notifyChanges();
5463
        }
5464
    }
5465

5466
    /**
5467
     * Deselect selected cells.
5468
     * @example
5469
     * ```typescript
5470
     * this.grid.clearCellSelection();
5471
     * ```
5472
     */
5473
    public clearCellSelection(): void {
5474
        this.selectionService.clear(true);
5475
        this.notifyChanges();
5476
    }
5477

5478
    /**
5479
     * @hidden @internal
5480
     */
5481
    public dragScroll(delta: { left: number; top: number }): void {
5482
        const horizontal = this.headerContainer.getScroll();
5483
        const vertical = this.verticalScrollContainer.getScroll();
5484
        const { left, top } = delta;
5485

5486
        horizontal.scrollLeft += left * this.DRAG_SCROLL_DELTA;
5487
        vertical.scrollTop += top * this.DRAG_SCROLL_DELTA;
5488
    }
5489

5490
    /**
5491
     * @hidden @internal
5492
     */
5493
    public isDefined(arg: any): boolean {
5494
        return arg !== undefined && arg !== null;
5495
    }
5496

5497
    /**
5498
     * Select range(s) of cells between certain rows and columns of the grid.
5499
     */
5500
    public selectRange(arg: GridSelectionRange | GridSelectionRange[] | null | undefined): void {
5501
        if (!this.isDefined(arg)) {
5502
            this.clearCellSelection();
5503
            return;
5504
        }
5505
        if (arg instanceof Array) {
5506
            arg.forEach(range => this.setSelection(range));
5507
        } else {
5508
            this.setSelection(arg);
5509
        }
5510
        this.notifyChanges();
5511
    }
5512

5513
    /**
5514
     * @hidden @internal
5515
     */
5516
    public columnToVisibleIndex(field: string | number): number {
5517
        const visibleColumns = this.visibleColumns;
5518
        if (typeof field === 'number') {
5519
            return field;
5520
        }
5521
        return visibleColumns.find(column => column.field === field).visibleIndex;
5522
    }
5523

5524
    /**
5525
     * @hidden @internal
5526
     */
5527
    public setSelection(range: GridSelectionRange): void {
5528
        const startNode = { row: range.rowStart, column: this.columnToVisibleIndex(range.columnStart) };
5529
        const endNode = { row: range.rowEnd, column: this.columnToVisibleIndex(range.columnEnd) };
5530

5531
        this.selectionService.pointerState.node = startNode;
5532
        this.selectionService.selectRange(endNode, this.selectionService.pointerState);
5533
        this.selectionService.addRangeMeta(endNode, this.selectionService.pointerState);
5534
        this.selectionService.initPointerState();
5535
    }
5536

5537
    /**
5538
     * Get the currently selected ranges in the grid.
5539
     */
5540
    public getSelectedRanges(): GridSelectionRange[] {
5541
        return this.selectionService.ranges;
5542
    }
2✔
5543

5544
    /**
5545
     *
2✔
5546
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
5547
     *
5548
     * @remarks
2✔
5549
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5550
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5551
     */
2✔
5552
    public getSelectedData(formatters = false, headers = false) {
5553
        const source = this.filteredSortedData;
5554
        return this.extractDataFromSelection(source, formatters, headers);
2✔
5555
    }
5556

5557
    /**
2✔
5558
     * Get current selected columns.
5559
     *
5560
     * @example
2✔
5561
     * Returns an array with selected columns
5562
     * ```typescript
5563
     * const selectedColumns = this.grid.selectedColumns();
2✔
5564
     * ```
5565
     */
5566
    public selectedColumns(): ColumnType[] {
2✔
5567
        const fields = this.selectionService.getSelectedColumns();
5568
        return fields.map(field => this.getColumnByName(field)).filter(field => field);
5569
    }
2✔
5570

5571
    /**
5572
     * Select specified columns.
2✔
5573
     *
5574
     * @example
5575
     * ```typescript
2✔
5576
     * this.grid.selectColumns(['ID','Name'], true);
5577
     * ```
5578
     * @param columns
2✔
5579
     * @param clearCurrentSelection if true clears the current selection
5580
     */
5581
    public selectColumns(columns: string[] | ColumnType[], clearCurrentSelection?: boolean) {
2✔
5582
        let fieldToSelect: string[] = [];
5583
        if (columns.length === 0 || typeof columns[0] === 'string') {
5584
            fieldToSelect = columns as string[];
2✔
5585
        } else {
5586
            (columns as ColumnType[]).forEach(col => {
5587
                if (col.columnGroup) {
2✔
5588
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
5589
                    fieldToSelect = [...fieldToSelect, ...children];
5590
                } else {
2✔
5591
                    fieldToSelect.push(col.field);
5592
                }
5593
            });
5594
        }
5595

5596
        this.selectionService.selectColumnsWithNoEvent(fieldToSelect, clearCurrentSelection);
5597
        this.notifyChanges();
5598
    }
5599

5600
    /**
5601
     * Deselect specified columns by field.
5602
     *
5603
     * @example
5604
     * ```typescript
5605
     * this.grid.deselectColumns(['ID','Name']);
5606
     * ```
5607
     * @param columns
5608
     */
5609
    public deselectColumns(columns: string[] | ColumnType[]) {
5610
        let fieldToDeselect: string[] = [];
5611
        if (columns.length === 0 || typeof columns[0] === 'string') {
5612
            fieldToDeselect = columns as string[];
5613
        } else {
5614
            (columns as ColumnType[]).forEach(col => {
5615
                if (col.columnGroup) {
5616
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
5617
                    fieldToDeselect = [...fieldToDeselect, ...children];
5618
                } else {
5619
                    fieldToDeselect.push(col.field);
5620
                }
5621
            });
5622
        }
5623
        this.selectionService.deselectColumnsWithNoEvent(fieldToDeselect);
5624
        this.notifyChanges();
5625
    }
5626

5627
    /**
5628
     * Deselects all columns
5629
     *
5630
     * @example
5631
     * ```typescript
5632
     * this.grid.deselectAllColumns();
5633
     * ```
5634
     */
5635
    public deselectAllColumns() {
5636
        this.selectionService.clearAllSelectedColumns();
5637
        this.notifyChanges();
5638
    }
5639

5640
    /**
5641
     * Selects all columns
5642
     *
5643
     * @example
5644
     * ```typescript
5645
     * this.grid.deselectAllColumns();
5646
     * ```
5647
     */
5648
    public selectAllColumns() {
5649
        this.selectColumns(this._columns.filter(c => !c.columnGroup));
5650
    }
5651

5652
    /**
5653
     *
5654
     * Returns an array of the current columns selection in the form of `[{ column.field: cell.value }, ...]`.
5655
     *
5656
     * @remarks
5657
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5658
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5659
     */
5660
    public getSelectedColumnsData(formatters = false, headers = false) {
5661
        const source = this.filteredSortedData ? this.filteredSortedData : this.data;
5662
        return this.extractDataFromColumnsSelection(source, formatters, headers);
5663
    }
5664

5665

5666
    /** @hidden @internal **/
5667
    public combineSelectedCellAndColumnData(columnData: any[], formatters = false, headers = false) {
5668
        const source = this.filteredSortedData;
5669
        return this.extractDataFromSelection(source, formatters, headers, columnData);
5670
    }
5671

5672
    /**
5673
     * @hidden @internal
5674
     */
5675
    public preventContainerScroll = (evt) => {
5676
        if (evt.target.scrollTop !== 0) {
5677
            this.verticalScrollContainer.addScrollTop(evt.target.scrollTop);
5678
            evt.target.scrollTop = 0;
5679
        }
5680
        if (evt.target.scrollLeft !== 0) {
5681
            this.headerContainer.scrollPosition += evt.target.scrollLeft;
5682
            evt.target.scrollLeft = 0;
5683
        }
5684
    };
5685

5686
    /**
5687
     * @hidden
5688
     * @internal
5689
     */
5690
    public copyHandler(event) {
5691
        const eventPathElements = event.composedPath().map(el => el.tagName?.toLowerCase());
5692
        if (eventPathElements.includes('igx-grid-filtering-row') ||
5693
            eventPathElements.includes('igx-grid-filtering-cell')) {
5694
            return;
5695
        }
5696

5697
        const selectedColumns = this.gridAPI.grid.selectedColumns();
5698
        const columnData = this.getSelectedColumnsData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
5699
        let selectedData;
5700
        if (event.type === 'copy') {
5701
            selectedData = this.getSelectedData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
5702
        }
5703

5704
        let data = [];
5705
        let result;
5706

5707
        if (event.code === 'KeyC' && (event.ctrlKey || event.metaKey) && event.currentTarget.className === 'igx-grid-thead__wrapper') {
5708
            if (selectedData.length) {
5709
                if (columnData.length === 0) {
5710
                    result = this.prepareCopyData(event, selectedData);
5711
                } else {
5712
                    data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
5713
                        this.clipboardOptions.copyHeaders);
5714
                    result = this.prepareCopyData(event, data[0], data[1]);
5715
                }
5716
            } else {
5717
                data = columnData;
5718
                result = this.prepareCopyData(event, data);
5719
            }
5720

5721
            navigator.clipboard.writeText(result).then().catch(e => console.error(e));
5722
        } else if (!this.clipboardOptions.enabled || this.crudService.cellInEditMode || event.type === 'keydown') {
5723
            return;
5724
        } else {
5725
            if (selectedColumns.length) {
5726
                data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
5727
                    this.clipboardOptions.copyHeaders);
5728
                result = this.prepareCopyData(event, data[0], data[1]);
5729
            } else {
5730
                data = selectedData;
5731
                result = this.prepareCopyData(event, data);
5732
            }
5733
            event.clipboardData.setData('text/plain', result);
5734
        }
5735
    }
5736

5737
    /**
5738
     * @hidden @internal
5739
     */
5740
    public prepareCopyData(event, data, keys?) {
5741
        const ev = { data, cancel: false } as IGridClipboardEvent;
5742
        this.gridCopy.emit(ev);
5743

5744
        if (ev.cancel) {
5745
            return;
5746
        }
5747

5748
        const transformer = new CharSeparatedValueData(ev.data, this.clipboardOptions.separator);
5749
        let result = keys ? transformer.prepareData(keys) : transformer.prepareData();
5750

5751
        if (!this.clipboardOptions.copyHeaders) {
5752
            result = result.substring(result.indexOf('\n') + 1);
5753
        }
5754

5755
        if (data && data.length > 0 && Object.values(data[0]).length === 1) {
5756
            result = result.slice(0, -2);
5757
        }
5758

5759
        event.preventDefault();
5760

5761
        /* Necessary for the hiearachical case but will probably have to
5762
           change how getSelectedData is propagated in the hiearachical grid
5763
        */
5764
        event.stopPropagation();
5765

5766
        return result;
5767
    }
5768

5769
    /**
5770
     * @hidden @internal
5771
     */
5772
    public showSnackbarFor(index: number) {
5773
        this.addRowSnackbar.actionText = index === -1 ? '' : this.snackbarActionText;
5774
        this.lastAddedRowIndex = index;
5775
        this.addRowSnackbar.open();
5776
    }
5777

5778
    /**
5779
     * Navigates to a position in the grid based on provided `rowindex` and `visibleColumnIndex`.
5780
     *
5781
     * @remarks
5782
     * Also can execute a custom logic over the target element,
5783
     * through a callback function that accepts { targetType: GridKeydownTargetType, target: Object }
5784
     * @example
5785
     * ```typescript
5786
     *  this.grid.navigateTo(10, 3, (args) => { args.target.nativeElement.focus(); });
5787
     * ```
5788
     */
5789
    public navigateTo(rowIndex: number, visibleColIndex = -1, cb: (args: any) => void = null) {
5790
        const totalItems = (this as any).totalItemCount ?? this.dataView.length - 1;
5791
        if (rowIndex < 0 || rowIndex > totalItems || (visibleColIndex !== -1
5792
            && this._columns.map(col => col.visibleIndex).indexOf(visibleColIndex) === -1)) {
5793
            return;
5794
        }
5795
        if (this.dataView.slice(rowIndex, rowIndex + 1).find(rec => rec.expression || rec.childGridsData)) {
5796
            visibleColIndex = -1;
5797
        }
5798
        // If the target row is pinned no need to scroll as well.
5799
        const shouldScrollVertically = this.navigation.shouldPerformVerticalScroll(rowIndex, visibleColIndex);
5800
        const shouldScrollHorizontally = this.navigation.shouldPerformHorizontalScroll(visibleColIndex, rowIndex);
5801
        if (shouldScrollVertically) {
5802
            this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () => {
5803
                if (shouldScrollHorizontally) {
5804
                    this.navigation.performHorizontalScrollToCell(visibleColIndex, () =>
5805
                        this.executeCallback(rowIndex, visibleColIndex, cb));
5806
                } else {
5807
                    this.executeCallback(rowIndex, visibleColIndex, cb);
5808
                }
5809
            });
5810
        } else if (shouldScrollHorizontally) {
5811
            this.navigation.performHorizontalScrollToCell(visibleColIndex, () => {
5812
                if (shouldScrollVertically) {
5813
                    this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () =>
5814
                        this.executeCallback(rowIndex, visibleColIndex, cb));
5815
                } else {
5816
                    this.executeCallback(rowIndex, visibleColIndex, cb);
5817
                }
5818
            });
5819
        } else {
5820
            this.executeCallback(rowIndex, visibleColIndex, cb);
5821
        }
5822
    }
5823

5824
    /**
5825
     * Returns `ICellPosition` which defines the next cell,
5826
     * according to the current position, that match specific criteria.
5827
     *
5828
     * @remarks
5829
     * You can pass callback function as a third parameter of `getPreviousCell` method.
5830
     * The callback function accepts IgxColumnComponent as a param
5831
     * @example
5832
     * ```typescript
5833
     *  const nextEditableCellPosition = this.grid.getNextCell(0, 3, (column) => column.editable);
5834
     * ```
5835
     */
5836
    public getNextCell(currRowIndex: number, curVisibleColIndex: number,
5837
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
5838
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
5839
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
5840
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
5841
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
5842
        }
5843
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => a - b) :
5844
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => a - b);
5845
        const nextCellIndex = colIndexes.find(index => index > curVisibleColIndex);
5846
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
5847
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && nextCellIndex !== undefined) {
5848
            return { rowIndex: currRowIndex, visibleColumnIndex: nextCellIndex };
5849
        } else {
5850
            const nextIndex = this.getNextDataRowIndex(currRowIndex)
5851
            if (colIndexes.length === 0 || nextIndex === currRowIndex) {
5852
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
5853
            } else {
5854
                return { rowIndex: nextIndex, visibleColumnIndex: colIndexes[0] };
5855
            }
5856
        }
5857
    }
5858

5859
    /**
5860
     * Returns `ICellPosition` which defines the previous cell,
5861
     * according to the current position, that match specific criteria.
5862
     *
5863
     * @remarks
5864
     * You can pass callback function as a third parameter of `getPreviousCell` method.
5865
     * The callback function accepts IgxColumnComponent as a param
5866
     * @example
5867
     * ```typescript
5868
     *  const previousEditableCellPosition = this.grid.getPreviousCell(0, 3, (column) => column.editable);
5869
     * ```
5870
     */
5871
    public getPreviousCell(currRowIndex: number, curVisibleColIndex: number,
5872
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
5873
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
5874
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
5875
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
5876
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
5877
        }
5878
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => b - a) :
5879
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => b - a);
5880
        const prevCellIndex = colIndexes.find(index => index < curVisibleColIndex);
5881
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
5882
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && prevCellIndex !== undefined) {
5883
            return { rowIndex: currRowIndex, visibleColumnIndex: prevCellIndex };
5884
        } else {
5885
            const prevIndex = this.getNextDataRowIndex(currRowIndex, true);
5886
            if (colIndexes.length === 0 || prevIndex === currRowIndex) {
5887
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
5888
            } else {
5889
                return { rowIndex: prevIndex, visibleColumnIndex: colIndexes[0] };
5890
            }
5891
        }
5892
    }
5893

5894
    /**
5895
     * @hidden
5896
     * @internal
5897
     */
5898
    public endRowEditTabStop(commit = true, event?: Event) {
5899
        const canceled = this.crudService.endEdit(commit, event);
5900

5901
        if (canceled) {
5902
            return true;
5903
        }
5904

5905
        const activeCell = this.gridAPI.grid.navigation.activeNode;
5906
        if (activeCell && activeCell.row !== -1) {
5907
            this.tbody.nativeElement.focus();
5908
        }
5909
    }
5910

5911
    /**
5912
     * @hidden @internal
5913
     */
5914
    public trackColumnChanges(index, col) {
5915
        return col.field + col._calcWidth;
5916
    }
5917

5918
    /**
5919
     * @hidden
5920
     */
5921
    public isExpandedGroup(_group: IGroupByRecord): boolean {
5922
        return undefined;
5923
    }
5924

5925
    /**
5926
     * @hidden @internal
5927
     * TODO: MOVE to CRUD
5928
     */
5929
    public openRowOverlay(id) {
5930
        this.configureRowEditingOverlay(id, this.rowList.length <= MIN_ROW_EDITING_COUNT_THRESHOLD);
5931

5932
        this.rowEditingOverlay.open(this.rowEditSettings);
5933
        this.rowEditingOverlay.element.addEventListener('wheel', this.rowEditingWheelHandler.bind(this));
5934
    }
5935

5936
    /**
5937
     * @hidden @internal
5938
     */
5939
    public closeRowEditingOverlay() {
5940
        this.rowEditingOverlay.element.removeEventListener('wheel', this.rowEditingWheelHandler);
5941
        this.rowEditPositioningStrategy.isTopInitialPosition = null;
5942
        this.rowEditingOverlay.close();
5943
        this.rowEditingOverlay.element.parentElement.style.display = '';
5944
    }
5945

5946
    /**
5947
     * @hidden @internal
5948
     */
5949
    public toggleRowEditingOverlay(show) {
5950
        const rowStyle = this.rowEditingOverlay.element.style;
5951
        if (show) {
5952
            rowStyle.display = 'block';
5953
        } else {
5954
            rowStyle.display = 'none';
5955
        }
5956
    }
5957

5958
    /**
5959
     * @hidden @internal
5960
     */
5961
    public repositionRowEditingOverlay(row: RowType) {
5962
        if (row && !this.rowEditingOverlay.collapsed) {
5963
            const rowStyle = this.rowEditingOverlay.element.parentElement.style;
5964
            if (row) {
5965
                rowStyle.display = '';
5966
                this.configureRowEditingOverlay(row.key);
5967
                this.rowEditingOverlay.reposition();
5968
            } else {
5969
                rowStyle.display = 'none';
5970
            }
5971
        }
5972
    }
5973

5974
    /**
5975
     * @hidden @internal
5976
     */
5977
    public cachedViewLoaded(args: ICachedViewLoadedEventArgs) {
5978
        if (this.hasHorizontalScroll()) {
5979
            const tmplId = args.context.templateID.type;
5980
            const index = args.context.index;
5981
            args.view.detectChanges();
5982
            this.zone.onStable.pipe(first()).subscribe(() => {
5983
                const row = tmplId === 'dataRow' ? this.gridAPI.get_row_by_index(index) : null;
5984
                const summaryRow = tmplId === 'summaryRow' ? this.summariesRowList.find((sr) => sr.dataRowIndex === index) : null;
5985
                if (row && row instanceof IgxRowDirective) {
5986
                    this._restoreVirtState(row);
5987
                } else if (summaryRow) {
5988
                    this._restoreVirtState(summaryRow);
5989
                }
5990
            });
5991
        }
5992
    }
5993

5994
    /**
5995
     * Opens the advanced filtering dialog.
5996
     */
5997
    public openAdvancedFilteringDialog(overlaySettings?: OverlaySettings) {
5998
        const settings = overlaySettings ? overlaySettings : this._advancedFilteringOverlaySettings;
5999
        if (!this._advancedFilteringOverlayId) {
6000
            this._advancedFilteringOverlaySettings.target =
6001
                (this as any).rootGrid ? (this as any).rootGrid.nativeElement : this.nativeElement;
6002
            this._advancedFilteringOverlaySettings.outlet = this.outlet;
6003

6004
            this._advancedFilteringOverlayId = this.overlayService.attach(
6005
                IgxAdvancedFilteringDialogComponent,
6006
                this.viewRef,
6007
                settings);
6008
            this.overlayService.show(this._advancedFilteringOverlayId);
6009
        }
6010
    }
6011

6012
    /**
6013
     * Closes the advanced filtering dialog.
6014
     *
6015
     * @param applyChanges indicates whether the changes should be applied
6016
     */
6017
    public closeAdvancedFilteringDialog(applyChanges: boolean) {
6018
        if (this._advancedFilteringOverlayId) {
6019
            const advancedFilteringOverlay = this.overlayService.getOverlayById(this._advancedFilteringOverlayId);
6020
            const advancedFilteringDialog = advancedFilteringOverlay.componentRef.instance as IgxAdvancedFilteringDialogComponent;
6021

6022
            if (applyChanges) {
6023
                advancedFilteringDialog.applyChanges();
6024
            }
6025
            advancedFilteringDialog.closeDialog();
6026
        }
6027
    }
6028

6029
    /**
6030
     * @hidden @internal
6031
     */
6032
    public getEmptyRecordObjectFor(inRow: RowType) {
6033
        const row = { ...inRow?.data };
6034
        Object.keys(row).forEach(key => row[key] = undefined);
6035
        const id = this.generateRowID();
6036
        row[this.primaryKey] = id;
6037
        return { rowID: id, data: row, recordRef: row };
6038
    }
6039

6040
    /**
6041
     * @hidden @internal
6042
     */
6043
    public hasHorizontalScroll() {
6044
        return this.totalWidth - this.unpinnedWidth > 0;
6045
    }
6046

6047
    /**
6048
     * @hidden @internal
6049
     */
6050
    public isSummaryRow(rowData): boolean {
6051
        return rowData && rowData.summaries && (rowData.summaries instanceof Map);
6052
    }
6053

6054
    /**
6055
     * @hidden @internal
6056
     */
6057
    public triggerPipes() {
6058
        this.pipeTrigger++;
6059
        this.cdr.detectChanges();
6060
    }
6061

6062
    /**
6063
     * @hidden
6064
     */
6065
    public rowEditingWheelHandler(event: WheelEvent) {
6066
        if (event.deltaY > 0) {
6067
            this.verticalScrollContainer.scrollNext();
6068
        } else {
6069
            this.verticalScrollContainer.scrollPrev();
6070
        }
6071
    }
6072

6073
    /**
6074
     * @hidden
6075
     */
6076
    public getUnpinnedIndexById(id) {
6077
        return this.unpinnedRecords.findIndex(x => x[this.primaryKey] === id);
6078
    }
6079

6080
    /**
6081
     * Finishes the row transactions on the current row and returns whether the grid editing was canceled.
6082
     *
6083
     * @remarks
6084
     * If `commit === true`, passes them from the pending state to the data (or transaction service)
6085
     * @example
6086
     * ```html
6087
     * <button igxButton (click)="grid.endEdit(true)">Commit Row</button>
6088
     * ```
6089
     * @param commit
6090
     */
6091
    // TODO: Facade for crud service refactoring. To be removed
6092
    // TODO: do not remove this, as it is used in rowEditTemplate, but mark is as internal and hidden
6093
    public endEdit(commit = true, event?: Event): boolean {
6094
        return this.crudService.endEdit(commit, event);
6095
    }
6096

6097
    /**
6098
     * Enters add mode by spawning the UI under the specified row by rowID.
6099
     *
6100
     * @remarks
6101
     * If null is passed as rowID, the row adding UI is spawned as the first record in the data view
6102
     * @remarks
6103
     * Spawning the UI to add a child for a record only works if you provide a rowID
6104
     * @example
6105
     * ```typescript
6106
     * this.grid.beginAddRowById('ALFKI');
6107
     * this.grid.beginAddRowById('ALFKI', true);
6108
     * this.grid.beginAddRowById(null);
6109
     * ```
6110
     * @param rowID - The rowID to spawn the add row UI for, or null to spawn it as the first record in the data view
6111
     * @param asChild - Whether the record should be added as a child. Only applicable to igxTreeGrid.
6112
     */
6113
    public beginAddRowById(rowID: any, asChild?: boolean): void {
6114
        let index = rowID;
6115
        if (rowID == null) {
6116
            if (asChild) {
6117
                console.warn('The record cannot be added as a child to an unspecified record.');
6118
                return;
6119
            }
6120
            index = null;
6121
        } else {
6122
            // find the index of the record with that PK
6123
            index = this.gridAPI.get_rec_index_by_id(rowID, this.dataView);
6124
            if (index === -1) {
6125
                console.warn('No row with the specified ID was found.');
6126
                return;
6127
            }
6128
        }
6129

6130
        this._addRowForIndex(index, asChild);
6131
    }
6132

6133
    protected _addRowForIndex(index: number, asChild?: boolean) {
6134
        if (!this.dataView.length) {
6135
            this.beginAddRowForIndex(index, asChild);
6136
            return;
6137
        }
6138
        // check if the index is valid - won't support anything outside the data view
6139
        if (index >= 0 && index < this.dataView.length) {
6140
            // check if the index is in the view port
6141
            if ((index < this.virtualizationState.startIndex ||
6142
                index >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) &&
6143
                !this.isRecordPinnedByViewIndex(index)) {
6144
                this.verticalScrollContainer.chunkLoad
6145
                    .pipe(first(), takeUntil(this.destroy$))
6146
                    .subscribe(() => {
6147
                        this.beginAddRowForIndex(index, asChild);
6148
                    });
6149
                this.navigateTo(index);
6150
                this.notifyChanges(true);
6151
                return;
6152
            }
6153
            this.beginAddRowForIndex(index, asChild);
6154
        } else {
6155
            console.warn('The row with the specified PK or index is outside of the current data view.');
6156
        }
6157
    }
6158

6159
    /**
6160
     * Enters add mode by spawning the UI at the specified index.
6161
     *
6162
     * @remarks
6163
     * Accepted values for index are integers from 0 to this.grid.dataView.length
6164
     * @example
6165
     * ```typescript
6166
     * this.grid.beginAddRowByIndex(0);
6167
     * ```
6168
     * @param index - The index to spawn the UI at. Accepts integers from 0 to this.grid.dataView.length
6169
     */
6170
    public beginAddRowByIndex(index: number): void {
6171
        if (index === 0) {
6172
            return this.beginAddRowById(null);
6173
        }
6174
        return this._addRowForIndex(index - 1);
6175
    }
6176

6177
    /**
6178
     * @hidden
6179
     */
6180
    public preventHeaderScroll(args) {
6181
        if (args.target.scrollLeft !== 0) {
6182
            (this.navigation as any).forOfDir().getScroll().scrollLeft = args.target.scrollLeft;
6183
            args.target.scrollLeft = 0;
6184
        }
6185
    }
6186

6187
    protected beginAddRowForIndex(index: number, asChild = false) {
6188
        // TODO is row from rowList suitable for enterAddRowMode
6189
        const row = index == null ?
6190
            null : this.rowList.find(r => r.index === index);
6191
        if (row !== undefined) {
6192
            this.crudService.enterAddRowMode(row, asChild);
6193
        } else {
6194
            console.warn('No row with the specified PK or index was found.');
6195
        }
6196
    }
6197

6198
    protected switchTransactionService(val: boolean) {
6199
        if (val) {
6200
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);
6201
        } else {
6202
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
6203
        }
6204

6205
        if (this.dataCloneStrategy) {
6206
            this._transactions.cloneStrategy = this.dataCloneStrategy;
6207
        }
6208
    }
6209

6210
    protected subscribeToTransactions(): void {
6211
        this.transactionChange$.next();
6212
        this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$)))
6213
            .subscribe(this.transactionStatusUpdate.bind(this));
6214
    }
6215

6216
    protected transactionStatusUpdate(event: StateUpdateEvent) {
6217
        let actions: Action<Transaction>[] = [];
6218
        if (event.origin === TransactionEventOrigin.REDO) {
6219
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.DELETE) : [];
6220
        } else if (event.origin === TransactionEventOrigin.UNDO) {
6221
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.ADD) : [];
6222
        }
6223
        if (actions.length > 0) {
6224
            for (const action of actions) {
6225
                if (this.selectionService.isRowSelected(action.transaction.id)) {
6226
                    this.selectionService.deselectRow(action.transaction.id);
6227
                }
6228
            }
6229
        }
6230
        if (event.origin === TransactionEventOrigin.REDO || event.origin === TransactionEventOrigin.UNDO) {
6231
            event.actions.forEach(x => {
6232
                if (x.transaction.type === TransactionType.UPDATE) {
6233
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
6234
                    this.validation.update(x.transaction.id, value ?? x.recordRef);
6235
                } else if (x.transaction.type === TransactionType.DELETE || x.transaction.type === TransactionType.ADD) {
6236
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
6237
                    if (value) {
6238
                        this.validation.create(x.transaction.id, value ?? x.recordRef);
6239
                        this.validation.update(x.transaction.id, value ?? x.recordRef);
6240
                        this.validation.markAsTouched(x.transaction.id);
6241
                    } else {
6242
                        this.validation.clear(x.transaction.id);
6243
                    }
6244
                }
6245

6246
            });
6247
        }
6248

6249
        this.selectionService.clearHeaderCBState();
6250
        this.summaryService.clearSummaryCache();
6251
        this.pipeTrigger++;
6252
        this.notifyChanges();
6253
    }
6254

6255
    protected writeToData(rowIndex: number, value: any) {
6256
        mergeObjects(this.gridAPI.get_all_data()[rowIndex], value);
6257
    }
6258

6259
    protected _restoreVirtState(row) {
6260
        // check virtualization state of data record added from cache
6261
        // in case state is no longer valid - update it.
6262
        const rowForOf = row.virtDirRow;
6263
        const gridScrLeft = rowForOf.getScroll().scrollLeft;
6264
        const left = -parseInt(rowForOf.dc.instance._viewContainer.element.nativeElement.style.left, 10);
6265
        const actualScrollLeft = left + rowForOf.getColumnScrollLeft(rowForOf.state.startIndex);
6266
        if (gridScrLeft !== actualScrollLeft) {
6267
            rowForOf.onHScroll(gridScrLeft);
6268
            rowForOf.cdr.detectChanges();
6269
        }
6270
    }
6271

6272
    protected changeRowEditingOverlayStateOnScroll(row: RowType) {
6273
        if (!this.rowEditable || !this.rowEditingOverlay || this.rowEditingOverlay.collapsed) {
6274
            return;
6275
        }
6276
        if (!row) {
6277
            this.toggleRowEditingOverlay(false);
6278
        } else {
6279
            this.repositionRowEditingOverlay(row);
6280
        }
6281
    }
6282

6283
    /**
6284
     * Should be called when data and/or isLoading input changes so that the overlay can be
6285
     * hidden/shown based on the current value of shouldOverlayLoading
6286
     */
6287
    protected evaluateLoadingState() {
6288
        if (this.shouldOverlayLoading) {
6289
            // a new overlay should be shown
6290
            const overlaySettings: OverlaySettings = {
6291
                outlet: this.loadingOutlet,
6292
                closeOnOutsideClick: false,
6293
                positionStrategy: new ContainerPositionStrategy()
6294
            };
6295
            this.loadingOverlay.open(overlaySettings);
6296
        } else {
6297
            this.loadingOverlay.close();
6298
        }
6299
    }
6300

6301
    /**
6302
     * @hidden
6303
     * Sets grid width i.e. this.calcWidth
6304
     */
6305
    protected calculateGridWidth() {
6306
        let width;
6307

6308
        if (this.isPercentWidth) {
6309
            /* width in %*/
6310
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width');
6311
            width = computed.indexOf('%') === -1 ? parseInt(computed, 10) : null;
6312
        } else {
6313
            width = parseInt(this.width, 10);
6314
        }
6315

6316
        if (!width && this.nativeElement) {
6317
            width = this.nativeElement.offsetWidth;
6318
        }
6319

6320

6321
        if (this.width === null || !width) {
6322
            width = this.getColumnWidthSum();
6323
        }
6324

6325
        if (this.hasVerticalScroll() && this.width !== null) {
6326
            width -= this.scrollSize;
6327
        }
6328
        if ((Number.isFinite(width) || width === null) && width !== this.calcWidth) {
6329
            this.calcWidth = width;
6330
        }
6331
        this._derivePossibleWidth();
6332
    }
6333

6334
    /**
6335
     * @hidden
6336
     * Sets columns defaultWidth property
6337
     */
6338
    protected _derivePossibleWidth() {
6339
        if (!this.columnWidthSetByUser) {
6340
            this._columnWidth = this.width !== null ? this.getPossibleColumnWidth() : this.minColumnWidth + 'px';
6341
        }
6342
        this._columns.forEach((column: IgxColumnComponent) => {
6343
            if (this.hasColumnLayouts && parseInt(this._columnWidth, 10)) {
6344
                const columnWidthCombined = parseInt(this._columnWidth, 10) * (column.colEnd ? column.colEnd - column.colStart : 1);
6345
                column.defaultWidth = columnWidthCombined + 'px';
6346
            } else {
6347
                // D.K. March 29th, 2021 #9145 Consider min/max width when setting defaultWidth property
6348
                column.defaultWidth = this.getExtremumBasedColWidth(column);
6349
                column.resetCaches();
6350
            }
6351
        });
6352
        this.resetCachedWidths();
6353
    }
6354

6355
    /**
6356
     * @hidden
6357
     * @internal
6358
     */
6359
    protected getExtremumBasedColWidth(column: IgxColumnComponent): string {
6360
        let width = this._columnWidth;
6361
        if (width && typeof width !== 'string') {
6362
            width = String(width);
6363
        }
6364
        const minWidth = width.indexOf('%') === -1 ? column.minWidthPx : column.minWidthPercent;
6365
        const maxWidth = width.indexOf('%') === -1 ? column.maxWidthPx : column.maxWidthPercent;
6366
        if (column.hidden) {
6367
            return width;
6368
        }
6369

6370
        if (minWidth > parseFloat(width)) {
6371
            width = String(column.minWidth);
6372
        } else if (maxWidth < parseFloat(width)) {
6373
            width = String(column.maxWidth);
6374
        }
6375

6376
        // if no px or % are defined in maxWidth/minWidth consider it px
6377
        if (width.indexOf('%') === -1 && width.indexOf('px') === -1) {
6378
            width += 'px';
6379
        }
6380
        return width;
6381
    }
6382

6383
    protected resetNotifyChanges() {
6384
        this._cdrRequestRepaint = false;
6385
        this._cdrRequests = false;
6386
    }
6387

6388
    /** @hidden @internal */
6389
    public resolveOutlet() {
6390
        return this._userOutletDirective ? this._userOutletDirective : this._outletDirective;
6391
    }
6392

6393
    /**
6394
     * Reorder columns in the main columnList and _columns collections.
6395
     *
6396
     * @hidden
6397
     */
6398
    protected _moveColumns(from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6399
        const orderedList = this._pinnedColumns.concat(this._unpinnedColumns);
6400
        const list = orderedList;
6401
        this._reorderColumns(from, to, pos, list);
6402
        const newList = this._resetColumnList(list);
6403
        this.updateColumns(newList);
6404
    }
6405

6406

6407
    /**
6408
     * Update internal column's collection.
6409
     * @hidden
6410
     */
6411
    public updateColumns(newColumns: IgxColumnComponent[]) {
6412
        // update internal collections to retain order.
6413
        this._pinnedColumns = newColumns
6414
            .filter((c) => c.pinned);
6415
        this._unpinnedColumns = newColumns.filter((c) => !c.pinned);
6416
        this._columns = newColumns;
6417
        this.resetCaches();
6418
    }
6419

6420
    /**
6421
     * @hidden
6422
     */
6423
    protected _resetColumnList(list?) {
6424
        if (!list) {
6425
            list = this._columns;
6426
        }
6427
        let newList = [];
6428
        list.filter(c => c.level === 0).forEach(p => {
6429
            newList.push(p);
6430
            if (p.columnGroup) {
6431
                newList = newList.concat(p.allChildren);
6432
            }
6433
        });
6434
        return newList;
6435
    }
6436

6437
    /**
6438
     * Reorders columns inside the passed column collection.
6439
     * When reordering column group collection, the collection is not flattened.
6440
     * In all other cases, the columns collection is flattened, this is why adittional calculations on the dropIndex are done.
6441
     *
6442
     * @hidden
6443
     */
6444
    protected _reorderColumns(from: IgxColumnComponent, to: IgxColumnComponent, position: DropPosition, columnCollection: any[],
6445
        inGroup = false) {
6446
        const fromIndex = columnCollection.indexOf(from);
6447
        const childColumnsCount = inGroup ? 1 : from.allChildren.length + 1;
6448
        columnCollection.splice(fromIndex, childColumnsCount);
6449
        let dropIndex = columnCollection.indexOf(to);
6450
        if (position === DropPosition.AfterDropTarget) {
6451
            dropIndex++;
6452
            if (!inGroup && to.columnGroup) {
6453
                dropIndex += to.allChildren.length;
6454
            }
6455
        }
6456
        columnCollection.splice(dropIndex, 0, from);
6457
    }
6458

6459
    /**
6460
     * Reorder column group collection.
6461
     *
6462
     * @hidden
6463
     */
6464
    protected _moveChildColumns(parent: IgxColumnComponent, from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
6465
        const buffer = parent.children.toArray();
6466
        this._reorderColumns(from, to, pos, buffer, true);
6467
        parent.children.reset(buffer);
6468
    }
6469

6470
    /**
6471
     * @hidden @internal
6472
     */
6473
    protected setupColumns() {
6474
        if (this.autoGenerate) {
6475
            this.autogenerateColumns();
6476
        } else {
6477
            this._columns = this.getColumnList();
6478
        }
6479

6480
        this.initColumns(this._columns, (col: IgxColumnComponent) => this.columnInit.emit(col));
6481
        this.columnListDiffer.diff(this.columnList);
6482

6483
        this.columnList.changes
6484
            .pipe(takeUntil(this.destroy$))
6485
            .subscribe((change: QueryList<IgxColumnComponent>) => {
6486
                this.onColumnsChanged(change);
6487
            });
6488
    }
6489

6490
    protected getColumnList() {
6491
        return this.columnList.toArray();
6492
    }
6493

6494
    /**
6495
     * @hidden
6496
     */
6497
    protected deleteRowFromData(rowID: any, index: number) {
6498
        //  if there is a row (index !== 0) delete it
6499
        //  if there is a row in ADD or UPDATE state change it's state to DELETE
6500
        if (index !== -1) {
6501
            if (this.transactions.enabled) {
6502
                const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null };
6503
                this.transactions.add(transaction, this.data[index]);
6504
            } else {
6505
                this.data.splice(index, 1);
6506
            }
6507
        } else {
6508
            const state: State = this.transactions.getState(rowID);
6509
            this.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef);
6510
        }
6511
    }
6512

6513

6514
    /**
6515
     * @hidden @internal
6516
     */
6517
    protected getDataBasedBodyHeight(): number {
6518
        return !this.data || (this.data.length < this._defaultTargetRecordNumber) ?
6519
            0 : this.defaultTargetBodyHeight;
6520
    }
6521

6522
    /**
6523
     * @hidden @internal
6524
     */
6525
    protected onPinnedRowsChanged(change: QueryList<IgxGridRowComponent>) {
6526
        const diff = this.rowListDiffer.diff(change);
6527
        if (diff) {
6528
            this.notifyChanges(true);
6529
        }
6530
    }
6531

6532
    /**
6533
     * @hidden
6534
     */
6535
    protected onColumnsChanged(change: QueryList<IgxColumnComponent>) {
6536
        const diff = this.columnListDiffer.diff(change);
6537

6538
        if (this.autoGenerate && this._columns.length === 0 && this._autoGeneratedCols.length > 0) {
6539
            // In Ivy if there are nested conditional templates the content children are re-evaluated
6540
            // hence autogenerated columns are cleared and need to be reset.
6541
            this.updateColumns(this._autoGeneratedCols);
6542
            return;
6543
        }
6544
        if (diff) {
6545
            let added = false;
6546
            let removed = false;
6547
            diff.forEachAddedItem((record: IterableChangeRecord<IgxColumnComponent>) => {
6548
                added = true;
6549
                if (record.item.pinned) {
6550
                    this._pinnedColumns.push(record.item);
6551
                } else {
6552
                    this._unpinnedColumns.push(record.item);
6553
                }
6554
            });
6555

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

6558
            diff.forEachRemovedItem((record: IterableChangeRecord<IgxColumnComponent | IgxColumnGroupComponent>) => {
6559
                const isColumnGroup = record.item instanceof IgxColumnGroupComponent;
6560
                if (!isColumnGroup) {
6561
                    // Clear Grouping
6562
                    this.gridAPI.clear_groupby(record.item.field);
6563

6564
                    // Clear Filtering
6565
                    this.filteringService.clear_filter(record.item.field);
6566

6567
                    // Close filter row
6568
                    if (this.filteringService.isFilterRowVisible
6569
                        && this.filteringService.filteredColumn
6570
                        && this.filteringService.filteredColumn.field === record.item.field) {
6571
                        this.filteringRow.close();
6572
                    }
6573

6574
                    // Clear Sorting
6575
                    this.gridAPI.clear_sort(record.item.field);
6576

6577
                    // Remove column selection
6578
                    this.selectionService.deselectColumnsWithNoEvent([record.item.field]);
6579
                }
6580
                removed = true;
6581
            });
6582

6583
            this.resetCaches();
6584

6585
            if (added || removed) {
6586
                this.onColumnsAddedOrRemoved();
6587
            }
6588
        }
6589
    }
6590

6591
    /**
6592
     * @hidden @internal
6593
     */
6594
    protected onColumnsAddedOrRemoved() {
6595
        this.summaryService.clearSummaryCache();
6596
        Promise.resolve().then(() => {
6597
            // `onColumnsChanged` can be executed midway a current detectChange cycle and markForCheck will be ignored then.
6598
            // This ensures that we will wait for the current cycle to end so we can trigger a new one and ngDoCheck to fire.
6599
            this.notifyChanges(true);
6600
        });
6601
    }
6602

6603
    /**
6604
     * @hidden
6605
     */
6606
    protected calculateGridSizes(recalcFeatureWidth = true) {
6607
        /*
6608
            TODO: (R.K.) This layered lasagne should be refactored
6609
            ASAP. The reason I have to reset the caches so many times is because
6610
            after teach `detectChanges` call they are filled with invalid
6611
            state. Of course all of this happens midway through the grid
6612
            sizing process which of course, uses values from the caches, thus resulting
6613
            in a broken layout.
6614
        */
6615
        this.cdr.detectChanges();
6616
        this.resetCaches(recalcFeatureWidth);
6617

6618
        const hasScroll = this.hasVerticalScroll();
6619
        this.calculateGridWidth();
6620
        this.resetCaches(recalcFeatureWidth);
6621
        this.cdr.detectChanges();
6622
        this.calculateGridHeight();
6623

6624
        if (this.rowEditable) {
6625
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
6626
        }
6627

6628
        if (this.filteringService.isFilterRowVisible) {
6629
            this.filteringRow.resetChipsArea();
6630
        }
6631

6632
        this.cdr.detectChanges();
6633
        // in case scrollbar has appeared recalc to size correctly.
6634
        if (hasScroll !== this.hasVerticalScroll()) {
6635
            this.calculateGridWidth();
6636
            this.cdr.detectChanges();
6637
        }
6638
        if (this.zone.isStable) {
6639
            this.zone.run(() => {
6640
                this._applyWidthHostBinding();
6641
                this.cdr.detectChanges();
6642
            });
6643
        } else {
6644
            this.zone.onStable.pipe(first()).subscribe(() => {
6645
                this.zone.run(() => {
6646
                    this._applyWidthHostBinding();
6647
                });
6648
            });
6649
        }
6650
        this.resetCaches(recalcFeatureWidth);
6651
        if (this.hasColumnsToAutosize) {
6652
            this.cdr.detectChanges();
6653
            this.zone.onStable.pipe(first()).subscribe(() => {
6654
                this.autoSizeColumnsInView();
6655
            });
6656
        }
6657
    }
6658

6659
    /**
6660
     * @hidden
6661
     * @internal
6662
     */
6663
    protected calcGridHeadRow() {
6664
        if (this.maxLevelHeaderDepth) {
6665
            this._baseFontSize = parseFloat(getComputedStyle(this.document.documentElement).getPropertyValue('font-size'));
6666
            const hasFilterRow = this._allowFiltering && this._filterMode === FilterMode.quickFilter;
6667
            const minSize = (this.maxLevelHeaderDepth + 1 + (hasFilterRow ? 1 : 0)) * this.defaultRowHeight / this._baseFontSize;
6668
            this.theadRow.nativeElement.style.minHeight = `${minSize}rem`;
6669
        }
6670
    }
6671

6672
    /**
6673
     * @hidden
6674
     * Sets TBODY height i.e. this.calcHeight
6675
     */
6676
    protected calculateGridHeight() {
6677
        this.calcGridHeadRow();
6678

6679
        this.calcHeight = this._calculateGridBodyHeight();
6680
        if (this.pinnedRowHeight && this.calcHeight) {
6681
            this.calcHeight -= this.pinnedRowHeight;
6682
        }
6683
    }
6684

6685
    /**
6686
     * @hidden
6687
     */
6688
    protected getGroupAreaHeight(): number {
6689
        return 0;
6690
    }
6691

6692
    /**
6693
     * @hidden
6694
     */
6695
    protected getComputedHeight(elem) {
6696
        return elem.offsetHeight ? parseFloat(this.document.defaultView.getComputedStyle(elem).getPropertyValue('height')) : 0;
6697
    }
6698
    /**
6699
     * @hidden
6700
     */
6701
    protected getFooterHeight(): number {
6702
        return this.summaryRowHeight || this.getComputedHeight(this.tfoot.nativeElement);
6703
    }
6704
    /**
6705
     * @hidden
6706
     */
6707
    protected getTheadRowHeight(): number {
6708
        const height = this.getComputedHeight(this.theadRow.nativeElement);
6709
        return (!this.allowFiltering || (this.allowFiltering && this.filterMode !== FilterMode.quickFilter)) ?
6710
            height - this.getFilterCellHeight() :
6711
            height;
6712
    }
6713

6714
    /**
6715
     * @hidden
6716
     */
6717
    protected getToolbarHeight(): number {
6718
        let toolbarHeight = 0;
6719
        if (this.toolbar.first) {
6720
            toolbarHeight = this.getComputedHeight(this.toolbar.first.nativeElement);
6721
        }
6722
        return toolbarHeight;
6723
    }
6724

6725
    /**
6726
     * @hidden
6727
     */
6728
    protected getPagingFooterHeight(): number {
6729
        let pagingHeight = 0;
6730
        if (this.footer) {
6731
            const height = this.getComputedHeight(this.footer.nativeElement);
6732
            pagingHeight = this.footer.nativeElement.firstElementChild ?
6733
                height : 0;
6734
        }
6735
        return pagingHeight;
6736
    }
6737

6738
    /**
6739
     * @hidden
6740
     */
6741
    protected getFilterCellHeight(): number {
6742
        const headerGroupNativeEl = (this.headerGroupsList.length !== 0) ?
6743
            this.headerGroupsList[0].nativeElement : null;
6744
        const filterCellNativeEl = (headerGroupNativeEl) ?
6745
            headerGroupNativeEl.querySelector('igx-grid-filtering-cell') as HTMLElement : null;
6746
        return (filterCellNativeEl) ? filterCellNativeEl.offsetHeight : 0;
6747
    }
6748

6749
    /**
6750
     * @hidden
6751
     */
6752
    protected _calculateGridBodyHeight(): number {
6753
        if (!this._height) {
6754
            return null;
6755
        }
6756
        const actualTheadRow = this.getTheadRowHeight();
6757
        const footerHeight = this.getFooterHeight();
6758
        const toolbarHeight = this.getToolbarHeight();
6759
        const pagingHeight = this.getPagingFooterHeight();
6760
        const groupAreaHeight = this.getGroupAreaHeight();
6761
        const scrHeight = this.getComputedHeight(this.scr.nativeElement);
6762
        const renderedHeight = toolbarHeight + actualTheadRow +
6763
            footerHeight + pagingHeight + groupAreaHeight +
6764
            scrHeight;
6765

6766
        let gridHeight = 0;
6767

6768
        if (this.isPercentHeight) {
6769
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('height');
6770
            const autoSize = this._shouldAutoSize(renderedHeight);
6771
            if (autoSize || computed.indexOf('%') !== -1) {
6772
                const bodyHeight = this.getDataBasedBodyHeight();
6773
                return bodyHeight > 0 ? bodyHeight : null;
6774
            }
6775
            gridHeight = parseFloat(computed);
6776
        } else {
6777
            gridHeight = parseInt(this._height, 10);
6778
        }
6779
        const height = Math.abs(gridHeight - renderedHeight);
6780

6781
        if (Math.round(height) === 0 || isNaN(gridHeight)) {
6782
            const bodyHeight = this.defaultTargetBodyHeight;
6783
            return bodyHeight > 0 ? bodyHeight : null;
6784
        }
6785
        return height;
6786
    }
6787

6788
    protected checkContainerSizeChange() {
6789
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
6790
        const origHeight = parentElement.offsetHeight;
6791
        this.nativeElement.style.display = 'none';
6792
        const height = parentElement.offsetHeight;
6793
        this.nativeElement.style.display = '';
6794
        return origHeight !== height;
6795
    }
6796

6797
    protected _shouldAutoSize(renderedHeight) {
6798
        this.tbody.nativeElement.style.display = 'none';
6799
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
6800
        let res = !parentElement ||
6801
            parentElement.clientHeight === 0 ||
6802
            parentElement.clientHeight === renderedHeight;
6803
        if ((!this.platform.isChromium && !this.platform.isFirefox) || this._autoSize) {
6804
            // If grid causes the parent container to extend (for example when container is flex)
6805
            // we should always auto-size since the actual size of the container will continuously change as the grid renders elements.
6806
            this._autoSize = false;
6807
            res = this.checkContainerSizeChange();
6808
        }
6809
        this.tbody.nativeElement.style.display = '';
6810
        return res;
6811
    }
6812

6813
    /**
6814
     * @hidden
6815
     * Gets calculated width of the unpinned area
6816
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
6817
     */
6818
    protected getUnpinnedWidth(takeHidden = false) {
6819
        let width = this.isPercentWidth ?
6820
            this.calcWidth :
6821
            parseInt(this.width, 10) || parseInt(this.hostWidth, 10) || this.calcWidth;
6822
        if (this.hasVerticalScroll() && !this.isPercentWidth) {
6823
            width -= this.scrollSize;
6824
        }
6825
        if (!this.isPinningToStart) {
6826
            width -= this.featureColumnsWidth();
6827
        }
6828

6829
        return width - this.getPinnedWidth(takeHidden);
6830
    }
6831

6832
    /**
6833
     * @hidden
6834
     */
6835
    protected _summaries(fieldName: string, hasSummary: boolean, summaryOperand?: any) {
6836
        const column = this.gridAPI.get_column_by_name(fieldName);
6837
        if (column) {
6838
            column.hasSummary = hasSummary;
6839
            if (summaryOperand) {
6840
                if (this.rootSummariesEnabled) {
6841
                    this.summaryService.retriggerRootPipe++;
6842
                }
6843
                column.summaries = summaryOperand;
6844
            }
6845
        }
6846
    }
6847

6848
    /**
6849
     * @hidden
6850
     */
6851
    protected _multipleSummaries(expressions: ISummaryExpression[], hasSummary: boolean) {
6852
        expressions.forEach((element) => {
6853
            this._summaries(element.fieldName, hasSummary, element.customSummary);
6854
        });
6855
    }
6856
    /**
6857
     * @hidden
6858
     */
6859
    protected _disableMultipleSummaries(expressions) {
6860
        expressions.forEach((column) => {
6861
            const columnName = column && column.fieldName ? column.fieldName : column;
6862
            this._summaries(columnName, false);
6863
        });
6864
    }
6865

6866
    /**
6867
     * @hidden
6868
     */
6869
    public resolveDataTypes(rec) {
6870
        if (typeof rec === 'number') {
6871
            return GridColumnDataType.Number;
6872
        } else if (typeof rec === 'boolean') {
6873
            return GridColumnDataType.Boolean;
6874
        } else if (typeof rec === 'object' && rec instanceof Date) {
6875
            return GridColumnDataType.Date;
6876
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
6877
            return GridColumnDataType.Image;
6878
        }
6879
        return GridColumnDataType.String;
6880
    }
6881

6882
    /**
6883
     * @hidden
6884
     */
6885
    protected autogenerateColumns() {
6886
        const data = this.gridAPI.get_data();
6887
        const fields = this.generateDataFields(data);
6888
        const columns = [];
6889

6890
        fields.forEach((field) => {
6891
            const ref = createComponent(IgxColumnComponent, { environmentInjector:  this.envInjector, elementInjector: this.injector});
6892
            ref.instance.field = field;
6893
            ref.instance.dataType = this.resolveDataTypes(data[0][field]);
6894
            ref.changeDetectorRef.detectChanges();
6895
            columns.push(ref.instance);
6896
        });
6897
        this._autoGeneratedCols = columns;
6898

6899
        this.updateColumns(columns);
6900
        if (data && data.length > 0) {
6901
            this.shouldGenerate = false;
6902
        }
6903
    }
6904

6905
    protected generateDataFields(data: any[]): string[] {
6906
        return Object.keys(data && data.length !== 0 ? data[0] : [])
6907
            .filter(key => !this.autoGenerateExclude.includes(key));
6908
    }
6909

6910
    /**
6911
     * @hidden
6912
     */
6913
    protected initColumns(collection: IgxColumnComponent[], cb: (args: any) => void = null) {
6914
        this._columnGroups = collection.some(col => col.columnGroup);
6915
        if (this.hasColumnLayouts) {
6916
            // Set overall row layout size
6917
            collection.forEach((col) => {
6918
                if (col.columnLayout) {
6919
                    const layoutSize = col.children ?
6920
                        col.children.reduce((acc, val) => Math.max(val.rowStart + val.gridRowSpan - 1, acc), 1) :
6921
                        1;
6922
                    this._multiRowLayoutRowSize = Math.max(layoutSize, this._multiRowLayoutRowSize);
6923
                }
6924
            });
6925
        }
6926
        if (this.hasColumnLayouts && this.hasColumnGroups) {
6927
            // invalid configuration - multi-row and column groups
6928
            // remove column groups
6929
            const columnLayoutColumns = collection.filter((col) => col.columnLayout || col.columnLayoutChild);
6930
            collection = columnLayoutColumns;
6931
        }
6932
        this._maxLevelHeaderDepth = null;
6933
        collection.forEach((column: IgxColumnComponent) => {
6934
            column.defaultWidth = this.columnWidthSetByUser ? this._columnWidth : column.defaultWidth ? column.defaultWidth : '';
6935

6936
            if (cb) {
6937
                cb(column);
6938
            }
6939
        });
6940

6941
        this.updateColumns(collection);
6942

6943
        if (this.hasColumnLayouts) {
6944
            collection.forEach((column: IgxColumnComponent) => {
6945
                column.populateVisibleIndexes();
6946
            });
6947
        }
6948
    }
6949

6950
    /**
6951
     * @hidden
6952
     */
6953
    protected reinitPinStates() {
6954
        this._pinnedColumns = this._columns
6955
            .filter((c) => c.pinned).sort((a, b) => this._pinnedColumns.indexOf(a) - this._pinnedColumns.indexOf(b));
6956
        this._unpinnedColumns = this.hasColumnGroups ? this._columns.filter((c) => !c.pinned) :
6957
            this._columns.filter((c) => !c.pinned)
6958
                .sort((a, b) => this._unpinnedColumns.indexOf(a) - this._unpinnedColumns.indexOf(b));
6959
    }
6960

6961
    protected extractDataFromSelection(source: any[], formatters = false, headers = false, columnData?: any[]): any[] {
6962
        let columnsArray: IgxColumnComponent[];
6963
        let record = {};
6964
        let selectedData = [];
6965
        let keys = [];
6966
        const selectionCollection = new Map();
6967
        const keysAndData = [];
6968
        const activeEl = this.selectionService.activeElement;
6969

6970
        if (this.nativeElement.tagName.toLowerCase() === 'igx-hierarchical-grid') {
6971
            const expansionRowIndexes = [];
6972
            for (const [key, value] of this.expansionStates.entries()) {
6973
                if (value) {
6974
                    const rowIndex = this.gridAPI.get_rec_index_by_id(key, this.dataView);
6975
                    expansionRowIndexes.push(rowIndex);
6976
                }
6977
            }
6978
            if (this.selectionService.selection.size > 0) {
6979
                if (expansionRowIndexes.length > 0) {
6980
                    for (const [key, value] of this.selectionService.selection.entries()) {
6981
                        const updatedKey = key;
6982
                        let subtract = 0;
6983
                        expansionRowIndexes.forEach((row) => {
6984
                            if (updatedKey > Number(row)) {
6985
                                subtract++;
6986
                            }
6987
                        });
6988
                        selectionCollection.set(updatedKey - subtract, value);
6989
                    }
6990
                }
6991
            } else if (activeEl) {
6992
                let subtract = 0;
6993
                if (expansionRowIndexes.length > 0) {
6994
                    expansionRowIndexes.forEach(row => {
6995
                        if (activeEl.row > Number(row)) {
6996
                            subtract++;
6997
                        }
6998
                    });
6999
                    activeEl.row -= subtract;
7000
                }
7001
            }
7002
        }
7003

7004
        const totalItems = (this as any).totalItemCount ?? 0;
7005
        const isRemote = totalItems && totalItems > this.dataView.length;
7006
        let selectionMap;
7007
        if (this.nativeElement.tagName.toLowerCase() === 'igx-hierarchical-grid' && selectionCollection.size > 0) {
7008
            selectionMap = isRemote ? Array.from(selectionCollection) :
7009
                Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length);
7010
        } else {
7011
            selectionMap = isRemote ? Array.from(this.selectionService.selection) :
7012
                Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length);
7013
        }
7014

7015
        if (this.cellSelection === GridSelectionMode.single && activeEl) {
7016
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
7017
        }
7018

7019
        if (this.cellSelection === GridSelectionMode.none && activeEl) {
7020
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
7021
        }
7022

7023
        if (columnData) {
7024
            selectedData = columnData;
7025
        }
7026

7027
        // eslint-disable-next-line prefer-const
7028
        for (let [row, set] of selectionMap) {
7029
            row = this.paginator && (this.pagingMode === GridPagingMode.Local && source === this.filteredSortedData) ? row + (this.perPage * this.page) : row;
7030
            row = isRemote ? row - this.virtualizationState.startIndex : row;
7031
            if (!source[row] || source[row].detailsData !== undefined) {
7032
                continue;
7033
            }
7034
            const temp = Array.from(set);
7035
            for (const each of temp) {
7036
                columnsArray = this.getSelectableColumnsAt(each);
7037
                columnsArray.forEach((col) => {
7038
                    if (col) {
7039
                        const key = !this.isPivot && headers ? col.header || col.field : col.field;
7040
                        const rowData = source[row].ghostRecord ? source[row].recordRef : source[row];
7041
                        const value = this.isPivot ? rowData.aggregationValues.get(col.field)
7042
                        : resolveNestedPath(rowData, col.field);
7043
                        record[key] = formatters && col.formatter ? col.formatter(value, rowData) : value;
7044
                        if (columnData) {
7045
                            if (!record[key]) {
7046
                                record[key] = '';
7047
                            }
7048
                            record[key] = record[key].toString().concat('recordRow-' + row);
7049
                        }
7050
                    }
7051
                });
7052
            }
7053
            if (Object.keys(record).length) {
7054
                if (columnData) {
7055
                    if (!keys.length) {
7056
                        keys = Object.keys(columnData[0]);
7057
                    }
7058
                    for (const [key, value] of Object.entries(record)) {
7059
                        if (!keys.includes(key)) {
7060
                            keys.push(key);
7061
                        }
7062
                        let c: any = value;
7063
                        const rowNumber = +c.split('recordRow-')[1];
7064
                        c = c.split('recordRow-')[0];
7065
                        record[key] = c;
7066
                        const mergedObj = Object.assign(selectedData[rowNumber], record);
7067
                        selectedData[rowNumber] = mergedObj;
7068
                    }
7069
                } else {
7070
                    selectedData.push(record);
7071
                }
7072
            }
7073
            record = {};
7074
        }
7075

7076
        if (keys.length) {
7077
            keysAndData.push(selectedData);
7078
            keysAndData.push(keys);
7079
            return keysAndData;
7080
        } else {
7081
            return selectedData;
7082
        }
7083
    }
7084

7085
    protected getSelectableColumnsAt(index) {
7086
        if (this.hasColumnLayouts) {
7087
            const visibleLayoutColumns = this.visibleColumns
7088
                .filter(col => col.columnLayout)
7089
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
7090
            const colLayout = visibleLayoutColumns[index];
7091
            return colLayout ? colLayout.children.toArray() : [];
7092
        } else {
7093
            const visibleColumns = this.visibleColumns
7094
                .filter(col => !col.columnGroup)
7095
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
7096
            return [visibleColumns[index]];
7097
        }
7098
    }
7099

7100
    protected autoSizeColumnsInView() {
7101
        if (!this.hasColumnsToAutosize) return;
7102
        const vState = this.headerContainer.state;
7103
        let colResized = false;
7104
        const unpinnedInView = this.headerContainer.igxGridForOf.slice(vState.startIndex, vState.startIndex + vState.chunkSize).flatMap(x => x.columnGroup ? x.allChildren : x);
7105
        const columnsInView = this.pinnedColumns.concat(unpinnedInView as IgxColumnComponent[]);
7106
        for (const col of columnsInView) {
7107
            if (!col.autoSize && col.headerCell) {
7108
                const cellsContentWidths = [];
7109
                if (col._cells.length !== this.rowList.length) {
7110
                    this.rowList.forEach(x => x.cdr.detectChanges());
7111
                }
7112
                const cells = this._dataRowList.map(x => x.cells.find(c => c.column === col));
7113
                cells.forEach((cell) => cellsContentWidths.push(cell?.nativeElement?.offsetWidth || 0));
7114
                const max = Math.max(...cellsContentWidths);
7115
                if (max === 0) {
7116
                    // cells not in DOM yet...
7117
                    continue;
7118
                }
7119
                const header = this.headerCellList.find(x => x.column === col);
7120
                cellsContentWidths.push(header.nativeElement.offsetWidth);
7121
                let maxSize = Math.ceil(Math.max(...cellsContentWidths)) + 1;
7122
                if (col.maxWidth && maxSize > col.maxWidthPx) {
7123
                    maxSize = col.maxWidthPx;
7124
                } else if (maxSize < col.minWidthPx) {
7125
                    maxSize = col.minWidthPx;
7126
                }
7127
                col.autoSize = maxSize;
7128
                col.resetCaches();
7129
                colResized = true;
7130
            }
7131
        }
7132
        if (colResized) {
7133
            this.resetCachedWidths();
7134
            this.cdr.detectChanges();
7135
        }
7136
    }
7137

7138
    protected extractDataFromColumnsSelection(source: any[], formatters = false, headers = false): any[] {
7139
        let record = {};
7140
        const selectedData = [];
7141
        const selectedColumns = this.selectedColumns();
7142
        if (selectedColumns.length === 0) {
7143
            return [];
7144
        }
7145

7146
        for (const data of source) {
7147
            selectedColumns.forEach((col) => {
7148
                const key = headers ? col.header || col.field : col.field;
7149
                record[key] = formatters && col.formatter ? col.formatter(data[col.field], data)
7150
                    : data[col.field];
7151
            });
7152

7153
            if (Object.keys(record).length) {
7154
                selectedData.push(record);
7155
            }
7156
            record = {};
7157
        }
7158
        return selectedData;
7159
    }
7160

7161
    /**
7162
     * @hidden
7163
     */
7164
    protected initPinning() {
7165
        const pinnedColumns = [];
7166
        const unpinnedColumns = [];
7167

7168
        this.calculateGridWidth();
7169
        this.resetCaches();
7170
        // When a column is a group or is inside a group, pin all related.
7171
        this._pinnedColumns.forEach(col => {
7172
            if (col.parent) {
7173
                col.parent.pinned = true;
7174
            }
7175
            if (col.columnGroup) {
7176
                col.children.forEach(child => child.pinned = true);
7177
            }
7178
        });
7179

7180
        // Make sure we don't exceed unpinned area min width and get pinned and unpinned col collections.
7181
        // We take into account top level columns (top level groups and non groups).
7182
        // If top level is unpinned the pinning handles all children to be unpinned as well.
7183
        for (const column of this._columns) {
7184
            if (column.pinned && !column.parent) {
7185
                pinnedColumns.push(column);
7186
            } else if (column.pinned && column.parent) {
7187
                if (column.topLevelParent.pinned) {
7188
                    pinnedColumns.push(column);
7189
                } else {
7190
                    column.pinned = false;
7191
                    unpinnedColumns.push(column);
7192
                }
7193
            } else {
7194
                unpinnedColumns.push(column);
7195
            }
7196
        }
7197

7198
        // Assign the applicable collections.
7199
        this._pinnedColumns = pinnedColumns;
7200
        this._unpinnedColumns = unpinnedColumns;
7201
        this.notifyChanges();
7202
    }
7203

7204
    /**
7205
     * @hidden
7206
     */
7207
    protected scrollTo(row: any | number, column: any | number, inCollection = this._filteredSortedUnpinnedData): void {
7208
        let delayScrolling = false;
7209

7210
        if (this.paginator && typeof (row) !== 'number') {
7211
            const rowIndex = inCollection.indexOf(row);
7212
            const page = Math.floor(rowIndex / this.perPage);
7213

7214
            if (this.page !== page) {
7215
                delayScrolling = true;
7216
                this.page = page;
7217
            }
7218
        }
7219

7220
        if (delayScrolling) {
7221
            this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
7222
                this.scrollDirective(this.verticalScrollContainer,
7223
                    typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
7224
            });
7225
        } else {
7226
            this.scrollDirective(this.verticalScrollContainer,
7227
                typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
7228
        }
7229

7230
        this.scrollToHorizontally(column);
7231
    }
7232

7233
    /**
7234
     * @hidden
7235
     */
7236
    protected scrollToHorizontally(column: any | number) {
7237
        let columnIndex = typeof column === 'number' ? column : this.getColumnByName(column).visibleIndex;
7238
        const scrollRow = this.rowList.find(r => !!r.virtDirRow);
7239
        const virtDir = scrollRow ? scrollRow.virtDirRow : null;
7240
        if (this.isPinningToStart && this.pinnedColumns.length) {
7241
            if (columnIndex >= this.pinnedColumns.length) {
7242
                columnIndex -= this.pinnedColumns.length;
7243
                this.scrollDirective(virtDir, columnIndex);
7244
            }
7245
        } else {
7246
            this.scrollDirective(virtDir, columnIndex);
7247
        }
7248
    }
7249

7250
    /**
7251
     * @hidden
7252
     */
7253
    protected scrollDirective(directive: IgxGridForOfDirective<any, any[]>, goal: number): void {
7254
        if (!directive) {
7255
            return;
7256
        }
7257
        directive.scrollTo(goal);
7258
    }
7259

7260

7261
    /**
7262
     * @hidden
7263
     */
7264
    protected getColumnWidthSum(): number {
7265
        let colSum = 0;
7266
        const cols = this.hasColumnLayouts ?
7267
            this.visibleColumns.filter(x => x.columnLayout) : this.visibleColumns.filter(x => !x.columnGroup);
7268
        cols.forEach((item) => {
7269
            colSum += parseInt((item.calcWidth || item.defaultWidth), 10) || this.minColumnWidth;
7270
        });
7271
        if (!colSum) {
7272
            return null;
7273
        }
7274
        this.cdr.detectChanges();
7275
        colSum += this.featureColumnsWidth();
7276
        return colSum;
7277
    }
7278

7279
    /**
7280
     * Notify changes, reset cache and populateVisibleIndexes.
7281
     *
7282
     * @hidden
7283
     */
7284
    private _columnsReordered(column: IgxColumnComponent) {
7285
        this.notifyChanges();
7286
        if (this.hasColumnLayouts) {
7287
            this._columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
7288
        }
7289
        // after reordering is done reset cached column collections.
7290
        this.resetColumnCollections();
7291
        column.resetCaches();
7292
    }
7293

7294
    protected buildDataView(_data: any[]) {
7295
        this._dataView = this.isRowPinningToTop ?
7296
            [...this.pinnedDataView, ...this.unpinnedDataView] :
7297
            [...this.unpinnedDataView, ...this.pinnedDataView];
7298
    }
7299

7300
    private _applyWidthHostBinding() {
7301
        let width = this._width;
7302
        if (width === null) {
7303
            let currentWidth = this.calcWidth;
7304
            if (this.hasVerticalScroll()) {
7305
                currentWidth += this.scrollSize;
7306
            }
7307
            width = currentWidth + 'px';
7308
            this.resetCaches();
7309
        }
7310
        this._hostWidth = width;
7311
        this.cdr.markForCheck();
7312
    }
7313

7314
    protected verticalScrollHandler(event) {
7315
        this.verticalScrollContainer.onScroll(event);
7316
        this.disableTransitions = true;
7317

7318
        this.zone.run(() => {
7319
            this.zone.onStable.pipe(first()).subscribe(() => {
7320
                this.verticalScrollContainer.chunkLoad.emit(this.verticalScrollContainer.state);
7321
                if (this.rowEditable) {
7322
                    this.changeRowEditingOverlayStateOnScroll(this.crudService.rowInEditMode);
7323
                }
7324
            });
7325
        });
7326
        this.disableTransitions = false;
7327

7328
        this.hideOverlays();
7329
        this.actionStrip?.hide();
7330
        if (this.actionStrip) {
7331
            this.actionStrip.context = null;
7332
        }
7333
        const args: IGridScrollEventArgs = {
7334
            direction: 'vertical',
7335
            event,
7336
            scrollPosition: this.verticalScrollContainer.scrollPosition
7337
        };
7338
        this.gridScroll.emit(args);
7339
    }
7340

7341
    protected horizontalScrollHandler(event) {
7342
        const scrollLeft = event.target.scrollLeft;
7343
        this.headerContainer.onHScroll(scrollLeft);
7344
        this._horizontalForOfs.forEach(vfor => vfor.onHScroll(scrollLeft));
7345
        this.cdr.markForCheck();
7346

7347
        this.zone.run(() => {
7348
            this.zone.onStable.pipe(first()).subscribe(() => {
7349
                this.parentVirtDir.chunkLoad.emit(this.headerContainer.state);
7350
                requestAnimationFrame(() => {
7351
                    this.autoSizeColumnsInView();
7352
                });
7353
            });
7354
        });
7355
        if(!this.navigation.isColumnFullyVisible(this.navigation.lastColumnIndex)) {
7356
            this.hideOverlays();
7357
        }
7358
        const args: IGridScrollEventArgs = { direction: 'horizontal', event, scrollPosition: this.headerContainer.scrollPosition };
7359
        this.gridScroll.emit(args);
7360
    }
7361

7362
    private executeCallback(rowIndex, visibleColIndex = -1, cb: (args: any) => void = null) {
7363
        if (!cb) {
7364
            return;
7365
        }
7366
        let row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
7367
        if (!row) {
7368
            if ((this as any).totalItemCount) {
7369
                this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
7370
                    this.cdr.detectChanges();
7371
                    row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
7372
                    const cbArgs = this.getNavigationArguments(row, visibleColIndex);
7373
                    cb(cbArgs);
7374
                });
7375
            }
7376
            const dataViewIndex = this._getDataViewIndex(rowIndex);
7377
            if (this.dataView[dataViewIndex].detailsData) {
7378
                this.navigation.setActiveNode({ row: rowIndex });
7379
                this.cdr.detectChanges();
7380
            }
7381

7382
            return;
7383
        }
7384
        const args = this.getNavigationArguments(row, visibleColIndex);
7385
        cb(args);
7386
    }
7387

7388
    private getNavigationArguments(row, visibleColIndex) {
7389
        let targetType: GridKeydownTargetType; let target;
7390
        switch (row.nativeElement.tagName.toLowerCase()) {
7391
            case 'igx-grid-groupby-row':
7392
                targetType = 'groupRow';
7393
                target = row;
7394
                break;
7395
            case 'igx-grid-summary-row':
7396
                targetType = 'summaryCell';
7397
                target = visibleColIndex !== -1 ?
7398
                    row.summaryCells.find(c => c.visibleColumnIndex === visibleColIndex) : row.summaryCells.first;
7399
                break;
7400
            case 'igx-child-grid-row':
7401
                targetType = 'hierarchicalRow';
7402
                target = row;
7403
                break;
7404
            default:
7405
                targetType = 'dataCell';
7406
                target = visibleColIndex !== -1 ? row.cells.find(c => c.visibleColumnIndex === visibleColIndex) : row.cells.first;
7407
                break;
7408
        }
7409
        return { targetType, target };
7410
    }
7411

7412
    private getNextDataRowIndex(currentRowIndex, previous = false): number {
7413
        const resolvedIndex = this._getDataViewIndex(currentRowIndex);
7414
        if (currentRowIndex < 0 || (currentRowIndex === 0 && previous) || (resolvedIndex >= this.dataView.length - 1 && !previous)) {
7415
            return currentRowIndex;
7416
        }
7417
        // find next/prev record that is editable.
7418
        const nextRowIndex = previous ? this.findPrevEditableDataRowIndex(currentRowIndex) :
7419
            this.dataView.findIndex((rec, index) =>
7420
                index > resolvedIndex && this.isEditableDataRecordAtIndex(index));
7421
        const nextDataIndex = this.getDataIndex(nextRowIndex);
7422
        return nextDataIndex !== -1 ? nextDataIndex : currentRowIndex;
7423
    }
7424

7425
    /**
7426
     * Returns the previous editable row index or -1 if no such row is found.
7427
     *
7428
     * @param currentIndex The index of the current editable record.
7429
     */
7430
    private findPrevEditableDataRowIndex(currentIndex): number {
7431
        let i = this.dataView.length;
7432
        const resolvedIndex = this._getDataViewIndex(currentIndex);
7433
        while (i--) {
7434
            if (i < resolvedIndex && this.isEditableDataRecordAtIndex(i)) {
7435
                return i;
7436
            }
7437
        }
7438
        return -1;
7439
    }
7440

7441

7442
    /**
7443
     * Returns if the record at the specified data view index is a an editable data record.
7444
     * If record is group rec, summary rec, child rec, ghost rec. etc. it is not editable.
7445
     *
7446
     * @param dataViewIndex The index of that record in the data view.
7447
     *
7448
     */
7449
    // TODO: Consider moving it into CRUD
7450
    private isEditableDataRecordAtIndex(dataViewIndex) {
7451
        const rec = this.dataView[dataViewIndex];
7452
        return !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData &&
7453
            !this.isGhostRecordAtIndex(dataViewIndex);
7454
    }
7455

7456
    /**
7457
     * Returns if the record at the specified data view index is a ghost.
7458
     * If record is pinned but is not in pinned area then it is a ghost record.
7459
     *
7460
     * @param dataViewIndex The index of that record in the data view.
7461
     */
7462
    private isGhostRecordAtIndex(dataViewIndex) {
7463
        const isPinned = this.isRecordPinned(this.dataView[dataViewIndex]);
7464
        const isInPinnedArea = this.isRecordPinnedByViewIndex(dataViewIndex);
7465
        return isPinned && !isInPinnedArea;
7466
    }
7467

7468
    private isValidPosition(rowIndex, colIndex): boolean {
7469
        const rows = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).length;
7470
        const cols = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0 && !col.hidden).length;
7471
        if (rows < 1 || cols < 1) {
7472
            return false;
7473
        }
7474
        if (rowIndex > -1 && rowIndex < this.dataView.length &&
7475
            colIndex > - 1 && colIndex <= Math.max(...this.visibleColumns.map(c => c.visibleIndex))) {
7476
            return true;
7477
        }
7478
        return false;
7479
    }
7480

7481
    private find(text: string, increment: number, caseSensitive?: boolean, exactMatch?: boolean, scroll?: boolean, endEdit = true) {
7482
        if (!this.rowList) {
7483
            return 0;
7484
        }
7485

7486
        if (endEdit) {
7487
            this.crudService.endEdit(false);
7488
        }
7489

7490
        if (!text) {
7491
            this.clearSearch();
7492
            return 0;
7493
        }
7494

7495
        const caseSensitiveResolved = caseSensitive ? true : false;
7496
        const exactMatchResolved = exactMatch ? true : false;
7497
        let rebuildCache = false;
7498

7499
        if (this.lastSearchInfo.searchText !== text ||
7500
            this.lastSearchInfo.caseSensitive !== caseSensitiveResolved ||
7501
            this.lastSearchInfo.exactMatch !== exactMatchResolved) {
7502
            this.lastSearchInfo = {
7503
                searchText: text,
7504
                activeMatchIndex: 0,
7505
                caseSensitive: caseSensitiveResolved,
7506
                exactMatch: exactMatchResolved,
7507
                matchInfoCache: [],
7508
                matchCount: 0,
7509
                content: ''
7510
            };
7511

7512
            rebuildCache = true;
7513
        } else {
7514
            this.lastSearchInfo.activeMatchIndex += increment;
7515
        }
7516

7517
        if (rebuildCache) {
7518
            this.rowList.forEach((row) => {
7519
                if (row.cells) {
7520
                    row.cells.forEach((c: IgxGridCellComponent) => {
7521
                        c.highlightText(text, caseSensitiveResolved, exactMatchResolved);
7522
                    });
7523
                }
7524
            });
7525

7526
            this.rebuildMatchCache();
7527
        }
7528

7529
        if (this.lastSearchInfo.activeMatchIndex >= this.lastSearchInfo.matchCount) {
7530
            this.lastSearchInfo.activeMatchIndex = 0;
7531
        } else if (this.lastSearchInfo.activeMatchIndex < 0) {
7532
            this.lastSearchInfo.activeMatchIndex = this.lastSearchInfo.matchCount - 1;
7533
        }
7534

7535
        if (this.lastSearchInfo.matchCount > 0) {
7536
            const matchInfo = this.lastSearchInfo.matchInfoCache[this.lastSearchInfo.activeMatchIndex];
7537
            this.lastSearchInfo = { ...this.lastSearchInfo };
7538

7539
            if (scroll !== false) {
7540
                this.scrollTo(matchInfo.row, matchInfo.column);
7541
            }
7542

7543
            IgxTextHighlightDirective.setActiveHighlight(this.id, {
7544
                column: matchInfo.column,
7545
                row: matchInfo.row,
7546
                index: matchInfo.index,
7547
                metadata: matchInfo.metadata,
7548
            });
7549

7550
        } else {
7551
            IgxTextHighlightDirective.clearActiveHighlight(this.id);
7552
        }
7553

7554
        return this.lastSearchInfo.matchCount;
7555
    }
7556

7557
    private rebuildMatchCache() {
7558
        this.lastSearchInfo.matchInfoCache = [];
7559

7560
        const caseSensitive = this.lastSearchInfo.caseSensitive;
7561
        const exactMatch = this.lastSearchInfo.exactMatch;
7562
        const searchText = caseSensitive ? this.lastSearchInfo.searchText : this.lastSearchInfo.searchText.toLowerCase();
7563
        const data = this.filteredSortedData;
7564
        const columnItems = this.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex);
7565

7566
        data.forEach((dataRow, rowIndex) => {
7567
            columnItems.forEach((c) => {
7568
                const pipeArgs = this.getColumnByName(c.field).pipeArgs;
7569
                const value = c.formatter ? c.formatter(resolveNestedPath(dataRow, c.field), dataRow) :
7570
                    c.dataType === 'number' ? formatNumber(resolveNestedPath(dataRow, c.field), this.locale, pipeArgs.digitsInfo) :
7571
                        c.dataType === 'date'
7572
                            ? formatDate(resolveNestedPath(dataRow, c.field), pipeArgs.format, this.locale, pipeArgs.timezone)
7573
                            : resolveNestedPath(dataRow, c.field);
7574
                if (value !== undefined && value !== null && c.searchable) {
7575
                    let searchValue = caseSensitive ? String(value) : String(value).toLowerCase();
7576

7577
                    if (exactMatch) {
7578
                        if (searchValue === searchText) {
7579
                            const mic: IMatchInfoCache = {
7580
                                row: dataRow,
7581
                                column: c.field,
7582
                                index: 0,
7583
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7584
                            };
7585

7586
                            this.lastSearchInfo.matchInfoCache.push(mic);
7587
                        }
7588
                    } else {
7589
                        let occurrenceIndex = 0;
7590
                        let searchIndex = searchValue.indexOf(searchText);
7591

7592
                        while (searchIndex !== -1) {
7593
                            const mic: IMatchInfoCache = {
7594
                                row: dataRow,
7595
                                column: c.field,
7596
                                index: occurrenceIndex++,
7597
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7598
                            };
7599

7600
                            this.lastSearchInfo.matchInfoCache.push(mic);
7601

7602
                            searchValue = searchValue.substring(searchIndex + searchText.length);
7603
                            searchIndex = searchValue.indexOf(searchText);
7604
                        }
7605
                    }
7606
                }
7607
            });
7608
        });
7609

7610
        this.lastSearchInfo.matchCount = this.lastSearchInfo.matchInfoCache.length;
7611
    }
7612

7613
    // TODO: About to Move to CRUD
7614
    private configureRowEditingOverlay(rowID: any, useOuter = false) {
7615
        let settings = this.rowEditSettings;
7616
        const overlay = this.overlayService.getOverlayById(this.rowEditingOverlay.overlayId);
7617
        if (overlay) {
7618
            settings = overlay.settings;
7619
        }
7620
        settings.outlet = useOuter ? this.parentRowOutletDirective : this.rowOutletDirective;
7621
        this.rowEditPositioningStrategy.settings.container = this.tbody.nativeElement;
7622
        const pinned = this._pinnedRecordIDs.indexOf(rowID) !== -1;
7623
        const targetRow = !pinned ?
7624
            this.gridAPI.get_row_by_key(rowID) as IgxRowDirective
7625
            : this.pinnedRows.find(x => x.key === rowID) as IgxRowDirective;
7626
        if (!targetRow) {
7627
            return;
7628
        }
7629
        settings.target = targetRow.element.nativeElement;
7630
        this.toggleRowEditingOverlay(true);
7631
    }
7632
}
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