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

IgniteUI / igniteui-angular / 6784798441

07 Nov 2023 12:56PM CUT coverage: 92.277% (-0.004%) from 92.281%
6784798441

push

github

web-flow
Merge pull request #13630 from IgniteUI/ganastasov/fix-13580-15.1

fix(button-group): reverted cancellable on selected and deselected events - 15.1

15311 of 17987 branches covered (0.0%)

26859 of 29107 relevant lines covered (92.28%)

29741.28 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

535

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1088

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

1102
    /* End of toolbar related definitions */
1103

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

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

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

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

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

3,881✔
1145

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

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

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

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

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

1176
    @ContentChild(IgxActionStripComponent)
1177
    public actionStrip: IgxActionStripComponent;
1178

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

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

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

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

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

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

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

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

3,881✔
1240

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

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

1266

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1512

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

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

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

1545

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1875
    public set paging(value: boolean) {
1876
        this._paging = value;
1877
        this.pipeTrigger++;
1878
    }
1879

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

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

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

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

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

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

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

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

1982

1983
    public set rowDraggable(val: boolean) {
1984
        this._rowDrag = val;
3,338✔
1985
        this.notifyChanges(true);
24,732✔
1986
    }
1987

3,338✔
1988
    /**
3,338✔
1989
     * @hidden
3,338✔
1990
     * @internal
3,338✔
1991
     */
3,338✔
1992
    public rowDragging = false;
3,338!
1993

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2201
    public get emptyFilteredGridMessage(): string {
2202
        return this._emptyFilteredGridMessage || this.resourceStrings.igx_grid_emptyFilteredGrid_message;
311,160✔
2203
    }
289,666✔
2204

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

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

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

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

2249
            this.filteringService.isFilterRowVisible = false;
2250
            this.filteringService.filteredColumn = null;
2251

2252
            this.notifyChanges(true);
17,648✔
2253
        }
2254
    }
2255

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

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

2274
            if (!this._init) {
3,143,332✔
2275
                this.notifyChanges(true);
253,530✔
2276
            }
2277
        }
2,889,802✔
2278
    }
2,889,802✔
2279

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

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

24,299✔
2305
        if (this.filteringService.isFilterRowVisible) {
2306
            this.filteringRow.close();
2307
        }
2308
        this.notifyChanges(true);
2309
    }
2310

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

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

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

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

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

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

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

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

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

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

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

2429
    public get sortingOptions() {
2430
        return this._sortingOptions;
2431
    }
2432

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

2448
    public get selectedRows(): any[] {
2449
        return this.selectionService.getSelectedRows();
2450
    }
2451

2452

2453
    /**
37,217✔
2454
     * A list of all `IgxGridHeaderGroupComponent`.
17,003✔
2455
     *
2456
     * @example
2457
     * ```typescript
127,612✔
2458
     * const headerGroupsList = this.grid.headerGroupsList;
20,214✔
2459
     * ```
20,214✔
2460
     */
20,214✔
2461
    public get headerGroupsList(): IgxGridHeaderGroupComponent[] {
103,788✔
2462
        return this.theadRow.groups;
2463
    }
20,214✔
2464

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

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

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

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

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

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

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

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

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

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

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

2606
    /**
2607
     * @hidden @internal
2608
     */
2609
    public get rowOutletDirective() {
2610
        return this.rowEditingOutletDirective;
2611
    }
2612

2613
    /**
2614
     * @hidden @internal
2615
     */
161✔
2616
    public get parentRowOutletDirective() {
161✔
2617
        return this.outlet;
161✔
2618
    }
161✔
2619

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

2630
    /**
2631

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

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

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

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

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

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

18✔
2690
    public get activeDescendant() {
18✔
2691
        const activeElem = this.navigation.activeNode;
18✔
2692

18✔
2693
        if (!activeElem || !Object.keys(activeElem).length) {
2694
            return this.id;
2695
        }
2696

2697
        return activeElem.row < 0 ?
2698
            `${this.id}_${activeElem.row}_${activeElem.mchCache.level}_${activeElem.column}` :
2699
            `${this.id}_${activeElem.row}_${activeElem.column}`;
2700
    }
2701

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

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

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

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

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

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

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

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

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

2795
    /**
2796
     * @hidden @internal
2797
     */
