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

IgniteUI / igniteui-angular / 6195893045

15 Sep 2023 08:42AM CUT coverage: 92.256% (-0.007%) from 92.263%
6195893045

push

github

web-flow
Merge pull request #13454 from IgniteUI/ddincheva/gridSizingFix--16.0

Fix: IgxGrid: Horizontal scrollbar is displayed when row selection is enabled --16.0.x

15311 of 17991 branches covered (0.0%)

1 of 1 new or added line in 1 file covered. (100.0%)

26842 of 29095 relevant lines covered (92.26%)

29469.83 hits per line

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

92.6
/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 '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';
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';
2✔
70
import { IgxGridSummaryService } from './summaries/grid-summary.service';
2✔
71
import { IgxSummaryRowComponent } from './summaries/summary-row.component';
72
import { IgxGridSelectionService } from './selection/selection.service';
21✔
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';
20✔
76
import { IgxGridColumnResizerComponent } from './resizing/resizer.component';
77
import { CharSeparatedValueData } from '../services/csv/char-separated-value-data';
78
import { IgxColumnResizingService } from './resizing/resizing.service';
79
import { FilteringStrategy, IFilteringStrategy } from '../data-operations/filtering-strategy';
200,934✔
80
import {
133,049✔
81
    IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandedIndicatorDirective,
82
    IgxHeaderCollapsedIndicatorDirective, IgxExcelStyleHeaderIconDirective, IgxSortAscendingHeaderIconDirective,
67,885✔
83
    IgxSortDescendingHeaderIconDirective, IgxSortHeaderIconDirective
84
} from './grid.directives';
85
import {
86
    GridKeydownTargetType,
83,654✔
87
    GridSelectionMode,
88
    GridSummaryPosition,
89
    GridSummaryCalculationMode,
48,073✔
90
    FilterMode,
91
    ColumnPinningPosition,
92
    RowPinningPosition,
1!
93
    GridPagingMode,
1✔
94
    GridValidationTrigger
1✔
95
} from './common/enums';
96
import {
97
    IGridCellEventArgs,
98
    IRowSelectionEventArgs,
99
    IPinColumnEventArgs,
247✔
100
    IGridEditEventArgs,
101
    IRowDataEventArgs,
102
    IColumnResizeEventArgs,
103
    IColumnMovingStartEventArgs,
3✔
104
    IColumnMovingEventArgs,
105
    IColumnMovingEndEventArgs,
106
    IGridKeydownEventArgs,
1,229✔
107
    IRowDragStartEventArgs,
108
    IRowDragEndEventArgs,
109
    IGridClipboardEvent,
110
    IGridToolbarExportEventArgs,
111
    ISearchInfo,
112
    ICellPosition,
113
    IRowToggleEventArgs,
114
    IColumnSelectionEventArgs,
115
    IPinRowEventArgs,
116
    IGridScrollEventArgs,
117
    IGridEditDoneEventArgs,
118
    IActiveNodeChangeEventArgs,
119
    ISortingEventArgs,
120
    IFilteringEventArgs,
121
    IColumnVisibilityChangedEventArgs,
122
    IColumnVisibilityChangingEventArgs,
1✔
123
    IPinColumnCancellableEventArgs
124
} from './common/events';
125
import { IgxAdvancedFilteringDialogComponent } from './filtering/advanced-filtering/advanced-filtering-dialog.component';
126
import {
10,397✔
127
    ColumnType,
128
    GridServiceType,
129
    GridType,
130
    IGridFormGroupCreatedEventArgs,
28,471✔
131
    IGridValidationStatusEventArgs,
132
    IgxGridEmptyTemplateContext,
133
    IgxGridHeaderTemplateContext,
134
    IgxGridRowDragGhostContext,
426✔
135
    IgxGridRowEditActionsTemplateContext,
136
    IgxGridRowEditTemplateContext,
137
    IgxGridRowEditTextTemplateContext,
138
    IgxGridRowTemplateContext,
27,753✔
139
    IgxGridTemplateContext,
140
    IgxHeadSelectorTemplateContext,
141
    IgxRowSelectorTemplateContext,
142
    IGX_GRID_SERVICE_BASE,
1,364✔
143
    ISizeInfo,
144
    RowType,
145
    IPinningConfig
4,338✔
146
} from './common/grid.interface';
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';
1✔
162
import { VerticalAlignment, HorizontalAlignment, PositionSettings, OverlaySettings } from '../services/overlay/utilities';
163
import { IgxOverlayService } from '../services/overlay/overlay';
164
import { ConnectedPositioningStrategy } from '../services/overlay/position/connected-positioning-strategy';
346✔
165
import { ContainerPositionStrategy } from '../services/overlay/position/container-position-strategy';
166
import { AbsoluteScrollStrategy } from '../services/overlay/scroll/absolute-scroll-strategy';
167
import { Action, StateUpdateEvent, TransactionEventOrigin } from '../services/transaction/transaction';
168
import { ISortingExpression } from '../data-operations/sorting-strategy';
169
import { IGridSortingStrategy } from './common/strategy';
170
import { IgxGridExcelStyleFilteringComponent } from './filtering/excel-style/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
interface IMatchInfoCache {
178
    row: any;
179
    index: number;
180
    column: string;
1✔
181
    metadata: Map<string, boolean>;
182
}
183

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

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

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

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

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