2798
    public get currentRowState(): any {
2799
        return this._currentRowState;
2800
    }
2801

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

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

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

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

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

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

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

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

2888
    public get pagingState() {
2889
        return this._pagingState;
2890
    }
2891

2892
    /**
34✔
2893
     * @hidden @internal
21✔
2894
     */
21✔
2895
    public rowEditMessage;
2896

13!
2897
    /**
×
2898
     * @hidden @internal
2899
     */
13✔
2900
    public snackbarActionText = this.resourceStrings.igx_grid_snackbar_addrow_actiontext;
2901

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

2915
    /**
2916
     * @hidden @internal
2917
     */
2918
    public disableTransitions = false;
2919

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

2931
    /**
2932
     * @hidden @internal
2933
     */
2934
    public columnWidthSetByUser = false;
2935

2936
    /**
2937
     * @hidden @internal
2938
     */
2939
    public pinnedRecords: any[];
19✔
2940

19✔
2941
    /**
2942
     * @hidden @internal
2943
     */
2944
    public unpinnedRecords: any[];
2945

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

2951
    /** @hidden @internal */
2952
    public resizeNotify = new Subject<void>();
2953

2954
    /** @hidden @internal */
2955
    public rowAddedNotifier = new Subject<IRowDataEventArgs>();
134✔
2956

1✔
2957
    /** @hidden @internal */
2958
    public rowDeletedNotifier = new Subject<IRowDataEventArgs>();
133✔
2959

133✔
2960
    /** @hidden @internal */
133✔
2961
    public pipeTriggerNotifier = new Subject();
1✔
2962

2963
    /** @hidden @internal */
132✔
2964
    public _filteredSortedPinnedData: any[];
132✔
2965

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

130✔
2969
    /** @hidden @internal */
130✔
2970
    public _filteredPinnedData: any[];
2971

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

64,242✔
3005
    /**
64,242✔
3006
    * @hidden @internal
3007
    */
3008
    public EMPTY_DATA = [];
35,054✔
3009

3010
    public isPivot = false;
3011

3012
    /** @hidden @internal */
3013
    public _baseFontSize: number;
3014

3015
    /**
3016
     * @hidden
3017
     */
3018
    public destroy$ = new Subject<any>();
3019

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

121✔
3081
    protected _pinnedRecordIDs = [];
3082

3083
    /**
9,383✔
3084
     * @hidden
3085
     */
3086
    protected _hasVisibleColumns;
3087
    protected _allowFiltering = false;
3088
    protected _allowAdvancedFiltering = false;
3089
    protected _filterMode: FilterMode = FilterMode.quickFilter;
3090

3091

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

40✔
3106
    /** @hidden @internal */
3107
    public get paginator() {
3108
        return this.paginationComponents?.first;
3109
    }
3110

3111
    /**
3112
     * @hidden @internal
3113
     */
3114
    public get scrollSize() {
3115
        return this.verticalScrollContainer.getScrollNativeSize();
3116
    }
3117

3118
    private _rowEditable = false;
3119
    private _currentRowState: any;
×
3120
    private _filteredSortedData = null;
3121
    private _filteredData = null;
3122

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

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

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

3151
    private _advancedFilteringOverlaySettings: OverlaySettings = {
3152
        closeOnOutsideClick: false,
1,677,556✔
3153
        modal: false,
263,909✔
3154
        positionStrategy: new ConnectedPositioningStrategy(this._advancedFilteringPositionSettings),
3155
    };
3156

3157
    private columnListDiffer;
3158
    private rowListDiffer;
3159
    private _height: string | null = '100%';
198,764✔
3160
    private _width: string | null = '100%';
3161
    private _rowHeight: number | undefined;
3162
    private _horizontalForOfs: Array<IgxGridForOfDirective<any>> = [];
3163
    private _multiRowLayoutRowSize = 1;
3164
    // Caches
3165
    private _totalWidth = NaN;
61,701!
3166
    private _pinnedVisible = [];
62,665!
3167
    private _unpinnedVisible = [];
3168
    private _pinnedWidth = NaN;
×
3169
    private _unpinnedWidth = NaN;
3170
    private _visibleColumns = [];
3171
    private _columnGroups = false;
32,014✔
3172