237
    /**
238
     * Controls whether columns moving is enabled in the grid.
819✔
239
     *
240
     */
241
    @Input()
11,261✔
242
    public moving = false;
243

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

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

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

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

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

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

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

314
    public set dataCloneStrategy(strategy: IDataCloneStrategy) {
825✔
315
        if (strategy) {
316
            this._dataCloneStrategy = strategy;
317
            this._transactions.cloneStrategy = strategy;
877✔
318
        }
319
    }
320

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

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

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

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

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

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

94✔
421
    /**
422
     * Gets/Sets the primary key.
423
     *
1,201,097✔
424
     * @example
425
     * ```html
426
     * <igx-grid #grid [data]="localData" [primaryKey]="'ProductID'" [autoGenerate]="true"></igx-grid>
3,891✔
427
     * ```
3,886✔
428
     */
3,886✔
429
    @WatchChanges()
3,886✔
430
    @Input()
3,886✔
431
    public primaryKey: any;
3,886✔
432

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

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

453
    /** @hidden @internal */
454
    public get excelStyleFilteringComponent() {
1,653,861✔
455
        return this.excelStyleFilteringComponents?.first;
456
    }
457

20!
458
    /** @hidden @internal */
20✔
459
    public get headerGroups() {
460
        return this.theadRow.groups;
461
    }
462

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

476

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

50,968✔
713
    /**
5,530✔
714
     * Emitted after sorting is completed.
715
     *
223,380✔
716
     * @remarks
45,438✔
717
     * Returns the sorting expression.
45,438✔
718
     * @example
719
     * ```html
720
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingDone)="sortingDone($event)"></igx-grid>
2,634✔
721
     * ```
722
     */
723
    @Output()
724
    public sortingDone = new EventEmitter<ISortingExpression | ISortingExpression[]>();
725

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

950
    /**
951
     * @hidden @internal
952
     */
178,801✔
953
    @Output()
954
    public expansionStatesChange = new EventEmitter<Map<any, boolean>>();
955

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

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

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

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

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

1013

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

2,464✔
1027
    /* End of toolbar related definitions */
2,464✔
1028

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

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

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

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

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

1070

1071
    /**
1072
     * @hidden @internal
1073
     */
1074
    @ViewChild(IgxSnackbarComponent)
36,159✔
1075
    public addRowSnackbar: IgxSnackbarComponent;
1076

1077
    /**
1078
     * @hidden @internal
1079
     */
1080
    @ViewChild(IgxGridColumnResizerComponent)
39,299✔
1081
    public resizeLine: IgxGridColumnResizerComponent;
1082

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

1089
    /**
1090
     * @hidden @internal
1091
     */