3173
    private _columnWidth: string;
3174

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

3184
    private lastAddedRowIndex;
3185
    private _currencyPositionLeft: boolean;
×
3186

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

3195
    private rowEditSettings: OverlaySettings = {
3196
        scrollStrategy: new AbsoluteScrollStrategy(),
179,711✔
3197
        modal: false,
3198
        closeOnOutsideClick: false,
3199
        outlet: this.rowOutletDirective,
3200
        positionStrategy: this.rowEditPositioningStrategy
3201
    };
3202

3203
    private transactionChange$ = new Subject<void>();
3204
    private _rendered = false;
3205
    private readonly DRAG_SCROLL_DELTA = 10;
3206
    private _dataCloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy();
3207
    private _autoSize = false;
18,469,017✔
3208
    private _sortHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
3209
    private _sortAscendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
3210
    private _sortDescendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
3211

3212
    /**
3213
     * @hidden @internal
16,005✔
3214
     */
3215
    protected get minColumnWidth() {
3216
        return MINIMUM_COLUMN_WIDTH;
3217
    }
3218

3219
    /**
×
3220
     * @hidden @internal
3221
     */
3222
    public abstract id: string;
3223
    public abstract data: any[] | null;
3224

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

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

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

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

69,058✔
3279
        if (this.crudService.row && this.crudService.row.getClassName() === IgxAddRow.name) {
697,275✔
3280
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
113,105✔
3281
        }
113,105✔
3282

113,105✔
3283
        return result;
3284
    }
69,058✔
3285

142,739✔
3286
    /**
356,204✔
3287
     * @hidden @internal
69,058✔
3288
     */
3289
    public get dataLength() {
3290
        return this.transactions.enabled ? this.dataWithAddedInTransactionRows.length : this.gridAPI.get_all_data().length;
69,058✔
3291
    }
3292

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

37,067✔
3301
        if (this.hasZeroResultFilter) {
3302
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
3303
        }
69,058✔
3304

1,422✔
3305
        if (this.hasNoData) {
3306
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate;
67,636✔
3307
        }
67,636!
3308
    }
3309

3310
    /**
67,636✔
3311
     * @hidden @internal
3312
     */
3313
    private get hasZeroResultFilter(): boolean {
3314
        return this.filteredData && this.filteredData.length === 0;
3315
    }
3316

157,690✔
3317
    /**
66,210✔
3318
     * @hidden @internal
3319
     */
91,480✔
3320
    private get hasNoData(): boolean {
91,480✔
3321
        return !this.data || this.dataLength === 0;
3322
    }
3323

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

3331
    /**
19,429✔
3332
     * @hidden @internal
41,587!
3333
     */
41,587✔
3334
    public get isMultiRowSelectionEnabled(): boolean {
41,587✔
3335
        return this.rowSelection === GridSelectionMode.multiple
5,686✔
3336
            || this.rowSelection === GridSelectionMode.multipleCascade;
3,635✔
3337
    }
3338

3339
    /**
41,587✔
3340
     * @hidden @internal
41,469✔
3341
     */
3342
    public get isRowSelectable(): boolean {
41,587✔
3343
        return this.rowSelection !== GridSelectionMode.none;
3344
    }
3345

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

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

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

3391
    /**
3392
     * @hidden
3393
     * @internal
202,894✔
3394
     */
3395
    @HostListener('mouseleave')
3396
    public hideActionStrip() {
3397
        this.actionStrip?.hide();
3398
    }
3399

3400
    /**
3401
     * @hidden
3402
     * @internal
3403
     */
3404
    public get headerFeaturesWidth() {
15,141✔
3405
        return this._headerFeaturesWidth;
3406
    }
3407

3408
    /**
3409
     * @hidden
3410
     * @internal
3411
     */
3412
    public isDetailRecord(_rec) {
3413
        return false;
3414
    }
3415

360,412✔
3416
    /**
3417
     * @hidden
3418
     * @internal
105✔
3419
     */
3420
    public isGroupByRecord(_rec) {
3421
        return false;
13✔
3422
    }
3423

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

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

3450
    /**
3451
     * @hidden
3452
     * @internal
3453
     */
3454
    public get hasDetails() {
3455
        return false;
3456
    }
3457

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

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