1092
    @ViewChild('igxLoadingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1093
    public loadingOutlet: IgxOverlayOutletDirective;
217,932✔
1094

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

1101
    /** @hidden @internal */
1102
    @ContentChild(IgxActionStripComponent)
1103
    public actionStrip: IgxActionStripComponent;
1104

1105
    /**
215,640✔
1106
     * @hidden @internal
1107
     */
1108
    @ContentChild(IgxExcelStyleLoadingValuesTemplateDirective, { read: IgxExcelStyleLoadingValuesTemplateDirective, static: true })
1109
    public excelStyleLoadingValuesTemplateDirective: IgxExcelStyleLoadingValuesTemplateDirective;
1110

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

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

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

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

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

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

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

1152

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

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

3,876✔
1178

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

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

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

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

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

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

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

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

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

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

3,876✔
1232
    /** @hidden @internal */
1233
    @ViewChild(IgxGridHeaderRowComponent, { static: true })
3,876✔
1234
    public theadRow: IgxGridHeaderRowComponent;
1235

1236
    /** @hidden @internal */
1237
    @ViewChild(IgxGridGroupByAreaComponent)
3,876✔
1238
    public groupArea: IgxGridGroupByAreaComponent;
1239

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1498
    /** @hidden @internal */
188,324✔
1499
    @ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef })
1500
    public excelStyleHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
1501

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

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

1✔
1527

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

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

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

817,227✔
1560
    /** @hidden @internal */
1561
    @ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef })
1562
    public sortDescendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
1563

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

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

1589
    /**
3,492,538✔
1590
     * @hidden
1591
     * @internal
1592
     */
1593
    @ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef })
1594
    public sortHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
1595

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

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

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

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

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

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

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

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

75✔
1658
    /** @hidden @internal */
1659
    @ContentChildren(IgxGridToolbarComponent)
1660
    public toolbar: QueryList<IgxGridToolbarComponent>;
487✔
1661

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

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

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

1678
    /**
6,430✔
1679
     * @hidden @internal
3,099✔
1680
     */
1681
    @ViewChild('defaultCollapsedTemplate', { read: TemplateRef, static: true })
1682
    protected defaultCollapsedTemplate: TemplateRef<any>;
3,099✔
1683

92✔
1684
    /**
92✔
1685
     * @hidden @internal
1686
     */
3,099✔
1687
    @ViewChild('defaultESFHeaderIcon', { read: TemplateRef, static: true })
1688
    protected defaultESFHeaderIconTemplate: TemplateRef<any>;
3,400✔
1689

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

705✔
1693
    @ViewChildren('row')
1694
    private _rowList: QueryList<IgxGridRowComponent>;
3,400✔
1695

1,256✔
1696
    @ViewChildren('pinnedRow')
1697
    private _pinnedRowList: QueryList<IgxGridRowComponent>;
3,400✔
1698

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

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

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

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

1724
    public get resourceStrings(): IGridResourceStrings {
200,315✔
1725
        if (!this._resourceStrings) {
1726
            this._resourceStrings = CurrentResourceStrings.GridResStrings;
1727
        }
1728
        return this._resourceStrings;
1729
    }
1730

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

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

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

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

1778
            value.type = FilteringExpressionsTreeType.Regular;
1779
            this._filteringExpressionsTree = value;
1780
            this.filteringPipeTrigger++;
42,659✔
1781
            this.filteringExpressionsTreeChange.emit(this._filteringExpressionsTree);
42,659✔
1782

42,659✔
1783
            if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
1784
                this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1785
                this._filteredData = null;
1786
            }
1787

1788
            this.filteringService.refreshExpressions();
10,001✔
1789
            this.selectionService.clearHeaderCBState();
31,610✔
1790
            this.summaryService.clearSummaryCache();
31,604✔
1791
            this.notifyChanges();
1792
        }
31,610✔
1793
    }
31,610✔
1794

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

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

107✔
1820
        if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
1821
            this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1822
            this._filteredData = null;
1823
        }
1824

1825
        this.selectionService.clearHeaderCBState();
1826
        this.summaryService.clearSummaryCache();
6,178✔
1827
        this.notifyChanges();
1828

1829
        // Wait for the change detection to update filtered data through the pipes and then emit the event.