3480
            if (overlay?.visible && !overlay.closeAnimationPlayer?.hasStarted()) {
5✔
3481
                this.overlayService.hide(overlayID);
6✔
3482

1✔
3483
                this.nativeElement.focus();
3484
            }
3485
        });
5✔
3486
    }
5✔
3487

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

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

3511
    /**
3512
     * @hidden
3513
     * @internal
3514
     */
65,001✔
3515
    public isRecordPinned(rec) {
3516
        return this.getInitialPinnedIndex(rec) !== -1;
3517
    }
3518

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

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

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

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

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

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

3592
        this.subscribeToTransactions();
3593

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

3608
        this.pipeTriggerNotifier.pipe(takeUntil(this.destroy$)).subscribe(() => this.pipeTrigger++);
3609
        this.columnMovingEnd.pipe(destructor).subscribe(() => this.crudService.endEdit(false));
3610

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

3620
        this.overlayService.opened.pipe(destructor).subscribe((event) => {
3621
            const overlaySettings = this.overlayService.getOverlayById(event.id)?.settings;
3622

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3795
        if (this.sortAscendingHeaderIconDirectiveTemplate) {
686✔
3796
            this.sortAscendingHeaderIconTemplate = this.sortAscendingHeaderIconDirectiveTemplate;
93✔
3797
        }
93!
3798

×
3799
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
3800
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
3801
        }
93✔
3802

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

3814
    /**
3815
     * @hidden @internal
3816
     */
3817
    public dataRebinding(event: IForOfDataChangingEventArgs) {
3818
        this.dataChanging.emit(event);
3819
    }
3820

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

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

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

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

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

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

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

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

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

429✔
3962
        const vertScrDC = this.verticalScrollContainer.displayContainer;
429✔
3963
        vertScrDC.addEventListener('scroll', this.preventContainerScroll.bind(this));
260✔
3964

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

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

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

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

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

4010
        if (this._cdrRequestRepaint) {
4011
            this.resetNotifyChanges();
4012
            this.calculateGridSizes();
4013
            this.refreshSearch(true);
616✔
4014
            return;
4015
        }
4016

4017
        if (this._cdrRequests) {
4018
            this.resetNotifyChanges();
4019
            this.cdr.detectChanges();
336,669✔
4020
        }
4021
    }
4022

4023
    /**
4024
     * @hidden
4025
     * @internal
122✔
4026
     */
122✔
4027
    public getDragGhostCustomTemplate() {
4028

4029
        return this.dragGhostCustomTemplate;
4030
    }
4031

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

4040
        this.destroy$.next(true);
4041
        this.destroy$.complete();
4042
        this.transactionChange$.next();
4043
        this.transactionChange$.complete();
92✔
4044
        this._destroyed = true;
4045

4046
        if (this._advancedFilteringOverlayId) {
4047
            this.overlayService.detach(this._advancedFilteringOverlayId);
4048
            delete this._advancedFilteringOverlayId;
4049
        }
4050

4051
        this.overlayIDs.forEach(overlayID => {
4052
            const overlay = this.overlayService.getOverlayById(overlayID);
4053

4054
            if (overlay && !overlay.detached) {
4055
                this.overlayService.detach(overlayID);
4056
            }
4057
        });
4058

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

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

2!
4081
        if (!col) {
×
4082
            return;
×
4083
        }
4084
        col.toggleVisibility(args.newValue);
2✔
4085
    }
4086

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

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

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

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

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

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

4169

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

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

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

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

4218
    public set outlet(val: IgxOverlayOutletDirective) {
4219
        this._userOutletDirective = val;
4220
    }
4221

303✔
4222

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

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

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

11,168✔
4274
    /**
510✔
4275
     * Gets the current width of the container for the pinned `IgxColumnComponent`s.
4276
     *
11,168✔
4277
     * @example
195✔
4278
     * ```typescript
4279
     * const pinnedWidth = this.grid.getPinnedWidth;
11,168✔
4280
     * ```
2,269✔
4281
     */
4282
    public get pinnedWidth() {
11,168✔
4283
        if (!isNaN(this._pinnedWidth)) {
3,264✔
4284
            return this._pinnedWidth;
4285
        }
11,168✔
4286
        this._pinnedWidth = this.getPinnedWidth();
4287
        return this._pinnedWidth;
4288
    }