1830
        requestAnimationFrame(() => this.filteringDone.emit(this._advancedFilteringExpressionsTree));
1831
    }
1832

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

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

1855
    @Input()
1856
    public get pagingMode() {
1857
        return this._pagingMode;
3,359✔
1858
    }
215✔
1859

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

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

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

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

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

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

1900
    public set hideRowSelectors(value: boolean) {
6,154✔
1901
        this._hideRowSelectors = value;
1902
        this.notifyChanges(true);
1903
    }
1904

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

17,109✔
1918
    public set rowDraggable(val: boolean) {
3,413✔
1919
        this._rowDrag = val;
3,413✔
1920
        this.notifyChanges(true);
3,413✔
1921
    }
2,634✔
1922

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

3,413✔
1934
    /**
3,413✔
1935
     * @hidden
3,413✔
1936
     * @internal
11✔
1937
     */
18✔
1938
    public rowDragging = false;
18✔
1939

17✔
1940
    /** @hidden @internal */
1941
    public dragRowID = null;
1942

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

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

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

1982
    public set height(value: string | null) {
1983
        if (this._height !== value) {
1984
            this._height = value;
1985
            this.nativeElement.style.height = value;
14,532✔
1986
            this.notifyChanges(true);
449,926✔
1987
        }
449,926✔
1988
    }
449,926✔
1989

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

1998
    /**
12,993✔
1999
     * Gets/Sets the width of the grid.
3,502✔
2000
     *
3,502✔
2001
     * @example
3,502✔
2002
     * ```typescript
3,502✔
2003
     * let gridWidth = this.grid.width;
2004
     * ```
9,491✔
2005
     */
4,041✔
2006
    @WatchChanges()
4,041✔
2007
    @Input()
2008
    public get width(): string | null {
2009
        return this._width;
2010
    }
2011

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

2020
    /** @hidden @internal */
3,335✔
2021
    public get headerWidth() {
24,761✔
2022
        return parseInt(this.width, 10) - 17;
2023
    }
3,335✔
2024

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

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

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

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

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

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

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

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

2135
    public get emptyFilteredGridMessage(): string {
2136
        return this._emptyFilteredGridMessage || this.resourceStrings.igx_grid_emptyFilteredGrid_message;
2137
    }
2138

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

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

2174
    public set allowFiltering(value) {
2175
        if (this._allowFiltering !== value) {
1,178,113✔
2176
            this._allowFiltering = value;
2177
            this.filteringService.registerSVGIcons();
5,974✔
2178

2179
            if (!this._init) {
16,235✔
2180
                this.calcGridHeadRow();
2181
            }
1,155,904✔
2182

2183
            this.filteringService.isFilterRowVisible = false;
2184
            this.filteringService.filteredColumn = null;
2185

2186
            this.notifyChanges(true);
2187
        }
2188
    }
11,851✔
2189

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

2203
    public set allowAdvancedFiltering(value) {
2204
        if (this._allowAdvancedFiltering !== value) {
2205
            this._allowAdvancedFiltering = value;
362,442✔
2206
            this.filteringService.registerSVGIcons();
2207

1,196✔
2208
            if (!this._init) {
2209
                this.notifyChanges(true);
2,748✔
2210
            }
2211
        }
358,498✔
2212
    }
2213

2214
    /**
2215
     * Gets/Sets the filter mode.
2216
     *
180,224✔
2217
     * @example
160,364✔
2218
     * ```html
2219
     * <igx-grid #grid [data]="localData" [filterMode]="'quickFilter'" [height]="'305px'" [autoGenerate]="true"></igx-grid>
19,860✔
2220
     * ```
19,860✔
2221
     * @remarks
2222
     * By default it's set to FilterMode.quickFilter.
2223
     */
2224
    @Input()
307,034✔
2225
    public get filterMode() {
285,770✔
2226
        return this._filterMode;
2227
    }
21,264✔
2228

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

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

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

26,832✔
2260
    public set summaryPosition(value: GridSummaryPosition) {
2261
        this._summaryPosition = value;
26,832✔
2262
        this.notifyChanges();
2263
    }
26,832✔
2264

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

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

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

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

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

606,360✔
2323
    public set filterStrategy(classRef: IFilteringStrategy) {
582,559✔
2324
        this._filterStrategy = classRef;
2325
    }
140,626✔
2326

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

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

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

12✔
2363
    public get sortingOptions() {
2✔
2364
        return this._sortingOptions;
2✔
2365
    }
2✔
2366

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

192,115✔
2382
    public get selectedRows(): any[] {
33,119✔
2383
        return this.selectionService.getSelectedRows();
2384
    }
2385

146✔
2386

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

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

2397
    /** @hidden @internal */
2398
    public get filterCellList(): IgxGridFilteringCellComponent[] {
2399
        return this.headerGroupsList.map(group => group.filter).filter(cell => cell);
36,810✔
2400
    }
16,829✔
2401

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

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

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

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

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

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

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

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

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

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

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

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

2543
    /**
161✔
2544

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2803
    public get pagingState() {
2804
        return this._pagingState;
2805
    }
2806

145✔
2807
    /**
2808
     * @hidden @internal
2809
     */
2810
    public rowEditMessage;
2811

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

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

2830
    /**
2831
     * @hidden @internal
2832
     */
2833
    public disableTransitions = false;
274✔
2834

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

2848
    /**
2849
     * @hidden @internal
2850
     */
2851
    public columnWidthSetByUser = false;
2852

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

2858
    /**
2859
     * @hidden @internal
2860
     */
2861
    public unpinnedRecords: any[];
2862

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

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

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

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

2877
    /** @hidden @internal */
2878
    public pipeTriggerNotifier = new Subject();
2879

2880
    /** @hidden @internal */
2881
    public _filteredSortedPinnedData: any[];
2882

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

2886
    /** @hidden @internal */
133✔
2887
    public _filteredPinnedData: any[];
133✔
2888

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

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

23✔
2927
    /** @hidden @internal */
23✔
2928
    public isPivot = false;
2929

23✔
2930
    /** @hidden @internal */
2931
    public _baseFontSize: number;
2932

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

2990
    protected _pinnedRecordIDs = [];
2991

2992
    /**
2993
     * @hidden
2994
     */
2995
    protected _hasVisibleColumns;
2996
    protected _allowFiltering = false;
3,634✔
2997
    protected _allowAdvancedFiltering = false;
9,354✔
2998
    protected _filterMode: FilterMode = FilterMode.quickFilter;
121✔
2999

121✔
3000

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

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

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

3027
    private _rowEditable = false;
3028
    private _currentRowState: any;
3029
    private _filteredSortedData = null;
3030
    private _filteredData = null;
3031

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

6✔
3045
    private _cdrRequests = false;
3046
    private _resourceStrings;
3047
    private _emptyGridMessage = null;
3048
    private _emptyFilteredGridMessage = null;
1,665,529✔
3049
    private _isLoading = false;
261,525✔
3050
    private _locale: string;
3051
    private overlayIDs = [];
3052
    private _sortingStrategy: IGridSortingStrategy;
3053
    private _pinning: IPinningConfig = { columns: ColumnPinningPosition.Start };
3054

3055
    private _hostWidth;
197,431✔
3056
    private _advancedFilteringOverlayId: string;
3057
    private _advancedFilteringPositionSettings: PositionSettings = {
3058
        verticalDirection: VerticalAlignment.Middle,
3059
        horizontalDirection: HorizontalAlignment.Center,
3060
        horizontalStartPoint: HorizontalAlignment.Center,
3061
        verticalStartPoint: VerticalAlignment.Middle
60,928!
3062
    };
61,895!
3063

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

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

15,824✔
3086
    private _columnWidth: string;
3087

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

3097
    private lastAddedRowIndex;
38,622✔
3098
    private _currencyPositionLeft: boolean;
3099

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

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

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

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

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

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

3150
    /**
68,328✔
3151
     * Returns an array containing the filtered sorted data.
692,245✔
3152
     *
112,286✔
3153
     * @example
112,286✔
3154
     * ```typescript
112,286✔
3155
     * const filteredSortedData = this.grid1.filteredSortedData;
3156
     * ```
68,328✔
3157
     */