4289

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

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

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

×
4324
        const headerStyle = this.document.defaultView.getComputedStyle(element);
4325
        const headerPadding = parseFloat(headerStyle.paddingLeft) + parseFloat(headerStyle.paddingRight) +
64,747✔
4326
            parseFloat(headerStyle.borderRightWidth);
1,521✔
4327

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

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

138✔
4353
    /**
4354
     * @hidden @internal
4355
     */
4356
    public get summariesMargin() {
4357
        return this.featureColumnsWidth();
4358
    }
4359

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

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

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

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

4416
    /**
3,205✔
4417
     * Gets the `width` to be set on `IgxGridHeaderGroupComponent`.
820✔
4418
     */
4419
    public getHeaderGroupWidth(column: IgxColumnComponent): string {
4420
        return this.hasColumnLayouts
2,385✔
4421
            ? ''
4422
            : `${Math.max(parseFloat(column.calcWidth), this.defaultHeaderGroupMinWidth)}px`;
20,606✔
4423
    }
3,205✔
4424

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

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

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

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

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

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

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

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

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

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

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

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

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

1,986✔
4610
    /**
4611
     * @hidden
4612
     * @internal
4613
     */
4614
    public get showAddButton() {
4615
        return this.rowEditable && this.dataView.length === 0 && this._columns.length > 0;
25,785✔
4616
    }
4617

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

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

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

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

4664
        this.columnMovingEnd.emit(eventArgs);
4665

4666
        if (eventArgs.cancel) {
4667
            return;
4668
        }
7,274✔
4669

602✔
4670
        if (column === target || (column.level !== target.level) ||
4671
            (column.topLevelParent !== target.topLevelParent)) {
6,672✔
4672
            return;
6,672✔
4673
        }
6,672✔
4674

6,672✔
4675
        if (column.level) {
6,672✔
4676
            this._moveChildColumns(column.parent, column, target, pos);
6,672✔
4677
        }
6,672✔
4678

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

4688
        if (!target.pinned && column.pinned) {
691✔
4689
            const unpinnedIndex = this._unpinnedColumns.indexOf(target);
4690
            const index = pos === DropPosition.AfterDropTarget ? unpinnedIndex + 1 : unpinnedIndex;
4691
            column.unpin(index);
5,353✔
4692
        }
4693

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

4698
        // if (!target.pinned && !column.pinned && !columnPinStateChanged) {
6,040✔
4699
        //     this._reorderColumns(column, target, pos, this._unpinnedColumns);
4700
        // }
4701

4!
4702
        this._moveColumns(column, target, pos);
4✔
4703
        this._columnsReordered(column);
4✔
4704
    }
4✔
4705

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

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

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

13✔
4770
        this.pipeTrigger++;
4771
        this.rowAddedNotifier.next({ data: data, owner: this, primaryKey: data[this.primaryKey] });
4772
        this.notifyChanges();
4773
    }
4774

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

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

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

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

21,885✔
4841
                const id = {
4842
                    rowID: rowSelector,
4843
                    columnID: col.index,
3,270✔
4844
                    rowIndex: index
3,270✔
4845
                };
144✔
4846

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

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

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

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

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

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

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

×
4942
        if (eventArgs.cancel) {
4943
            return;
×
4944
        }
4945

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

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

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

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

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

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

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

5072
    /**
5073
     * @hidden @internal
5074
     */
3,751✔
5075
    public refreshGridState(_args?) {
22,106✔
5076
        this.crudService.endEdit(true);
350✔
5077
        this.selectionService.clearHeaderCBState();
5078
        this.summaryService.clearSummaryCache();
21,756✔
5079
        this.summaryPipeTrigger++;
111!
5080
        this.cdr.detectChanges();
111✔
5081
    }
5082

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

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

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

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

5136
        if (eventArgs.cancel) {
5137
            return;
5138
        }
5139
        this.crudService.endEdit(false);
5140

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

5149
        return true;
195✔
5150
    }
195!
5151

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

128✔
5169
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, null, row, false);
128✔
5170
        this.rowPinning.emit(eventArgs);
10✔
5171

5172
        if (eventArgs.cancel) {
5173
            return;
128✔
5174
        }