140,582✔
3158
    public get filteredSortedData(): any[] {
355,235✔
3159
        return this._filteredSortedData;
68,328✔
3160
    }
3161

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

3181
    /**
3182
     * @hidden @internal
66,911✔
3183
     */
3184
    public get dataWithAddedInTransactionRows() {
3185
        const result = cloneArray(this.gridAPI.get_all_data());
3186
        if (this.transactions.enabled) {
3187
            result.push(...this.transactions.getAggregatedChanges(true)
3188
                .filter(t => t.type === TransactionType.ADD)
155,808✔
3189
                .map(t => t.newValue));
66,136✔
3190
        }
3191

89,672✔
3192
        if (this.crudService.row && this.crudService.row.getClassName() === IgxAddRow.name) {
89,672✔
3193
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
3194
        }
3195

3196
        return result;
3197
    }
3198

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

41,124✔
3206
    /**
41,124✔
3207
     * @hidden @internal
5,570✔
3208
     */
3,543✔
3209
    public get template(): TemplateRef<any> {
3210
        if (this.isLoading && (this.hasZeroResultFilter || this.hasNoData)) {
3211
            return this.loadingGridTemplate ? this.loadingGridTemplate : this.loadingGridDefaultTemplate;
41,124✔
3212
        }
41,006✔
3213

3214
        if (this.hasZeroResultFilter) {
41,124✔
3215
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
3216
        }
3217

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

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

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

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

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

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

199,921✔
3259
    /**
3260
     * @hidden @internal
3261
     */
3262
    public get isCellSelectable() {
15,095✔
3263
        return this.cellSelection !== GridSelectionMode.none;
3264
    }
3265

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

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

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

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

30✔
3325
    /**
3326
     * @hidden
3327
     * @internal
3328
     */
3329
    public isDetailRecord(_rec) {
3330
        return false;
3331
    }
3332

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

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

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

1✔
3367
    /**
3368
     * @hidden
3369
     * @internal
3370
     */
3371
    public get hasDetails() {
3372
        return false;
65,119✔
3373
    }
3374

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

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

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

3400
                this.nativeElement.focus();
3401
            }
3402
        });
3403
    }
3404

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

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

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

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

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

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

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

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

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

3✔
3509
        this.subscribeToTransactions();
3✔
3510

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3712
        if (this.sortAscendingHeaderIconDirectiveTemplate) {
3713
            this.sortAscendingHeaderIconTemplate = this.sortAscendingHeaderIconDirectiveTemplate;
3714
        }
1✔
3715

300✔
3716
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
41✔
3717
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
41✔
3718
        }
2✔
3719

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3933
    /**
3934
     * @hidden
3935
     * @internal
3936
     */
3✔
3937
    public getDragGhostCustomTemplate() {
3✔
3938

2!
3939
        return this.dragGhostCustomTemplate;
×
3940
    }
×
3941

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

×
3950
        this.destroy$.next(true);
3951
        this.destroy$.complete();
3952
        this.transactionChange$.next();
3✔
3953
        this.transactionChange$.complete();
3954
        this._destroyed = true;
3955

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

3!
3961
        this.overlayIDs.forEach(overlayID => {
3962
            const overlay = this.overlayService.getOverlayById(overlayID);
3✔
3963

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

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

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

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

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

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

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

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

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

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

301✔
4079

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

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

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

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

4128
    public set outlet(val: IgxOverlayOutletDirective) {
6,014✔
4129
        this._userOutletDirective = val;
4130
    }
11,033✔
4131

509✔
4132

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

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

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

1,520✔
4184
    /** @hidden @internal */
4185
    public get pinnedWidth() {
4186
        if (!isNaN(this._pinnedWidth)) {
64,021✔
4187
            return this._pinnedWidth;
248✔
4188
        }
4189
        this._pinnedWidth = this.getPinnedWidth();
64,021✔
4190
        return this._pinnedWidth;
4191
    }
4192

7,543✔
4193
    /** @hidden @internal */
7,543✔
4194
    public get unpinnedWidth() {
4195
        if (!isNaN(this._unpinnedWidth)) {
4196
            return this._unpinnedWidth;
4197
        }
198,612!
4198
        this._unpinnedWidth = this.getUnpinnedWidth();
4199
        return this._unpinnedWidth;
4200
    }
4201

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

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

32,430✔
4220
        const headerStyle = this.document.defaultView.getComputedStyle(element);
5,332✔
4221
        const headerPadding = parseFloat(headerStyle.paddingLeft) + parseFloat(headerStyle.paddingRight) +
5,332✔
4222
            parseFloat(headerStyle.borderRightWidth);
4223

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

83✔
4417
    /**
4418
     * @hidden
7,203✔
4419
     * @internal
197✔
4420
     */
197✔
4421
    public get showRowSelectors(): boolean {
197✔
4422
        return this.isRowSelectable && this.hasVisibleColumns && !this.hideRowSelectors;
4423
    }
4424

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

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

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

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

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

6,551✔
4479
        this.columnMovingEnd.emit(eventArgs);
4480

4481
        if (eventArgs.cancel) {
4482
            return;
4483
        }
4484

6,551✔
4485
        if (column === target || (column.level !== target.level) ||
6,551✔
4486
            (column.topLevelParent !== target.topLevelParent)) {
4487
            return;
4488
        }
4489

4490
        if (column.level) {
4491
            this._moveChildColumns(column.parent, column, target, pos);
4492
        }
4493

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

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

4509
        // if (target.pinned && column.pinned && !columnPinStateChanged) {
6,551✔
4510
        //     this._reorderColumns(column, target, pos, this._pinnedColumns);
4511
        // }
4512

4513
        // if (!target.pinned && !column.pinned && !columnPinStateChanged) {
4514
        //     this._reorderColumns(column, target, pos, this._unpinnedColumns);
4515
        // }
5,529✔
4516

4517
        this._moveColumns(column, target, pos);
5,529✔
4518
        this._columnsReordered(column);
4519
    }
5,529✔
4520

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2!
4735
        const eventArgs: ISortingEventArgs = { owner: this, sortingExpressions: sortingState, cancel: false };
×
4736
        this.sorting.emit(eventArgs);
×
4737

×
4738
        if (eventArgs.cancel) {
×
4739
            return;
×
4740
        }
×
4741

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

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

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

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

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

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

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

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

61✔
4879
    // TODO: We have return values here. Move them to event args ??
6✔
4880

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

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

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

3,747✔
4932
        if (eventArgs.cancel) {
22,091✔
4933
            return;
350✔
4934
        }
4935
        this.crudService.endEdit(false);
21,741✔
4936

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

4945
        return true;
21,630✔
4946
    }
4947

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

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

5✔
4968
        if (eventArgs.cancel) {
5!
4969
            return;
4970
        }
4971

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

4980
        return true;
191✔
4981
    }
233✔
4982

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

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

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

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

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

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

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

328✔
5069
            return this.find(this.lastSearchInfo.searchText,
5070
                0,
5071
                this.lastSearchInfo.caseSensitive,
5072
                this.lastSearchInfo.exactMatch,
5073
                false,
328✔
5074
                endEdit);
5075
        } else {
5076
            return 0;
319✔
5077
        }
319✔
5078
    }
2,021✔
5079

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

1,626✔
5099
        this.rowList.forEach((row) => {
551✔
5100
            if (row.cells) {
9!
5101
                row.cells.forEach((c: IgxGridCellComponent) => {
×
5102
                    c.clearHighlight();
×
5103
                });
×
5104
            }
×
5105
        });
×
5106
    }
5107

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

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

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

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

5136
    public set hasVisibleColumns(value) {
462✔
5137
        this._hasVisibleColumns = value;
1,283!
5138
    }
462✔
5139

5140
    /** @hidden @internal */
542✔
5141
    public get hasMovableColumns(): boolean {
5142
        return this.moving;
15✔
5143
    }
30✔
5144

30!
5145
    /** @hidden @internal */