128✔
5175

5176
        this.crudService.endEdit(false);
5177
        this._pinnedRecordIDs.splice(index, 1);
13,175✔
5178
        this.pipeTrigger++;
5179
        if (this.gridAPI.grid) {
5180
            this.cdr.detectChanges();
5181
            this.rowPinned.emit(eventArgs);
5182
        }
7,334✔
5183

7,334✔
5184
        return true;
2✔
5185
    }
2!
5186

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

7,334✔
5192
    public get totalHeight() {
7,334✔
5193
        return this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
5194
    }
5195

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

205✔
5415

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

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

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

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

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

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

48✔
5461
    /**
253✔
5462
     * @hidden @internal
253✔
5463
     */
253✔
5464
    public get outerWidth() {
49✔
5465
        return this.hasVerticalScroll() ? this.calcWidth + this.scrollSize : this.calcWidth;
5466
    }
253✔
5467

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

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

5492
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
5493

5494

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

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

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

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

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

5533
        return columnWidth + 'px';
5534
    }
5535

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

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

5568
        return sum;
5569
    }
5570

5571
    /**
5572
     * @hidden @internal
5573
     */
5574
    public isColumnGrouped(_fieldName: string): boolean {
5575
        return false;
5576
    }
5577

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

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

5601
        return this.selectionService.areAllRowSelected() ? 'Deselect all' : 'Select all';
5602
    }
5603

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

5613
        return 0;
5614
    }
5615

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

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

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

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

5665
    public set selectRowOnClick(enabled: boolean) {
5666
        this._selectRowOnClick = enabled;
5667
    }
5668

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

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

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

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

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

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

5758
        horizontal.scrollLeft += left * this.DRAG_SCROLL_DELTA;
5759
        vertical.scrollTop += top * this.DRAG_SCROLL_DELTA;
5760
    }
5761

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

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

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

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

5803
        this.selectionService.pointerState.node = startNode;
5804
        this.selectionService.selectRange(endNode, this.selectionService.pointerState);
5805
        this.selectionService.addRangeMeta(endNode, this.selectionService.pointerState);
5806
        this.selectionService.initPointerState();
5807
    }
5808

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

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

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

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

5868
        this.selectionService.selectColumnsWithNoEvent(fieldToSelect, clearCurrentSelection);
5869
        this.notifyChanges();
5870
    }
5871

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

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

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

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

5937

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

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

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

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

5975
        let data = [];
5976
        let result;
5977

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

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

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

6015
        if (ev.cancel) {
6016
            return;
6017
        }
6018

6019
        const transformer = new CharSeparatedValueData(ev.data, this.clipboardOptions.separator);
6020
        let result = keys ? transformer.prepareData(keys) : transformer.prepareData();
6021

6022
        if (!this.clipboardOptions.copyHeaders) {
6023
            result = result.substring(result.indexOf('\n') + 1);
6024
        }
6025

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

6030
        event.preventDefault();
6031

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

6037
        return result;
6038
    }
6039

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

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

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

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

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

6172
        if (canceled) {
6173
            return true;
6174
        }
6175

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

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

6189
    /**
6190
     * @hidden
6191
     */
6192
    public isExpandedGroup(_group: IGroupByRecord): boolean {
6193
        return undefined;
6194
    }
6195

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

6203
        this.rowEditingOverlay.open(this.rowEditSettings);
6204
        this.rowEditingOverlay.element.addEventListener('wheel', this.rowEditingWheelHandler.bind(this));
6205
    }
6206

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

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

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

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

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

6275
            this._advancedFilteringOverlayId = this.overlayService.attach(
6276
                IgxAdvancedFilteringDialogComponent,
6277
                this.viewRef,
6278
                settings);
6279
            this.overlayService.show(this._advancedFilteringOverlayId);
6280
        }
6281
    }
6282

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

6293
            if (applyChanges) {
6294
                advancedFilteringDialog.applyChanges();
6295
            }
6296
            advancedFilteringDialog.closeDialog();
6297
        }
6298
    }
6299

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

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

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

6325
    /**
6326
     * @hidden @internal
6327
     */
6328
    public triggerPipes() {
6329
        this.pipeTrigger++;
6330
        this.cdr.detectChanges();
6331
    }
6332

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

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

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

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

6401
        this._addRowForIndex(index, asChild);
6402
    }
6403

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

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

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

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

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

6476
        if (this.dataCloneStrategy) {
6477
            this._transactions.cloneStrategy = this.dataCloneStrategy;
6478
        }
6479
    }
6480

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

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

6517
            });
6518
        }
6519

6520
        this.selectionService.clearHeaderCBState();
6521
        this.summaryService.clearSummaryCache();
6522
        this.pipeTrigger++;
6523
        this.notifyChanges();
6524
    };
6525

6526
    protected writeToData(rowIndex: number, value: any) {
6527
        mergeObjects(this.gridAPI.get_all_data()[rowIndex], value);
6528
    }
6529

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

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

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

6572
    /**
6573
     * @hidden
6574
     * Sets grid width i.e. this.calcWidth
6575
     */
6576
    protected calculateGridWidth() {
6577
        let width;
6578

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

6587
        if (!width && this.nativeElement) {
6588
            width = this.nativeElement.offsetWidth;
6589
        }
6590

6591

6592
        if (this.width === null || !width) {
6593
            width = this.getColumnWidthSum();
6594
        }
6595

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

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

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

6641
        if (minWidth > parseFloat(width)) {
6642
            width = String(column.minWidth);
6643
        } else if (maxWidth < parseFloat(width)) {
6644
            width = String(column.maxWidth);
6645
        }
6646

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

6654
    protected resetNotifyChanges() {
6655
        this._cdrRequestRepaint = false;
6656
        this._cdrRequests = false;
6657
    }
6658

6659
    /** @hidden @internal */
6660
    public resolveOutlet() {
6661
        return this._userOutletDirective ? this._userOutletDirective : this._outletDirective;
6662
    }
6663

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

6677

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

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

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

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

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

6751
        this.initColumns(this._columns, (col: IgxColumnComponent) => this.columnInit.emit(col));
6752
        this.columnListDiffer.diff(this.columnList);
6753

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

6761
    protected getColumnList() {
6762
        return this.columnList.toArray();
6763
    }
6764

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

6784

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

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

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

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

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

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

6835
                    // Clear Filtering
6836
                    this.filteringService.clear_filter(record.item.field);
6837

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

6845
                    // Clear Sorting
6846
                    this.gridAPI.clear_sort(record.item.field);
6847

6848
                    // Remove column selection
6849
                    this.selectionService.deselectColumnsWithNoEvent([record.item.field]);
6850
                }
6851
                removed = true;
6852
            });
6853

6854
            this.resetCaches();
6855

6856
            if (added || removed) {
6857
                this.onColumnsAddedOrRemoved();
6858
            }
6859
        }
6860
    }
6861

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

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

6889
        const hasScroll = this.hasVerticalScroll();
6890
        this.calculateGridWidth();
6891
        this.resetCaches(recalcFeatureWidth);
6892
        this.cdr.detectChanges();
6893
        this.calculateGridHeight();
6894

6895
        if (this.rowEditable) {
6896
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
6897
        }
6898

6899
        if (this.filteringService.isFilterRowVisible) {
6900
            this.filteringRow.resetChipsArea();
6901
        }
6902

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

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

6943
    /**
6944
     * @hidden
6945
     * Sets TBODY height i.e. this.calcHeight
6946
     */
6947
    protected calculateGridHeight() {
6948
        this.calcGridHeadRow();
6949

6950
        this.calcHeight = this._calculateGridBodyHeight();
6951
        if (this.pinnedRowHeight && this.calcHeight) {
6952
            this.calcHeight -= this.pinnedRowHeight;
6953
        }
6954
    }
6955

6956
    /**
6957
     * @hidden
6958
     */
6959
    protected getGroupAreaHeight(): number {
6960
        return 0;
6961
    }
6962

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

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

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

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

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

7037
        let gridHeight = 0;
7038

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

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

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

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

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

7100
        return width - this.getPinnedWidth(takeHidden);
7101
    }
7102

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

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

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

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

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

7170
        this.updateColumns(columns);
7171
        if (data && data.length > 0) {
7172
            this.shouldGenerate = false;
7173
        }
7174
    }
7175

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

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

7207
            if (cb) {
7208
                cb(column);
7209
            }