7✔
5146
    public get hasColumnGroups(): boolean {
5147
        return this._columnGroups;
5148
    }
23✔
5149

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

5155

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

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

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

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

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

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

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

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

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

5232
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
5233

232✔
5234

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

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

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

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

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

205✔
5273
        return columnWidth + 'px';
205✔
5274
    }
205✔
5275

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

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

2,157✔
5308
        return sum;
2,157✔
5309
    }
2,157✔
5310

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

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

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

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

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

5353
        return 0;
5354
    }
5355

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5594
        this.selectionService.selectColumnsWithNoEvent(fieldToSelect, clearCurrentSelection);
5595
        this.notifyChanges();
5596
    }
5597

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

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

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

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

5663

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

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

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

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

5702
        let data = [];
5703
        let result;
5704

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

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

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

5742
        if (ev.cancel) {
5743
            return;
5744
        }
5745

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

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

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

5757
        event.preventDefault();
5758

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

5764
        return result;
5765
    }
5766

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

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

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

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

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

5899
        if (canceled) {
5900
            return true;
5901
        }
5902

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

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

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

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

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

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

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

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

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

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

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

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

6020
            if (applyChanges) {
6021
                advancedFilteringDialog.applyChanges();
6022
            }
6023
            advancedFilteringDialog.closeDialog();
6024
        }
6025
    }
6026

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

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

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

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

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

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

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

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

6128
        this._addRowForIndex(index, asChild);
6129
    }
6130

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

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

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

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

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

6203
        if (this.dataCloneStrategy) {
6204
            this._transactions.cloneStrategy = this.dataCloneStrategy;
6205
        }
6206
    }
6207

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

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

6244
            });
6245
        }
6246

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

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

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

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

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

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

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

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

6318

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

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

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

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

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

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

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

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

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

6404

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

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

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

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

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

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

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

6488
    protected getColumnList() {
6489
        return this.columnList.toArray();
6490
    }
6491

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

6511

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

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

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

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

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

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

6562
                    // Clear Filtering
6563
                    this.filteringService.clear_filter(record.item.field);
6564

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

6572
                    // Clear Sorting
6573
                    this.gridAPI.clear_sort(record.item.field);
6574

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

6581
            this.resetCaches();
6582

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

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

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

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

6622
        if (this.rowEditable) {
6623
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
6624
        }
6625

6626
        if (this.filteringService.isFilterRowVisible) {
6627
            this.filteringRow.resetChipsArea();
6628
        }
6629

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

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

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

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

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

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

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

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

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

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

6764
        let gridHeight = 0;
6765

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

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

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

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

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

6827
        return width - this.getPinnedWidth(takeHidden);
6828
    }
6829

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

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

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

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

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

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

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

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

6934
            if (cb) {
6935
                cb(column);
6936
            }
6937
        });
6938

6939
        this.updateColumns(collection);
6940

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

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

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

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

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

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

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

7021
        if (columnData) {
7022
            selectedData = columnData;
7023
        }
7024

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

7228
        this.scrollToHorizontally(column);
7229
    }
7230

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

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

7258

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

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

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

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

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

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

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

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

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

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

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

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

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

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

7439

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

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

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

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

7484
        if (endEdit) {
7485
            this.crudService.endEdit(false);
7486
        }
7487

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

7493
        const caseSensitiveResolved = caseSensitive ? true : false;
7494
        const exactMatchResolved = exactMatch ? true : false;
7495
        let rebuildCache = false;
7496

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

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

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

7524
            this.rebuildMatchCache();
7525
        }
7526

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

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

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

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

7548
        } else {
7549
            IgxTextHighlightDirective.clearActiveHighlight(this.id);
7550
        }
7551

7552
        return this.lastSearchInfo.matchCount;
7553
    }
7554

7555
    private rebuildMatchCache() {
7556
        this.lastSearchInfo.matchInfoCache = [];
7557

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

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

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

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

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

7598
                            this.lastSearchInfo.matchInfoCache.push(mic);
7599

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

7608
        this.lastSearchInfo.matchCount = this.lastSearchInfo.matchInfoCache.length;
7609
    }
7610

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