7210
        });
7211

7212
        this.updateColumns(collection);
7213

7214
        if (this.hasColumnLayouts) {
7215
            collection.forEach((column: IgxColumnComponent) => {
7216
                column.populateVisibleIndexes();
7217
            });
7218
        }
7219
    }
7220

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

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

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

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

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

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

7294
        if (columnData) {
7295
            selectedData = columnData;
7296
        }
7297

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

7347
        if (keys.length) {
7348
            keysAndData.push(selectedData);
7349
            keysAndData.push(keys);
7350
            return keysAndData;
7351
        } else {
7352
            return selectedData;
7353
        }
7354
    }
7355

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

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

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

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

7424
            if (Object.keys(record).length) {
7425
                selectedData.push(record);
7426
            }
7427
            record = {};
7428
        }
7429
        return selectedData;
7430
    }
7431

7432
    /**
7433
     * @hidden
7434
     */
7435
    protected initPinning() {
7436
        const pinnedColumns = [];
7437
        const unpinnedColumns = [];
7438

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

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

7469
        // Assign the applicable collections.
7470
        this._pinnedColumns = pinnedColumns;
7471
        this._unpinnedColumns = unpinnedColumns;
7472
        this.notifyChanges();
7473
    }
7474

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

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

7485
            if (this.paginator.page !== page) {
7486
                delayScrolling = true;
7487
                this.paginator.page = page;
7488
            }
7489
        }
7490

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

7501
        this.scrollToHorizontally(column);
7502
    }
7503

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

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

7531

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

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

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

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

7585
    protected verticalScrollHandler(event) {
7586
        this.verticalScrollContainer.onScroll(event);
7587
        this.disableTransitions = true;
7588

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

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

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

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

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

7653
            return;
7654
        }
7655
        const args = this.getNavigationArguments(row, visibleColIndex);
7656
        cb(args);
7657
    }
7658

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

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

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

7712

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

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

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

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

7757
        if (endEdit) {
7758
            this.crudService.endEdit(false);
7759
        }
7760

7761
        if (!text) {
7762
            this.clearSearch();
7763
            return 0;
7764
        }
7765

7766
        const caseSensitiveResolved = caseSensitive ? true : false;
7767
        const exactMatchResolved = exactMatch ? true : false;
7768
        let rebuildCache = false;
7769

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

7781
            rebuildCache = true;
7782
        } else {
7783
            this.lastSearchInfo.activeMatchIndex += increment;
7784
        }
7785

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

7795
            this.rebuildMatchCache();
7796
        }
7797

7798
        if (this.lastSearchInfo.activeMatchIndex >= this.lastSearchInfo.matchInfoCache.length) {
7799
            this.lastSearchInfo.activeMatchIndex = 0;
7800
        } else if (this.lastSearchInfo.activeMatchIndex < 0) {
7801
            this.lastSearchInfo.activeMatchIndex = this.lastSearchInfo.matchInfoCache.length - 1;
7802
        }
7803

7804
        if (this.lastSearchInfo.matchInfoCache.length) {
7805
            const matchInfo = this.lastSearchInfo.matchInfoCache[this.lastSearchInfo.activeMatchIndex];
7806
            this.lastSearchInfo = { ...this.lastSearchInfo };
7807

7808
            if (scroll !== false) {
7809
                this.scrollTo(matchInfo.row, matchInfo.column);
7810
            }
7811

7812
            IgxTextHighlightDirective.setActiveHighlight(this.id, {
7813
                column: matchInfo.column,
7814
                row: matchInfo.row,
7815
                index: matchInfo.index,
7816
                metadata: matchInfo.metadata,
7817
            });
7818

7819
        } else {
7820
            IgxTextHighlightDirective.clearActiveHighlight(this.id);
7821
        }
7822

7823
        return this.lastSearchInfo.matchInfoCache.length;
7824
    }
7825

7826
    private rebuildMatchCache() {
7827
        this.lastSearchInfo.matchInfoCache = [];
7828

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

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

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

7870
                            searchValue = searchValue.substring(searchIndex + searchText.length);
7871
                            searchIndex = searchValue.indexOf(searchText);
7872
                        }
7873
                    }
7874
                }
7875
            });
7876
        });
7877
    }
7878

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