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

IgniteUI / igniteui-angular / 13331632524

14 Feb 2025 02:51PM CUT coverage: 22.015% (-69.6%) from 91.622%
13331632524

Pull #15372

github

web-flow
Merge d52d57714 into bcb78ae0a
Pull Request #15372: chore(*): test ci passing

1990 of 15592 branches covered (12.76%)

431 of 964 new or added lines in 18 files covered. (44.71%)

19956 existing lines in 307 files now uncovered.

6452 of 29307 relevant lines covered (22.02%)

249.17 hits per line

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

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

183
interface IMatchInfoCache {
184
    row: any;
185
    index: number;
186
    column: string;
187
    metadata: Map<string, boolean>;
188
}
189

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

200
/* blazorIndirectRender
201
   blazorComponent
202
   omitModule
203
   wcSkipComponentSuffix */
204
@Directive()
205
export abstract class IgxGridBaseDirective implements GridType,
2✔
206
    OnInit, DoCheck, OnDestroy, AfterContentInit, AfterViewInit {
207

208
    /**
209
     * Gets/Sets the display time for the row adding snackbar notification.
210
     *
211
     * @remarks
212
     * By default it is 6000ms.
213
     */
214
    @Input()
215
    public snackbarDisplayTime = 6000;
39✔
216

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

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

247
    /**
248
     * Controls whether columns moving is enabled in the grid.
249
     *
250
     */
251
    @Input({ transform: booleanAttribute })
252
    public moving = false;
39✔
253

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

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

276
    /**
277
     * Gets/Sets a custom template when loading.
278
     *
279
     * @example
280
     * ```html
281
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [loadingGridTemplate]="myTemplate" [autoGenerate]="true"></igx-grid>
282
     * ```
283
     */
284
    @Input()
285
    public loadingGridTemplate: TemplateRef<void>;
286

287
    /**
288
     * Get/Set IgxSummaryRow height
289
     */
290
    @Input()
291
    public set summaryRowHeight(value: number) {
UNCOV
292
        this._summaryRowHeight = value | 0;
×
UNCOV
293
        this.summaryService.summaryHeight = value;
×
UNCOV
294
        if (!this._init) {
×
UNCOV
295
            this.reflow();
×
296
        }
297
    }
298

299
    public get summaryRowHeight(): number {
300
        if (this.hasSummarizedColumns && this.rootSummariesEnabled) {
5,724✔
301
            return this._summaryRowHeight || this.summaryService.calcMaxSummaryHeight();
5,611✔
302
        }
303
        return 0;
113✔
304
    }
305

306
    /** @hidden @internal */
307
    public get hasColumnsToAutosize() {
308
        return this._columns.some(x => x.width === 'fit-content');
961✔
309
    }
310

311
    /**
312
     * Gets/Sets the data clone strategy of the grid when in edit mode.
313
     *
314
     * @example
315
     * ```html
316
     *  <igx-grid #grid [data]="localData" [dataCloneStrategy]="customCloneStrategy"></igx-grid>
317
     * ```
318
     */
319
    @Input()
320
    public get dataCloneStrategy(): IDataCloneStrategy {
321
        return this._dataCloneStrategy;
39✔
322
    }
323

324
    public set dataCloneStrategy(strategy: IDataCloneStrategy) {
UNCOV
325
        if (strategy) {
×
UNCOV
326
            this._dataCloneStrategy = strategy;
×
UNCOV
327
            this._transactions.cloneStrategy = strategy;
×
328
        }
329
    }
330

331
    /**
332
     * Controls the copy behavior of the grid.
333
     */
334
    @Input()
335
    public clipboardOptions: IClipboardOptions = {
39✔
336
        /**
337
         * Enables/disables the copy behavior
338
         */
339
        enabled: true,
340
        /**
341
         * Include the columns headers in the clipboard output.
342
         */
343
        copyHeaders: true,
344
        /**
345
         * Apply the columns formatters (if any) on the data in the clipboard output.
346
         */
347
        copyFormatters: true,
348
        /**
349
         * The separator used for formatting the copy output. Defaults to `\t`.
350
         */
351
        separator: '\t'
352
    };
353

354
    /**
355
     * Emitted after filtering is performed.
356
     *
357
     * @remarks
358
     * Returns the filtering expressions tree of the column for which filtering was performed.
359
     * @example
360
     * ```html
361
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
362
     *              (filteringExpressionsTreeChange)="filteringExprTreeChange($event)"></igx-grid>
363
     * ```
364
     */
365
    @Output()
366
    public filteringExpressionsTreeChange = new EventEmitter<IFilteringExpressionsTree>();
39✔
367

368
    /**
369
     * Emitted after advanced filtering is performed.
370
     *
371
     * @remarks
372
     * Returns the advanced filtering expressions tree.
373
     * @example
374
     * ```html
375
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
376
     *           (advancedFilteringExpressionsTreeChange)="advancedFilteringExprTreeChange($event)"></igx-grid>
377
     * ```
378
     */
379
    @Output()
380
    public advancedFilteringExpressionsTreeChange = new EventEmitter<IFilteringExpressionsTree>();
39✔
381

382
    /**
383
     * Emitted when grid is scrolled horizontally/vertically.
384
     *
385
     * @example
386
     * ```html
387
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true"
388
     *              (gridScroll)="onScroll($event)"></igx-grid>
389
     * ```
390
     */
391
    @Output()
392
    public gridScroll = new EventEmitter<IGridScrollEventArgs>();
39✔
393

394
    /* treatAsRef */
395
    /**
396
     * Sets a conditional class selector to the grid's row element.
397
     * Accepts an object literal, containing key-value pairs,
398
     * where the key is the name of the CSS class and the value is
399
     * either a callback function that returns a boolean, or boolean, like so:
400
     * ```typescript
401
     * callback = (row: RowType) => { return row.selected > 6; }
402
     * rowClasses = { 'className' : this.callback };
403
     * ```
404
     * ```html
405
     * <igx-grid #grid [data]="Data" [rowClasses] = "rowClasses" [autoGenerate]="true"></igx-grid>
406
     * ```
407
     *
408
     * @memberof IgxColumnComponent
409
     */
410
    @Input()
411
    public rowClasses: any;
412

413
    /* treatAsRef */
414
    /**
415
     * Sets conditional style properties on the grid row element.
416
     * It accepts an object literal where the keys are
417
     * the style properties and the value is an expression to be evaluated.
418
     * ```typescript
419
     * styles = {
420
     *  background: 'yellow',
421
     *  color: (row: RowType) => row.selected : 'red': 'white'
422
     * }
423
     * ```
424
     * ```html
425
     * <igx-grid #grid [data]="Data" [rowStyles]="styles" [autoGenerate]="true"></igx-grid>
426
     * ```
427
     *
428
     * @memberof IgxColumnComponent
429
     */
430
    @Input()
431
    public rowStyles = null;
39✔
432

433
    /**
434
     * Gets/Sets the primary key.
435
     *
436
     * @example
437
     * ```html
438
     * <igx-grid #grid [data]="localData" [primaryKey]="'ProductID'" [autoGenerate]="true"></igx-grid>
439
     * ```
440
     */
441
    @WatchChanges()
442
    @Input()
443
    public get primaryKey(): string {
444
        return this._primaryKey;
140,182✔
445
    }
446

447
    public set primaryKey(value: string) {
448
        this._primaryKey = value;
3✔
449
        this.checkPrimaryKeyField();
3✔
450
    }
451

452
    /* blazorSuppress */
453
    /**
454
     * Gets/Sets a unique values strategy used by the Excel Style Filtering
455
     *
456
     * @remarks
457
     * Provides a callback for loading unique column values on demand.
458
     * If this property is provided, the unique values it generates will be used by the Excel Style Filtering.
459
     * @example
460
     * ```html
461
     * <igx-grid [data]="localData" [filterMode]="'excelStyleFilter'" [uniqueColumnValuesStrategy]="columnValuesStrategy"></igx-grid>
462
     * ```
463
     */
464
    @Input()
465
    public uniqueColumnValuesStrategy: (column: ColumnType,
466
        filteringExpressionsTree: IFilteringExpressionsTree,
467
        done: (values: any[]) => void) => void;
468

469
    /** @hidden @internal */
470
    @ContentChildren(IgxGridExcelStyleFilteringComponent, { read: IgxGridExcelStyleFilteringComponent, descendants: false })
471
    public excelStyleFilteringComponents: QueryList<IgxGridExcelStyleFilteringComponent>;
472

473
    /** @hidden @internal */
474
    public get excelStyleFilteringComponent() {
UNCOV
475
        return this.excelStyleFilteringComponents?.first;
×
476
    }
477

478
    /** @hidden @internal */
479
    public get headerGroups() {
UNCOV
480
        return this.theadRow.groups;
×
481
    }
482

483
    /**
484
     * Emitted when a cell is clicked.
485
     *
486
     * @remarks
487
     * Returns the `IgxGridCell`.
488
     * @example
489
     * ```html
490
     * <igx-grid #grid (cellClick)="cellClick($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
491
     * ```
492
     */
493
    @Output()
494
    public cellClick = new EventEmitter<IGridCellEventArgs>();
39✔
495

496
    /**
497
     * Emitted when a row is clicked.
498
     *
499
     * @remarks
500
     * Returns the `IgxGridRow`.
501
     * @example
502
     * ```html
503
     * <igx-grid #grid (rowClick)="rowClick($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
504
     * ```
505
     */
506
    @Output()
507
    public rowClick = new EventEmitter<IGridRowEventArgs>();
39✔
508

509

510
    /**
511
     * Emitted when formGroup is created on edit of row/cell.
512
     *
513
     * @example
514
     * ```html
515
     * <igx-grid #grid (formGroupCreated)="formGroupCreated($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
516
     * ```
517
     */
518
    @Output()
519
    public formGroupCreated = new EventEmitter<IGridFormGroupCreatedEventArgs>();
39✔
520

521
    /**
522
     * Emitted when grid's validation status changes.
523
     *
524
     * @example
525
     * ```html
526
     * <igx-grid #grid (validationStatusChange)="validationStatusChange($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
527
     * ```
528
     */
529
    @Output()
530
    public validationStatusChange = new EventEmitter<IGridValidationStatusEventArgs>();
39✔
531

532
    /**
533
     * Emitted when a cell is selected.
534
     *
535
     * @remarks
536
     *  Returns the `IgxGridCell`.
537
     * @example
538
     * ```html
539
     * <igx-grid #grid (selected)="onCellSelect($event)" [data]="localData" [height]="'305px'" [autoGenerate]="true"></igx-grid>
540
     * ```
541
     */
542
    @Output()
543
    public selected = new EventEmitter<IGridCellEventArgs>();
39✔
544

545
    /**
546
     *  Emitted when `IgxGridRowComponent` is selected.
547
     *
548
     * @example
549
     * ```html
550
     * <igx-grid #grid (rowSelectionChanging)="rowSelectionChanging($event)" [data]="localData" [autoGenerate]="true"></igx-grid>
551
     * ```
552
     */
553
    @Output()
554
    public rowSelectionChanging = new EventEmitter<IRowSelectionEventArgs>();
39✔
555

556
    /**
557
     *  Emitted when `IgxColumnComponent` is selected.
558
     *
559
     * @example
560
     * ```html
561
     * <igx-grid #grid (columnSelectionChanging)="columnSelectionChanging($event)" [data]="localData" [autoGenerate]="true"></igx-grid>
562
     * ```
563
     */
564
    @Output()
565
    public columnSelectionChanging = new EventEmitter<IColumnSelectionEventArgs>();
39✔
566

567
    /**
568
     * Emitted before `IgxColumnComponent` is pinned.
569
     *
570
     * @remarks
571
     * The index at which to insert the column may be changed through the `insertAtIndex` property.
572
     * @example
573
     * ```typescript
574
     * public columnPinning(event) {
575
     *     if (event.column.field === "Name") {
576
     *       event.insertAtIndex = 0;
577
     *     }
578
     * }
579
     * ```
580
     */
581
    @Output()
582
    public columnPin = new EventEmitter<IPinColumnCancellableEventArgs>();
39✔
583

584
    /**
585
     * Emitted after `IgxColumnComponent` is pinned.
586
     *
587
     * @remarks
588
     * The index that the column is inserted at may be changed through the `insertAtIndex` property.
589
     * @example
590
     * ```typescript
591
     * public columnPinning(event) {
592
     *     if (event.column.field === "Name") {
593
     *       event.insertAtIndex = 0;
594
     *     }
595
     * }
596
     * ```
597
     */
598
    @Output()
599
    public columnPinned = new EventEmitter<IPinColumnEventArgs>();
39✔
600

601
    /**
602
     * Emitted when cell enters edit mode.
603
     *
604
     * @remarks
605
     * This event is cancelable.
606
     * @example
607
     * ```html
608
     * <igx-grid #grid3 (cellEditEnter)="editStart($event)" [data]="data" [primaryKey]="'ProductID'">
609
     * </igx-grid>
610
     * ```
611
     */
612
    @Output()
613
    public cellEditEnter = new EventEmitter<IGridEditEventArgs>();
39✔
614

615
    /**
616
     * Emitted when cell exits edit mode.
617
     *
618
     * @example
619
     * ```html
620
     * <igx-grid #grid3 (cellEditExit)="editExit($event)" [data]="data" [primaryKey]="'ProductID'">
621
     * </igx-grid>
622
     * ```
623
     */
624
    @Output()
625
    public cellEditExit = new EventEmitter<IGridEditDoneEventArgs>();
39✔
626

627
    /**
628
     * Emitted when cell has been edited.
629
     *
630
     * @remarks
631
     * Event is fired after editing is completed, when the cell is exiting edit mode.
632
     * This event is cancelable.
633
     * @example
634
     * ```html
635
     * <igx-grid #grid3 (cellEdit)="editDone($event)" [data]="data" [primaryKey]="'ProductID'">
636
     * </igx-grid>
637
     * ```
638
     */
639
    @Output()
640
    public cellEdit = new EventEmitter<IGridEditEventArgs>();
39✔
641

642
    /* blazorCSSuppress */
643
    /**
644
     * Emitted after cell has been edited and editing has been committed.
645
     *
646
     * @example
647
     * ```html
648
     * <igx-grid #grid3 (cellEditDone)="editDone($event)" [data]="data" [primaryKey]="'ProductID'">
649
     * </igx-grid>
650
     * ```
651
     */
652
    @Output()
653
    public cellEditDone = new EventEmitter<IGridEditDoneEventArgs>();
39✔
654

655
    /**
656
     * Emitted when a row enters edit mode.
657
     *
658
     * @remarks
659
     * Emitted when [rowEditable]="true".
660
     * This event is cancelable.
661
     * @example
662
     * ```html
663
     * <igx-grid #grid3 (rowEditEnter)="editStart($event)" [primaryKey]="'ProductID'" [rowEditable]="true">
664
     * </igx-grid>
665
     * ```
666
     */
667
    @Output()
668
    public rowEditEnter = new EventEmitter<IGridEditEventArgs>();
39✔
669

670
    /**
671
     * Emitted when exiting edit mode for a row.
672
     *
673
     * @remarks
674
     * Emitted when [rowEditable]="true" & `endEdit(true)` is called.
675
     * Emitted when changing rows during edit mode, selecting an un-editable cell in the edited row,
676
     * performing paging operation, column resizing, pinning, moving or hitting `Done`
677
     * button inside of the rowEditingOverlay, or hitting the `Enter` key while editing a cell.
678
     * This event is cancelable.
679
     * @example
680
     * ```html
681
     * <igx-grid #grid3 (rowEdit)="editDone($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
682
     * </igx-grid>
683
     * ```
684
     */
685
    @Output()
686
    public rowEdit = new EventEmitter<IGridEditEventArgs>();
39✔
687

688
    /**
689
     * Emitted after exiting edit mode for a row and editing has been committed.
690
     *
691
     * @remarks
692
     * Emitted when [rowEditable]="true" & `endEdit(true)` is called.
693
     * Emitted when changing rows during edit mode, selecting an un-editable cell in the edited row,
694
     * performing paging operation, column resizing, pinning, moving or hitting `Done`
695
     * button inside of the rowEditingOverlay, or hitting the `Enter` key while editing a cell.
696
     * @example
697
     * ```html
698
     * <igx-grid #grid3 (rowEditDone)="editDone($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
699
     * </igx-grid>
700
     * ```
701
     */
702
    @Output()
703
    public rowEditDone = new EventEmitter<IGridEditDoneEventArgs>();
39✔
704

705
    /**
706
     * Emitted when row editing is canceled.
707
     *
708
     * @remarks
709
     * Emits when [rowEditable]="true" & `endEdit(false)` is called.
710
     * Emitted when changing hitting `Esc` key during cell editing and when click on the `Cancel` button
711
     * in the row editing overlay.
712
     * @example
713
     * ```html
714
     * <igx-grid #grid3 (rowEditExit)="editExit($event)" [data]="data" [primaryKey]="'ProductID'" [rowEditable]="true">
715
     * </igx-grid>
716
     * ```
717
     */
718
    @Output()
719
    public rowEditExit = new EventEmitter<IGridEditDoneEventArgs>();
39✔
720

721
    /**
722
     * Emitted when a column is initialized.
723
     *
724
     * @remarks
725
     * Returns the column object.
726
     * @example
727
     * ```html
728
     * <igx-grid #grid [data]="localData" (columnInit)="initColumns($event)" [autoGenerate]="true"></igx-grid>
729
     * ```
730
     */
731
    @Output()
732
    public columnInit = new EventEmitter<IgxColumnComponent>();
39✔
733

734
    /* blazorInclude */
735
    /**
736
     * @hidden @internal
737
     */
738
    @Output()
739
    public columnsAutogenerated = new EventEmitter<IColumnsAutoGeneratedEventArgs>();
39✔
740

741
    /**
742
     * Emitted before sorting expressions are applied.
743
     *
744
     * @remarks
745
     * Returns an `ISortingEventArgs` object. `sortingExpressions` key holds the sorting expressions.
746
     * @example
747
     * ```html
748
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sorting)="sorting($event)"></igx-grid>
749
     * ```
750
     */
751
    @Output()
752
    public sorting = new EventEmitter<ISortingEventArgs>();
39✔
753

754
    /**
755
     * Emitted after sorting is completed.
756
     *
757
     * @remarks
758
     * Returns the sorting expression.
759
     * @example
760
     * ```html
761
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingDone)="sortingDone($event)"></igx-grid>
762
     * ```
763
     */
764
    @Output()
765
    public sortingDone = new EventEmitter<ISortingExpression | ISortingExpression[]>();
39✔
766

767
    /**
768
     * Emitted before filtering expressions are applied.
769
     *
770
     * @remarks
771
     * Returns an `IFilteringEventArgs` object. `filteringExpressions` key holds the filtering expressions for the column.
772
     * @example
773
     * ```html
774
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true" (filtering)="filtering($event)"></igx-grid>
775
     * ```
776
     */
777
    @Output()
778
    public filtering = new EventEmitter<IFilteringEventArgs>();
39✔
779

780
    /**
781
     * Emitted after filtering is performed through the UI.
782
     *
783
     * @remarks
784
     * Returns the filtering expressions tree of the column for which filtering was performed.
785
     * @example
786
     * ```html
787
     * <igx-grid #grid [data]="localData" [height]="'305px'" [autoGenerate]="true" (filteringDone)="filteringDone($event)"></igx-grid>
788
     * ```
789
     */
790
    @Output()
791
    public filteringDone = new EventEmitter<IFilteringExpressionsTree>();
39✔
792

793
    /* blazorCSSuppress */
794
    /**
795
     * Emitted when a row is added.
796
     *
797
     * @remarks
798
     * Returns the data for the new `IgxGridRowComponent` object.
799
     * @example
800
     * ```html
801
     * <igx-grid #grid [data]="localData" (rowAdded)="rowAdded($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
802
     * ```
803
     */
804
    @Output()
805
    public rowAdded = new EventEmitter<IRowDataEventArgs>();
39✔
806

807
    /* blazorCSSuppress */
808
    /**
809
     * Emitted when a row is deleted.
810
     *
811
     * @remarks
812
     * Returns an `IRowDataEventArgs` object.
813
     * @example
814
     * ```html
815
     * <igx-grid #grid [data]="localData" (rowDeleted)="rowDeleted($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
816
     * ```
817
     */
818
    @Output()
819
    public rowDeleted = new EventEmitter<IRowDataEventArgs>();
39✔
820

821
    /**
822
     * Emmited when deleting a row.
823
     *
824
     * @remarks
825
     * This event is cancelable.
826
     * Returns an IRowDataCancellableEventArgs` object.
827
     * @example
828
     * ```html
829
     * <igx-grid #grid [data]="localData" (rowDelete)="rowDelete($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
830
     * ```
831
     */
832
    @Output()
833
    public rowDelete = new EventEmitter<IRowDataCancelableEventArgs>();
39✔
834

835
    /**
836
     * Emmited just before the newly added row is commited.
837
     *
838
     * @remarks
839
     * This event is cancelable.
840
     * Returns an IRowDataCancellableEventArgs` object.
841
     * @example
842
     * ```html
843
     * <igx-grid #grid [data]="localData" (rowAdd)="rowAdd($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
844
     * ```
845
     */
846
    @Output()
847
    public rowAdd = new EventEmitter<IRowDataCancelableEventArgs>();
39✔
848

849
    /**
850
     * Emitted after column is resized.
851
     *
852
     * @remarks
853
     * Returns the `IgxColumnComponent` object's old and new width.
854
     * @example
855
     * ```html
856
     * <igx-grid #grid [data]="localData" (columnResized)="resizing($event)" [autoGenerate]="true"></igx-grid>
857
     * ```
858
     */
859
    @Output()
860
    public columnResized = new EventEmitter<IColumnResizeEventArgs>();
39✔
861

862
    /**
863
     * Emitted when a cell or row is right clicked.
864
     *
865
     * @remarks
866
     * Returns the `IgxGridCell` object if the immediate context menu target is a cell or an `IgxGridRow` otherwise.
867
     * ```html
868
     * <igx-grid #grid [data]="localData" (contextMenu)="contextMenu($event)" [autoGenerate]="true"></igx-grid>
869
     * ```
870
     */
871
    @Output()
872
    public contextMenu = new EventEmitter<IGridContextMenuEventArgs>();
39✔
873

874
    /**
875
     * Emitted when a cell is double clicked.
876
     *
877
     * @remarks
878
     * Returns the `IgxGridCell` object.
879
     * @example
880
     * ```html
881
     * <igx-grid #grid [data]="localData" (doubleClick)="dblClick($event)" [autoGenerate]="true"></igx-grid>
882
     * ```
883
     */
884
    @Output()
885
    public doubleClick = new EventEmitter<IGridCellEventArgs>();
39✔
886

887
    /**
888
     * Emitted before column visibility is changed.
889
     *
890
     * @remarks
891
     * Args: { column: any, newValue: boolean }
892
     * @example
893
     * ```html
894
     * <igx-grid (columnVisibilityChanging)="visibilityChanging($event)"></igx-grid>
895
     * ```
896
     */
897
    @Output()
898
    public columnVisibilityChanging = new EventEmitter<IColumnVisibilityChangingEventArgs>();
39✔
899

900
    /**
901
     * Emitted after column visibility is changed.
902
     *
903
     * @remarks
904
     * Args: { column: IgxColumnComponent, newValue: boolean }
905
     * @example
906
     * ```html
907
     * <igx-grid (columnVisibilityChanged)="visibilityChanged($event)"></igx-grid>
908
     * ```
909
     */
910
    @Output()
911
    public columnVisibilityChanged = new EventEmitter<IColumnVisibilityChangedEventArgs>();
39✔
912

913
    /**
914
     * Emitted when column moving starts.
915
     *
916
     * @remarks
917
     * Returns the moved `IgxColumnComponent` object.
918
     * @example
919
     * ```html
920
     * <igx-grid (columnMovingStart)="movingStart($event)"></igx-grid>
921
     * ```
922
     */
923
    @Output()
924
    public columnMovingStart = new EventEmitter<IColumnMovingStartEventArgs>();
39✔
925

926
    /**
927
     * Emitted during the column moving operation.
928
     *
929
     * @remarks
930
     * Returns the source and target `IgxColumnComponent` objects. This event is cancelable.
931
     * @example
932
     * ```html
933
     * <igx-grid (columnMoving)="moving($event)"></igx-grid>
934
     * ```
935
     */
936
    @Output()
937
    public columnMoving = new EventEmitter<IColumnMovingEventArgs>();
39✔
938

939
    /**
940
     * Emitted when column moving ends.
941
     *
942
     * @remarks
943
     * Returns the source and target `IgxColumnComponent` objects.
944
     * @example
945
     * ```html
946
     * <igx-grid (columnMovingEnd)="movingEnds($event)"></igx-grid>
947
     * ```
948
     */
949
    @Output()
950
    public columnMovingEnd = new EventEmitter<IColumnMovingEndEventArgs>();
39✔
951

952
    /**
953
     * Emitted when keydown is triggered over element inside grid's body.
954
     *
955
     * @remarks
956
     * This event is fired only if the key combination is supported in the grid.
957
     * Return the target type, target object and the original event. This event is cancelable.
958
     * @example
959
     * ```html
960
     *  <igx-grid (gridKeydown)="customKeydown($event)"></igx-grid>
961
     * ```
962
     */
963
    @Output()
964
    public gridKeydown = new EventEmitter<IGridKeydownEventArgs>();
39✔
965

966
    /**
967
     * Emitted when start dragging a row.
968
     *
969
     * @remarks
970
     * Return the dragged row.
971
     */
972
    @Output()
973
    public rowDragStart = new EventEmitter<IRowDragStartEventArgs>();
39✔
974

975
    /**
976
     * Emitted when dropping a row.
977
     *
978
     * @remarks
979
     * Return the dropped row.
980
     */
981
    @Output()
982
    public rowDragEnd = new EventEmitter<IRowDragEndEventArgs>();
39✔
983

984
    /**
985
     * Emitted when a copy operation is executed.
986
     *
987
     * @remarks
988
     * Fired only if copy behavior is enabled through the [`clipboardOptions`]{@link IgxGridBaseDirective#clipboardOptions}.
989
     */
990
    @Output()
991
    public gridCopy = new EventEmitter<IGridClipboardEvent>();
39✔
992

993
    /**
994
     * @hidden @internal
995
     */
996
    @Output()
997
    public expansionStatesChange = new EventEmitter<Map<any, boolean>>();
39✔
998

999
    /* blazorInclude */
1000
    /** @hidden @internal */
1001
    @Output()
1002
    public selectedRowsChange = new EventEmitter<any[]>();
39✔
1003

1004
    /**
1005
     * Emitted when the expanded state of a row gets changed.
1006
     *
1007
     * @example
1008
     * ```html
1009
     * <igx-grid [data]="employeeData" (rowToggle)="rowToggle($event)" [autoGenerate]="true"></igx-grid>
1010
     * ```
1011
     */
1012
    @Output()
1013
    public rowToggle = new EventEmitter<IRowToggleEventArgs>();
39✔
1014

1015
    /**
1016
     * Emitted when the pinned state of a row is changed.
1017
     *
1018
     * @example
1019
     * ```html
1020
     * <igx-grid [data]="employeeData" (rowPinning)="rowPin($event)" [autoGenerate]="true"></igx-grid>
1021
     * ```
1022
     */
1023
    @Output()
1024
    public rowPinning = new EventEmitter<IPinRowEventArgs>();
39✔
1025

1026
    /**
1027
     * Emitted when the pinned state of a row is changed.
1028
     *
1029
     * @example
1030
     * ```html
1031
     * <igx-grid [data]="employeeData" (rowPinned)="rowPin($event)" [autoGenerate]="true"></igx-grid>
1032
     * ```
1033
     */
1034
    @Output()
1035
    public rowPinned = new EventEmitter<IPinRowEventArgs>();
39✔
1036

1037
    /**
1038
     * Emmited when the active node is changed.
1039
     *
1040
     * @example
1041
     * ```
1042
     * <igx-grid [data]="data" [autoGenerate]="true" (activeNodeChange)="activeNodeChange($event)"></igx-grid>
1043
     * ```
1044
     */
1045
    @Output()
1046
    public activeNodeChange = new EventEmitter<IActiveNodeChangeEventArgs>();
39✔
1047

1048
    /**
1049
     * Emitted before sorting is performed.
1050
     *
1051
     * @remarks
1052
     * Returns the sorting expressions.
1053
     * @example
1054
     * ```html
1055
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (sortingExpressionsChange)="sortingExprChange($event)"></igx-grid>
1056
     * ```
1057
     */
1058
    @Output()
1059
    public sortingExpressionsChange = new EventEmitter<ISortingExpression[]>();
39✔
1060

1061

1062
    /**
1063
     * Emitted when an export process is initiated by the user.
1064
     *
1065
     * @example
1066
     * ```typescript
1067
     * toolbarExporting(event: IGridToolbarExportEventArgs){
1068
     *     const toolbarExporting = event;
1069
     * }
1070
     * ```
1071
     */
1072
    @Output()
1073
    public toolbarExporting = new EventEmitter<IGridToolbarExportEventArgs>();
39✔
1074

1075
    /* End of toolbar related definitions */
1076

1077
    /**
1078
     * Emitted when making a range selection.
1079
     *
1080
     * @remarks
1081
     * Range selection can be made either through drag selection or through keyboard selection.
1082
     */
1083
    @Output()
1084
    public rangeSelected = new EventEmitter<GridSelectionRange>();
39✔
1085

1086
    /** Emitted after the ngAfterViewInit hook. At this point the grid exists in the DOM */
1087
    @Output()
1088
    public rendered = new EventEmitter<boolean>();
39✔
1089

1090
    /**
1091
     * @hidden @internal
1092
     */
1093
    @Output()
1094
    public localeChange = new EventEmitter<boolean>();
39✔
1095

1096
    /**
1097
     * Emitted before the grid's data view is changed because of a data operation, rebinding, etc.
1098
     *
1099
     * @example
1100
     * ```typescript
1101
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanging)='handleDataChangingEvent()'></igx-grid>
1102
     * ```
1103
     */
1104
    @Output()
1105
    public dataChanging = new EventEmitter<IForOfDataChangingEventArgs>();
39✔
1106

1107
    /**
1108
     * Emitted after the grid's data view is changed because of a data operation, rebinding, etc.
1109
     *
1110
     * @example
1111
     * ```typescript
1112
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataChanged)='handleDataChangedEvent()'></igx-grid>
1113
     * ```
1114
     */
1115
    @Output()
1116
    public dataChanged = new EventEmitter<any>();
39✔
1117

1118

1119
    /**
1120
     * @hidden @internal
1121
     */
1122
    @ViewChild(IgxSnackbarComponent)
1123
    public addRowSnackbar: IgxSnackbarComponent;
1124

1125
    /**
1126
     * @hidden @internal
1127
     */
1128
    @ViewChild(IgxGridColumnResizerComponent)
1129
    public resizeLine: IgxGridColumnResizerComponent;
1130

1131
    /**
1132
     * @hidden @internal
1133
     */
1134
    @ViewChild('loadingOverlay', { read: IgxToggleDirective, static: true })
1135
    public loadingOverlay: IgxToggleDirective;
1136

1137
    /**
1138
     * @hidden @internal
1139
     */
1140
    @ViewChild('igxLoadingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1141
    public loadingOutlet: IgxOverlayOutletDirective;
1142

1143
    /* reactContentChildren */
1144
    /* blazorInclude */
1145
    /* blazorTreatAsCollection */
1146
    /* blazorCollectionName: ColumnCollection */
1147
    /* ngQueryListName: columnList */
1148
    /**
1149
     * @hidden @internal
1150
     */
1151
    @ContentChildren(IgxColumnComponent, { read: IgxColumnComponent, descendants: true })
1152
    public columnList: QueryList<IgxColumnComponent> = new QueryList<IgxColumnComponent>();
39✔
1153

1154
    /* contentChildren */
1155
    /* blazorInclude */
1156
    /* blazorTreatAsCollection */
1157
    /* blazorCollectionName: ActionStripCollection */
1158
    /* blazorCollectionItemName: ActionStrip */
1159
    /* ngQueryListName: actionStripComponents */
1160
    /** @hidden @internal */
1161
    @ContentChildren(IgxActionStripToken)
1162
    protected actionStripComponents: QueryList<IgxActionStripToken>;
1163

1164
    /** @hidden @internal */
1165
    public get actionStrip() {
1166
        return this.actionStripComponents?.first;
39✔
1167
    }
1168

1169
    /**
1170
     * @hidden @internal
1171
     */
1172
    @ContentChild(IgxExcelStyleLoadingValuesTemplateDirective, { read: IgxExcelStyleLoadingValuesTemplateDirective, static: true })
1173
    public excelStyleLoadingValuesTemplateDirective: IgxExcelStyleLoadingValuesTemplateDirective;
1174

1175
    /** @hidden @internal */
1176
    @ViewChild('emptyFilteredGrid', { read: TemplateRef, static: true })
1177
    public emptyFilteredGridTemplate: TemplateRef<any>;
1178

1179
    /** @hidden @internal */
1180
    @ViewChild('defaultEmptyGrid', { read: TemplateRef, static: true })
1181
    public emptyGridDefaultTemplate: TemplateRef<any>;
1182

1183
    /**
1184
     * @hidden @internal
1185
     */
1186
    @ViewChild('defaultLoadingGrid', { read: TemplateRef, static: true })
1187
    public loadingGridDefaultTemplate: TemplateRef<any>;
1188

1189
    /**
1190
     * @hidden @internal
1191
     */
1192
    @ViewChild('scrollContainer', { read: IgxGridForOfDirective, static: true })
1193
    public parentVirtDir: IgxGridForOfDirective<any, any[]>;
1194

1195
    /**
1196
     * @hidden
1197
     * @internal
1198
     */
1199
    @ContentChildren(IgxHeadSelectorDirective, { read: TemplateRef, descendants: false })
1200
    public headSelectorsTemplates: QueryList<TemplateRef<IgxHeadSelectorTemplateContext>>;
1201

1202
    /**
1203
     * @hidden
1204
     * @internal
1205
     */
1206
    @ContentChildren(IgxRowSelectorDirective, { read: TemplateRef, descendants: false })
1207
    public rowSelectorsTemplates: QueryList<TemplateRef<IgxRowSelectorTemplateContext>>;
1208

1209
    /**
1210
     * @hidden
1211
     * @internal
1212
     */
1213
    @ContentChildren(IgxRowDragGhostDirective, { read: TemplateRef, descendants: false })
1214
    public dragGhostCustomTemplates: QueryList<TemplateRef<IgxGridRowDragGhostContext>>;
1215

1216

1217
    /**
1218
     * Gets the custom template, if any, used for row drag ghost.
1219
     */
1220
    @Input()
1221
    public get dragGhostCustomTemplate() {
UNCOV
1222
        return this._dragGhostCustomTemplate || this.dragGhostCustomTemplates?.first;
×
1223
    }
1224

1225
    /**
1226
     * Sets a custom template for the row drag ghost.
1227
     *```html
1228
     * <ng-template #template igxRowDragGhost>
1229
     *    <igx-icon>menu</igx-icon>
1230
     * </ng-template>
1231
     * ```
1232
     * ```typescript
1233
     * @ViewChild("'template'", {read: TemplateRef })
1234
     * public template: TemplateRef<any>;
1235
     * this.grid.dragGhostCustomTemplate = this.template;
1236
     * ```
1237
     */
1238
    public set dragGhostCustomTemplate(template: TemplateRef<IgxGridRowDragGhostContext>) {
UNCOV
1239
        this._dragGhostCustomTemplate = template;
×
1240
    }
1241

1242

1243
    /**
1244
     * @hidden @internal
1245
     */
1246
    @ViewChild('verticalScrollContainer', { read: IgxGridForOfDirective, static: true })
1247
    public verticalScrollContainer: IgxGridForOfDirective<any, any[]>;
1248

1249
    /**
1250
     * @hidden @internal
1251
     */
1252
    @ViewChild('verticalScrollHolder', { read: IgxGridForOfDirective, static: true })
1253
    public verticalScroll: IgxGridForOfDirective<any, any[]>;
1254

1255
    /**
1256
     * @hidden @internal
1257
     */
1258
    @ViewChild('scr', { read: ElementRef, static: true })
1259
    public scr: ElementRef;
1260

1261
    /** @hidden @internal */
1262
    @ViewChild('headSelectorBaseTemplate', { read: TemplateRef, static: true })
1263
    public headerSelectorBaseTemplate: TemplateRef<any>;
1264

1265
    /**
1266
     * @hidden @internal
1267
     */
1268
    @ViewChild('footer', { read: ElementRef })
1269
    public footer: ElementRef;
1270

1271
    /** @hidden @internal */
1272
    public get headerContainer() {
1273
        return this.theadRow?.headerForOf;
113✔
1274
    }
1275

1276
    /** @hidden @internal */
1277
    public get headerSelectorContainer() {
1278
        return this.theadRow?.headerSelectorContainer;
281✔
1279
    }
1280

1281
    /** @hidden @internal */
1282
    public get headerDragContainer() {
UNCOV
1283
        return this.theadRow?.headerDragContainer;
×
1284
    }
1285

1286
    /** @hidden @internal */
1287
    public get headerGroupContainer() {
1288
        return this.theadRow?.headerGroupContainer;
281✔
1289
    }
1290

1291
    /** @hidden @internal */
1292
    public get filteringRow(): IgxGridFilteringRowComponent {
UNCOV
1293
        return this.theadRow?.filterRow;
×
1294
    }
1295

1296
    /** @hidden @internal */
1297
    @ViewChild(IgxGridHeaderRowComponent, { static: true })
1298
    public theadRow: IgxGridHeaderRowComponent;
1299

1300
    /** @hidden @internal */
1301
    @ViewChild(IgxGridGroupByAreaComponent)
1302
    public groupArea: IgxGridGroupByAreaComponent;
1303

1304
    /**
1305
     * @hidden @internal
1306
     */
1307
    @ViewChild('tbody', { static: true })
1308
    public tbody: ElementRef;
1309

1310
    @ViewChild(IgxGridBodyDirective, { static: true, read: ElementRef })
1311
    protected tbodyContainer: ElementRef;
1312

1313
    /**
1314
     * @hidden @internal
1315
     */
1316
    @ViewChild('pinContainer', { read: ElementRef })
1317
    public pinContainer: ElementRef;
1318

1319
    /**
1320
     * @hidden @internal
1321
     */
1322
    @ViewChild('tfoot', { static: true })
1323
    public tfoot: ElementRef<HTMLElement>;
1324

1325
    /**
1326
     * @hidden @internal
1327
     */
1328
    @ViewChild('igxRowEditingOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1329
    public rowEditingOutletDirective: IgxOverlayOutletDirective;
1330

1331
    /**
1332
     * @hidden @internal
1333
     */
1334
    @ViewChildren(IgxTemplateOutletDirective, { read: IgxTemplateOutletDirective })
1335
    public tmpOutlets: QueryList<any> = new QueryList<any>();
39✔
1336

1337
    /**
1338
     * @hidden
1339
     * @internal
1340
     */
1341
    @ViewChild('dragIndicatorIconBase', { read: TemplateRef, static: true })
1342
    public dragIndicatorIconBase: TemplateRef<any>;
1343

1344
    /**
1345
     * @hidden @internal
1346
     */
1347
    @ContentChildren(IgxRowEditTemplateDirective, { descendants: false, read: TemplateRef })
1348
    public rowEditCustomDirectives: QueryList<TemplateRef<IgxGridRowEditTemplateContext>>;
1349

1350
    /**
1351
     * @hidden @internal
1352
     */
1353
    @ContentChildren(IgxRowEditTextDirective, { descendants: false, read: TemplateRef })
1354
    public rowEditTextDirectives: QueryList<TemplateRef<IgxGridRowEditTextTemplateContext>>;
1355

1356
    /**
1357
     * Gets the row edit text template.
1358
     */
1359
    @Input()
1360
    public get rowEditTextTemplate(): TemplateRef<IgxGridRowEditTextTemplateContext> {
UNCOV
1361
        return this._rowEditTextTemplate || this.rowEditTextDirectives?.first;
×
1362
    }
1363
    /**
1364
     * Sets the row edit text template.
1365
     *```html
1366
     * <ng-template #template igxRowEditText let-rowChangesCount>
1367
     * Changes: {{rowChangesCount}}
1368
     * </ng-template>
1369
     * ```
1370
     *```typescript
1371
     * @ViewChild('template', {read: TemplateRef })
1372
     * public template: TemplateRef<any>;
1373
     * this.grid.rowEditTextTemplate = this.template;
1374
     * ```
1375
     */
1376
    public set rowEditTextTemplate(template: TemplateRef<IgxGridRowEditTextTemplateContext>) {
UNCOV
1377
        this._rowEditTextTemplate = template;
×
1378
    }
1379

1380
    /**
1381
     * @hidden @internal
1382
     */
1383
    @ContentChild(IgxRowAddTextDirective, { read: TemplateRef })
1384
    public rowAddText: TemplateRef<IgxGridEmptyTemplateContext>;
1385

1386
    /**
1387
     * Gets the row add text template.
1388
     */
1389
    @Input()
1390
    public get rowAddTextTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
UNCOV
1391
        return this._rowAddTextTemplate || this.rowAddText;
×
1392
    }
1393
    /**
1394
     * Sets the row add text template.
1395
     *```html
1396
     * <ng-template #template igxRowAddText>
1397
     * Adding Row
1398
     * </ng-template>
1399
     * ```
1400
     *```typescript
1401
     * @ViewChild('template', {read: TemplateRef })
1402
     * public template: TemplateRef<any>;
1403
     * this.grid.rowAddTextTemplate = this.template;
1404
     * ```
1405
     */
1406
    public set rowAddTextTemplate(template: TemplateRef<IgxGridEmptyTemplateContext>) {
UNCOV
1407
        this._rowAddTextTemplate = template;
×
1408
    }
1409

1410
    /**
1411
     * @hidden @internal
1412
     */
1413
    @ContentChildren(IgxRowEditActionsDirective, { descendants: false, read: TemplateRef })
1414
    public rowEditActionsDirectives: QueryList<TemplateRef<IgxGridRowEditActionsTemplateContext>>;
1415

1416
    /**
1417
     * Gets the row edit actions template.
1418
     */
1419
    @Input()
1420
    public get rowEditActionsTemplate(): TemplateRef<IgxGridRowEditActionsTemplateContext> {
UNCOV
1421
        return this._rowEditActionsTemplate || this.rowEditActionsDirectives?.first;
×
1422
    }
1423
    /**
1424
     * Sets the row edit actions template.
1425
     *```html
1426
     * <ng-template #template igxRowEditActions let-endRowEdit>
1427
     *     <button type="button" igxButton igxRowEditTabStop (click)="endRowEdit(false)">Cancel</button>
1428
     *     <button type="button" igxButton igxRowEditTabStop (click)="endRowEdit(true)">Apply</button>
1429
     * </ng-template>
1430
     * ```
1431
     *```typescript
1432
     * @ViewChild('template', {read: TemplateRef })
1433
     * public template: TemplateRef<any>;
1434
     * this.grid.rowEditActionsTemplate = this.template;
1435
     * ```
1436
     */
1437
    public set rowEditActionsTemplate(template: TemplateRef<IgxGridRowEditActionsTemplateContext>) {
UNCOV
1438
        this._rowEditActionsTemplate = template;
×
1439
    }
1440

1441
    /**
1442
     * The custom template, if any, that should be used when rendering a row expand indicator.
1443
     */
1444
    @ContentChild(IgxRowExpandedIndicatorDirective, { read: TemplateRef })
1445
    protected rowExpandedIndicatorDirectiveTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
39✔
1446

1447
    /**
1448
     * Gets the row expand indicator template.
1449
    */
1450
    @Input()
1451
    public get rowExpandedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1452
        return this._rowExpandedIndicatorTemplate || this.rowExpandedIndicatorDirectiveTemplate;
1✔
1453
    }
1454

1455
    /**
1456
     * Sets the row expand indicator template.
1457
     *```html
1458
     *<ng-template igxRowExpandedIndicator>
1459
     *  <igx-icon role="button">remove</igx-icon>
1460
     *</ng-template>
1461
     * ```
1462
     *```typescript
1463
     * @ViewChild('template', {read: TemplateRef })
1464
     * public template: TemplateRef<any>;
1465
     * this.grid.rowExpandedIndicatorTemplate = this.template;
1466
     * ```
1467
    */
1468
    public set rowExpandedIndicatorTemplate(template: TemplateRef<IgxGridRowTemplateContext>) {
1469
        this._rowExpandedIndicatorTemplate = template;
1✔
1470
    }
1471

1472
    /**
1473
     * The custom template, if any, that should be used when rendering a row collapse indicator.
1474
     */
1475
    @ContentChild(IgxRowCollapsedIndicatorDirective, { read: TemplateRef })
1476
    protected rowCollapsedIndicatorDirectiveTemplate: TemplateRef<IgxGridRowTemplateContext> = null;
39✔
1477

1478
    /**
1479
     * Gets the row collapse indicator template.
1480
    */
1481
    @Input()
1482
    public get rowCollapsedIndicatorTemplate(): TemplateRef<IgxGridRowTemplateContext> {
1483
        return this._rowCollapsedIndicatorTemplate || this.rowCollapsedIndicatorDirectiveTemplate;
55✔
1484
    }
1485

1486
    /**
1487
     * Sets the row collapse indicator template.
1488
     *```html
1489
     *<ng-template igxRowCollapsedIndicator>
1490
     *  <igx-icon role="button">add</igx-icon>
1491
     *</ng-template>
1492
     * ```
1493
     *```typescript
1494
     * @ViewChild('template', {read: TemplateRef })
1495
     * public template: TemplateRef<any>;
1496
     * this.grid.rowCollapsedIndicatorTemplate = this.template;
1497
     * ```
1498
    */
1499
    public set rowCollapsedIndicatorTemplate(template: TemplateRef<IgxGridRowTemplateContext>) {
1500
        this._rowCollapsedIndicatorTemplate = template;
1✔
1501
    }
1502

1503
    /**
1504
     * The custom template, if any, that should be used when rendering a header expand indicator.
1505
     */
1506
    @ContentChild(IgxHeaderExpandedIndicatorDirective, { read: TemplateRef })
1507
    protected headerExpandedIndicatorDirectiveTemplate: TemplateRef<IgxGridTemplateContext> = null;
39✔
1508

1509
    /**
1510
     * Gets the header expand indicator template.
1511
    */
1512
    @Input()
1513
    public get headerExpandedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
1514
        return this._headerExpandIndicatorTemplate || this.headerExpandedIndicatorDirectiveTemplate;
14✔
1515
    }
1516

1517
    /**
1518
     * Sets the header expand indicator template.
1519
     *```html
1520
     *<ng-template igxHeaderExpandedIndicator>
1521
     *  <igx-icon role="button">remove</igx-icon>
1522
     *</ng-template>
1523
     * ```
1524
     *```typescript
1525
     * @ViewChild('template', {read: TemplateRef })
1526
     * public template: TemplateRef<any>;
1527
     * this.grid.headerExpandedIndicatorTemplate = this.template;
1528
     * ```
1529
    */
1530
    public set headerExpandedIndicatorTemplate(template: TemplateRef<IgxGridTemplateContext>) {
1531
        this._headerExpandIndicatorTemplate = template;
1✔
1532
    }
1533

1534
    /**
1535
     * The custom template, if any, that should be used when rendering a header collapse indicator.
1536
     */
1537
    @ContentChild(IgxHeaderCollapsedIndicatorDirective, { read: TemplateRef })
1538
    protected headerCollapsedIndicatorDirectiveTemplate: TemplateRef<IgxGridTemplateContext> = null;
39✔
1539

1540
    /**
1541
     * Gets the row collapse indicator template.
1542
    */
1543
    @Input()
1544
    public get headerCollapsedIndicatorTemplate(): TemplateRef<IgxGridTemplateContext> {
1545
        return this._headerCollapseIndicatorTemplate || this.headerCollapsedIndicatorDirectiveTemplate;
1✔
1546
    }
1547

1548
    /**
1549
     * Sets the row collapse indicator template.
1550
     *```html
1551
     *<ng-template igxHeaderCollapsedIndicator>
1552
     *  <igx-icon role="button">add</igx-icon>
1553
     *</ng-template>
1554
     * ```
1555
     *```typescript
1556
     * @ViewChild('template', {read: TemplateRef })
1557
     * public template: TemplateRef<any>;
1558
     * this.grid.headerCollapsedIndicatorTemplate = this.template;
1559
     * ```
1560
    */
1561
    public set headerCollapsedIndicatorTemplate(template: TemplateRef<IgxGridTemplateContext>) {
1562
        this._headerCollapseIndicatorTemplate = template;
1✔
1563
    }
1564

1565
    /** @hidden @internal */
1566
    @ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef })
1567
    public excelStyleHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
39✔
1568

1569
    /**
1570
     * Gets the excel style header icon.
1571
    */
1572
    @Input()
1573
    public get excelStyleHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1574
        return this._excelStyleHeaderIconTemplate || this.excelStyleHeaderIconDirectiveTemplate;
1✔
1575
    }
1576

1577
    /**
1578
     * Sets the excel style header icon.
1579
     *```html
1580
     *<ng-template #template igxExcelStyleHeaderIcon>
1581
     * <igx-icon>filter_alt</igx-icon>
1582
     *</ng-template>
1583
     * ```
1584
     *```typescript
1585
     * @ViewChild('template', {read: TemplateRef })
1586
     * public template: TemplateRef<any>;
1587
     * this.grid.excelStyleHeaderIconTemplate = this.template;
1588
     * ```
1589
    */
1590
    public set excelStyleHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1591
        this._excelStyleHeaderIconTemplate = template;
1✔
1592
    }
1593

1594

1595
    /**
1596
     * @hidden
1597
     * @internal
1598
     */
1599
    @ContentChild(IgxSortAscendingHeaderIconDirective, { read: TemplateRef })
1600
    public sortAscendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
39✔
1601

1602
    /**
1603
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1604
     */
1605
    @Input()
1606
    public get sortAscendingHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1607
        return this._sortAscendingHeaderIconTemplate;
1✔
1608
    }
1609

1610
    /**
1611
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1612
     *```html
1613
     * <ng-template #template igxSortAscendingHeaderIcon>
1614
     *    <igx-icon>expand_less</igx-icon>
1615
     * </ng-template>
1616
     * ```
1617
     * ```typescript
1618
     * @ViewChild("'template'", {read: TemplateRef })
1619
     * public template: TemplateRef<any>;
1620
     * this.grid.sortAscendingHeaderIconTemplate = this.template;
1621
     * ```
1622
     */
1623
    public set sortAscendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1624
        this._sortAscendingHeaderIconTemplate = template;
1✔
1625
    }
1626

1627
    /** @hidden @internal */
1628
    @ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef })
1629
    public sortDescendingHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
39✔
1630

1631
    /**
1632
     * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1633
     */
1634
    @Input()
1635
    public get sortDescendingHeaderIconTemplate() {
1636
        return this._sortDescendingHeaderIconTemplate;
1✔
1637
    }
1638

1639
    /**
1640
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1641
     *```html
1642
     * <ng-template #template igxSortDescendingHeaderIcon>
1643
     *    <igx-icon>expand_more</igx-icon>
1644
     * </ng-template>
1645
     * ```
1646
     * ```typescript
1647
     * @ViewChild("'template'", {read: TemplateRef })
1648
     * public template: TemplateRef<any>;
1649
     * this.grid.sortDescendingHeaderIconTemplate = this.template;
1650
     * ```
1651
     */
1652
    public set sortDescendingHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1653
        this._sortDescendingHeaderIconTemplate = template;
1✔
1654
    }
1655

1656
    /**
1657
     * @hidden
1658
     * @internal
1659
     */
1660
    @ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef })
1661
    public sortHeaderIconDirectiveTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
39✔
1662

1663
    /**
1664
     * Gets custom template, if any, that should be used when rendering a header sorting indicator when columns are not sorted.
1665
     */
1666
    @Input()
1667
    public get sortHeaderIconTemplate(): TemplateRef<IgxGridHeaderTemplateContext> {
1668
        return this._sortHeaderIconTemplate;
1✔
1669
    }
1670

1671
    /**
1672
     * Sets a custom template that should be used when rendering a header sorting indicator when columns are not sorted.
1673
     *```html
1674
     * <ng-template #template igxSortHeaderIcon>
1675
     *    <igx-icon>unfold_more</igx-icon>
1676
     * </ng-template>
1677
     * ```
1678
     * ```typescript
1679
     * @ViewChild("'template'", {read: TemplateRef })
1680
     * public template: TemplateRef<any>;
1681
     * this.grid.sortHeaderIconTemplate = this.template;
1682
     * ```
1683
     */
1684
    public set sortHeaderIconTemplate(template: TemplateRef<IgxGridHeaderTemplateContext>) {
1685
        this._sortHeaderIconTemplate = template;
1✔
1686
    }
1687

1688
    /**
1689
     * @hidden
1690
     * @internal
1691
     */
1692
    @ContentChildren(IgxDragIndicatorIconDirective, { read: TemplateRef, descendants: false })
1693
    public dragIndicatorIconTemplates: QueryList<TemplateRef<IgxGridEmptyTemplateContext>>;
1694

1695
    /**
1696
     * @hidden @internal
1697
     */
1698
    @ViewChildren(IgxRowEditTabStopDirective)
1699
    public rowEditTabsDEFAULT: QueryList<IgxRowEditTabStopDirective>;
1700

1701
    /**
1702
     * @hidden @internal
1703
     */
1704
    @ContentChildren(IgxRowEditTabStopDirective, { descendants: true })
1705
    public rowEditTabsCUSTOM: QueryList<IgxRowEditTabStopDirective>;
1706

1707
    /**
1708
     * @hidden @internal
1709
     */
1710
    @ViewChild('rowEditingOverlay', { read: IgxToggleDirective })
1711
    public rowEditingOverlay: IgxToggleDirective;
1712

1713
    /**
1714
     * @hidden @internal
1715
     */
1716
    @HostBinding('attr.tabindex')
1717
    public tabindex = 0;
39✔
1718

1719
    /**
1720
     * @hidden @internal
1721
     */
1722
    @HostBinding('attr.role')
1723
    public hostRole = 'grid';
39✔
1724

1725
    /* contentChildren */
1726
    /* blazorInclude */
1727
    /* blazorTreatAsCollection */
1728
    /* blazorCollectionName: GridToolbarCollection */
1729
    /* ngQueryListName: toolbar */
1730
    /** @hidden @internal */
1731
    @ContentChildren(IgxToolbarToken)
1732
    public toolbar: QueryList<IgxGridToolbarComponent>;
1733

1734
    /* contentChildren */
1735
    /* blazorInclude */
1736
    /* blazorTreatAsCollection */
1737
    /* blazorCollectionName: PaginatorCollection */
1738
    /* ngQueryListName: paginationComponents */
1739
    /** @hidden @internal */
1740
    @ContentChildren(IgxPaginatorToken)
1741
    protected paginationComponents: QueryList<IgxPaginatorComponent>;
1742

1743
    /**
1744
     * @hidden @internal
1745
     */
1746
    @ViewChild('igxFilteringOverlayOutlet', { read: IgxOverlayOutletDirective, static: true })
1747
    protected _outletDirective: IgxOverlayOutletDirective;
1748

1749
    /**
1750
     * @hidden @internal
1751
     * @igxElementsAnchor
1752
     */
1753
    @ViewChild('sink', { read: ViewContainerRef, static: true })
1754
    public anchor: ViewContainerRef;
1755

1756
    /**
1757
     * @hidden @internal
1758
     */
1759
    @ViewChild('defaultExpandedTemplate', { read: TemplateRef, static: true })
1760
    protected defaultExpandedTemplate: TemplateRef<any>;
1761

1762
    /**
1763
     * @hidden @internal
1764
     */
1765
    @ViewChild('defaultCollapsedTemplate', { read: TemplateRef, static: true })
1766
    protected defaultCollapsedTemplate: TemplateRef<any>;
1767

1768
    /**
1769
     * @hidden @internal
1770
     */
1771
    @ViewChild('defaultESFHeaderIcon', { read: TemplateRef, static: true })
1772
    protected defaultESFHeaderIconTemplate: TemplateRef<any>;
1773

1774
    @ViewChildren('summaryRow', { read: IgxSummaryRowComponent })
1775
    protected _summaryRowList: QueryList<IgxSummaryRowComponent>;
1776

1777
    @ViewChildren('row')
1778
    private _rowList: QueryList<IgxGridRowComponent>;
1779

1780
    @ViewChildren('pinnedRow')
1781
    private _pinnedRowList: QueryList<IgxGridRowComponent>;
1782

1783
    /**
1784
     * @hidden @internal
1785
     */
1786
    @ViewChild('defaultRowEditTemplate', { read: TemplateRef, static: true })
1787
    private defaultRowEditTemplate: TemplateRef<IgxGridRowEditTemplateContext>;
1788

1789
    @ViewChildren(IgxRowDirective, { read: IgxRowDirective })
1790
    private _dataRowList: QueryList<IgxRowDirective>;
1791

1792
    @HostBinding('class.igx-grid')
1793
    protected baseClass = 'igx-grid';
39✔
1794

1795

1796
    /**
1797
     * Gets/Sets the resource strings.
1798
     *
1799
     * @remarks
1800
     * By default it uses EN resources.
1801
     */
1802
    @Input()
1803
    public set resourceStrings(value: IGridResourceStrings) {
1804
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
1✔
1805
    }
1806

1807
    public get resourceStrings(): IGridResourceStrings {
1808
        return this._resourceStrings;
4,095✔
1809
    }
1810

1811
    /**
1812
     * Gets/Sets the filtering logic of the `IgxGridComponent`.
1813
     *
1814
     * @remarks
1815
     * The default is AND.
1816
     * @example
1817
     * ```html
1818
     * <igx-grid [data]="Data" [autoGenerate]="true" [filteringLogic]="filtering"></igx-grid>
1819
     * ```
1820
     */
1821
    @WatchChanges()
1822
    @Input()
1823
    public get filteringLogic() {
UNCOV
1824
        return this._filteringExpressionsTree.operator;
×
1825
    }
1826

1827
    public set filteringLogic(value: FilteringLogic) {
UNCOV
1828
        this._filteringExpressionsTree.operator = value;
×
1829
    }
1830

1831
    /* mustSetInCodePlatforms: WebComponents;Blazor */
1832
    /**
1833
     * Gets/Sets the filtering state.
1834
     *
1835
     * @example
1836
     * ```html
1837
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(filteringExpressionsTree)]="model.filteringExpressions"></igx-grid>
1838
     * ```
1839
     * @remarks
1840
     * Supports two-way binding.
1841
     */
1842
    @WatchChanges()
1843
    @Input()
1844
    public get filteringExpressionsTree() {
1845
        return this._filteringExpressionsTree;
418✔
1846
    }
1847

1848
    public set filteringExpressionsTree(value) {
1849
        if (value && isTree(value)) {
6✔
1850
            for (let index = 0; index < value.filteringOperands.length; index++) {
6✔
NEW
1851
                if (!(isTree(value.filteringOperands[index]))) {
×
NEW
1852
                    const newExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, value.filteringOperands[index].fieldName);
×
NEW
1853
                    newExpressionsTree.filteringOperands.push(value.filteringOperands[index]);
×
NEW
1854
                    value.filteringOperands[index] = newExpressionsTree;
×
1855
                }
1856
            }
1857

1858
            value.type = FilteringExpressionsTreeType.Regular;
6✔
1859
            if (value && this.columns) {
6!
1860
                this._filteringExpressionsTree = recreateTreeFromFields(value, this.columns) as IFilteringExpressionsTree;
6✔
1861
            } else {
NEW
1862
                this._filteringExpressionsTree = value;
×
1863
            }
1864
            this.filteringPipeTrigger++;
6✔
1865
            this.filteringExpressionsTreeChange.emit(this._filteringExpressionsTree);
6✔
1866

1867
            if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
6!
1868
                this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
UNCOV
1869
                this._filteredData = null;
×
1870
            }
1871

1872
            this.filteringService.refreshExpressions();
6✔
1873
            this.selectionService.clearHeaderCBState();
6✔
1874
            this.summaryService.clearSummaryCache();
6✔
1875
            this.notifyChanges();
6✔
1876
        }
1877
    }
1878

1879
    /**
1880
     * Gets/Sets the advanced filtering state.
1881
     *
1882
     * @example
1883
     * ```typescript
1884
     * let advancedFilteringExpressionsTree = this.grid.advancedFilteringExpressionsTree;
1885
     * this.grid.advancedFilteringExpressionsTree = logic;
1886
     * ```
1887
     */
1888
    @WatchChanges()
1889
    @Input()
1890
    public get advancedFilteringExpressionsTree() {
1891
        return this._advancedFilteringExpressionsTree;
1,465✔
1892
    }
1893

1894
    public set advancedFilteringExpressionsTree(value) {
1895
        const filteringEventArgs: IFilteringEventArgs = {
35✔
1896
            owner: this,
1897
            filteringExpressions: value,
1898
            cancel: false
1899
        };
1900

1901
        this.filtering.emit(filteringEventArgs);
35✔
1902

1903
        if (filteringEventArgs.cancel) {
35✔
1904
            return;
1✔
1905
        }
1906

1907
        if (value && isTree(value)) {
34✔
1908
            value.type = FilteringExpressionsTreeType.Advanced;
26✔
1909
            this._advancedFilteringExpressionsTree = recreateTreeFromFields(value, this.columns) as IFilteringExpressionsTree;
26✔
1910
            this.filteringPipeTrigger++;
26✔
1911
        } else {
1912
            this._advancedFilteringExpressionsTree = null;
8✔
1913
        }
1914
        this.advancedFilteringExpressionsTreeChange.emit(this._advancedFilteringExpressionsTree);
34✔
1915

1916
        if (this.filteringService.isFilteringExpressionsTreeEmpty(this._filteringExpressionsTree) &&
34✔
1917
            this.filteringService.isFilteringExpressionsTreeEmpty(this._advancedFilteringExpressionsTree)) {
1918
            this._filteredData = null;
13✔
1919
        }
1920

1921
        this.selectionService.clearHeaderCBState();
34✔
1922
        this.summaryService.clearSummaryCache();
34✔
1923
        this.notifyChanges();
34✔
1924

1925
        // Wait for the change detection to update filtered data through the pipes and then emit the event.
1926
        requestAnimationFrame(() => this.filteringDone.emit(this._advancedFilteringExpressionsTree));
34✔
1927
    }
1928

1929
    /**
1930
     * Gets/Sets the locale.
1931
     *
1932
     * @remarks
1933
     * If not set, returns browser's language.
1934
     */
1935
    @Input()
1936
    public get locale(): string {
1937
        return this._locale;
12,959✔
1938
    }
1939

1940
    public set locale(value: string) {
1941
        if (value !== this._locale) {
39✔
1942
            this._locale = value;
39✔
1943
            this._currencyPositionLeft = undefined;
39✔
1944
            this.summaryService.clearSummaryCache();
39✔
1945
            this.pipeTrigger++;
39✔
1946
            this.notifyChanges();
39✔
1947
            this.localeChange.emit();
39✔
1948
        }
1949
    }
1950

1951
    @Input()
1952
    public get pagingMode() {
UNCOV
1953
        return this._pagingMode;
×
1954
    }
1955

1956
    public set pagingMode(val: GridPagingMode) {
UNCOV
1957
        this._pagingMode = val;
×
UNCOV
1958
        this.pipeTrigger++;
×
UNCOV
1959
        this.notifyChanges(true);
×
1960
    }
1961

1962
    /** @hidden @internal */
1963
    public get page(): number {
1964
        return this.paginator?.page || 0;
20,282✔
1965
    }
1966

1967
    public set page(val: number) {
UNCOV
1968
        if (this.paginator) {
×
UNCOV
1969
            this.paginator.page = val;
×
1970
        }
1971
    }
1972

1973
    /** @hidden @internal */
1974
    public get perPage(): number {
1975
        return this.paginator?.perPage || DEFAULT_ITEMS_PER_PAGE;
20,282✔
1976
    }
1977

1978
    public set perPage(val: number) {
UNCOV
1979
        if (this.paginator) {
×
UNCOV
1980
            this.paginator.perPage = val;
×
1981
        }
1982
    }
1983

1984
    /**
1985
     * Gets/Sets if the row selectors are hidden.
1986
     *
1987
     * @remarks
1988
     *  By default row selectors are shown
1989
     */
1990
    @WatchChanges()
1991
    @Input({ transform: booleanAttribute })
1992
    public get hideRowSelectors() {
UNCOV
1993
        return this._hideRowSelectors;
×
1994
    }
1995

1996
    public set hideRowSelectors(value: boolean) {
UNCOV
1997
        this._hideRowSelectors = value;
×
UNCOV
1998
        this.notifyChanges(true);
×
1999
    }
2000

2001
    /**
2002
     * Gets/Sets whether rows can be moved.
2003
     *
2004
     * @example
2005
     * ```html
2006
     * <igx-grid #grid [rowDraggable]="true"></igx-grid>
2007
     * ```
2008
     */
2009
    @Input({ transform: booleanAttribute })
2010
    public get rowDraggable(): boolean {
2011
        return this._rowDrag && this.hasVisibleColumns;
2,167!
2012
    }
2013

2014
    public set rowDraggable(val: boolean) {
UNCOV
2015
        this._rowDrag = val;
×
UNCOV
2016
        this.notifyChanges(true);
×
2017
    }
2018

2019
    /**
2020
     * Gets/Sets the trigger for validators used when editing the grid.
2021
     *
2022
     * @example
2023
     * ```html
2024
     * <igx-grid #grid validationTrigger='blur'></igx-grid>
2025
     * ```
2026
     */
2027
    @Input()
2028
    public validationTrigger: GridValidationTrigger = 'change';
39✔
2029

2030
    /**
2031
     * @hidden
2032
     * @internal
2033
     */
2034
    public rowDragging = false;
39✔
2035

2036
    /** @hidden @internal */
2037
    public dragRowID = null;
39✔
2038

2039
    /**
2040
     * Gets/Sets whether the rows are editable.
2041
     *
2042
     * @remarks
2043
     * By default it is set to false.
2044
     * @example
2045
     * ```html
2046
     * <igx-grid #grid [rowEditable]="true" [primaryKey]="'ProductID'" ></igx-grid>
2047
     * ```
2048
     */
2049
    @WatchChanges()
2050
    @Input({ transform: booleanAttribute })
2051
    public get rowEditable(): boolean {
2052
        return this._rowEditable;
113,663✔
2053
    }
2054

2055
    public set rowEditable(val: boolean) {
UNCOV
2056
        if (!this._init) {
×
UNCOV
2057
            this.refreshGridState();
×
2058
        }
UNCOV
2059
        this._rowEditable = val;
×
UNCOV
2060
        this.notifyChanges();
×
2061
    }
2062

2063
    /**
2064
     * Gets/Sets the height.
2065
     *
2066
     * @example
2067
     * ```html
2068
     * <igx-grid #grid [data]="Data" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2069
     * ```
2070
     */
2071
    @WatchChanges()
2072
    @HostBinding('style.height')
2073
    @Input()
2074
    public get height(): string | null {
2075
        return this._height;
628✔
2076
    }
2077

2078
    public set height(value: string | null) {
2079
        if (this._height !== value) {
40✔
2080
            this._height = value;
40✔
2081
            this.nativeElement.style.height = value;
40✔
2082
            this.notifyChanges(true);
40✔
2083
        }
2084
    }
2085

2086
    /**
2087
     * @hidden @internal
2088
     */
2089
    @HostBinding('style.width')
2090
    public get hostWidth() {
2091
        return this._width || this._hostWidth;
588!
2092
    }
2093

2094
    /**
2095
     * Gets/Sets the width of the grid.
2096
     *
2097
     * @example
2098
     * ```typescript
2099
     * let gridWidth = this.grid.width;
2100
     * ```
2101
     */
2102
    @WatchChanges()
2103
    @Input()
2104
    public get width(): string | null {
2105
        return this._width;
1,120✔
2106
    }
2107

2108
    public set width(value: string | null) {
2109
        if (this._width !== value) {
1✔
2110
            this._width = value;
1✔
2111
            this.nativeElement.style.width = value;
1✔
2112
            this.notifyChanges(true);
1✔
2113
        }
2114
    }
2115

2116
    /** @hidden @internal */
2117
    public get headerWidth() {
2118
        return parseInt(this.width, 10) - 17;
×
2119
    }
2120

2121
    /**
2122
     * Gets/Sets the row height.
2123
     *
2124
     * @example
2125
     * ```html
2126
     * <igx-grid #grid [data]="localData" [rowHeight]="100" [autoGenerate]="true"></igx-grid>
2127
     * ```
2128
     */
2129
    @WatchChanges()
2130
    @Input()
2131
    public get rowHeight(): number {
2132
        return this._rowHeight ? this._rowHeight : this.defaultRowHeight;
1,848!
2133
    }
2134

2135
    public set rowHeight(value: number | string) {
UNCOV
2136
        if (typeof value !== 'number') {
×
UNCOV
2137
            value = parseInt(value, 10);
×
2138
        }
UNCOV
2139
        this._rowHeight = value;
×
2140
    }
2141

2142
    /**
2143
     * Gets/Sets the default width of the columns.
2144
     *
2145
     * @example
2146
     * ```html
2147
     * <igx-grid #grid [data]="localData" [columnWidth]="100" [autoGenerate]="true"></igx-grid>
2148
     * ```
2149
     */
2150
    @WatchChanges()
2151
    @Input()
2152
    public get columnWidth(): string {
UNCOV
2153
        return this._columnWidth;
×
2154
    }
2155
    public set columnWidth(value: string) {
UNCOV
2156
        this._columnWidth = value;
×
UNCOV
2157
        this.columnWidthSetByUser = true;
×
UNCOV
2158
        this.notifyChanges(true);
×
2159
    }
2160

2161
    /**
2162
     * Get/Sets the message displayed when there are no records.
2163
     *
2164
     * @example
2165
     * ```html
2166
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
2167
     * ```
2168
     */
2169
    @Input()
2170
    public set emptyGridMessage(value: string) {
2171
        this._emptyGridMessage = value;
×
2172
    }
2173
    public get emptyGridMessage(): string {
UNCOV
2174
        return this._emptyGridMessage || this.resourceStrings.igx_grid_emptyGrid_message;
×
2175
    }
2176

2177
    /**
2178
     * Gets/Sets whether the grid is going to show a loading indicator.
2179
     *
2180
     * @example
2181
     * ```html
2182
     * <igx-grid #grid [data]="Data" [isLoading]="true" [autoGenerate]="true"></igx-grid>
2183
     * ```
2184
     */
2185
    @WatchChanges()
2186
    @Input({ transform: booleanAttribute })
2187
    public set isLoading(value: boolean) {
UNCOV
2188
        if (this._isLoading !== value) {
×
UNCOV
2189
            this._isLoading = value;
×
UNCOV
2190
            if (this.data) {
×
UNCOV
2191
                this.evaluateLoadingState();
×
2192
            }
2193
        }
UNCOV
2194
        Promise.resolve().then(() => {
×
2195
            // wait for the current detection cycle to end before triggering a new one.
UNCOV
2196
            this.notifyChanges();
×
2197
        });
2198
    }
2199

2200
    public get isLoading(): boolean {
2201
        return this._isLoading;
839✔
2202
    }
2203

2204
    /**
2205
     * Gets/Sets whether the columns should be auto-generated once again after the initialization of the grid
2206
     *
2207
     * @remarks
2208
     * This will allow to bind the grid to remote data and having auto-generated columns at the same time.
2209
     * Note that after generating the columns, this property would be disabled to avoid re-creating
2210
     * columns each time a new data is assigned.
2211
     * @example
2212
     * ```typescript
2213
     *  this.grid.shouldGenerate = true;
2214
     * ```
2215
     * @deprecated in version 18.2.0. Column re-creation now relies on `autoGenerate` instead.
2216
     */
2217
    public get shouldGenerate(): boolean {
2218
        return this.autoGenerate;
×
2219
    }
2220

2221
    public set shouldGenerate(value: boolean) {
2222
        this.autoGenerate = value;
×
2223
    }
2224

2225
    /**
2226
     * Gets/Sets the message displayed when there are no records and the grid is filtered.
2227
     *
2228
     * @example
2229
     * ```html
2230
     * <igx-grid #grid [data]="Data" [emptyGridMessage]="'The grid is empty'" [autoGenerate]="true"></igx-grid>
2231
     * ```
2232
     */
2233
    @Input()
2234
    public set emptyFilteredGridMessage(value: string) {
2235
        this._emptyFilteredGridMessage = value;
×
2236
    }
2237

2238
    public get emptyFilteredGridMessage(): string {
2239
        return this._emptyFilteredGridMessage || this.resourceStrings.igx_grid_emptyFilteredGrid_message;
4✔
2240
    }
2241

2242
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
2243
    /**
2244
     * Gets/Sets the initial pinning configuration.
2245
     *
2246
     * @remarks
2247
     * Allows to apply pinning the columns to the start or the end.
2248
     * Note that pinning to both sides at a time is not allowed.
2249
     * @example
2250
     * ```html
2251
     * <igx-grid [pinning]="pinningConfig"></igx-grid>
2252
     * ```
2253
     */
2254
    @Input()
2255
    public get pinning() {
2256
        return this._pinning;
33,321✔
2257
    }
2258
    public set pinning(value) {
UNCOV
2259
        if (value !== this._pinning) {
×
UNCOV
2260
            this.resetCaches();
×
2261
        }
UNCOV
2262
        this._pinning = value;
×
2263
    }
2264

2265
    /**
2266
     * Gets/Sets if the filtering is enabled.
2267
     *
2268
     * @example
2269
     * ```html
2270
     * <igx-grid #grid [data]="localData" [allowFiltering]="true" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2271
     * ```
2272
     */
2273
    @Input({ transform: booleanAttribute })
2274
    public get allowFiltering() {
2275
        return this._allowFiltering;
4,796✔
2276
    }
2277

2278
    public set allowFiltering(value) {
UNCOV
2279
        if (this._allowFiltering !== value) {
×
UNCOV
2280
            this._allowFiltering = value;
×
UNCOV
2281
            this.filteringService.registerSVGIcons();
×
2282

2283

UNCOV
2284
            this.filteringService.isFilterRowVisible = false;
×
UNCOV
2285
            this.filteringService.filteredColumn = null;
×
2286

UNCOV
2287
            this.notifyChanges(true);
×
2288
        }
2289
    }
2290

2291
    /**
2292
     * Gets/Sets a value indicating whether the advanced filtering is enabled.
2293
     *
2294
     * @example
2295
     * ```html
2296
     * <igx-grid #grid [data]="localData" [allowAdvancedFiltering]="true" [autoGenerate]="true"></igx-grid>
2297
     * ```
2298
     */
2299
    @Input({ transform: booleanAttribute })
2300
    public get allowAdvancedFiltering() {
2301
        return this._allowAdvancedFiltering;
516✔
2302
    }
2303

2304
    public set allowAdvancedFiltering(value) {
2305
        if (this._allowAdvancedFiltering !== value) {
36✔
2306
            this._allowAdvancedFiltering = value;
36✔
2307
            this.filteringService.registerSVGIcons();
36✔
2308

2309
            if (!this._init) {
36✔
2310
                this.notifyChanges(true);
2✔
2311
            }
2312
        }
2313
    }
2314

2315
    /**
2316
     * Gets/Sets the filter mode.
2317
     *
2318
     * @example
2319
     * ```html
2320
     * <igx-grid #grid [data]="localData" [filterMode]="'quickFilter'" [height]="'305px'" [autoGenerate]="true"></igx-grid>
2321
     * ```
2322
     * @remarks
2323
     * By default it's set to FilterMode.quickFilter.
2324
     */
2325
    @Input()
2326
    public get filterMode() {
UNCOV
2327
        return this._filterMode;
×
2328
    }
2329

2330
    public set filterMode(value: FilterMode) {
UNCOV
2331
        switch (value) {
×
2332
            case FilterMode.excelStyleFilter:
2333
            case FilterMode.quickFilter:
UNCOV
2334
                this._filterMode = value;
×
UNCOV
2335
                break;
×
2336
            default:
2337
                break;
×
2338
        }
2339

UNCOV
2340
        if (this.filteringService.isFilterRowVisible) {
×
UNCOV
2341
            this.filteringRow.close();
×
2342
        }
UNCOV
2343
        this.notifyChanges(true);
×
2344
    }
2345

2346
    /**
2347
     * Gets/Sets the summary position.
2348
     *
2349
     * @example
2350
     * ```html
2351
     * <igx-grid #grid [data]="localData" summaryPosition="top" [autoGenerate]="true"></igx-grid>
2352
     * ```
2353
     * @remarks
2354
     * By default it is bottom.
2355
     */
2356
    @Input()
2357
    public get summaryPosition() {
2358
        return this._summaryPosition;
393✔
2359
    }
2360

2361
    public set summaryPosition(value: GridSummaryPosition) {
UNCOV
2362
        this._summaryPosition = value;
×
UNCOV
2363
        this.notifyChanges();
×
2364
    }
2365

2366
    /**
2367
     * Gets/Sets the summary calculation mode.
2368
     *
2369
     * @example
2370
     * ```html
2371
     * <igx-grid #grid [data]="localData" summaryCalculationMode="rootLevelOnly" [autoGenerate]="true"></igx-grid>
2372
     * ```
2373
     * @remarks
2374
     * By default it is rootAndChildLevels which means the summaries are calculated for the root level and each child level.
2375
     */
2376
    @Input()
2377
    public get summaryCalculationMode() {
2378
        return this._summaryCalculationMode;
6,970✔
2379
    }
2380

2381
    public set summaryCalculationMode(value: GridSummaryCalculationMode) {
UNCOV
2382
        this._summaryCalculationMode = value;
×
UNCOV
2383
        if (!this._init) {
×
UNCOV
2384
            this.crudService.endEdit(false);
×
UNCOV
2385
            this.summaryService.resetSummaryHeight();
×
UNCOV
2386
            this.notifyChanges(true);
×
2387
        }
2388
    }
2389

2390
    /**
2391
     * Controls whether the summary row is visible when groupBy/parent row is collapsed.
2392
     *
2393
     * @example
2394
     * ```html
2395
     * <igx-grid #grid [data]="localData" [showSummaryOnCollapse]="true" [autoGenerate]="true"></igx-grid>
2396
     * ```
2397
     * @remarks
2398
     * By default showSummaryOnCollapse is set to 'false' which means that the summary row is not visible
2399
     * when the groupBy/parent row is collapsed.
2400
     */
2401
    @Input({ transform: booleanAttribute })
2402
    public get showSummaryOnCollapse() {
2403
        return this._showSummaryOnCollapse;
393✔
2404
    }
2405

2406
    public set showSummaryOnCollapse(value: boolean) {
UNCOV
2407
        this._showSummaryOnCollapse = value;
×
UNCOV
2408
        this.notifyChanges();
×
2409
    }
2410

2411
    /**
2412
     * Gets/Sets the filtering strategy of the grid.
2413
     *
2414
     * @example
2415
     * ```html
2416
     *  <igx-grid #grid [data]="localData" [filterStrategy]="filterStrategy"></igx-grid>
2417
     * ```
2418
     */
2419
    @Input()
2420
    public get filterStrategy(): IFilteringStrategy {
2421
        return this._filterStrategy;
406✔
2422
    }
2423

2424
    public set filterStrategy(classRef: IFilteringStrategy) {
2425
        this._filterStrategy = classRef;
2✔
2426
    }
2427

2428
    /**
2429
     * Gets/Sets the sorting strategy of the grid.
2430
     *
2431
     * @example
2432
     * ```html
2433
     *  <igx-grid #grid [data]="localData" [sortStrategy]="sortStrategy"></igx-grid>
2434
     * ```
2435
     */
2436
    @Input()
2437
    public get sortStrategy(): IGridSortingStrategy {
2438
        return this._sortingStrategy;
406✔
2439
    }
2440

2441
    public set sortStrategy(value: IGridSortingStrategy) {
UNCOV
2442
        this._sortingStrategy = value;
×
2443
    }
2444

2445
    /**
2446
     * Gets/Sets the sorting options - single or multiple sorting.
2447
     * Accepts an `ISortingOptions` object with any of the `mode` properties.
2448
     *
2449
     * @example
2450
     * ```typescript
2451
     * const _sortingOptions: ISortingOptions = {
2452
     *      mode: 'single'
2453
     * }
2454
     * ```html
2455
     * <igx-grid [sortingOptions]="sortingOptions"><igx-grid>
2456
     * ```
2457
     */
2458
    @Input()
2459
    public set sortingOptions(value: ISortingOptions) {
UNCOV
2460
        if (!this._init) {
×
2461
            // clear sort only if option is changed runtime. No need to clear on initial load.
UNCOV
2462
            this.clearSort();
×
2463
        }
UNCOV
2464
        this._sortingOptions = Object.assign(this._sortingOptions, value);
×
2465
    }
2466

2467
    public get sortingOptions() {
UNCOV
2468
        return this._sortingOptions;
×
2469
    }
2470

2471
    /* blazorByValueArray */
2472
    /* blazorAlwaysWriteback */
2473
    /* @tsTwoWayProperty (true, "SelectedRowsChange", "Detail", false) */
2474
    /* blazorPrimitiveValue */
2475
    /**
2476
     * Gets/Sets the current selection state.
2477
     *
2478
     * @remarks
2479
     * Represents the selected rows' IDs (primary key or rowData)
2480
     * @example
2481
     * ```html
2482
     * <igx-grid [data]="localData" primaryKey="ID" rowSelection="multiple" [selectedRows]="[0, 1, 2]"><igx-grid>
2483
     * ```
2484
     */
2485
    @Input()
2486
    public set selectedRows(rowIDs: any[]) {
UNCOV
2487
        this.selectRows(rowIDs || [], true);
×
2488
    }
2489

2490
    public get selectedRows(): any[] {
UNCOV
2491
        return this.selectionService.getSelectedRows();
×
2492
    }
2493

2494

2495
    /** @hidden @internal */
2496
    public get headerGroupsList(): IgxGridHeaderGroupComponent[] {
2497
        return this.theadRow.groups;
212✔
2498
    }
2499

2500
    /** @hidden @internal */
2501
    public get headerCellList(): IgxGridHeaderComponent[] {
UNCOV
2502
        return this.headerGroupsList.map(headerGroup => headerGroup.header).filter(header => header);
×
2503
    }
2504

2505
    /** @hidden @internal */
2506
    public get filterCellList(): IgxGridFilteringCellComponent[] {
UNCOV
2507
        return this.headerGroupsList.map(group => group.filter).filter(cell => cell);
×
2508
    }
2509

2510
    /**
2511
     * @hidden @internal
2512
     */
2513
    public get summariesRowList() {
UNCOV
2514
        const res = new QueryList<any>();
×
UNCOV
2515
        if (!this._summaryRowList) {
×
2516
            return res;
×
2517
        }
UNCOV
2518
        const sumList = this._summaryRowList.filter((item) => item.element.nativeElement.parentElement !== null);
×
UNCOV
2519
        res.reset(sumList);
×
UNCOV
2520
        return res;
×
2521
    }
2522

2523
    /* csSuppress */
2524
    /**
2525
     * A list of `IgxGridRowComponent`.
2526
     *
2527
     * @example
2528
     * ```typescript
2529
     * const rowList = this.grid.rowList;
2530
     * ```
2531
     */
2532
    public get rowList() {
2533
        const res = new QueryList<IgxRowDirective>();
45✔
2534
        if (!this._rowList) {
45!
2535
            return res;
×
2536
        }
2537
        const rList = this._rowList
45✔
2538
            .filter((item) => item.element.nativeElement.parentElement !== null)
168✔
2539
            .sort((a, b) => a.index - b.index);
124✔
2540
        res.reset(rList);
45✔
2541
        return res;
45✔
2542
    }
2543

2544
    /* csSuppress */
2545
    /**
2546
     * A list of currently rendered `IgxGridRowComponent`'s.
2547
     *
2548
     * @example
2549
     * ```typescript
2550
     * const dataList = this.grid.dataRowList;
2551
     * ```
2552
     */
2553
    public get dataRowList(): QueryList<IgxRowDirective> {
2554
        const res = new QueryList<IgxRowDirective>();
326✔
2555
        if (!this._dataRowList) {
326✔
2556
            return res;
77✔
2557
        }
2558
        const rList = this._dataRowList.filter(item => item.element.nativeElement.parentElement !== null).sort((a, b) => a.index - b.index);
708✔
2559
        res.reset(rList);
249✔
2560
        return res;
249✔
2561
    }
2562

2563
    /**
2564
     * Gets the header row selector template.
2565
     */
2566
    @Input()
2567
    public get headSelectorTemplate(): TemplateRef<IgxHeadSelectorTemplateContext> {
UNCOV
2568
        return this._headSelectorTemplate || this.headSelectorsTemplates?.first;
×
2569
    }
2570

2571
    /**
2572
     * Sets the header row selector template.
2573
     * ```html
2574
     * <ng-template #template igxHeadSelector let-headContext>
2575
     * {{ headContext.selectedCount }} / {{ headContext.totalCount  }}
2576
     * </ng-template>
2577
     * ```
2578
     * ```typescript
2579
     * @ViewChild("'template'", {read: TemplateRef })
2580
     * public template: TemplateRef<any>;
2581
     * this.grid.headSelectorTemplate = this.template;
2582
     * ```
2583
     */
2584
    public set headSelectorTemplate(template: TemplateRef<IgxHeadSelectorTemplateContext>) {
UNCOV
2585
        this._headSelectorTemplate = template;
×
2586
    }
2587

2588
    /**
2589
     * @hidden
2590
     * @internal
2591
     */
2592
    public get isPinningToStart() {
2593
        return this.pinning.columns !== ColumnPinningPosition.End;
29,146✔
2594
    }
2595

2596
    /**
2597
     * @hidden
2598
     * @internal
2599
     */
2600
    public get isRowPinningToTop() {
2601
        return this.pinning.rows !== RowPinningPosition.Bottom;
4,175✔
2602
    }
2603

2604
    /**
2605
     * Gets the row selector template.
2606
     */
2607
    @Input()
2608
    public get rowSelectorTemplate(): TemplateRef<IgxRowSelectorTemplateContext> {
UNCOV
2609
        return this._rowSelectorTemplate || this.rowSelectorsTemplates?.first;
×
2610
    }
2611

2612
    /**
2613
         * Sets a custom template for the row selectors.
2614
         * ```html
2615
         * <ng-template #template igxRowSelector let-rowContext>
2616
         *    <igx-checkbox [checked]="rowContext.selected"></igx-checkbox>
2617
         * </ng-template>
2618
         * ```
2619
         * ```typescript
2620
         * @ViewChild("'template'", {read: TemplateRef })
2621
         * public template: TemplateRef<any>;
2622
         * this.grid.rowSelectorTemplate = this.template;
2623
         * ```
2624
         */
2625
    public set rowSelectorTemplate(template: TemplateRef<IgxRowSelectorTemplateContext>) {
UNCOV
2626
        this._rowSelectorTemplate = template;
×
2627
    }
2628

2629
    /**
2630
     * @hidden @internal
2631
     */
2632
    public get rowOutletDirective() {
2633
        return this.rowEditingOutletDirective;
39✔
2634
    }
2635

2636
    /**
2637
     * @hidden @internal
2638
     */
2639
    public get parentRowOutletDirective() {
UNCOV
2640
        return this.outlet;
×
2641
    }
2642

2643
    /**
2644
     * @hidden @internal
2645
     */
2646
    public get rowEditCustom(): TemplateRef<IgxGridRowEditTemplateContext> {
UNCOV
2647
        if (this.rowEditCustomDirectives && this.rowEditCustomDirectives.first) {
×
UNCOV
2648
            return this.rowEditCustomDirectives.first;
×
2649
        }
UNCOV
2650
        return null;
×
2651
    }
2652

2653
    /**
2654

2655
    /**
2656
     * @hidden @internal
2657
     */
2658
    public get rowEditContainer(): TemplateRef<IgxGridRowEditTemplateContext> {
UNCOV
2659
        return this.rowEditCustom ? this.rowEditCustom : this.defaultRowEditTemplate;
×
2660
    }
2661

2662
    /**
2663
     * The custom template, if any, that should be used when rendering the row drag indicator icon
2664
     */
2665
    @Input()
2666
    public get dragIndicatorIconTemplate(): TemplateRef<IgxGridEmptyTemplateContext> {
2667
        return this._customDragIndicatorIconTemplate || this.dragIndicatorIconTemplates?.first;
1✔
2668
    }
2669

2670
    /**
2671
     * Sets a custom template that should be used when rendering the row drag indicator icon.
2672
     *```html
2673
     * <ng-template #template igxDragIndicatorIcon>
2674
     *    <igx-icon>expand_less</igx-icon>
2675
     * </ng-template>
2676
     * ```
2677
     * ```typescript
2678
     * @ViewChild("'template'", {read: TemplateRef })
2679
     * public template: TemplateRef<any>;
2680
     * this.grid.dragIndicatorIconTemplate = this.template;
2681
     * ```
2682
     */
2683
    public set dragIndicatorIconTemplate(val: TemplateRef<IgxGridEmptyTemplateContext>) {
2684
        this._customDragIndicatorIconTemplate = val;
1✔
2685
    }
2686

2687
    /**
2688
     * @hidden @internal
2689
     */
2690
    public get firstEditableColumnIndex(): number {
UNCOV
2691
        const index = this.visibleColumns.filter(col => col.editable)
×
UNCOV
2692
            .map(c => c.visibleIndex).sort((a, b) => a - b);
×
UNCOV
2693
        return index.length ? index[0] : null;
×
2694
    }
2695

2696
    /**
2697
     * @hidden @internal
2698
     */
2699
    public get lastEditableColumnIndex(): number {
UNCOV
2700
        const index = this.visibleColumns.filter(col => col.editable)
×
UNCOV
2701
            .map(c => c.visibleIndex).sort((a, b) => a > b ? -1 : 1);
×
UNCOV
2702
        return index.length ? index[0] : null;
×
2703
    }
2704

2705
    /**
2706
     * @hidden @internal
2707
     * TODO: Nav service logic doesn't handle 0 results from this querylist
2708
     */
2709
    public get rowEditTabs(): QueryList<IgxRowEditTabStopDirective> {
UNCOV
2710
        return this.rowEditTabsCUSTOM.length ? this.rowEditTabsCUSTOM : this.rowEditTabsDEFAULT;
×
2711
    }
2712

2713
    /** @hidden @internal */
2714
    public get activeDescendant() {
2715
        const activeElem = this.navigation.activeNode;
1,218✔
2716

2717
        if (!activeElem || !Object.keys(activeElem).length) {
1,218✔
2718
            return this.id;
1,218✔
2719
        }
2720

UNCOV
2721
        return activeElem.row < 0 ?
×
2722
            `${this.id}_${activeElem.row}_${activeElem.mchCache.level}_${activeElem.column}` :
2723
            `${this.id}_${activeElem.row}_${activeElem.column}`;
2724
    }
2725

2726
    /** @hidden @internal */
2727
    public get bannerClass(): string {
UNCOV
2728
        const position = this.rowEditPositioningStrategy.isTop ? 'igx-banner__border-top' : 'igx-banner__border-bottom';
×
UNCOV
2729
        return `igx-banner ${position}`;
×
2730
    }
2731

2732
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
2733
    /**
2734
     * Gets/Sets the sorting state.
2735
     *
2736
     * @remarks
2737
     * Supports two-way data binding.
2738
     * @example
2739
     * ```html
2740
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(sortingExpressions)]="model.sortingExpressions"></igx-grid>
2741
     * ```
2742
     */
2743
    @WatchChanges()
2744
    @Input()
2745
    public get sortingExpressions(): ISortingExpression[] {
2746
        return this._sortingExpressions;
2,739✔
2747
    }
2748

2749
    public set sortingExpressions(value: ISortingExpression[]) {
UNCOV
2750
        this._sortingExpressions = cloneArray(value);
×
UNCOV
2751
        this.sortingExpressionsChange.emit(this._sortingExpressions);
×
UNCOV
2752
        this.notifyChanges();
×
2753
    }
2754

2755
    /**
2756
     * @hidden @internal
2757
     */
2758
    public get maxLevelHeaderDepth() {
2759
        if (this._maxLevelHeaderDepth === null) {
×
2760
            this._maxLevelHeaderDepth = this.hasColumnLayouts ?
×
2761
                this._columns.reduce((acc, col) => Math.max(acc, col.rowStart), 0) :
×
2762
                this._columns.reduce((acc, col) => Math.max(acc, col.level), 0);
×
2763
        }
2764
        return this._maxLevelHeaderDepth;
×
2765
    }
2766

2767
    /**
2768
     * Gets the number of hidden columns.
2769
     *
2770
     * @example
2771
     * ```typescript
2772
     * const hiddenCol = this.grid.hiddenColumnsCount;
2773
     * ``
2774
     */
2775
    public get hiddenColumnsCount() {
UNCOV
2776
        return this._columns.filter((col) => col.columnGroup === false && col.hidden === true).length;
×
2777
    }
2778

2779
    /**
2780
     * Gets the number of pinned columns.
2781
     */
2782
    public get pinnedColumnsCount() {
UNCOV
2783
        return this.pinnedColumns.filter(col => !col.columnLayout).length;
×
2784
    }
2785

2786
    /**
2787
     * Gets/Sets whether the grid has batch editing enabled.
2788
     * When batch editing is enabled, changes are not made directly to the underlying data.
2789
     * Instead, they are stored as transactions, which can later be committed w/ the `commit` method.
2790
     *
2791
     * @example
2792
     * ```html
2793
     * <igx-grid [batchEditing]="true" [data]="someData">
2794
     * </igx-grid>
2795
     * ```
2796
     */
2797
    @Input({ transform: booleanAttribute })
2798
    public get batchEditing(): boolean {
UNCOV
2799
        return this._batchEditing;
×
2800
    }
2801

2802
    public set batchEditing(val: boolean) {
UNCOV
2803
        if (val !== this._batchEditing) {
×
UNCOV
2804
            delete this._transactions;
×
UNCOV
2805
            this._batchEditing = val;
×
UNCOV
2806
            this.switchTransactionService(val);
×
UNCOV
2807
            this.subscribeToTransactions();
×
2808
        }
2809
    }
2810

2811
    /* blazorSuppress */
2812
    /**
2813
     * Get transactions service for the grid.
2814
     */
2815
    public get transactions(): TransactionService<Transaction, State> {
2816
        if (this._diTransactions && !this.batchEditing) {
69,359!
UNCOV
2817
            return this._diTransactions;
×
2818
        }
2819
        return this._transactions;
69,359✔
2820
    }
2821

2822
    /**
2823
     * @hidden @internal
2824
     */
2825
    public get currentRowState(): any {
2826
        return this._currentRowState;
×
2827
    }
2828

2829
    /**
2830
     * @hidden @internal
2831
     */
2832
    public get currencyPositionLeft(): boolean {
UNCOV
2833
        if (this._currencyPositionLeft !== undefined) {
×
UNCOV
2834
            return this._currencyPositionLeft;
×
2835
        }
UNCOV
2836
        const format = getLocaleNumberFormat(this.locale, NumberFormatStyle.Currency);
×
UNCOV
2837
        const formatParts = format.split(',');
×
UNCOV
2838
        const i = formatParts.indexOf(formatParts.find(c => c.includes('¤')));
×
UNCOV
2839
        return this._currencyPositionLeft = i < 1;
×
2840
    }
2841

2842
    /**
2843
     * Gets/Sets cell selection mode.
2844
     *
2845
     * @remarks
2846
     * By default the cell selection mode is multiple
2847
     * @param selectionMode: GridSelectionMode
2848
     */
2849
    @WatchChanges()
2850
    @Input()
2851
    public get cellSelection() {
2852
        return this._cellSelectionMode;
9,938✔
2853
    }
2854

2855
    public set cellSelection(selectionMode: GridSelectionMode) {
UNCOV
2856
        this._cellSelectionMode = selectionMode;
×
2857
        // if (this.gridAPI.grid) {
UNCOV
2858
        this.selectionService.clear(true);
×
UNCOV
2859
        this.notifyChanges();
×
2860
        // }
2861
    }
2862

2863
    /**
2864
     * Gets/Sets row selection mode
2865
     *
2866
     * @remarks
2867
     * By default the row selection mode is 'none'
2868
     * Note that in IgxGrid and IgxHierarchicalGrid 'multipleCascade' behaves like 'multiple'
2869
     */
2870
    @WatchChanges()
2871
    @Input()
2872
    public get rowSelection() {
2873
        return this._rowSelectionMode;
1,888✔
2874
    }
2875

2876
    public set rowSelection(selectionMode: GridSelectionMode) {
2877
        this._rowSelectionMode = selectionMode;
1✔
2878
        if (!this._init) {
1!
UNCOV
2879
            this.selectionService.clearAllSelectedRows();
×
UNCOV
2880
            this.notifyChanges(true);
×
2881
        }
2882
    }
2883

2884
    /**
2885
     * Gets/Sets column selection mode
2886
     *
2887
     * @remarks
2888
     * By default the row selection mode is none
2889
     * @param selectionMode: GridSelectionMode
2890
     */
2891
    @WatchChanges()
2892
    @Input()
2893
    public get columnSelection() {
2894
        return this._columnSelectionMode;
2,336✔
2895
    }
2896

2897
    public set columnSelection(selectionMode: GridSelectionMode) {
UNCOV
2898
        this._columnSelectionMode = selectionMode;
×
2899
        // if (this.gridAPI.grid) {
UNCOV
2900
        this.selectionService.clearAllSelectedColumns();
×
UNCOV
2901
        this.notifyChanges(true);
×
2902
        // }
2903
    }
2904

2905
    /**
2906
     * @hidden @internal
2907
     */
2908
    public set pagingState(value) {
UNCOV
2909
        this._pagingState = value;
×
UNCOV
2910
        if (this.paginator && !this._init) {
×
UNCOV
2911
            this.paginator.totalRecords = value.metadata.countRecords;
×
2912
        }
2913
    }
2914

2915
    public get pagingState() {
UNCOV
2916
        return this._pagingState;
×
2917
    }
2918

2919
    /**
2920
     * @hidden @internal
2921
     */
2922
    public rowEditMessage;
2923

2924
    /**
2925
     * @hidden @internal
2926
     */
2927
    public calcWidth: number;
2928
    /**
2929
     * @hidden @internal
2930
     */
2931
    public calcHeight = 0;
39✔
2932
    /**
2933
     * @hidden @internal
2934
     */
2935
    public tfootHeight: number;
2936

2937
    /**
2938
     * @hidden @internal
2939
     */
2940
    public disableTransitions = false;
39✔
2941

2942
    /**
2943
     * Represents the last search information.
2944
     */
2945
    public get lastSearchInfo(): ISearchInfo {
2946
        return this._lastSearchInfo;
11,956✔
2947
    }
2948

2949
    /**
2950
     * @hidden @internal
2951
     */
2952
    public columnWidthSetByUser = false;
39✔
2953

2954
    /**
2955
     * @hidden @internal
2956
     */
2957
    public pinnedRecords: any[];
2958

2959
    /**
2960
     * @hidden @internal
2961
     */
2962
    public unpinnedRecords: any[];
2963

2964
    /**
2965
     * @hidden @internal
2966
     */
2967
    public rendered$ = this.rendered.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true }));
39✔
2968

2969
    /** @hidden @internal */
2970
    public resizeNotify = new Subject<void>();
39✔
2971

2972
    /** @hidden @internal */
2973
    public rowAddedNotifier = new Subject<IRowDataEventArgs>();
39✔
2974

2975
    /** @hidden @internal */
2976
    public rowDeletedNotifier = new Subject<IRowDataEventArgs>();
39✔
2977

2978
    /** @hidden @internal */
2979
    public pipeTriggerNotifier = new Subject();
39✔
2980

2981
    /** @hidden @internal */
2982
    public _filteredSortedPinnedData: any[];
2983

2984
    /** @hidden @internal */
2985
    public _filteredSortedUnpinnedData: any[];
2986

2987
    /** @hidden @internal */
2988
    public _filteredPinnedData: any[];
2989

2990
    /**
2991
     * @hidden
2992
     */
2993
    public _filteredUnpinnedData;
2994
    /**
2995
     * @hidden @internal
2996
     */
2997
    public _destroyed = false;
39✔
2998
    /**
2999
     * @hidden @internal
3000
     */
3001
    public _totalRecords = -1;
39✔
3002
    /**
3003
     * @hidden @internal
3004
     */
3005
    public columnsWithNoSetWidths = null;
39✔
3006
    /**
3007
     * @hidden @internal
3008
     */
3009
    public pipeTrigger = 0;
39✔
3010
    /**
3011
     * @hidden @internal
3012
     */
3013
    public filteringPipeTrigger = 0;
39✔
3014

3015
    /**
3016
     * @hidden @internal
3017
     */
3018
    public isColumnWidthSum = false;
39✔
3019

3020
    /**
3021
     * @hidden @internal
3022
     */
3023
    public summaryPipeTrigger = 0;
39✔
3024
    /**
3025
     * @hidden @internal
3026
     */
3027
    public groupablePipeTrigger = 0;
39✔
3028

3029
    /**
3030
    * @hidden @internal
3031
    */
3032
    public EMPTY_DATA = [];
39✔
3033

3034
    /** @hidden @internal */
3035
    public get type(): GridType["type"] {
3036
        return 'flat';
9✔
3037
    }
3038

3039
    /** @hidden @internal */
3040
    public _baseFontSize: number;
3041

3042
    /**
3043
     * @hidden
3044
     */
3045
    public destroy$ = new Subject<any>();
39✔
3046
    /**
3047
     * @hidden
3048
     */
3049
    protected _pagingMode = GridPagingMode.Local;
39✔
3050
    /**
3051
     * @hidden
3052
     */
3053
    protected _pagingState;
3054
    /**
3055
     * @hidden
3056
     */
3057
    protected _hideRowSelectors = false;
39✔
3058
    /**
3059
     * @hidden
3060
     */
3061
    protected _rowDrag = false;
39✔
3062
    /**
3063
     * @hidden
3064
     */
3065
    protected _columns: IgxColumnComponent[] = [];
39✔
3066
    /**
3067
     * @hidden
3068
     */
3069
    protected _pinnedColumns: IgxColumnComponent[] = [];
39✔
3070
    /**
3071
     * @hidden
3072
     */
3073
    protected _unpinnedColumns: IgxColumnComponent[] = [];
39✔
3074
    /**
3075
     * @hidden
3076
     */
3077
    protected _filteringExpressionsTree: IFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And);
39✔
3078
    /**
3079
     * @hidden
3080
     */
3081
    protected _advancedFilteringExpressionsTree: IFilteringExpressionsTree;
3082
    /**
3083
     * @hidden
3084
     */
3085
    protected _sortingExpressions: Array<ISortingExpression> = [];
39✔
3086
    /**
3087
     * @hidden
3088
     */
3089
    protected _maxLevelHeaderDepth = null;
39✔
3090
    /**
3091
     * @hidden
3092
     */
3093
    protected _columnHiding = false;
39✔
3094
    /**
3095
     * @hidden
3096
     */
3097
    protected _columnPinning = false;
39✔
3098

3099
    protected _pinnedRecordIDs = [];
39✔
3100

3101
    /**
3102
     * @hidden
3103
     */
3104
    protected _hasVisibleColumns;
3105
    protected _allowFiltering = false;
39✔
3106
    protected _allowAdvancedFiltering = false;
39✔
3107
    protected _filterMode: FilterMode = FilterMode.quickFilter;
39✔
3108

3109

3110
    protected _defaultTargetRecordNumber = 10;
39✔
3111
    protected _expansionStates: Map<any, boolean> = new Map<any, boolean>();
39✔
3112
    protected _defaultExpandState = false;
39✔
3113
    protected _headerFeaturesWidth = NaN;
39✔
3114
    protected _init = true;
39✔
3115
    protected _firstAutoResize = true;
39✔
3116
    protected _autoSizeColumnsNotify = new Subject<void>();
39✔
3117
    protected _cdrRequestRepaint = false;
39✔
3118
    protected _userOutletDirective: IgxOverlayOutletDirective;
3119
    protected _transactions: TransactionService<Transaction, State>;
3120
    protected _batchEditing = false;
39✔
3121
    protected _sortingOptions: ISortingOptions = { mode: 'multiple' };
39✔
3122
    protected _filterStrategy: IFilteringStrategy = new FilteringStrategy();
39✔
3123
    protected _autoGeneratedCols = [];
39✔
3124
    protected _dataView = [];
39✔
3125
    protected _lastSearchInfo: ISearchInfo = {
39✔
3126
        searchText: '',
3127
        caseSensitive: false,
3128
        exactMatch: false,
3129
        activeMatchIndex: 0,
3130
        matchInfoCache: [],
3131
        matchCount: 0,
3132
        content: ''
3133
    };
3134
    protected gridComputedStyles;
3135

3136
    /** @hidden @internal */
3137
    public get paginator() {
3138
        return this.paginationComponents?.first;
40,355✔
3139
    }
3140

3141
    /**
3142
     * @hidden @internal
3143
     */
3144
    public get scrollSize() {
3145
        return this.verticalScrollContainer.getScrollNativeSize();
1,631✔
3146
    }
3147

3148
    private _primaryKey: string;
3149
    private _rowEditable = false;
39✔
3150
    private _currentRowState: any;
3151
    private _filteredSortedData = null;
39✔
3152
    private _filteredData = null;
39✔
3153

3154
    private _customDragIndicatorIconTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3155
    private _excelStyleHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext>;
3156
    private _rowSelectorTemplate: TemplateRef<IgxRowSelectorTemplateContext>;
3157
    private _headSelectorTemplate: TemplateRef<IgxHeadSelectorTemplateContext>;
3158
    private _rowEditTextTemplate: TemplateRef<IgxGridRowEditTextTemplateContext>;
3159
    private _rowAddTextTemplate: TemplateRef<IgxGridEmptyTemplateContext>;
3160
    private _rowEditActionsTemplate: TemplateRef<IgxGridRowEditActionsTemplateContext>;
3161
    private _dragGhostCustomTemplate: TemplateRef<IgxGridRowDragGhostContext>;
3162
    private _rowExpandedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3163
    private _rowCollapsedIndicatorTemplate: TemplateRef<IgxGridRowTemplateContext>;
3164
    private _headerExpandIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
3165
    private _headerCollapseIndicatorTemplate: TemplateRef<IgxGridTemplateContext>;
3166

3167
    private _cdrRequests = false;
39✔
3168
    private _resourceStrings = getCurrentResourceStrings(GridResourceStringsEN);
39✔
3169
    private _emptyGridMessage = null;
39✔
3170
    private _emptyFilteredGridMessage = null;
39✔
3171
    private _isLoading = false;
39✔
3172
    private _locale: string;
3173
    private overlayIDs = [];
39✔
3174
    private _sortingStrategy: IGridSortingStrategy;
3175
    private _pinning: IPinningConfig = { columns: ColumnPinningPosition.Start };
39✔
3176
    private _shouldRecalcRowHeight = false;
39✔
3177

3178
    private _hostWidth;
3179
    private _advancedFilteringOverlayId: string;
3180
    private _advancedFilteringPositionSettings: PositionSettings = {
39✔
3181
        verticalDirection: VerticalAlignment.Middle,
3182
        horizontalDirection: HorizontalAlignment.Center,
3183
        horizontalStartPoint: HorizontalAlignment.Center,
3184
        verticalStartPoint: VerticalAlignment.Middle
3185
    };
3186

3187
    private _advancedFilteringOverlaySettings: OverlaySettings = {
39✔
3188
        closeOnOutsideClick: false,
3189
        modal: false,
3190
        positionStrategy: new ConnectedPositioningStrategy(this._advancedFilteringPositionSettings),
3191
    };
3192

3193
    private columnListDiffer;
3194
    private rowListDiffer;
3195
    private _height: string | null = '100%';
39✔
3196
    private _width: string | null = '100%';
39✔
3197
    private _rowHeight: number | undefined;
3198
    private _horizontalForOfs: Array<IgxGridForOfDirective<any, any[]>> = [];
39✔
3199
    private _multiRowLayoutRowSize = 1;
39✔
3200
    // Caches
3201
    private _totalWidth = NaN;
39✔
3202
    private _pinnedVisible = [];
39✔
3203
    private _unpinnedVisible = [];
39✔
3204
    private _pinnedWidth = NaN;
39✔
3205
    private _unpinnedWidth = NaN;
39✔
3206
    private _visibleColumns = [];
39✔
3207
    private _columnGroups = false;
39✔
3208

3209
    private _columnWidth: string;
3210

3211
    private _summaryPosition: GridSummaryPosition = GridSummaryPosition.bottom;
39✔
3212
    private _summaryCalculationMode: GridSummaryCalculationMode = GridSummaryCalculationMode.rootAndChildLevels;
39✔
3213
    private _showSummaryOnCollapse = false;
39✔
3214
    private _summaryRowHeight = 0;
39✔
3215
    private _cellSelectionMode: GridSelectionMode = GridSelectionMode.multiple;
39✔
3216
    private _rowSelectionMode: GridSelectionMode = GridSelectionMode.none;
39✔
3217
    private _selectRowOnClick = true;
39✔
3218
    private _columnSelectionMode: GridSelectionMode = GridSelectionMode.none;
39✔
3219

3220
    private lastAddedRowIndex;
3221

3222
    private _currencyPositionLeft: boolean;
3223

3224
    private rowEditPositioningStrategy = new RowEditPositionStrategy({
39✔
3225
        horizontalDirection: HorizontalAlignment.Right,
3226
        verticalDirection: VerticalAlignment.Bottom,
3227
        horizontalStartPoint: HorizontalAlignment.Left,
3228
        verticalStartPoint: VerticalAlignment.Bottom,
3229
        closeAnimation: null
3230
    });
3231

3232
    private rowEditSettings: OverlaySettings = {
39✔
3233
        scrollStrategy: new AbsoluteScrollStrategy(),
3234
        modal: false,
3235
        closeOnOutsideClick: false,
3236
        outlet: this.rowOutletDirective,
3237
        positionStrategy: this.rowEditPositioningStrategy
3238
    };
3239

3240
    private transactionChange$ = new Subject<void>();
39✔
3241
    private _rendered = false;
39✔
3242
    private readonly DRAG_SCROLL_DELTA = 10;
39✔
3243
    private _dataCloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy();
39✔
3244
    private _autoSize = false;
39✔
3245
    private _sortHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
39✔
3246
    private _sortAscendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
39✔
3247
    private _sortDescendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
39✔
3248
    private _gridSize: Size = Size.Large;
39✔
3249
    private _defaultRowHeight = 50;
39✔
3250

3251
    /**
3252
     * @hidden @internal
3253
     */
3254
    public get minColumnWidth() {
3255
        return MINIMUM_COLUMN_WIDTH;
116✔
3256
    }
3257

3258
    protected get isCustomSetRowHeight(): boolean {
3259
        return !isNaN(this._rowHeight);
9,938✔
3260
    }
3261

3262
    /**
3263
     * @hidden @internal
3264
     */
3265
    public abstract id: string;
3266
    /* blazorSuppress */
3267
    public abstract data: any[] | null;
3268

3269
    /**
3270
     * Returns an array of objects containing the filtered data.
3271
     *
3272
     * @example
3273
     * ```typescript
3274
     * let filteredData = this.grid.filteredData;
3275
     * ```
3276
     */
3277
    public get filteredData() {
3278
        return this._filteredData;
578✔
3279
    }
3280

3281
    /**
3282
     * Returns an array containing the filtered sorted data.
3283
     *
3284
     * @example
3285
     * ```typescript
3286
     * const filteredSortedData = this.grid1.filteredSortedData;
3287
     * ```
3288
     */
3289
    public get filteredSortedData(): any[] {
UNCOV
3290
        return this._filteredSortedData;
×
3291
    }
3292

3293
    /**
3294
     * @hidden @internal
3295
     */
3296
    public get rowChangesCount() {
UNCOV
3297
        if (!this.crudService.row) {
×
UNCOV
3298
            return 0;
×
3299
        }
UNCOV
3300
        const f = (obj: any) => {
×
UNCOV
3301
            let changes = 0;
×
UNCOV
3302
            Object.keys(obj).forEach(key => isObject(obj[key]) ? changes += f(obj[key]) : changes++);
×
UNCOV
3303
            return changes;
×
3304
        };
UNCOV
3305
        if (this.transactions.getState(this.crudService.row.id)?.type === TransactionType.ADD) {
×
UNCOV
3306
            return this._columns.filter(c => c.field).length;
×
3307
        }
UNCOV
3308
        const rowChanges = this.transactions.getAggregatedValue(this.crudService.row.id, false);
×
UNCOV
3309
        return rowChanges ? f(rowChanges) : 0;
×
3310
    }
3311

3312
    /**
3313
     * @hidden @internal
3314
     */
3315
    public get dataWithAddedInTransactionRows() {
UNCOV
3316
        const result = cloneArray(this.gridAPI.get_all_data());
×
UNCOV
3317
        if (this.transactions.enabled) {
×
UNCOV
3318
            result.push(...this.transactions.getAggregatedChanges(true)
×
UNCOV
3319
                .filter(t => t.type === TransactionType.ADD)
×
UNCOV
3320
                .map(t => t.newValue));
×
3321
        }
3322

UNCOV
3323
        if (this.crudService.row && this.crudService.row.getClassName() === IgxAddRow.name) {
×
UNCOV
3324
            result.splice(this.crudService.row.index, 0, this.crudService.row.data);
×
3325
        }
3326

UNCOV
3327
        return result;
×
3328
    }
3329

3330
    /**
3331
     * @hidden @internal
3332
     */
3333
    public get dataLength() {
3334
        return this.transactions.enabled ? this.dataWithAddedInTransactionRows.length : this.gridAPI.get_all_data().length;
402!
3335
    }
3336

3337
    /**
3338
     * @hidden @internal
3339
     */
3340
    public get template(): TemplateRef<IgxGridTemplateContext> {
3341
        if (this.isLoading && (this.hasZeroResultFilter || this.hasNoData)) {
406!
UNCOV
3342
            return this.loadingGridTemplate ? this.loadingGridTemplate : this.loadingGridDefaultTemplate;
×
3343
        }
3344

3345
        if (this.hasZeroResultFilter) {
406✔
3346
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyFilteredGridTemplate;
4!
3347
        }
3348

3349
        if (this.hasNoData) {
402!
UNCOV
3350
            return this.emptyGridTemplate ? this.emptyGridTemplate : this.emptyGridDefaultTemplate;
×
3351
        }
3352
    }
3353

3354
    /**
3355
     * @hidden @internal
3356
     */
3357
    private get hasZeroResultFilter(): boolean {
3358
        return this.filteredData && this.filteredData.length === 0;
406✔
3359
    }
3360

3361
    /**
3362
     * @hidden @internal
3363
     */
3364
    private get hasNoData(): boolean {
3365
        return !this.data || this.dataLength === 0;
402✔
3366
    }
3367

3368
    /**
3369
     * @hidden @internal
3370
     */
3371
    public get shouldOverlayLoading(): boolean {
3372
        return this.isLoading && !this.hasNoData && !this.hasZeroResultFilter;
433!
3373
    }
3374

3375
    /**
3376
     * @hidden @internal
3377
     */
3378
    public get isMultiRowSelectionEnabled(): boolean {
UNCOV
3379
        return this.rowSelection === GridSelectionMode.multiple
×
3380
            || this.rowSelection === GridSelectionMode.multipleCascade;
3381
    }
3382

3383
    /**
3384
     * @hidden @internal
3385
     */
3386
    public get isRowSelectable(): boolean {
3387
        return this.rowSelection !== GridSelectionMode.none;
1,886✔
3388
    }
3389

3390
    /**
3391
     * @hidden @internal
3392
     */
3393
    public get isCellSelectable() {
UNCOV
3394
        return this.cellSelection !== GridSelectionMode.none;
×
3395
    }
3396

3397
    /**
3398
     * @hidden @internal
3399
     */
3400
    public get columnInDrag() {
3401
        return this.gridAPI.cms.column;
2,336✔
3402
    }
3403

3404
    constructor(
3405
        public readonly validation: IgxGridValidationService,
39✔
3406
        /** @hidden @internal */
3407
        public readonly selectionService: IgxGridSelectionService,
39✔
3408
        protected colResizingService: IgxColumnResizingService,
39✔
3409
        @Inject(IGX_GRID_SERVICE_BASE) public readonly gridAPI: GridServiceType,
39✔
3410
        protected transactionFactory: IgxFlatTransactionFactory,
39✔
3411
        private elementRef: ElementRef<HTMLElement>,
39✔
3412
        protected zone: NgZone,
39✔
3413
        /** @hidden @internal */
3414
        @Inject(DOCUMENT) public document: any,
39✔
3415
        public readonly cdr: ChangeDetectorRef,
39✔
3416
        protected differs: IterableDiffers,
39✔
3417
        protected viewRef: ViewContainerRef,
39✔
3418
        protected injector: Injector,
39✔
3419
        protected envInjector: EnvironmentInjector,
39✔
3420
        public navigation: IgxGridNavigationService,
39✔
3421
        /** @hidden @internal */
3422
        public filteringService: IgxFilteringService,
39✔
3423
        protected textHighlightService: IgxTextHighlightService,
39✔
3424
        @Inject(IgxOverlayService) protected overlayService: IgxOverlayService,
39✔
3425
        /** @hidden @internal */
3426
        public summaryService: IgxGridSummaryService,
39✔
3427
        @Inject(LOCALE_ID) private localeId: string,
39✔
3428
        protected platform: PlatformUtil,
39✔
3429
        @Optional() @Inject(IgxGridTransaction) protected _diTransactions?: TransactionService<Transaction, State>,
39✔
3430
    ) {
3431
        this.locale = this.locale || this.localeId;
39✔
3432
        this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
39✔
3433
        this._transactions.cloneStrategy = this.dataCloneStrategy;
39✔
3434
        this.cdr.detach();
39✔
3435
        this.selectionService.selectedRowsChange.pipe(takeUntil(this.destroy$)).subscribe((args: any[]) => {
39✔
UNCOV
3436
            this.selectedRowsChange.emit(args);
×
3437
        });
3438
        IgcTrialWatermark.register();
39✔
3439
    }
3440

3441
    /**
3442
     * @hidden
3443
     * @internal
3444
     */
3445
    @HostListener('mouseleave')
3446
    public hideActionStrip() {
UNCOV
3447
        this.actionStrip?.hide();
×
3448
    }
3449

3450
    /**
3451
     * @hidden
3452
     * @internal
3453
     */
3454
    public get headerFeaturesWidth() {
UNCOV
3455
        return this._headerFeaturesWidth;
×
3456
    }
3457

3458
    /**
3459
     * @hidden
3460
     * @internal
3461
     */
3462
    public isDetailRecord(_rec) {
UNCOV
3463
        return false;
×
3464
    }
3465

3466
    /**
3467
     * @hidden
3468
     * @internal
3469
     */
3470
    public isGroupByRecord(_rec) {
UNCOV
3471
        return false;
×
3472
    }
3473

3474
    /**
3475
     * @hidden @internal
3476
     */
3477
    public isGhostRecord(record: any): boolean {
3478
        return record.ghostRecord !== undefined;
2,934✔
3479
    }
3480
    /**
3481
     * @hidden @internal
3482
     */
3483
    public isAddRowRecord(record: any): boolean {
3484
        return record.addRow !== undefined;
×
3485
    }
3486

3487
    /**
3488
     * @hidden
3489
     * Returns the row index of a row that takes into account the full view data like pinning.
3490
     */
3491
    public getDataViewIndex(rowIndex, pinned) {
3492
        if (pinned && !this.isRowPinningToTop) {
1,442!
UNCOV
3493
            rowIndex = rowIndex + this.unpinnedDataView.length;
×
3494
        } else if (!pinned && this.isRowPinningToTop) {
1,442✔
3495
            rowIndex = rowIndex + this.pinnedDataView.length;
1,442✔
3496
        }
3497
        return rowIndex;
1,442✔
3498
    }
3499

3500
    /**
3501
     * @hidden
3502
     * @internal
3503
     */
3504
    public get hasDetails() {
UNCOV
3505
        return false;
×
3506
    }
3507

3508
    /**
3509
     * Returns the state of the grid virtualization.
3510
     *
3511
     * @remarks
3512
     * Includes the start index and how many records are rendered.
3513
     * @example
3514
     * ```typescript
3515
     * const gridVirtState = this.grid1.virtualizationState;
3516
     * ```
3517
     */
3518
    public get virtualizationState() {
UNCOV
3519
        return this.verticalScrollContainer.state;
×
3520
    }
3521

3522
    /**
3523
     * @hidden
3524
     * @internal
3525
     */
3526
    public hideOverlays() {
UNCOV
3527
        this.overlayIDs.forEach(overlayID => {
×
UNCOV
3528
            const overlay = this.overlayService.getOverlayById(overlayID);
×
3529

UNCOV
3530
            if (overlay?.visible && !overlay.closeAnimationPlayer?.hasStarted()) {
×
UNCOV
3531
                this.overlayService.hide(overlayID);
×
3532

UNCOV
3533
                this.nativeElement.focus();
×
3534
            }
3535
        });
3536
    }
3537

3538
    /**
3539
     * Returns whether the record is pinned or not.
3540
     *
3541
     * @param rowIndex Index of the record in the `dataView` collection.
3542
     *
3543
     * @hidden
3544
     * @internal
3545
     */
3546
    public isRecordPinnedByViewIndex(rowIndex: number) {
3547
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this.pinnedDataView.length) ||
1,787!
3548
            (!this.isRowPinningToTop && rowIndex >= this.unpinnedDataView.length);
3549
    }
3550

3551
    /**
3552
     * Returns whether the record is pinned or not.
3553
     *
3554
     * @param rowIndex Index of the record in the `filteredSortedData` collection.
3555
     */
3556
    public isRecordPinnedByIndex(rowIndex: number) {
UNCOV
3557
        return this.hasPinnedRecords && (this.isRowPinningToTop && rowIndex < this._filteredSortedPinnedData.length) ||
×
3558
            (!this.isRowPinningToTop && rowIndex >= this._filteredSortedUnpinnedData.length);
3559
    }
3560

3561
    /**
3562
     * @hidden
3563
     * @internal
3564
     */
3565
    public isRecordPinned(rec) {
3566
        return this.getInitialPinnedIndex(rec) !== -1;
10,100✔
3567
    }
3568

3569
    /**
3570
     * @hidden
3571
     * @internal
3572
     * Returns the record index in order of pinning by the user. Does not consider sorting/filtering.
3573
     */
3574
    public getInitialPinnedIndex(rec) {
3575
        const id = this.gridAPI.get_row_id(rec);
10,100✔
3576
        return this._pinnedRecordIDs.indexOf(id);
10,100✔
3577
    }
3578

3579
    /**
3580
     * @hidden
3581
     * @internal
3582
     */
3583
    public get hasPinnedRecords() {
3584
        return this._pinnedRecordIDs.length > 0;
3,605✔
3585
    }
3586

3587
    /**
3588
     * @hidden
3589
     * @internal
3590
     */
3591
    public get pinnedRecordsCount() {
3592
        return this._pinnedRecordIDs.length;
67✔
3593
    }
3594

3595
    /**
3596
     * @hidden
3597
     * @internal
3598
     */
3599
    public get crudService() {
3600
        return this.gridAPI.crudService;
12,988✔
3601
    }
3602

3603
    /**
3604
     * @hidden
3605
     * @internal
3606
     */
3607
    public _setupServices() {
3608
        this.gridAPI.grid = this as any;
37✔
3609
        this.crudService.grid = this as any;
37✔
3610
        this.selectionService.grid = this as any;
37✔
3611
        this.validation.grid = this as any;
37✔
3612
        this.navigation.grid = this as any;
37✔
3613
        this.filteringService.grid = this as any;
37✔
3614
        this.summaryService.grid = this as any;
37✔
3615
    }
3616

3617
    /**
3618
     * @hidden
3619
     * @internal
3620
     */
3621
    public _setupListeners() {
3622
        const destructor = takeUntil<any>(this.destroy$);
37✔
3623
        fromEvent(this.nativeElement, 'focusout').pipe(filter(() => !!this.navigation.activeNode), destructor).subscribe((event) => {
58✔
3624
            const activeNode = this.navigation.activeNode;
58✔
3625
            if (!this.crudService.cell && !!activeNode &&
58!
3626
                ((event.target === this.tbody.nativeElement && activeNode.row >= 0 &&
3627
                    activeNode.row < this.dataView.length)
3628
                    || (event.target === this.theadRow.nativeElement && activeNode.row === -1)
3629
                    || (event.target === this.tfoot.nativeElement && activeNode.row === this.dataView.length)) &&
3630
                !(this.rowEditable && this.crudService.rowEditingBlocked && this.crudService.rowInEditMode)) {
×
UNCOV
3631
                this.clearActiveNode();
×
3632
            }
3633
        });
3634
        this.rowAddedNotifier.pipe(destructor).subscribe(args => this.refreshGridState(args));
37✔
3635
        this.rowDeletedNotifier.pipe(destructor).subscribe(args => {
37✔
UNCOV
3636
            this.summaryService.deleteOperation = true;
×
UNCOV
3637
            this.summaryService.clearSummaryCache(args);
×
3638
        });
3639

3640
        this.subscribeToTransactions();
37✔
3641

3642
        this.resizeNotify.pipe(
37✔
3643
            filter(() => !this._init),
36✔
3644
            throttleTime(40, animationFrameScheduler, { leading: true, trailing: true }),
3645
            destructor
3646
        )
3647
        .subscribe(() => {
3648
            this.zone.run(() => {
36✔
3649
                // do not trigger reflow if element is detached.
3650
                if (this.nativeElement.isConnected) {
36!
UNCOV
3651
                    if (this.shouldResize) {
×
3652
                        // resizing occurs due to the change of --ig-size css var
UNCOV
3653
                        this._gridSize = this.gridSize;
×
UNCOV
3654
                        this.updateDefaultRowHeight();
×
UNCOV
3655
                        this._autoSize = this.isPercentHeight && this.calcHeight !== this.getDataBasedBodyHeight();
×
UNCOV
3656
                        this.crudService.endEdit(false);
×
UNCOV
3657
                        if (this._summaryRowHeight === 0) {
×
UNCOV
3658
                            this.summaryService.summaryHeight = 0;
×
3659
                        }
3660
                    }
UNCOV
3661
                    this.notifyChanges(true);
×
3662
                }
3663
            });
3664
        });
3665

3666
        this.pipeTriggerNotifier.pipe(takeUntil(this.destroy$)).subscribe(() => this.pipeTrigger++);
37✔
3667
        this.columnMovingEnd.pipe(destructor).subscribe(() => this.crudService.endEdit(false));
37✔
3668

3669
        this.overlayService.opening.pipe(destructor).subscribe((event) => {
37✔
3670
            if (this._advancedFilteringOverlayId === event.id) {
82✔
3671
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
37✔
3672
                if (instance) {
37✔
3673
                    instance.initialize(this as any, this.overlayService, event.id);
37✔
3674
                }
3675
            }
3676
        });
3677

3678
        this.overlayService.opened.pipe(destructor).subscribe((event) => {
37✔
3679
            const overlaySettings = this.overlayService.getOverlayById(event.id)?.settings;
82✔
3680

3681
            // do not hide the advanced filtering overlay on scroll
3682
            if (this._advancedFilteringOverlayId === event.id) {
82✔
3683
                const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
37✔
3684
                if (instance) {
37✔
3685
                    instance.lastActiveNode = this.navigation.activeNode;
37✔
3686
                    instance.queryBuilder.setAddButtonFocus();
37✔
3687
                }
3688
                return;
37✔
3689
            }
3690

3691
            // do not hide the overlay if it's attached to a row
3692
            if (this.rowEditingOverlay?.overlayId === event.id) {
45!
UNCOV
3693
                return;
×
3694
            }
3695

3696
            if (overlaySettings?.outlet === this.outlet && this.overlayIDs.indexOf(event.id) === -1) {
45!
UNCOV
3697
                this.overlayIDs.push(event.id);
×
3698
            }
3699
        });
3700

3701
        this.overlayService.closed.pipe(filter(() => !this._init), destructor).subscribe((event) => {
60✔
3702
            if (this._advancedFilteringOverlayId === event.id) {
60✔
3703
                this.overlayService.detach(this._advancedFilteringOverlayId);
19✔
3704
                this._advancedFilteringOverlayId = null;
19✔
3705
                return;
19✔
3706
            }
3707

3708
            const ind = this.overlayIDs.indexOf(event.id);
41✔
3709
            if (ind !== -1) {
41!
UNCOV
3710
                this.overlayIDs.splice(ind, 1);
×
3711
            }
3712
        });
3713

3714
        this.verticalScrollContainer.dataChanging.pipe(filter(() => !this._init), destructor).subscribe(($event) => {
64✔
3715
            const shouldRecalcSize = this.isPercentHeight &&
27!
3716
                (!this.calcHeight || this.calcHeight === this.getDataBasedBodyHeight() ||
3717
                    this.calcHeight === this.renderedRowHeight * this._defaultTargetRecordNumber);
3718
            if (shouldRecalcSize) {
27!
UNCOV
3719
                this.calculateGridHeight();
×
UNCOV
3720
                $event.containerSize = this.calcHeight;
×
3721
            }
3722
            this.evaluateLoadingState();
27✔
3723
        });
3724

3725
        this.verticalScrollContainer.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
55✔
3726
            // called to recalc all widths that may have changes as a result of
3727
            // the vert. scrollbar showing/hiding
3728
            this.notifyChanges(true);
20✔
3729
            this.cdr.detectChanges();
20✔
3730
            Promise.resolve().then(() => this.headerContainer.updateScroll());
20✔
3731
        });
3732

3733

3734
        this.headerContainer?.scrollbarVisibilityChanged.pipe(filter(() => !this._init), destructor).subscribe(() => {
40✔
3735
            // the horizontal scrollbar showing/hiding
3736
            // update scrollbar visibility and recalc heights
3737
            this.notifyChanges(true);
1✔
3738
            this.cdr.detectChanges();
1✔
3739
        });
3740

3741
        this.verticalScrollContainer.contentSizeChange.pipe(filter(() => !this._init), throttleTime(30), destructor).subscribe(() => {
37✔
UNCOV
3742
            this.notifyChanges(true);
×
3743
        });
3744

3745
        // notifier for column autosize requests
3746
        this._autoSizeColumnsNotify.pipe(
37✔
3747
            throttleTime(0, animationFrameScheduler, { leading: false, trailing: true }),
3748
            destructor
3749
        )
3750
        .subscribe(() => {
UNCOV
3751
            this.autoSizeColumnsInView();
×
UNCOV
3752
            this._firstAutoResize = false;
×
3753
        });
3754
    }
3755

3756
    /**
3757
     * @hidden
3758
     */
3759
    public ngOnInit() {
3760
        this._setupServices();
37✔
3761
        this._setupListeners();
37✔
3762
        this.rowListDiffer = this.differs.find([]).create(null);
37✔
3763
        // compare based on field, not on object ref.
3764
        this.columnListDiffer = this.differs.find([]).create((index, col: ColumnType) => col.field);
259✔
3765
        this.calcWidth = this.width && this.width.indexOf('%') === -1 ? parseInt(this.width, 10) : 0;
37✔
3766
        this.gridComputedStyles = this.document.defaultView.getComputedStyle(this.nativeElement);
37✔
3767
    }
3768

3769
    /**
3770
     * @hidden
3771
     * @internal
3772
     */
3773
    public resetColumnsCaches() {
3774
        this._columns.forEach(column => column.resetCaches());
1,916✔
3775
    }
3776

3777
    /**
3778
     * @hidden @internal
3779
     */
3780
    public generateRowID(): string | number {
UNCOV
3781
        const primaryColumn = this._columns.find(col => col.field === this.primaryKey);
×
UNCOV
3782
        const idType = this.data.length ?
×
3783
            this.resolveDataTypes(this.data[0][this.primaryKey]) : primaryColumn ? primaryColumn.dataType : 'string';
×
UNCOV
3784
        return idType === 'string' ? crypto.randomUUID() : FAKE_ROW_ID--;
×
3785
    }
3786

3787
    /**
3788
     * @hidden
3789
     * @internal
3790
     */
3791
    public resetForOfCache() {
3792
        const firstVirtRow = this.dataRowList.first;
289✔
3793
        if (firstVirtRow) {
289✔
3794
            if (this._cdrRequests) {
135✔
3795
                firstVirtRow.virtDirRow.cdr.detectChanges();
45✔
3796
            }
3797
            firstVirtRow.virtDirRow.assumeMaster();
135✔
3798
        }
3799
    }
3800

3801
    /**
3802
     * @hidden
3803
     * @internal
3804
     */
3805
    public setFilteredData(data, pinned: boolean) {
3806
        if (this.hasPinnedRecords && pinned) {
23!
UNCOV
3807
            this._filteredPinnedData = data || [];
×
UNCOV
3808
            const filteredUnpinned = this._filteredUnpinnedData || [];
×
UNCOV
3809
            const filteredData = [... this._filteredPinnedData, ...filteredUnpinned];
×
UNCOV
3810
            this._filteredData = filteredData.length > 0 ? filteredData : this._filteredUnpinnedData;
×
3811
        } else if (this.hasPinnedRecords && !pinned) {
23!
UNCOV
3812
            this._filteredUnpinnedData = data;
×
3813
        } else {
3814
            this._filteredData = data;
23✔
3815
        }
3816
    }
3817

3818
    /**
3819
     * @hidden
3820
     * @internal
3821
     */
3822
    public resetColumnCollections() {
3823
        if (this.hasColumnLayouts) {
289!
UNCOV
3824
            this._columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
×
3825
        }
3826
        this._visibleColumns.length = 0;
289✔
3827
        this._pinnedVisible.length = 0;
289✔
3828
        this._unpinnedVisible.length = 0;
289✔
3829
    }
3830

3831
    /**
3832
     * @hidden
3833
     * @internal
3834
     */
3835
    public resetCachedWidths() {
3836
        this._unpinnedWidth = NaN;
399✔
3837
        this._pinnedWidth = NaN;
399✔
3838
        this._totalWidth = NaN;
399✔
3839
    }
3840

3841
    /**
3842
     * @hidden
3843
     * @internal
3844
     */
3845
    public resetCaches(recalcFeatureWidth = true) {
79✔
3846
        if (recalcFeatureWidth) {
289✔
3847
            this._headerFeaturesWidth = NaN;
289✔
3848
            this.summaryService.summaryHeight = 0;
289✔
3849
        }
3850
        this.resetColumnsCaches();
289✔
3851
        this.resetColumnCollections();
289✔
3852
        this.resetForOfCache();
289✔
3853
        this.resetCachedWidths();
289✔
3854
        this.hasVisibleColumns = undefined;
289✔
3855
        this._columnGroups = this._columns.some(col => col.columnGroup);
1,884✔
3856
    }
3857

3858
    /**
3859
     * @hidden
3860
     */
3861
    public ngAfterContentInit() {
3862
        if (this.sortHeaderIconDirectiveTemplate) {
37!
UNCOV
3863
            this.sortHeaderIconTemplate = this.sortHeaderIconDirectiveTemplate;
×
3864
        }
3865

3866
        if (this.sortAscendingHeaderIconDirectiveTemplate) {
37!
UNCOV
3867
            this.sortAscendingHeaderIconTemplate = this.sortAscendingHeaderIconDirectiveTemplate;
×
3868
        }
3869

3870
        if (this.sortDescendingHeaderIconDirectiveTemplate) {
37!
UNCOV
3871
            this.sortDescendingHeaderIconTemplate = this.sortDescendingHeaderIconDirectiveTemplate;
×
3872
        }
3873

3874
        this.setupColumns();
37✔
3875
        this.toolbar.changes.pipe(filter(() => !this._init), takeUntil(this.destroy$)).subscribe(() => this.notifyChanges(true));
37✔
3876
        this.setUpPaginator();
37✔
3877
        this.paginationComponents.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
37✔
UNCOV
3878
            this.setUpPaginator();
×
3879
        });
3880
        if (this.actionStrip) {
37!
UNCOV
3881
            this.actionStrip.menuOverlaySettings.outlet = this.outlet;
×
3882
        }
3883
    }
3884

3885
    /**
3886
     * @hidden @internal
3887
     */
3888
    public dataRebinding(event: IForOfDataChangingEventArgs) {
3889
        if (event.state.chunkSize == 0) {
64✔
3890
            this._shouldRecalcRowHeight = true;
37✔
3891
        }
3892
        this.dataChanging.emit(event);
64✔
3893
    }
3894

3895
    /**
3896
     * @hidden @internal
3897
     */
3898
    public dataRebound(event) {
3899
        this.selectionService.clearHeaderCBState();
64✔
3900
        if (this._shouldRecalcRowHeight) {
64✔
3901
            this._shouldRecalcRowHeight = false;
37✔
3902
            this.updateDefaultRowHeight();
37✔
3903
        }
3904
        this.dataChanged.emit(event);
64✔
3905
    }
3906

3907
    /** @hidden @internal */
3908
    public createFilterDropdown(column: ColumnType, options: OverlaySettings) {
UNCOV
3909
        options.outlet = this.outlet;
×
UNCOV
3910
        if (this.excelStyleFilteringComponent) {
×
UNCOV
3911
            this.excelStyleFilteringComponent.initialize(column, this.overlayService);
×
UNCOV
3912
            const id = this.overlayService.attach(this.excelStyleFilteringComponent.element, options);
×
UNCOV
3913
            this.excelStyleFilteringComponent.overlayComponentId = id;
×
UNCOV
3914
            return id;
×
3915
        }
UNCOV
3916
        const id = this.overlayService.attach(IgxGridExcelStyleFilteringComponent, this.viewRef, options);
×
UNCOV
3917
        return id;
×
3918
    }
3919

3920
    /** @hidden @internal */
3921
    public setUpPaginator() {
3922
        if (this.paginator) {
37!
UNCOV
3923
            this.paginator.pageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
×
3924
                .subscribe(() => {
UNCOV
3925
                    this.selectionService.clear(true);
×
UNCOV
3926
                    this.crudService.endEdit(false);
×
UNCOV
3927
                    this.pipeTrigger++;
×
UNCOV
3928
                    this.navigateTo(0);
×
UNCOV
3929
                    this.notifyChanges();
×
3930
                });
UNCOV
3931
            this.paginator.perPageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
×
3932
                .subscribe(() => {
UNCOV
3933
                    this.selectionService.clear(true);
×
UNCOV
3934
                    this.page = 0;
×
UNCOV
3935
                    this.crudService.endEdit(false);
×
UNCOV
3936
                    this.notifyChanges();
×
3937
                });
3938
        } else {
3939
            this.markForCheck();
37✔
3940
        }
3941
    }
3942

3943
    /**
3944
     * @hidden
3945
     * @internal
3946
     */
3947
    public setFilteredSortedData(data, pinned: boolean) {
3948
        data = data || [];
67!
3949
        if (this.pinnedRecordsCount > 0) {
67!
UNCOV
3950
            if (pinned) {
×
UNCOV
3951
                this._filteredSortedPinnedData = data;
×
UNCOV
3952
                this.pinnedRecords = data;
×
UNCOV
3953
                this._filteredSortedData = this.isRowPinningToTop ? [... this._filteredSortedPinnedData, ... this._filteredSortedUnpinnedData] :
×
3954
                    [... this._filteredSortedUnpinnedData, ... this._filteredSortedPinnedData];
UNCOV
3955
                this.refreshSearch(true, false);
×
3956
            } else {
UNCOV
3957
                this._filteredSortedUnpinnedData = data;
×
3958
            }
3959
        } else {
3960
            this._filteredSortedData = data;
67✔
3961
            this.refreshSearch(true, false);
67✔
3962
        }
3963
        this.buildDataView(data);
67✔
3964
    }
3965

3966
    /**
3967
     * @hidden @internal
3968
     */
3969
    public resetHorizontalVirtualization() {
3970
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
392✔
3971
        this._horizontalForOfs = [
67✔
3972
            ...this._dataRowList.filter(elementFilter).map(item => item.virtDirRow),
332✔
3973
            ...this._summaryRowList.filter(elementFilter).map(item => item.virtDirRow)
60✔
3974
        ];
3975
    }
3976

3977
    /**
3978
     * @hidden @internal
3979
     */
3980
    public _setupRowObservers() {
3981
        const elementFilter = (item: IgxRowDirective | IgxSummaryRowComponent) => this.isDefined(item.nativeElement.parentElement);
111✔
3982
        const extractForOfs = pipe(map((collection: any[]) => collection.filter(elementFilter).map(item => item.virtDirRow)));
111✔
3983
        const rowListObserver = extractForOfs(this._dataRowList.changes);
37✔
3984
        const summaryRowObserver = extractForOfs(this._summaryRowList.changes);
37✔
3985
        rowListObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
37✔
3986
            this.resetHorizontalVirtualization();
30✔
3987
        });
3988
        summaryRowObserver.pipe(takeUntil(this.destroy$)).subscribe(() => {
37✔
UNCOV
3989
            this.resetHorizontalVirtualization();
×
3990
        });
3991
        this.resetHorizontalVirtualization();
37✔
3992
    }
3993

3994
    /**
3995
     * @hidden @internal
3996
     */
3997
    public _zoneBegoneListeners() {
3998
        this.zone.runOutsideAngular(() => {
37✔
3999
            this.verticalScrollContainer.getScroll().addEventListener('scroll', this.verticalScrollHandler.bind(this));
37✔
4000
            this.headerContainer?.getScroll().addEventListener('scroll', this.horizontalScrollHandler.bind(this));
37✔
4001
            if (this.hasColumnsToAutosize) {
37!
UNCOV
4002
                this.headerContainer?.dataChanged.pipe(takeUntil(this.destroy$)).subscribe(() => {
×
UNCOV
4003
                    this.cdr.detectChanges();
×
UNCOV
4004
                    this.zone.onStable.pipe(first()).subscribe(() => {
×
UNCOV
4005
                        this.autoSizeColumnsInView();
×
4006
                    });
4007
                });
4008
            }
4009
            // Window resize observer not needed because when you resize the window element the tbody container always resize so
4010
            // it would always notify resizing, thus a change detection and recalculation of sizes will occur
4011
            resizeObservable(this.nativeElement).pipe(first(), takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
37✔
4012
            resizeObservable(this.tbodyContainer.nativeElement).pipe(takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());
37✔
4013
        });
4014
    }
4015

4016
    /**
4017
     * @hidden
4018
     */
4019
    public ngAfterViewInit() {
4020
        this.initPinning();
37✔
4021
        this.calculateGridSizes();
37✔
4022
        this._init = false;
37✔
4023
        this.cdr.reattach();
37✔
4024
        this._setupRowObservers();
37✔
4025
        this._zoneBegoneListeners();
37✔
4026

4027
        const vertScrDC = this.verticalScrollContainer.displayContainer;
37✔
4028
        vertScrDC.addEventListener('scroll', this.preventContainerScroll.bind(this));
37✔
4029

4030
        this._pinnedRowList.changes
37✔
4031
            .pipe(takeUntil(this.destroy$))
4032
            .subscribe((change: QueryList<IgxGridRowComponent>) => {
UNCOV
4033
                this.onPinnedRowsChanged(change);
×
4034
            });
4035

4036
        this.addRowSnackbar?.clicked.pipe(takeUntil(this.destroy$)).subscribe(() => {
37✔
UNCOV
4037
            const rec = this.filteredSortedData[this.lastAddedRowIndex];
×
UNCOV
4038
            this.scrollTo(rec, 0);
×
UNCOV
4039
            this.addRowSnackbar.close();
×
4040
        });
4041

4042
        // Keep the stream open for future subscribers
4043
        this.rendered$.pipe(takeUntil(this.destroy$)).subscribe(() => {
37✔
4044
            if (this.paginator) {
37!
UNCOV
4045
                this.paginator.totalRecords = this.totalRecords ? this.totalRecords : this.paginator.totalRecords;
×
UNCOV
4046
                this.paginator.overlaySettings = { outlet: this.outlet };
×
4047
            }
4048
            if (this.hasColumnsToAutosize) {
37!
UNCOV
4049
                this.autoSizeColumnsInView();
×
4050
            }
4051
            this._rendered = true;
37✔
4052
        });
4053
        Promise.resolve().then(() => this.rendered.next(true));
37✔
4054

4055
    }
4056

4057
    /**
4058
     * @hidden @internal
4059
     */
4060
    public notifyChanges(repaint = false) {
122✔
4061
        this._cdrRequests = true;
4,073✔
4062
        this._cdrRequestRepaint = repaint;
4,073✔
4063
        this.cdr.markForCheck();
4,073✔
4064
    }
4065

4066
    /**
4067
     * @hidden @internal
4068
     */
4069
    public ngDoCheck() {
4070
        if (this._init) {
294✔
4071
            return;
39✔
4072
        }
4073

4074
        if (this._cdrRequestRepaint) {
255✔
4075
            this.resetNotifyChanges();
33✔
4076
            this.calculateGridSizes();
33✔
4077
            this.refreshSearch(true);
33✔
4078
            return;
33✔
4079
        }
4080

4081
        if (this._cdrRequests) {
222✔
4082
            this.resetNotifyChanges();
41✔
4083
            this.cdr.detectChanges();
41✔
4084
        }
4085
    }
4086

4087
    /**
4088
     * @hidden
4089
     * @internal
4090
     */
4091
    public getDragGhostCustomTemplate() {
4092

UNCOV
4093
        return this.dragGhostCustomTemplate;
×
4094
    }
4095

4096
    /**
4097
     * @hidden @internal
4098
     */
4099
    public ngOnDestroy() {
4100
        this.tmpOutlets.forEach((tmplOutlet) => {
19✔
4101
            tmplOutlet.cleanCache();
90✔
4102
        });
4103

4104
        this.destroy$.next(true);
19✔
4105
        this.destroy$.complete();
19✔
4106
        this.transactionChange$.next();
19✔
4107
        this.transactionChange$.complete();
19✔
4108
        this._destroyed = true;
19✔
4109

4110
        this.textHighlightService.destroyGroup(this.id);
19✔
4111

4112
        if (this._advancedFilteringOverlayId) {
19!
4113
            this.overlayService.detach(this._advancedFilteringOverlayId);
×
4114
            delete this._advancedFilteringOverlayId;
×
4115
        }
4116

4117
        this.overlayIDs.forEach(overlayID => {
19✔
UNCOV
4118
            const overlay = this.overlayService.getOverlayById(overlayID);
×
4119

UNCOV
4120
            if (overlay && !overlay.detached) {
×
UNCOV
4121
                this.overlayService.detach(overlayID);
×
4122
            }
4123
        });
4124

4125

4126
        this.zone.runOutsideAngular(() => {
19✔
4127
            this.verticalScrollContainer?.getScroll()?.removeEventListener('scroll', this.verticalScrollHandler);
19✔
4128
            this.headerContainer?.getScroll()?.removeEventListener('scroll', this.horizontalScrollHandler);
19✔
4129
            const vertScrDC = this.verticalScrollContainer?.displayContainer;
19✔
4130
            vertScrDC?.removeEventListener('scroll', this.preventContainerScroll);
19✔
4131
        });
4132
    }
4133

4134
    /**
4135
     * Toggles the specified column's visibility.
4136
     *
4137
     * @example
4138
     * ```typescript
4139
     * this.grid1.toggleColumnVisibility({
4140
     *       column: this.grid1.columns[0],
4141
     *       newValue: true
4142
     * });
4143
     * ```
4144
     */
4145
    public toggleColumnVisibility(args: IColumnVisibilityChangedEventArgs) {
4146
        const col = args.column ? this._columns.find((c) => c === args.column) : undefined;
×
4147

4148
        if (!col) {
×
4149
            return;
×
4150
        }
4151
        col.toggleVisibility(args.newValue);
×
4152
    }
4153

4154
    /* blazorSuppress */
4155
    /**
4156
     * Gets/Sets a list of key-value pairs [row ID, expansion state].
4157
     *
4158
     * @remarks
4159
     * Includes only states that differ from the default one.
4160
     * Supports two-way binding.
4161
     * @example
4162
     * ```html
4163
     * <igx-grid #grid [data]="data" [(expansionStates)]="model.expansionStates">
4164
     * </igx-grid>
4165
     * ```
4166
     */
4167
    @Input()
4168
    public get expansionStates() {
4169
        return this._expansionStates;
541✔
4170
    }
4171

4172
    /* blazorSuppress */
4173
    public set expansionStates(value) {
UNCOV
4174
        this._expansionStates = new Map<any, boolean>(value);
×
UNCOV
4175
        this.expansionStatesChange.emit(this._expansionStates);
×
UNCOV
4176
        this.notifyChanges(true);
×
UNCOV
4177
        if (this.gridAPI.grid) {
×
UNCOV
4178
            this.cdr.detectChanges();
×
4179
        }
4180
    }
4181

4182
    /**
4183
     * Expands all rows.
4184
     *
4185
     * @example
4186
     * ```typescript
4187
     * this.grid.expandAll();
4188
     * ```
4189
     */
4190
    public expandAll() {
UNCOV
4191
        this._defaultExpandState = true;
×
UNCOV
4192
        this.expansionStates = new Map<any, boolean>();
×
4193
    }
4194

4195
    /**
4196
     * Collapses all rows.
4197
     *
4198
     * @example
4199
     * ```typescript
4200
     * this.grid.collapseAll();
4201
     * ```
4202
     */
4203
    public collapseAll() {
UNCOV
4204
        this._defaultExpandState = false;
×
UNCOV
4205
        this.expansionStates = new Map<any, boolean>();
×
4206
    }
4207

4208
    /**
4209
     * Expands the row by its id.
4210
     *
4211
     * @remarks
4212
     * ID is either the primaryKey value or the data record instance.
4213
     * @example
4214
     * ```typescript
4215
     * this.grid.expandRow(rowID);
4216
     * ```
4217
     * @param rowID The row id - primaryKey value or the data record instance.
4218
     */
4219
    public expandRow(rowID: any) {
UNCOV
4220
        this.gridAPI.set_row_expansion_state(rowID, true);
×
4221
    }
4222

4223
    /**
4224
     * Collapses the row by its id.
4225
     *
4226
     * @remarks
4227
     * ID is either the primaryKey value or the data record instance.
4228
     * @example
4229
     * ```typescript
4230
     * this.grid.collapseRow(rowID);
4231
     * ```
4232
     * @param rowID The row id - primaryKey value or the data record instance.
4233
     */
4234
    public collapseRow(rowID: any) {
UNCOV
4235
        this.gridAPI.set_row_expansion_state(rowID, false);
×
4236
    }
4237

4238

4239
    /**
4240
     * Toggles the row by its id.
4241
     *
4242
     * @remarks
4243
     * ID is either the primaryKey value or the data record instance.
4244
     * @example
4245
     * ```typescript
4246
     * this.grid.toggleRow(rowID);
4247
     * ```
4248
     * @param rowID The row id - primaryKey value or the data record instance.
4249
     */
4250
    public toggleRow(rowID: any) {
UNCOV
4251
        const rec = this.gridAPI.get_rec_by_id(rowID);
×
UNCOV
4252
        const state = this.gridAPI.get_row_expansion_state(rec);
×
UNCOV
4253
        this.gridAPI.set_row_expansion_state(rowID, !state);
×
4254
    }
4255

4256
    /**
4257
     * @hidden
4258
     * @internal
4259
     */
4260
    public getDefaultExpandState(_rec: any) {
UNCOV
4261
        return this._defaultExpandState;
×
4262
    }
4263

4264
    /**
4265
     * Gets the native element.
4266
     *
4267
     * @example
4268
     * ```typescript
4269
     * const nativeEl = this.grid.nativeElement.
4270
     * ```
4271
     */
4272
    public get nativeElement() {
4273
        return this.elementRef.nativeElement;
337✔
4274
    }
4275

4276
    /**
4277
     * Gets/Sets the outlet used to attach the grid's overlays to.
4278
     *
4279
     * @remarks
4280
     * If set, returns the outlet defined outside the grid. Otherwise returns the grid's internal outlet directive.
4281
     */
4282
    @Input()
4283
    public get outlet() {
4284
        return this.resolveOutlet();
2,418✔
4285
    }
4286

4287
    public set outlet(val: IgxOverlayOutletDirective) {
4288
        this._userOutletDirective = val;
×
4289
    }
4290

4291

4292
    /**
4293
     * Gets the default row height.
4294
     *
4295
     * @example
4296
     * ```typescript
4297
     * const rowHeigh = this.grid.defaultRowHeight;
4298
     * ```
4299
     */
4300
    public get defaultRowHeight(): number {
4301
        return this._defaultRowHeight;
1,848✔
4302
    }
4303

4304
    /**
4305
     * @hidden @internal
4306
     */
4307
    public get defaultSummaryHeight(): number {
4308
        switch (this.gridSize) {
541!
4309
            case Size.Medium:
UNCOV
4310
                return 30;
×
4311
            case Size.Small:
UNCOV
4312
                return 24;
×
4313
            default:
4314
                return 36;
541✔
4315
        }
4316
    }
4317

4318
    /**
4319
     * Returns the `IgxGridHeaderGroupComponent`'s minimum allowed width.
4320
     *
4321
     * @remarks
4322
     * Used internally for restricting header group component width.
4323
     * The values below depend on the header cell default right/left padding values.
4324
     */
4325
    public get defaultHeaderGroupMinWidth(): number {
4326
        switch (this.gridSize) {
4,672!
4327
            case Size.Medium:
UNCOV
4328
                return 32;
×
4329
            case Size.Small:
UNCOV
4330
                return 24;
×
4331
            default:
4332
                return 48;
4,672✔
4333
        }
4334
    }
4335

4336
    /** @hidden @internal */
4337
    public get pinnedWidth() {
4338
        if (!isNaN(this._pinnedWidth)) {
2,030✔
4339
            return this._pinnedWidth;
1,818✔
4340
        }
4341
        this._pinnedWidth = this.getPinnedWidth();
212✔
4342
        return this._pinnedWidth;
212✔
4343
    }
4344

4345
    /** @hidden @internal */
4346
    public get unpinnedWidth() {
4347
        if (!isNaN(this._unpinnedWidth)) {
2,741✔
4348
            return this._unpinnedWidth;
2,526✔
4349
        }
4350
        this._unpinnedWidth = this.getUnpinnedWidth();
215✔
4351
        return this._unpinnedWidth;
215✔
4352
    }
4353

4354
    /**
4355
     * @hidden @internal
4356
     */
4357
    public isHorizontalScrollHidden = false;
39✔
4358

4359
    /**
4360
     * @hidden @internal
4361
     * Gets the header cell inner width for auto-sizing.
4362
     */
4363
    public getHeaderCellWidth(element: HTMLElement): ISizeInfo {
UNCOV
4364
        const range = this.document.createRange();
×
UNCOV
4365
        const headerWidth = this.platform.getNodeSizeViaRange(range,
×
4366
            element,
4367
            element.parentElement);
4368

UNCOV
4369
        const headerStyle = this.document.defaultView.getComputedStyle(element);
×
UNCOV
4370
        const headerPadding = parseFloat(headerStyle.paddingLeft) + parseFloat(headerStyle.paddingRight) +
×
4371
            parseFloat(headerStyle.borderRightWidth);
4372

4373
        // Take into consideration the header group element, since column pinning applies borders to it if its not a columnGroup.
UNCOV
4374
        const headerGroupStyle = this.document.defaultView.getComputedStyle(element.parentElement);
×
UNCOV
4375
        const borderSize = parseFloat(headerGroupStyle.borderRightWidth) + parseFloat(headerGroupStyle.borderLeftWidth);
×
UNCOV
4376
        return { width: Math.ceil(headerWidth), padding: Math.ceil(headerPadding + borderSize) };
×
4377
    }
4378

4379
    /**
4380
     * @hidden @internal
4381
     * Gets the combined width of the columns that are specific to the enabled grid features. They are fixed.
4382
     */
4383
    public featureColumnsWidth(expander?: ElementRef) {
4384
        if (Number.isNaN(this._headerFeaturesWidth)) {
899✔
4385
            // TODO: platformUtil.isBrowser check
4386
            const rowSelectArea = this.headerSelectorContainer?.nativeElement?.getBoundingClientRect ?
281!
4387
                this.headerSelectorContainer.nativeElement.getBoundingClientRect().width : 0;
4388
            const rowDragArea = this.rowDraggable && this.headerDragContainer?.nativeElement?.getBoundingClientRect ?
281!
4389
                this.headerDragContainer.nativeElement.getBoundingClientRect().width : 0;
4390
            const groupableArea = this.headerGroupContainer?.nativeElement?.getBoundingClientRect ?
281!
4391
                this.headerGroupContainer.nativeElement.getBoundingClientRect().width : 0;
4392
            const expanderWidth = expander?.nativeElement?.getBoundingClientRect ? expander.nativeElement.getBoundingClientRect().width : 0;
281✔
4393
            this._headerFeaturesWidth = rowSelectArea + rowDragArea + groupableArea + expanderWidth;
281✔
4394
        }
4395
        return this._headerFeaturesWidth;
899✔
4396
    }
4397

4398
    /**
4399
     * @hidden @internal
4400
     */
4401
    public get summariesMargin() {
4402
        return this.featureColumnsWidth();
356✔
4403
    }
4404

4405
    /**
4406
     * Gets an array of `IgxColumnComponent`s.
4407
     *
4408
     * @example
4409
     * ```typescript
4410
     * const colums = this.grid.columns.
4411
     * ```
4412
     */
4413
    public get columns(): IgxColumnComponent[] {
4414
        return this._columns || [];
852!
4415
    }
4416

4417
    /**
4418
     * Gets an array of the pinned `IgxColumnComponent`s.
4419
     *
4420
     * @example
4421
     * ```typescript
4422
     * const pinnedColumns = this.grid.pinnedColumns.
4423
     * ```
4424
     */
4425
    public get pinnedColumns(): IgxColumnComponent[] {
4426
        if (this._pinnedVisible.length) {
46,554!
UNCOV
4427
            return this._pinnedVisible;
×
4428
        }
4429
        this._pinnedVisible = this._pinnedColumns.filter(col => !col.hidden);
46,554✔
4430
        return this._pinnedVisible;
46,554✔
4431
    }
4432

4433
    /* csSuppress */
4434
    /**
4435
     * Gets an array of the pinned `IgxRowComponent`s.
4436
     *
4437
     * @example
4438
     * ```typescript
4439
     * const pinnedRow = this.grid.pinnedRows;
4440
     * ```
4441
     */
4442
    public get pinnedRows(): IgxGridRowComponent[] {
UNCOV
4443
        return this._pinnedRowList.toArray().sort((a, b) => a.index - b.index);
×
4444
    }
4445

4446
    /**
4447
     * Gets an array of unpinned `IgxColumnComponent`s.
4448
     *
4449
     * @example
4450
     * ```typescript
4451
     * const unpinnedColumns = this.grid.unpinnedColumns.
4452
     * ```
4453
     */
4454
    public get unpinnedColumns(): IgxColumnComponent[] {
4455
        if (this._unpinnedVisible.length) {
3,444✔
4456
            return this._unpinnedVisible;
3,231✔
4457
        }
4458
        this._unpinnedVisible = this._unpinnedColumns.filter((col) => !col.hidden);
1,420✔
4459
        return this._unpinnedVisible;
213✔
4460
    }
4461

4462
    /**
4463
     * Gets the `width` to be set on `IgxGridHeaderGroupComponent`.
4464
     */
4465
    public getHeaderGroupWidth(column: IgxColumnComponent): string {
4466
        return this.hasColumnLayouts
×
4467
            ? ''
4468
            : `${Math.max(parseFloat(column.calcWidth), this.defaultHeaderGroupMinWidth)}px`;
4469
    }
4470

4471
    /**
4472
     * Returns the `IgxColumnComponent` by field name.
4473
     *
4474
     * @example
4475
     * ```typescript
4476
     * const myCol = this.grid1.getColumnByName("ID");
4477
     * ```
4478
     * @param name
4479
     */
4480
    public getColumnByName(name: string): IgxColumnComponent {
4481
        return this._columns.find((col) => col.field === name);
5,028✔
4482
    }
4483

4484
    public getColumnByVisibleIndex(index: number): IgxColumnComponent {
UNCOV
4485
        return this.visibleColumns.find((col) =>
×
UNCOV
4486
            !col.columnGroup && !col.columnLayout &&
×
4487
            col.visibleIndex === index
4488
        );
4489
    }
4490

4491
    /**
4492
     * Recalculates all widths of columns that have size set to `auto`.
4493
     *
4494
     * @example
4495
     * ```typescript
4496
     * this.grid1.recalculateAutoSizes();
4497
     * ```
4498
     */
4499
    public recalculateAutoSizes() {
4500
        // reset auto-size and calculate it again.
UNCOV
4501
        this._columns.forEach(x => x.autoSize = undefined);
×
UNCOV
4502
        this.resetCaches();
×
UNCOV
4503
        this.zone.onStable.pipe(first()).subscribe(() => {
×
UNCOV
4504
            this.cdr.detectChanges();
×
UNCOV
4505
            this.autoSizeColumnsInView();
×
4506
        });
4507
    }
4508

4509
    /**
4510
     * Returns an array of visible `IgxColumnComponent`s.
4511
     *
4512
     * @example
4513
     * ```typescript
4514
     * const visibleColumns = this.grid.visibleColumns.
4515
     * ```
4516
     */
4517
    public get visibleColumns(): IgxColumnComponent[] {
4518
        if (this._visibleColumns.length) {
314✔
4519
            return this._visibleColumns;
123✔
4520
        }
4521
        this._visibleColumns = this._columns.filter(c => !c.hidden);
1,187✔
4522
        return this._visibleColumns;
191✔
4523
    }
4524

4525
    /**
4526
     * Returns the total number of records.
4527
     *
4528
     * @remarks
4529
     * Only functions when paging is enabled.
4530
     * @example
4531
     * ```typescript
4532
     * const totalRecords = this.grid.totalRecords;
4533
     * ```
4534
     */
4535
    @Input()
4536
    public get totalRecords(): number {
UNCOV
4537
        return this._totalRecords >= 0 ? this._totalRecords : this.pagingState?.metadata.countRecords;
×
4538
    }
4539

4540
    public set totalRecords(total: number) {
UNCOV
4541
        if (total >= 0) {
×
UNCOV
4542
            if (this.paginator) {
×
UNCOV
4543
                this.paginator.totalRecords = total;
×
4544
            }
UNCOV
4545
            this._totalRecords = total;
×
UNCOV
4546
            this.pipeTrigger++;
×
UNCOV
4547
            this.notifyChanges();
×
4548
        }
4549
    }
4550

4551
    /** @hidden @internal */
4552
    public get totalWidth(): number {
4553
        if (!isNaN(this._totalWidth)) {
106✔
4554
            return this._totalWidth;
36✔
4555
        }
4556
        // Take only top level columns
4557
        const cols = this.visibleColumns.filter(col => col.level === 0 && !col.pinned);
467✔
4558
        let totalWidth = 0;
70✔
4559
        let i = 0;
70✔
4560
        for (i; i < cols.length; i++) {
70✔
4561
            totalWidth += parseFloat(cols[i].calcWidth) || 0;
463!
4562
        }
4563
        this._totalWidth = totalWidth;
70✔
4564
        return totalWidth;
70✔
4565
    }
4566

4567
    /**
4568
     * @hidden
4569
     * @internal
4570
     */
4571
    public get showRowSelectors(): boolean {
4572
        return this.isRowSelectable && this.hasVisibleColumns && !this.hideRowSelectors;
1,873!
4573
    }
4574

4575
    /**
4576
     * @hidden
4577
     * @internal
4578
     */
4579
    public get showAddButton() {
4580
        return this.rowEditable && this.dataView.length === 0 && this._columns.length > 0;
4!
4581
    }
4582

4583
    /**
4584
     * @hidden
4585
     * @internal
4586
     */
4587
    public get showDragIcons(): boolean {
4588
        return this.rowDraggable && this._columns.length > this.hiddenColumnsCount;
×
4589
    }
4590

4591
    /**
4592
     * @hidden
4593
     * @internal
4594
     */
4595
    protected _getDataViewIndex(index: number): number {
UNCOV
4596
        let newIndex = index;
×
UNCOV
4597
        if ((index < 0 || index >= this.dataView.length) && this.pagingMode === 1 && this.page !== 0) {
×
4598
            newIndex = index - this.perPage * this.page;
×
UNCOV
4599
        } else if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
×
4600
            newIndex = index - this.gridAPI.grid.virtualizationState.startIndex;
×
4601
        }
UNCOV
4602
        return newIndex;
×
4603
    }
4604

4605
    /**
4606
     * @hidden
4607
     * @internal
4608
     */
4609
    protected getDataIndex(dataViewIndex: number): number {
UNCOV
4610
        let newIndex = dataViewIndex;
×
UNCOV
4611
        if (this.gridAPI.grid.verticalScrollContainer.isRemote) {
×
4612
            newIndex = dataViewIndex + this.gridAPI.grid.virtualizationState.startIndex;
×
4613
        }
UNCOV
4614
        return newIndex;
×
4615
    }
4616

4617
    /**
4618
     * Places a column before or after the specified target column.
4619
     *
4620
     * @example
4621
     * ```typescript
4622
     * grid.moveColumn(column, target);
4623
     * ```
4624
     */
4625
    public moveColumn(column: IgxColumnComponent, target: IgxColumnComponent, pos: DropPosition = DropPosition.AfterDropTarget) {
×
4626
        // M.A. May 11th, 2021 #9508 Make the event cancelable
UNCOV
4627
        const eventArgs: IColumnMovingEndEventArgs = { source: column, target, cancel: false };
×
4628

UNCOV
4629
        this.columnMovingEnd.emit(eventArgs);
×
4630

UNCOV
4631
        if (eventArgs.cancel) {
×
UNCOV
4632
            return;
×
4633
        }
4634

UNCOV
4635
        if (column === target || (column.level !== target.level) ||
×
4636
            (column.topLevelParent !== target.topLevelParent)) {
UNCOV
4637
            return;
×
4638
        }
4639

UNCOV
4640
        if (column.level) {
×
UNCOV
4641
            this._moveChildColumns(column.parent, column, target, pos);
×
4642
        }
4643

4644
        // let columnPinStateChanged;
4645
        // pinning and unpinning will work correctly even without passing index
4646
        // but is easier to calclulate the index here, and later use it in the pinning event args
UNCOV
4647
        if (target.pinned && !column.pinned) {
×
UNCOV
4648
            const pinnedIndex = this._pinnedColumns.indexOf(target);
×
UNCOV
4649
            const index = pos === DropPosition.AfterDropTarget ? pinnedIndex + 1 : pinnedIndex;
×
UNCOV
4650
            column.pin(index);
×
4651
        }
4652

UNCOV
4653
        if (!target.pinned && column.pinned) {
×
UNCOV
4654
            const unpinnedIndex = this._unpinnedColumns.indexOf(target);
×
UNCOV
4655
            const index = pos === DropPosition.AfterDropTarget ? unpinnedIndex + 1 : unpinnedIndex;
×
UNCOV
4656
            column.unpin(index);
×
4657
        }
4658

4659
        // if (target.pinned && column.pinned && !columnPinStateChanged) {
4660
        //     this._reorderColumns(column, target, pos, this._pinnedColumns);
4661
        // }
4662

4663
        // if (!target.pinned && !column.pinned && !columnPinStateChanged) {
4664
        //     this._reorderColumns(column, target, pos, this._unpinnedColumns);
4665
        // }
4666

UNCOV
4667
        this._moveColumns(column, target, pos);
×
UNCOV
4668
        this._columnsReordered(column);
×
4669
    }
4670

4671
    /**
4672
     * Triggers change detection for the `IgxGridComponent`.
4673
     * Calling markForCheck also triggers the grid pipes explicitly, resulting in all updates being processed.
4674
     * May degrade performance if used when not needed, or if misused:
4675
     * ```typescript
4676
     * // DON'Ts:
4677
     * // don't call markForCheck from inside a loop
4678
     * // don't call markForCheck when a primitive has changed
4679
     * grid.data.forEach(rec => {
4680
     *  rec = newValue;
4681
     *  grid.markForCheck();
4682
     * });
4683
     *
4684
     * // DOs
4685
     * // call markForCheck after updating a nested property
4686
     * grid.data.forEach(rec => {
4687
     *  rec.nestedProp1.nestedProp2 = newValue;
4688
     * });
4689
     * grid.markForCheck();
4690
     * ```
4691
     *
4692
     * @example
4693
     * ```typescript
4694
     * grid.markForCheck();
4695
     * ```
4696
     */
4697
    public markForCheck() {
4698
        this.pipeTrigger++;
37✔
4699
        this.cdr.detectChanges();
37✔
4700
    }
4701

4702
    /* csSuppress */
4703
    /**
4704
     * Creates a new `IgxGridRowComponent` and adds the data record to the end of the data source.
4705
     *
4706
     * @example
4707
     * ```typescript
4708
     * this.grid1.addRow(record);
4709
     * ```
4710
     * @param data
4711
     */
4712
    public addRow(data: any): void {
4713
        // commit pending states prior to adding a row
UNCOV
4714
        this.crudService.endEdit(true);
×
UNCOV
4715
        this.gridAPI.addRowToData(data);
×
4716

UNCOV
4717
        this.pipeTrigger++;
×
UNCOV
4718
        this.rowAddedNotifier.next({ data: data, rowData: data, owner: this, primaryKey: data[this.primaryKey], rowKey: data[this.primaryKey] });
×
UNCOV
4719
        this.notifyChanges();
×
4720
    }
4721

4722
    /* blazorCSSuppress */
4723
    /**
4724
     * Removes the `IgxGridRowComponent` and the corresponding data record by primary key.
4725
     *
4726
     * @remarks
4727
     * Requires that the `primaryKey` property is set.
4728
     * The method accept rowSelector as a parameter, which is the rowID.
4729
     * @example
4730
     * ```typescript
4731
     * this.grid1.deleteRow(0);
4732
     * ```
4733
     * @param rowSelector
4734
     */
4735
    public deleteRow(rowSelector: any): any {
UNCOV
4736
        if (this.primaryKey !== undefined && this.primaryKey !== null) {
×
UNCOV
4737
            return this.deleteRowById(rowSelector);
×
4738
        }
4739
    }
4740

4741
    /** @hidden */
4742
    public deleteRowById(rowId: any): any {
UNCOV
4743
        const args: IRowDataCancelableEventArgs = {
×
4744
            rowID: rowId,
4745
            primaryKey: rowId,
4746
            rowKey: rowId,
4747
            rowData: this.getRowData(rowId),
4748
            data: this.getRowData(rowId),
4749
            oldValue: this.getRowData(rowId),
4750
            owner: this,
4751
            isAddRow: false,
4752
            cancel: false
4753
        };
UNCOV
4754
        this.rowDelete.emit(args);
×
UNCOV
4755
        if (args.cancel) {
×
4756
            return;
×
4757
        }
4758

UNCOV
4759
        const record = this.gridAPI.deleteRowById(rowId);
×
UNCOV
4760
        if (record !== null && record !== undefined) {
×
UNCOV
4761
            const rowDeletedEventArgs: IRowDataEventArgs = {
×
4762
                data: record,
4763
                rowData: record,
4764
                owner: this,
4765
                primaryKey: record[this.primaryKey],
4766
                rowKey: record[this.primaryKey]
4767
            };
UNCOV
4768
            this.rowDeleted.emit(rowDeletedEventArgs);
×
4769
        }
UNCOV
4770
        return record;
×
4771
    }
4772

4773
    /* blazorCSSuppress */
4774
    /**
4775
     * Updates the `IgxGridRowComponent` and the corresponding data record by primary key.
4776
     *
4777
     * @remarks
4778
     * Requires that the `primaryKey` property is set.
4779
     * @example
4780
     * ```typescript
4781
     * this.gridWithPK.updateCell('Updated', 1, 'ProductName');
4782
     * ```
4783
     * @param value the new value which is to be set.
4784
     * @param rowSelector corresponds to rowID.
4785
     * @param column corresponds to column field.
4786
     */
4787
    public updateCell(value: any, rowSelector: any, column: string): void {
UNCOV
4788
        if (this.isDefined(this.primaryKey)) {
×
UNCOV
4789
            const col = this._columns.find(c => c.field === column);
×
UNCOV
4790
            if (col) {
×
4791
                // Simplify
UNCOV
4792
                const rowData = this.gridAPI.getRowData(rowSelector);
×
UNCOV
4793
                const index = this.gridAPI.get_row_index_in_data(rowSelector);
×
4794
                // If row passed is invalid
UNCOV
4795
                if (index < 0) {
×
UNCOV
4796
                    return;
×
4797
                }
4798

UNCOV
4799
                const id = {
×
4800
                    rowID: rowSelector,
4801
                    columnID: col.index,
4802
                    rowIndex: index
4803
                };
4804

UNCOV
4805
                const cell = new IgxCell(id, index, col, rowData[col.field], value, rowData, this as any);
×
UNCOV
4806
                const formControl = this.validation.getFormControl(cell.id.rowID, cell.column.field);
×
UNCOV
4807
                formControl.setValue(value);
×
UNCOV
4808
                this.gridAPI.update_cell(cell);
×
UNCOV
4809
                this.cdr.detectChanges();
×
4810
            }
4811
        }
4812
    }
4813

4814
    /* blazorCSSuppress */
4815
    /**
4816
     * Updates the `IgxGridRowComponent`
4817
     *
4818
     * @remarks
4819
     * The row is specified by
4820
     * rowSelector parameter and the data source record with the passed value.
4821
     * This method will apply requested update only if primary key is specified in the grid.
4822
     * @example
4823
     * ```typescript
4824
     * grid.updateRow({
4825
     *       ProductID: 1, ProductName: 'Spearmint', InStock: true, UnitsInStock: 1, OrderDate: new Date('2005-03-21')
4826
     *   }, 1);
4827
     * ```
4828
     * @param value–
4829
     * @param rowSelector correspond to rowID
4830
     */
4831
    // TODO: prevent event invocation
4832
    public updateRow(value: any, rowSelector: any): void {
UNCOV
4833
        if (this.isDefined(this.primaryKey)) {
×
UNCOV
4834
            const editableCell = this.crudService.cell;
×
UNCOV
4835
            if (editableCell && editableCell.id.rowID === rowSelector) {
×
4836
                this.crudService.endCellEdit();
×
4837
            }
UNCOV
4838
            const row = new IgxEditRow(rowSelector, -1, this.gridAPI.getRowData(rowSelector), this as any);
×
UNCOV
4839
            this.gridAPI.update_row(row, value);
×
4840

4841
            // TODO: fix for #5934 and probably break for #5763
4842
            // consider adding of third optional boolean parameter in updateRow.
4843
            // If developer set this parameter to true we should call notifyChanges(true), and
4844
            // vise-versa if developer set it to false we should call notifyChanges(false).
4845
            // The parameter should default to false
UNCOV
4846
            this.notifyChanges();
×
4847
        }
4848
    }
4849

4850
    /**
4851
     * Returns the data that is contained in the row component.
4852
     *
4853
     * @remarks
4854
     * If the primary key is not specified the row selector match the row data.
4855
     * @example
4856
     * ```typescript
4857
     * const data = grid.getRowData(94741);
4858
     * ```
4859
     * @param rowSelector correspond to rowID
4860
     */
4861
    public getRowData(rowSelector: any): any {
UNCOV
4862
        if (!this.primaryKey) {
×
UNCOV
4863
            return rowSelector;
×
4864
        }
UNCOV
4865
        const data = this.gridAPI.get_all_data(this.transactions.enabled);
×
UNCOV
4866
        const index = this.gridAPI.get_row_index_in_data(rowSelector);
×
UNCOV
4867
        return index < 0 ? {} : data[index];
×
4868
    }
4869

4870
    /**
4871
     * Sort a single `IgxColumnComponent`.
4872
     *
4873
     * @remarks
4874
     * Sort the `IgxGridComponent`'s `IgxColumnComponent` based on the provided array of sorting expressions.
4875
     * @example
4876
     * ```typescript
4877
     * this.grid.sort({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false });
4878
     * ```
4879
     */
4880
    public sort(expression: ISortingExpression | Array<ISortingExpression>): void {
UNCOV
4881
        const sortingState = cloneArray(this.sortingExpressions);
×
4882

UNCOV
4883
        if (expression instanceof Array) {
×
UNCOV
4884
            for (const each of expression) {
×
UNCOV
4885
                this.gridAPI.prepare_sorting_expression([sortingState], each);
×
4886
            }
4887
        } else {
UNCOV
4888
            if (this._sortingOptions.mode === 'single') {
×
UNCOV
4889
                this._columns.forEach((col) => {
×
UNCOV
4890
                    if (!(col.field === expression.fieldName)) {
×
UNCOV
4891
                        this.clearSort(col.field);
×
4892
                    }
4893
                });
4894
            }
UNCOV
4895
            this.gridAPI.prepare_sorting_expression([sortingState], expression);
×
4896
        }
4897

UNCOV
4898
        const eventArgs: ISortingEventArgs = { owner: this, sortingExpressions: sortingState, cancel: false };
×
UNCOV
4899
        this.sorting.emit(eventArgs);
×
4900

UNCOV
4901
        if (eventArgs.cancel) {
×
4902
            return;
×
4903
        }
4904

UNCOV
4905
        this.crudService.endEdit(false);
×
UNCOV
4906
        if (expression instanceof Array) {
×
UNCOV
4907
            this.gridAPI.sort_multiple(expression);
×
4908
        } else {
UNCOV
4909
            this.gridAPI.sort(expression);
×
4910
        }
UNCOV
4911
        requestAnimationFrame(() => this.sortingDone.emit(expression));
×
4912
    }
4913

4914
    /**
4915
     * Filters a single `IgxColumnComponent`.
4916
     *
4917
     * @example
4918
     * ```typescript
4919
     * public filter(term) {
4920
     *      this.grid.filter("ProductName", term, IgxStringFilteringOperand.instance().condition("contains"));
4921
     * }
4922
     * ```
4923
     * @param name
4924
     * @param value
4925
     * @param conditionOrExpressionTree
4926
     * @param ignoreCase
4927
     */
4928
    public filter(name: string, value: any, conditionOrExpressionTree?: IFilteringOperation | IFilteringExpressionsTree,
4929
        ignoreCase?: boolean) {
UNCOV
4930
        this.filteringService.filter(name, value, conditionOrExpressionTree, ignoreCase);
×
4931
    }
4932

4933
    /**
4934
     * Filters all the `IgxColumnComponent` in the `IgxGridComponent` with the same condition.
4935
     *
4936
     * @example
4937
     * ```typescript
4938
     * grid.filterGlobal('some', IgxStringFilteringOperand.instance().condition('contains'));
4939
     * ```
4940
     * @param value
4941
     * @param condition
4942
     * @param ignoreCase
4943
     * @deprecated in version 19.0.0.
4944
     */
4945
    public filterGlobal(value: any, condition, ignoreCase?) {
UNCOV
4946
        this.filteringService.filterGlobal(value, condition, ignoreCase);
×
4947
    }
4948

4949
    /**
4950
     * Enables summaries for the specified column and applies your customSummary.
4951
     *
4952
     * @remarks
4953
     * If you do not provide the customSummary, then the default summary for the column data type will be applied.
4954
     * @example
4955
     * ```typescript
4956
     * grid.enableSummaries([{ fieldName: 'ProductName' }, { fieldName: 'ID' }]);
4957
     * ```
4958
     * Enable summaries for the listed columns.
4959
     * @example
4960
     * ```typescript
4961
     * grid.enableSummaries('ProductName');
4962
     * ```
4963
     * @param rest
4964
     */
4965
    public enableSummaries(...rest) {
UNCOV
4966
        if (rest.length === 1 && Array.isArray(rest[0])) {
×
UNCOV
4967
            this._multipleSummaries(rest[0], true);
×
4968
        } else {
UNCOV
4969
            this._summaries(rest[0], true, rest[1]);
×
4970
        }
4971
    }
4972

4973
    /**
4974
     * Disable summaries for the specified column.
4975
     *
4976
     * @example
4977
     * ```typescript
4978
     * grid.disableSummaries('ProductName');
4979
     * ```
4980
     * @remarks
4981
     * Disable summaries for the listed columns.
4982
     * @example
4983
     * ```typescript
4984
     * grid.disableSummaries([{ fieldName: 'ProductName' }]);
4985
     * ```
4986
     */
4987
    public disableSummaries(...rest) {
UNCOV
4988
        if (rest.length === 1 && Array.isArray(rest[0])) {
×
UNCOV
4989
            this._disableMultipleSummaries(rest[0]);
×
4990
        } else {
UNCOV
4991
            this._summaries(rest[0], false);
×
4992
        }
4993
    }
4994

4995
    /**
4996
     * If name is provided, clears the filtering state of the corresponding `IgxColumnComponent`.
4997
     *
4998
     * @remarks
4999
     * Otherwise clears the filtering state of all `IgxColumnComponent`s.
5000
     * @example
5001
     * ```typescript
5002
     * this.grid.clearFilter();
5003
     * ```
5004
     * @param name
5005
     */
5006
    public clearFilter(name?: string) {
UNCOV
5007
        this.filteringService.clearFilter(name);
×
5008
    }
5009

5010
    /**
5011
     * If name is provided, clears the sorting state of the corresponding `IgxColumnComponent`.
5012
     *
5013
     * @remarks
5014
     * otherwise clears the sorting state of all `IgxColumnComponent`.
5015
     * @example
5016
     * ```typescript
5017
     * this.grid.clearSort();
5018
     * ```
5019
     * @param name
5020
     */
5021
    public clearSort(name?: string) {
UNCOV
5022
        if (!name) {
×
UNCOV
5023
            this.sortingExpressions = [];
×
UNCOV
5024
            return;
×
5025
        }
UNCOV
5026
        if (!this.gridAPI.get_column_by_name(name)) {
×
5027
            return;
×
5028
        }
UNCOV
5029
        this.gridAPI.clear_sort(name);
×
5030
    }
5031

5032
    /**
5033
     * @hidden @internal
5034
     */
5035
    public refreshGridState(_args?) {
UNCOV
5036
        this.crudService.endEdit(true);
×
UNCOV
5037
        this.selectionService.clearHeaderCBState();
×
UNCOV
5038
        this.summaryService.clearSummaryCache();
×
UNCOV
5039
        this.summaryPipeTrigger++;
×
UNCOV
5040
        this.cdr.detectChanges();
×
5041
    }
5042

5043
    // TODO: We have return values here. Move them to event args ??
5044

5045
    /**
5046
     * Pins a column by field name.
5047
     *
5048
     * @remarks
5049
     * Returns whether the operation is successful.
5050
     * @example
5051
     * ```typescript
5052
     * this.grid.pinColumn("ID");
5053
     * ```
5054
     * @param columnName
5055
     * @param index
5056
     */
5057
    public pinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
UNCOV
5058
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
×
UNCOV
5059
        return col.pin(index);
×
5060
    }
5061

5062
    /**
5063
     * Unpins a column by field name. Returns whether the operation is successful.
5064
     *
5065
     * @example
5066
     * ```typescript
5067
     * this.grid.pinColumn("ID");
5068
     * ```
5069
     * @param columnName
5070
     * @param index
5071
     */
5072
    public unpinColumn(columnName: string | IgxColumnComponent, index?: number): boolean {
UNCOV
5073
        const col = columnName instanceof IgxColumnComponent ? columnName : this.getColumnByName(columnName);
×
UNCOV
5074
        return col.unpin(index);
×
5075
    }
5076

5077
    /* csSuppress */
5078
    /**
5079
     * Pin the row by its id.
5080
     *
5081
     * @remarks
5082
     * ID is either the primaryKey value or the data record instance.
5083
     * @example
5084
     * ```typescript
5085
     * this.grid.pinRow(rowID);
5086
     * ```
5087
     * @param rowID The row id - primaryKey value or the data record instance.
5088
     * @param index The index at which to insert the row in the pinned collection.
5089
     */
5090
    public pinRow(rowID: any, index?: number, row?: RowType): boolean {
UNCOV
5091
        if (this._pinnedRecordIDs.indexOf(rowID) !== -1) {
×
UNCOV
5092
            return false;
×
5093
        }
UNCOV
5094
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, index, row, true);
×
UNCOV
5095
        this.rowPinning.emit(eventArgs);
×
5096

UNCOV
5097
        if (eventArgs.cancel) {
×
UNCOV
5098
            return;
×
5099
        }
UNCOV
5100
        this.crudService.endEdit(false);
×
5101

UNCOV
5102
        const insertIndex = typeof eventArgs.insertAtIndex === 'number' ? eventArgs.insertAtIndex : this._pinnedRecordIDs.length;
×
UNCOV
5103
        this._pinnedRecordIDs.splice(insertIndex, 0, rowID);
×
UNCOV
5104
        this.pipeTrigger++;
×
UNCOV
5105
        if (this.gridAPI.grid) {
×
UNCOV
5106
            this.cdr.detectChanges();
×
UNCOV
5107
            this.rowPinned.emit(eventArgs);
×
5108
        }
5109

UNCOV
5110
        return true;
×
5111
    }
5112

5113
    /* csSuppress */
5114
    /**
5115
     * Unpin the row by its id.
5116
     *
5117
     * @remarks
5118
     * ID is either the primaryKey value or the data record instance.
5119
     * @example
5120
     * ```typescript
5121
     * this.grid.unpinRow(rowID);
5122
     * ```
5123
     * @param rowID The row id - primaryKey value or the data record instance.
5124
     */
5125
    public unpinRow(rowID: any, row?: RowType): boolean {
UNCOV
5126
        const index = this._pinnedRecordIDs.indexOf(rowID);
×
UNCOV
5127
        if (index === -1) {
×
5128
            return false;
×
5129
        }
5130

UNCOV
5131
        const eventArgs = this.gridAPI.get_pin_row_event_args(rowID, null, row, false);
×
UNCOV
5132
        this.rowPinning.emit(eventArgs);
×
5133

UNCOV
5134
        if (eventArgs.cancel) {
×
UNCOV
5135
            return;
×
5136
        }
5137

UNCOV
5138
        this.crudService.endEdit(false);
×
UNCOV
5139
        this._pinnedRecordIDs.splice(index, 1);
×
UNCOV
5140
        this.pipeTrigger++;
×
UNCOV
5141
        if (this.gridAPI.grid) {
×
UNCOV
5142
            this.cdr.detectChanges();
×
UNCOV
5143
            this.rowPinned.emit(eventArgs);
×
5144
        }
5145

UNCOV
5146
        return true;
×
5147
    }
5148

5149
    /** @hidden @internal */
5150
    public get pinnedRowHeight() {
5151
        const containerHeight = this.pinContainer ? this.pinContainer.nativeElement.offsetHeight : 0;
807!
5152
        return this.hasPinnedRecords ? containerHeight : 0;
807!
5153
    }
5154

5155
    /** @hidden @internal */
5156
    public get totalHeight() {
5157
        return this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
406✔
5158
    }
5159

5160
    /**
5161
     * Recalculates grid width/height dimensions.
5162
     *
5163
     * @remarks
5164
     * Should be run when changing DOM elements dimentions manually that affect the grid's size.
5165
     * @example
5166
     * ```typescript
5167
     * this.grid.reflow();
5168
     * ```
5169
     */
5170
    public reflow() {
UNCOV
5171
        this.calculateGridSizes();
×
5172
    }
5173

5174
    /**
5175
     * Finds the next occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
5176
     *
5177
     * @remarks
5178
     * Returns how many times the grid contains the string.
5179
     * @example
5180
     * ```typescript
5181
     * this.grid.findNext("financial");
5182
     * ```
5183
     * @param text the string to search.
5184
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
5185
     * @param exactMatch optionally, if the text should match the entire value  (defaults to false).
5186
     */
5187
    public findNext(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
UNCOV
5188
        return this.find(text, 1, caseSensitive, exactMatch);
×
5189
    }
5190

5191
    /**
5192
     * Finds the previous occurrence of a given string in the grid and scrolls to the cell if it isn't visible.
5193
     *
5194
     * @remarks
5195
     * Returns how many times the grid contains the string.
5196
     * @example
5197
     * ```typescript
5198
     * this.grid.findPrev("financial");
5199
     * ```
5200
     * @param text the string to search.
5201
     * @param caseSensitive optionally, if the search should be case sensitive (defaults to false).
5202
     * @param exactMatch optionally, if the text should match the entire value (defaults to false).
5203
     */
5204
    public findPrev(text: string, caseSensitive?: boolean, exactMatch?: boolean): number {
UNCOV
5205
        return this.find(text, -1, caseSensitive, exactMatch);
×
5206
    }
5207

5208
    /**
5209
     * Reapplies the existing search.
5210
     *
5211
     * @remarks
5212
     * Returns how many times the grid contains the last search.
5213
     * @example
5214
     * ```typescript
5215
     * this.grid.refreshSearch();
5216
     * ```
5217
     * @param updateActiveInfo
5218
     */
5219
    public refreshSearch(updateActiveInfo?: boolean, endEdit = true): number {
33✔
5220
        if (this._lastSearchInfo.searchText) {
100!
UNCOV
5221
            this.rebuildMatchCache();
×
5222

UNCOV
5223
            if (updateActiveInfo) {
×
UNCOV
5224
                const activeInfo = this.textHighlightService.highlightGroupsMap.get(this.id);
×
UNCOV
5225
                this._lastSearchInfo.matchInfoCache.forEach((match, i) => {
×
UNCOV
5226
                    if (match.column === activeInfo.column &&
×
5227
                        match.row === activeInfo.row &&
5228
                        match.index === activeInfo.index &&
5229
                        compareMaps(match.metadata, activeInfo.metadata)) {
UNCOV
5230
                        this._lastSearchInfo.activeMatchIndex = i;
×
5231
                    }
5232
                });
5233
            }
5234

UNCOV
5235
            return this.find(this._lastSearchInfo.searchText,
×
5236
                0,
5237
                this._lastSearchInfo.caseSensitive,
5238
                this._lastSearchInfo.exactMatch,
5239
                false,
5240
                endEdit);
5241
        } else {
5242
            return 0;
100✔
5243
        }
5244
    }
5245

5246
    /**
5247
     * Removes all the highlights in the cell.
5248
     *
5249
     * @example
5250
     * ```typescript
5251
     * this.grid.clearSearch();
5252
     * ```
5253
     */
5254
    public clearSearch() {
UNCOV
5255
        this._lastSearchInfo = {
×
5256
            searchText: '',
5257
            caseSensitive: false,
5258
            exactMatch: false,
5259
            activeMatchIndex: 0,
5260
            matchInfoCache: [],
5261
            matchCount: 0,
5262
            content: ''
5263
        };
5264

UNCOV
5265
        this.rowList.forEach((row) => {
×
UNCOV
5266
            if (row.cells) {
×
UNCOV
5267
                row.cells.forEach((c: IgxGridCellComponent) => {
×
UNCOV
5268
                    c.clearHighlight();
×
5269
                });
5270
            }
5271
        });
5272
    }
5273

5274
    /** @hidden @internal */
5275
    public get hasEditableColumns(): boolean {
UNCOV
5276
        return this._columns.some((col) => col.editable);
×
5277
    }
5278

5279
    /** @hidden @internal */
5280
    public get hasSummarizedColumns(): boolean {
5281
        const summarizedColumns = this._columns.filter(col => col.hasSummary && !col.hidden);
45,001✔
5282
        return summarizedColumns.length > 0;
6,523✔
5283
    }
5284

5285
    /**
5286
     * @hidden @internal
5287
     */
5288
    public get rootSummariesEnabled(): boolean {
5289
        return this.summaryCalculationMode !== GridSummaryCalculationMode.childLevelsOnly;
6,577✔
5290
    }
5291

5292
    /**
5293
     * @hidden @internal
5294
     */
5295
    public get hasVisibleColumns(): boolean {
5296
        if (this._hasVisibleColumns === undefined) {
419✔
5297
            return this._columns ? this._columns.some(c => !c.hidden) : false;
419!
5298
        }
5299
        return this._hasVisibleColumns;
×
5300
    }
5301

5302
    public set hasVisibleColumns(value) {
5303
        this._hasVisibleColumns = value;
289✔
5304
    }
5305

5306
    /** @hidden @internal */
5307
    public get hasMovableColumns(): boolean {
5308
        return this.moving;
×
5309
    }
5310

5311
    /** @hidden @internal */
5312
    public get hasColumnGroups(): boolean {
UNCOV
5313
        return this._columnGroups;
×
5314
    }
5315

5316
    /** @hidden @internal */
5317
    public get hasColumnLayouts() {
5318
        return !!this._columns.some(col => col.columnLayout);
133,824✔
5319
    }
5320

5321

5322
    /**
5323
     * @hidden @internal
5324
     */
5325
    public get multiRowLayoutRowSize() {
UNCOV
5326
        return this._multiRowLayoutRowSize;
×
5327
    }
5328

5329
    /**
5330
     * @hidden
5331
     */
5332
    protected get rowBasedHeight() {
5333
        return this.dataLength * this.rowHeight;
×
5334
    }
5335

5336
    /**
5337
     * @hidden
5338
     */
5339
    protected get isPercentWidth() {
5340
        return this.width && this.width.indexOf('%') !== -1;
393✔
5341
    }
5342

5343
    protected get shouldResize(): boolean {
UNCOV
5344
        return this._gridSize !== this.gridSize;
×
5345
    }
5346

5347
    /**
5348
     * @hidden @internal
5349
     */
5350
    public get isPercentHeight() {
5351
        return this._height && this._height.indexOf('%') !== -1;
170✔
5352
    }
5353

5354
    /**
5355
     * @hidden
5356
     */
5357
    protected get defaultTargetBodyHeight(): number {
UNCOV
5358
        const allItems = this.dataLength;
×
UNCOV
5359
        return this.renderedActualRowHeight * Math.min(this._defaultTargetRecordNumber,
×
5360
            this.paginator ? Math.min(allItems, this.paginator.perPage) : allItems);
×
5361
    }
5362

5363
    /**
5364
     * @hidden @internal
5365
     * The rowHeight input is bound to min-height css prop of rows that adds a 1px border in all cases
5366
     */
5367
    public get renderedRowHeight(): number {
5368
        return this.rowHeight + 1;
406✔
5369
    }
5370

5371
    /**
5372
     * @hidden @internal
5373
     */
5374
    public get outerWidth() {
UNCOV
5375
        return this.hasVerticalScroll() ? this.calcWidth + this.scrollSize : this.calcWidth;
×
5376
    }
5377

5378
    /**
5379
     * @hidden @internal
5380
     * Gets the size of the grid
5381
     */
5382
    public get gridSize(): Size {
5383
        return this.gridComputedStyles?.getPropertyValue('--component-size') || Size.Large;
8,153✔
5384
    }
5385

5386
    /**
5387
     * @hidden @internal
5388
     * Gets the visible content height that includes header + tbody + footer.
5389
     */
5390
    public getVisibleContentHeight() {
UNCOV
5391
        let height = this.theadRow.nativeElement.clientHeight + this.tbody.nativeElement.clientHeight;
×
UNCOV
5392
        if (this.hasSummarizedColumns) {
×
UNCOV
5393
            height += this.tfoot.nativeElement.clientHeight;
×
5394
        }
UNCOV
5395
        return height;
×
5396
    }
5397

5398
    /**
5399
     * @hidden @internal
5400
     */
5401
    public getPossibleColumnWidth(baseWidth: number = null) {
122✔
5402
        let computedWidth;
5403
        if (baseWidth !== null) {
122!
5404
            computedWidth = baseWidth;
×
5405
        } else {
5406
            computedWidth = this.calcWidth ||
122!
5407
                parseFloat(this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width'));
5408
        }
5409

5410
        const visibleChildColumns = this.visibleColumns.filter(c => !c.columnGroup);
753✔
5411

5412

5413
        // Column layouts related
5414
        let visibleCols = [];
122✔
5415
        const columnBlocks = this.visibleColumns.filter(c => c.columnGroup);
753✔
5416
        const colsPerBlock = columnBlocks.map(block => block.getInitialChildColumnSizes(block.children));
122✔
5417
        const combinedBlocksSize = colsPerBlock.reduce((acc, item) => acc + item.length, 0);
122✔
5418
        colsPerBlock.forEach(blockCols => visibleCols = visibleCols.concat(blockCols));
122✔
5419
        //
5420

5421
        const columnsWithSetWidths = this.hasColumnLayouts ?
122!
UNCOV
5422
            visibleCols.filter(c => c.widthSetByUser) :
×
5423
            visibleChildColumns.filter(c => c.widthSetByUser && c.width !== 'fit-content');
750✔
5424

5425
        const columnsToSize = this.hasColumnLayouts ?
122!
5426
            combinedBlocksSize - columnsWithSetWidths.length :
5427
            visibleChildColumns.length - columnsWithSetWidths.length;
5428
        const sumExistingWidths = columnsWithSetWidths
122✔
5429
            .reduce((prev, curr) => {
5430
                const colWidth = curr.width;
720✔
5431
                let widthValue = parseFloat(colWidth);
720✔
5432
                if (isNaN(widthValue)) {
720!
5433
                    widthValue = MINIMUM_COLUMN_WIDTH;
×
5434
                }
5435
                const currWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1 ?
720!
5436
                    widthValue / 100 * computedWidth :
5437
                    widthValue;
5438
                return prev + currWidth;
720✔
5439
            }, 0);
5440

5441
        // When all columns are hidden, return 0px width
5442
        if (!sumExistingWidths && !columnsToSize) {
122✔
5443
            return '0px';
6✔
5444
        }
5445
        computedWidth -= this.featureColumnsWidth();
116✔
5446

5447
        const columnWidth = !Number.isFinite(sumExistingWidths) ?
116!
5448
            Math.max(computedWidth / columnsToSize, this.minColumnWidth) :
5449
            Math.max((computedWidth - sumExistingWidths) / columnsToSize, this.minColumnWidth);
5450

5451
        return columnWidth + 'px';
116✔
5452
    }
5453

5454
    /**
5455
     * @hidden @internal
5456
     */
5457
    public hasVerticalScroll() {
5458
        if (this._init) {
1,663✔
5459
            return false;
919✔
5460
        }
5461
        const isScrollable = this.verticalScrollContainer ? this.verticalScrollContainer.isScrollable() : false;
744!
5462
        return !!(this.calcWidth && this.dataView && this.dataView.length > 0 && isScrollable);
744✔
5463
    }
5464

5465
    /**
5466
     * Gets calculated width of the pinned area.
5467
     *
5468
     * @example
5469
     * ```typescript
5470
     * const pinnedWidth = this.grid.getPinnedWidth();
5471
     * ```
5472
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
5473
     */
5474
    public getPinnedWidth(takeHidden = false) {
212✔
5475
        const fc = takeHidden ? this._pinnedColumns : this.pinnedColumns;
427!
5476
        let sum = 0;
427✔
5477
        for (const col of fc) {
427✔
UNCOV
5478
            if (col.level === 0) {
×
UNCOV
5479
                sum += parseInt(col.calcWidth, 10);
×
5480
            }
5481
        }
5482
        if (this.isPinningToStart) {
427✔
5483
            sum += this.featureColumnsWidth();
427✔
5484
        }
5485

5486
        return sum;
427✔
5487
    }
5488

5489
    /**
5490
     * @hidden @internal
5491
     */
5492
    public isColumnGrouped(_fieldName: string): boolean {
5493
        return false;
×
5494
    }
5495

5496
    /**
5497
     * @hidden @internal
5498
     * TODO: REMOVE
5499
     */
5500
    public onHeaderSelectorClick(event) {
UNCOV
5501
        if (!this.isMultiRowSelectionEnabled) {
×
5502
            return;
×
5503
        }
UNCOV
5504
        if (this.selectionService.areAllRowSelected()) {
×
UNCOV
5505
            this.selectionService.clearRowSelection(event);
×
5506
        } else {
UNCOV
5507
            this.selectionService.selectAllRows(event);
×
5508
        }
5509
    }
5510

5511
    /**
5512
     * @hidden @internal
5513
     */
5514
    public get headSelectorBaseAriaLabel() {
UNCOV
5515
        if (this._filteringExpressionsTree.filteringOperands.length > 0) {
×
UNCOV
5516
            return this.selectionService.areAllRowSelected() ? 'Deselect all filtered' : 'Select all filtered';
×
5517
        }
5518

UNCOV
5519
        return this.selectionService.areAllRowSelected() ? 'Deselect all' : 'Select all';
×
5520
    }
5521

5522
    /**
5523
     * @hidden
5524
     * @internal
5525
     */
5526
    public get totalRowsCountAfterFilter() {
UNCOV
5527
        if (this.data) {
×
UNCOV
5528
            return this.selectionService.allData.length;
×
5529
        }
5530

5531
        return 0;
×
5532
    }
5533

5534
    /** @hidden @internal */
5535
    public get pinnedDataView(): any[] {
5536
        return this.pinnedRecords ? this.pinnedRecords : [];
1,576✔
5537
    }
5538

5539
    /** @hidden @internal */
5540
    public get unpinnedDataView(): any[] {
5541
        return this.unpinnedRecords ? this.unpinnedRecords : this.verticalScrollContainer?.igxForOf || [];
134✔
5542
    }
5543

5544
    /**
5545
     * Returns the currently transformed paged/filtered/sorted/grouped/pinned/unpinned row data, displayed in the grid.
5546
     *
5547
     * @example
5548
     * ```typescript
5549
     *      const dataView = this.grid.dataView;
5550
     * ```
5551
     */
5552
    public get dataView() {
5553
        return this._dataView;
2,250✔
5554
    }
5555

5556
    /**
5557
     * Gets/Sets whether clicking over a row should select/deselect it
5558
     *
5559
     * @remarks
5560
     * By default it is set to true
5561
     * @param enabled: boolean
5562
     */
5563
    @WatchChanges()
5564
    @Input({ transform: booleanAttribute })
5565
    public get selectRowOnClick() {
UNCOV
5566
        return this._selectRowOnClick;
×
5567
    }
5568

5569
    public set selectRowOnClick(enabled: boolean) {
UNCOV
5570
        this._selectRowOnClick = enabled;
×
5571
    }
5572

5573
    /**
5574
     * Select specified rows by ID.
5575
     *
5576
     * @example
5577
     * ```typescript
5578
     * this.grid.selectRows([1,2,5], true);
5579
     * ```
5580
     * @param rowIDs
5581
     * @param clearCurrentSelection if true clears the current selection
5582
     */
5583
    public selectRows(rowIDs: any[], clearCurrentSelection?: boolean) {
UNCOV
5584
        this.selectionService.selectRowsWithNoEvent(rowIDs, clearCurrentSelection);
×
UNCOV
5585
        this.notifyChanges();
×
5586
    }
5587

5588
    /**
5589
     * Deselect specified rows by ID.
5590
     *
5591
     * @example
5592
     * ```typescript
5593
     * this.grid.deselectRows([1,2,5]);
5594
     * ```
5595
     * @param rowIDs
5596
     */
5597
    public deselectRows(rowIDs: any[]) {
UNCOV
5598
        this.selectionService.deselectRowsWithNoEvent(rowIDs);
×
UNCOV
5599
        this.notifyChanges();
×
5600
    }
5601

5602
    /**
5603
     * Selects all rows
5604
     *
5605
     * @remarks
5606
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
5607
     * If you set the parameter onlyFilterData to false that will select all rows in the grid exept deleted rows.
5608
     * @example
5609
     * ```typescript
5610
     * this.grid.selectAllRows();
5611
     * this.grid.selectAllRows(false);
5612
     * ```
5613
     * @param onlyFilterData
5614
     */
5615
    public selectAllRows(onlyFilterData = true) {
×
UNCOV
5616
        const data = onlyFilterData && this.filteredData ? this.filteredData : this.gridAPI.get_all_data(true);
×
UNCOV
5617
        const rowIDs = this.selectionService.getRowIDs(data).filter(rID => !this.gridAPI.row_deleted_transaction(rID));
×
UNCOV
5618
        this.selectRows(rowIDs);
×
5619
    }
5620

5621
    /**
5622
     * Deselects all rows
5623
     *
5624
     * @remarks
5625
     * By default if filtering is in place, selectAllRows() and deselectAllRows() select/deselect all filtered rows.
5626
     * If you set the parameter onlyFilterData to false that will deselect all rows in the grid exept deleted rows.
5627
     * @example
5628
     * ```typescript
5629
     * this.grid.deselectAllRows();
5630
     * ```
5631
     * @param onlyFilterData
5632
     */
5633
    public deselectAllRows(onlyFilterData = true) {
×
UNCOV
5634
        if (onlyFilterData && this.filteredData && this.filteredData.length > 0) {
×
UNCOV
5635
            this.deselectRows(this.selectionService.getRowIDs(this.filteredData));
×
5636
        } else {
UNCOV
5637
            this.selectionService.clearAllSelectedRows();
×
UNCOV
5638
            this.notifyChanges();
×
5639
        }
5640
    }
5641

5642
    /**
5643
     * Deselect selected cells.
5644
     * @example
5645
     * ```typescript
5646
     * this.grid.clearCellSelection();
5647
     * ```
5648
     */
5649
    public clearCellSelection(): void {
UNCOV
5650
        this.selectionService.clear(true);
×
UNCOV
5651
        this.notifyChanges();
×
5652
    }
5653

5654
    /**
5655
     * @hidden @internal
5656
     */
5657
    public dragScroll(delta: { left: number; top: number }): void {
UNCOV
5658
        const horizontal = this.headerContainer.getScroll();
×
UNCOV
5659
        const vertical = this.verticalScrollContainer.getScroll();
×
UNCOV
5660
        const { left, top } = delta;
×
5661

UNCOV
5662
        horizontal.scrollLeft += left * this.DRAG_SCROLL_DELTA;
×
UNCOV
5663
        vertical.scrollTop += top * this.DRAG_SCROLL_DELTA;
×
5664
    }
5665

5666
    /**
5667
     * @hidden @internal
5668
     */
5669
    public isDefined(arg: any): boolean {
5670
        return arg !== undefined && arg !== null;
503✔
5671
    }
5672

5673
    /**
5674
     * Select range(s) of cells between certain rows and columns of the grid.
5675
     */
5676
    public selectRange(arg: GridSelectionRange | GridSelectionRange[] | null | undefined): void {
UNCOV
5677
        if (!this.isDefined(arg)) {
×
UNCOV
5678
            this.clearCellSelection();
×
UNCOV
5679
            return;
×
5680
        }
UNCOV
5681
        if (arg instanceof Array) {
×
UNCOV
5682
            arg.forEach(range => this.setSelection(range));
×
5683
        } else {
UNCOV
5684
            this.setSelection(arg);
×
5685
        }
UNCOV
5686
        this.notifyChanges();
×
5687
    }
5688

5689
    /**
5690
     * @hidden @internal
5691
     */
5692
    public columnToVisibleIndex(field: string | number): number {
UNCOV
5693
        const visibleColumns = this.visibleColumns;
×
UNCOV
5694
        if (typeof field === 'number') {
×
UNCOV
5695
            return field;
×
5696
        }
UNCOV
5697
        return visibleColumns.find(column => column.field === field).visibleIndex;
×
5698
    }
5699

5700
    /**
5701
     * @hidden @internal
5702
     */
5703
    public setSelection(range: GridSelectionRange): void {
UNCOV
5704
        const startNode = { row: range.rowStart, column: this.columnToVisibleIndex(range.columnStart) };
×
UNCOV
5705
        const endNode = { row: range.rowEnd, column: this.columnToVisibleIndex(range.columnEnd) };
×
5706

UNCOV
5707
        this.selectionService.pointerState.node = startNode;
×
UNCOV
5708
        this.selectionService.selectRange(endNode, this.selectionService.pointerState);
×
UNCOV
5709
        this.selectionService.addRangeMeta(endNode, this.selectionService.pointerState);
×
UNCOV
5710
        this.selectionService.initPointerState();
×
5711
    }
5712

5713
    /**
5714
     * Get the currently selected ranges in the grid.
5715
     */
5716
    public getSelectedRanges(): GridSelectionRange[] {
UNCOV
5717
        return this.selectionService.ranges;
×
5718
    }
5719

5720
    /**
5721
     *
5722
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
5723
     *
5724
     * @remarks
5725
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5726
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5727
     */
5728
    public getSelectedData(formatters = false, headers = false) {
×
UNCOV
5729
        const source = this.filteredSortedData;
×
UNCOV
5730
        return this.extractDataFromSelection(source, formatters, headers);
×
5731
    }
5732

5733
    /**
5734
     * Get current selected columns.
5735
     *
5736
     * @example
5737
     * Returns an array with selected columns
5738
     * ```typescript
5739
     * const selectedColumns = this.grid.selectedColumns();
5740
     * ```
5741
     */
5742
    public selectedColumns(): ColumnType[] {
UNCOV
5743
        const fields = this.selectionService.getSelectedColumns();
×
UNCOV
5744
        return fields.map(field => this.getColumnByName(field)).filter(field => field);
×
5745
    }
5746

5747
    /**
5748
     * Select specified columns.
5749
     *
5750
     * @example
5751
     * ```typescript
5752
     * this.grid.selectColumns(['ID','Name'], true);
5753
     * ```
5754
     * @param columns
5755
     * @param clearCurrentSelection if true clears the current selection
5756
     */
5757
    public selectColumns(columns: string[] | ColumnType[], clearCurrentSelection?: boolean) {
UNCOV
5758
        let fieldToSelect: string[] = [];
×
UNCOV
5759
        if (columns.length === 0 || typeof columns[0] === 'string') {
×
UNCOV
5760
            fieldToSelect = columns as string[];
×
5761
        } else {
UNCOV
5762
            (columns as ColumnType[]).forEach(col => {
×
UNCOV
5763
                if (col.columnGroup) {
×
5764
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
×
5765
                    fieldToSelect = [...fieldToSelect, ...children];
×
5766
                } else {
UNCOV
5767
                    fieldToSelect.push(col.field);
×
5768
                }
5769
            });
5770
        }
5771

UNCOV
5772
        this.selectionService.selectColumnsWithNoEvent(fieldToSelect, clearCurrentSelection);
×
UNCOV
5773
        this.notifyChanges();
×
5774
    }
5775

5776
    /**
5777
     * Deselect specified columns by field.
5778
     *
5779
     * @example
5780
     * ```typescript
5781
     * this.grid.deselectColumns(['ID','Name']);
5782
     * ```
5783
     * @param columns
5784
     */
5785
    public deselectColumns(columns: string[] | ColumnType[]) {
UNCOV
5786
        let fieldToDeselect: string[] = [];
×
UNCOV
5787
        if (columns.length === 0 || typeof columns[0] === 'string') {
×
UNCOV
5788
            fieldToDeselect = columns as string[];
×
5789
        } else {
UNCOV
5790
            (columns as ColumnType[]).forEach(col => {
×
UNCOV
5791
                if (col.columnGroup) {
×
5792
                    const children = col.allChildren.filter(c => !c.columnGroup).map(c => c.field);
×
5793
                    fieldToDeselect = [...fieldToDeselect, ...children];
×
5794
                } else {
UNCOV
5795
                    fieldToDeselect.push(col.field);
×
5796
                }
5797
            });
5798
        }
UNCOV
5799
        this.selectionService.deselectColumnsWithNoEvent(fieldToDeselect);
×
UNCOV
5800
        this.notifyChanges();
×
5801
    }
5802

5803
    /**
5804
     * Deselects all columns
5805
     *
5806
     * @example
5807
     * ```typescript
5808
     * this.grid.deselectAllColumns();
5809
     * ```
5810
     */
5811
    public deselectAllColumns() {
UNCOV
5812
        this.selectionService.clearAllSelectedColumns();
×
UNCOV
5813
        this.notifyChanges();
×
5814
    }
5815

5816
    /**
5817
     * Selects all columns
5818
     *
5819
     * @example
5820
     * ```typescript
5821
     * this.grid.deselectAllColumns();
5822
     * ```
5823
     */
5824
    public selectAllColumns() {
UNCOV
5825
        this.selectColumns(this._columns.filter(c => !c.columnGroup));
×
5826
    }
5827

5828
    /**
5829
     *
5830
     * Returns an array of the current columns selection in the form of `[{ column.field: cell.value }, ...]`.
5831
     *
5832
     * @remarks
5833
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
5834
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
5835
     */
5836
    public getSelectedColumnsData(formatters = false, headers = false) {
×
UNCOV
5837
        const source = this.filteredSortedData ? this.filteredSortedData : this.data;
×
UNCOV
5838
        return this.extractDataFromColumnsSelection(source, formatters, headers);
×
5839
    }
5840

5841

5842
    /** @hidden @internal **/
5843
    public combineSelectedCellAndColumnData(columnData: any[], formatters = false, headers = false) {
×
5844
        const source = this.filteredSortedData;
×
5845
        return this.extractDataFromSelection(source, formatters, headers, columnData);
×
5846
    }
5847

5848
    /**
5849
     * @hidden @internal
5850
     */
5851
    public preventContainerScroll = (evt) => {
39✔
UNCOV
5852
        if (evt.target.scrollTop !== 0) {
×
5853
            this.verticalScrollContainer.addScroll(evt.target.scrollTop);
×
5854
            evt.target.scrollTop = 0;
×
5855
        }
UNCOV
5856
        if (evt.target.scrollLeft !== 0) {
×
5857
            this.headerContainer.scrollPosition += evt.target.scrollLeft;
×
5858
            evt.target.scrollLeft = 0;
×
5859
        }
5860
    };
5861

5862
    /**
5863
     * @hidden
5864
     * @internal
5865
     */
5866
    public copyHandler(event) {
UNCOV
5867
        const eventPathElements = event.composedPath().map(el => el.tagName?.toLowerCase());
×
UNCOV
5868
        if (eventPathElements.includes('igx-grid-filtering-row') ||
×
5869
            eventPathElements.includes('igx-grid-filtering-cell')) {
UNCOV
5870
            return;
×
5871
        }
5872

UNCOV
5873
        const selectedColumns = this.gridAPI.grid.selectedColumns();
×
UNCOV
5874
        const columnData = this.getSelectedColumnsData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
×
5875
        let selectedData;
UNCOV
5876
        if (event.type === 'copy') {
×
UNCOV
5877
            selectedData = this.getSelectedData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
×
5878
        }
5879

UNCOV
5880
        let data = [];
×
5881
        let result;
5882

UNCOV
5883
        if (event.code === 'KeyC' && (event.ctrlKey || event.metaKey) && event.currentTarget.className === 'igx-grid-thead__wrapper') {
×
5884
            if (selectedData.length) {
×
5885
                if (columnData.length === 0) {
×
5886
                    result = this.prepareCopyData(event, selectedData);
×
5887
                } else {
5888
                    data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
×
5889
                        this.clipboardOptions.copyHeaders);
5890
                    result = this.prepareCopyData(event, data[0], data[1]);
×
5891
                }
5892
            } else {
5893
                data = columnData;
×
5894
                result = this.prepareCopyData(event, data);
×
5895
            }
5896

5897
            navigator.clipboard.writeText(result).then().catch(e => console.error(e));
×
UNCOV
5898
        } else if (!this.clipboardOptions.enabled || this.crudService.cellInEditMode || event.type === 'keydown') {
×
UNCOV
5899
            return;
×
5900
        } else {
UNCOV
5901
            if (selectedColumns.length) {
×
5902
                data = this.combineSelectedCellAndColumnData(columnData, this.clipboardOptions.copyFormatters,
×
5903
                    this.clipboardOptions.copyHeaders);
5904
                result = this.prepareCopyData(event, data[0], data[1]);
×
5905
            } else {
UNCOV
5906
                data = selectedData;
×
UNCOV
5907
                result = this.prepareCopyData(event, data);
×
5908
            }
UNCOV
5909
            event.clipboardData.setData('text/plain', result);
×
5910
        }
5911
    }
5912

5913
    /**
5914
     * @hidden @internal
5915
     */
5916
    public prepareCopyData(event, data, keys?) {
UNCOV
5917
        const ev = { data, cancel: false } as IGridClipboardEvent;
×
UNCOV
5918
        this.gridCopy.emit(ev);
×
5919

UNCOV
5920
        if (ev.cancel) {
×
UNCOV
5921
            return;
×
5922
        }
5923

UNCOV
5924
        const transformer = new CharSeparatedValueData(ev.data, this.clipboardOptions.separator);
×
UNCOV
5925
        let result = keys ? transformer.prepareData(keys) : transformer.prepareData();
×
5926

UNCOV
5927
        if (!this.clipboardOptions.copyHeaders) {
×
UNCOV
5928
            result = result.substring(result.indexOf('\n') + 1);
×
5929
        }
5930

UNCOV
5931
        if (data && data.length > 0 && Object.values(data[0]).length === 1) {
×
UNCOV
5932
            result = result.slice(0, -2);
×
5933
        }
5934

UNCOV
5935
        event.preventDefault();
×
5936

5937
        /* Necessary for the hiearachical case but will probably have to
5938
           change how getSelectedData is propagated in the hiearachical grid
5939
        */
UNCOV
5940
        event.stopPropagation();
×
5941

UNCOV
5942
        return result;
×
5943
    }
5944

5945
    /**
5946
     * @hidden @internal
5947
     */
5948
    public showSnackbarFor(index: number) {
UNCOV
5949
        this.addRowSnackbar.actionText = index === -1 ? '' : this.resourceStrings.igx_grid_snackbar_addrow_actiontext;
×
UNCOV
5950
        this.lastAddedRowIndex = index;
×
UNCOV
5951
        this.addRowSnackbar.open();
×
5952
    }
5953

5954
    /* blazorCsSuppress */
5955
    /**
5956
     * Navigates to a position in the grid based on provided `rowindex` and `visibleColumnIndex`.
5957
     *
5958
     * @remarks
5959
     * Also can execute a custom logic over the target element,
5960
     * through a callback function that accepts { targetType: GridKeydownTargetType, target: Object }
5961
     * @example
5962
     * ```typescript
5963
     *  this.grid.navigateTo(10, 3, (args) => { args.target.nativeElement.focus(); });
5964
     * ```
5965
     */
5966
    public navigateTo(rowIndex: number, visibleColIndex = -1, cb: (args: any) => void = null) {
×
UNCOV
5967
        const totalItems = (this as any).totalItemCount ?? this.dataView.length - 1;
×
UNCOV
5968
        if (rowIndex < 0 || rowIndex > totalItems || (visibleColIndex !== -1
×
UNCOV
5969
            && this._columns.map(col => col.visibleIndex).indexOf(visibleColIndex) === -1)) {
×
5970
            return;
×
5971
        }
UNCOV
5972
        if (this.dataView.slice(rowIndex, rowIndex + 1).find(rec => rec.expression || rec.childGridsData)) {
×
UNCOV
5973
            visibleColIndex = -1;
×
5974
        }
5975
        // If the target row is pinned no need to scroll as well.
UNCOV
5976
        const shouldScrollVertically = this.navigation.shouldPerformVerticalScroll(rowIndex, visibleColIndex);
×
UNCOV
5977
        const shouldScrollHorizontally = this.navigation.shouldPerformHorizontalScroll(visibleColIndex, rowIndex);
×
UNCOV
5978
        if (shouldScrollVertically) {
×
UNCOV
5979
            this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () => {
×
UNCOV
5980
                if (shouldScrollHorizontally) {
×
UNCOV
5981
                    this.navigation.performHorizontalScrollToCell(visibleColIndex, () =>
×
UNCOV
5982
                        this.executeCallback(rowIndex, visibleColIndex, cb));
×
5983
                } else {
UNCOV
5984
                    this.executeCallback(rowIndex, visibleColIndex, cb);
×
5985
                }
5986
            });
UNCOV
5987
        } else if (shouldScrollHorizontally) {
×
UNCOV
5988
            this.navigation.performHorizontalScrollToCell(visibleColIndex, () => {
×
UNCOV
5989
                if (shouldScrollVertically) {
×
5990
                    this.navigation.performVerticalScrollToCell(rowIndex, visibleColIndex, () =>
×
5991
                        this.executeCallback(rowIndex, visibleColIndex, cb));
×
5992
                } else {
UNCOV
5993
                    this.executeCallback(rowIndex, visibleColIndex, cb);
×
5994
                }
5995
            });
5996
        } else {
UNCOV
5997
            this.executeCallback(rowIndex, visibleColIndex, cb);
×
5998
        }
5999
    }
6000

6001
    /* blazorCsSuppress */
6002
    /**
6003
     * Returns `ICellPosition` which defines the next cell,
6004
     * according to the current position, that match specific criteria.
6005
     *
6006
     * @remarks
6007
     * You can pass callback function as a third parameter of `getPreviousCell` method.
6008
     * The callback function accepts IgxColumnComponent as a param
6009
     * @example
6010
     * ```typescript
6011
     *  const nextEditableCellPosition = this.grid.getNextCell(0, 3, (column) => column.editable);
6012
     * ```
6013
     */
6014
    public getNextCell(currRowIndex: number, curVisibleColIndex: number,
6015
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
×
UNCOV
6016
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
×
UNCOV
6017
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
×
UNCOV
6018
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
×
UNCOV
6019
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
×
6020
        }
UNCOV
6021
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => a - b) :
×
6022
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => a - b);
×
UNCOV
6023
        const nextCellIndex = colIndexes.find(index => index > curVisibleColIndex);
×
UNCOV
6024
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
×
UNCOV
6025
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && nextCellIndex !== undefined) {
×
UNCOV
6026
            return { rowIndex: currRowIndex, visibleColumnIndex: nextCellIndex };
×
6027
        } else {
UNCOV
6028
            const nextIndex = this.getNextDataRowIndex(currRowIndex)
×
UNCOV
6029
            if (colIndexes.length === 0 || nextIndex === currRowIndex) {
×
6030
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
×
6031
            } else {
UNCOV
6032
                return { rowIndex: nextIndex, visibleColumnIndex: colIndexes[0] };
×
6033
            }
6034
        }
6035
    }
6036

6037
    /* blazorCsSuppress */
6038
    /**
6039
     * Returns `ICellPosition` which defines the previous cell,
6040
     * according to the current position, that match specific criteria.
6041
     *
6042
     * @remarks
6043
     * You can pass callback function as a third parameter of `getPreviousCell` method.
6044
     * The callback function accepts IgxColumnComponent as a param
6045
     * @example
6046
     * ```typescript
6047
     *  const previousEditableCellPosition = this.grid.getPreviousCell(0, 3, (column) => column.editable);
6048
     * ```
6049
     */
6050
    public getPreviousCell(currRowIndex: number, curVisibleColIndex: number,
6051
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
×
UNCOV
6052
        const columns = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0);
×
UNCOV
6053
        const dataViewIndex = this._getDataViewIndex(currRowIndex);
×
UNCOV
6054
        if (!this.isValidPosition(dataViewIndex, curVisibleColIndex)) {
×
UNCOV
6055
            return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
×
6056
        }
UNCOV
6057
        const colIndexes = callback ? columns.filter((col) => callback(col)).map(editCol => editCol.visibleIndex).sort((a, b) => b - a) :
×
UNCOV
6058
            columns.map(editCol => editCol.visibleIndex).sort((a, b) => b - a);
×
UNCOV
6059
        const prevCellIndex = colIndexes.find(index => index < curVisibleColIndex);
×
UNCOV
6060
        if (this.dataView.slice(dataViewIndex, dataViewIndex + 1)
×
UNCOV
6061
            .find(rec => !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData) && prevCellIndex !== undefined) {
×
UNCOV
6062
            return { rowIndex: currRowIndex, visibleColumnIndex: prevCellIndex };
×
6063
        } else {
UNCOV
6064
            const prevIndex = this.getNextDataRowIndex(currRowIndex, true);
×
UNCOV
6065
            if (colIndexes.length === 0 || prevIndex === currRowIndex) {
×
UNCOV
6066
                return { rowIndex: currRowIndex, visibleColumnIndex: curVisibleColIndex };
×
6067
            } else {
UNCOV
6068
                return { rowIndex: prevIndex, visibleColumnIndex: colIndexes[0] };
×
6069
            }
6070
        }
6071
    }
6072

6073
    /**
6074
     * @hidden
6075
     * @internal
6076
     */
6077
    public endRowEditTabStop(commit = true, event?: Event) {
×
UNCOV
6078
        const canceled = this.crudService.endEdit(commit, event);
×
6079

UNCOV
6080
        if (canceled) {
×
UNCOV
6081
            return true;
×
6082
        }
6083

UNCOV
6084
        this.navigation.restoreActiveNodeFocus();
×
6085
    }
6086

6087
    /**
6088
     * @hidden @internal
6089
     */
6090
    public trackColumnChanges(index, col) {
6091
        return col.field + col._calcWidth;
14,951✔
6092
    }
6093

6094
    /**
6095
     * @hidden
6096
     */
6097
    public isExpandedGroup(_group: IGroupByRecord): boolean {
6098
        return undefined;
×
6099
    }
6100

6101
    /**
6102
     * @hidden @internal
6103
     * TODO: MOVE to CRUD
6104
     */
6105
    public openRowOverlay(id) {
UNCOV
6106
        this.configureRowEditingOverlay(id, this.rowList.length <= MIN_ROW_EDITING_COUNT_THRESHOLD);
×
6107

UNCOV
6108
        this.rowEditingOverlay.open(this.rowEditSettings);
×
UNCOV
6109
        this.rowEditingOverlay.element.addEventListener('wheel', this.rowEditingWheelHandler.bind(this));
×
6110
    }
6111

6112
    /**
6113
     * @hidden @internal
6114
     */
6115
    public closeRowEditingOverlay() {
UNCOV
6116
        this.rowEditingOverlay.element.removeEventListener('wheel', this.rowEditingWheelHandler);
×
UNCOV
6117
        this.rowEditPositioningStrategy.isTopInitialPosition = null;
×
UNCOV
6118
        this.rowEditingOverlay.close();
×
UNCOV
6119
        this.rowEditingOverlay.element.parentElement.style.display = '';
×
6120
    }
6121

6122
    /**
6123
     * @hidden @internal
6124
     */
6125
    public toggleRowEditingOverlay(show) {
UNCOV
6126
        const rowStyle = this.rowEditingOverlay.element.style;
×
UNCOV
6127
        if (show) {
×
UNCOV
6128
            rowStyle.display = 'block';
×
6129
        } else {
6130
            rowStyle.display = 'none';
×
6131
        }
6132
    }
6133

6134
    /**
6135
     * @hidden @internal
6136
     */
6137
    public repositionRowEditingOverlay(row: RowType) {
UNCOV
6138
        if (row && !this.rowEditingOverlay.collapsed) {
×
UNCOV
6139
            const rowStyle = this.rowEditingOverlay.element.parentElement.style;
×
UNCOV
6140
            if (row) {
×
UNCOV
6141
                rowStyle.display = '';
×
UNCOV
6142
                this.configureRowEditingOverlay(row.key);
×
UNCOV
6143
                this.rowEditingOverlay.reposition();
×
6144
            } else {
6145
                rowStyle.display = 'none';
×
6146
            }
6147
        }
6148
    }
6149

6150
    /**
6151
     * @hidden @internal
6152
     */
6153
    public cachedViewLoaded(args: ICachedViewLoadedEventArgs) {
UNCOV
6154
        if (this.hasHorizontalScroll()) {
×
UNCOV
6155
            const tmplId = args.context.templateID.type;
×
UNCOV
6156
            const index = args.context.index;
×
UNCOV
6157
            args.view.detectChanges();
×
UNCOV
6158
            this.zone.onStable.pipe(first()).subscribe(() => {
×
UNCOV
6159
                const row = tmplId === 'dataRow' ? this.gridAPI.get_row_by_index(index) : null;
×
UNCOV
6160
                const summaryRow = tmplId === 'summaryRow' ? this.summariesRowList.find((sr) => sr.dataRowIndex === index) : null;
×
UNCOV
6161
                if (row && row instanceof IgxRowDirective) {
×
UNCOV
6162
                    this._restoreVirtState(row);
×
UNCOV
6163
                } else if (summaryRow) {
×
UNCOV
6164
                    this._restoreVirtState(summaryRow);
×
6165
                }
6166
            });
6167
        }
6168
    }
6169

6170
    /**
6171
     * Opens the advanced filtering dialog.
6172
     */
6173
    public openAdvancedFilteringDialog(overlaySettings?: OverlaySettings) {
6174
        const settings = overlaySettings ? overlaySettings : this._advancedFilteringOverlaySettings;
38!
6175
        if (!this._advancedFilteringOverlayId) {
38✔
6176
            this._advancedFilteringOverlaySettings.target =
37✔
6177
                (this as any).rootGrid ? (this as any).rootGrid.nativeElement : this.nativeElement;
37!
6178
            this._advancedFilteringOverlaySettings.outlet = this.outlet;
37✔
6179

6180
            this._advancedFilteringOverlayId = this.overlayService.attach(
37✔
6181
                IgxAdvancedFilteringDialogComponent,
6182
                this.viewRef,
6183
                settings);
6184
            this.overlayService.show(this._advancedFilteringOverlayId);
37✔
6185
        }
6186
    }
6187

6188
    /**
6189
     * Closes the advanced filtering dialog.
6190
     *
6191
     * @param applyChanges indicates whether the changes should be applied
6192
     */
6193
    public closeAdvancedFilteringDialog(applyChanges: boolean) {
6194
        if (this._advancedFilteringOverlayId) {
3✔
6195
            const advancedFilteringOverlay = this.overlayService.getOverlayById(this._advancedFilteringOverlayId);
3✔
6196
            const advancedFilteringDialog = advancedFilteringOverlay.componentRef.instance as IgxAdvancedFilteringDialogComponent;
3✔
6197

6198
            if (applyChanges) {
3✔
6199
                advancedFilteringDialog.applyChanges();
1✔
6200
            }
6201
            advancedFilteringDialog.closeDialog();
3✔
6202
        }
6203
    }
6204

6205
    /**
6206
     * @hidden @internal
6207
     */
6208
    public getEmptyRecordObjectFor(inRow: RowType) {
UNCOV
6209
        const row = { ...inRow?.data };
×
UNCOV
6210
        Object.keys(row).forEach(key => row[key] = undefined);
×
UNCOV
6211
        const id = this.generateRowID();
×
UNCOV
6212
        row[this.primaryKey] = id;
×
UNCOV
6213
        return { rowID: id, data: row, recordRef: row };
×
6214
    }
6215

6216
    /**
6217
     * @hidden @internal
6218
     */
6219
    public hasHorizontalScroll() {
6220
        return this.totalWidth - this.unpinnedWidth > 0 && this.width !== null;
106✔
6221
    }
6222

6223
    /**
6224
     * @hidden @internal
6225
     */
6226
    public isSummaryRow(rowData): boolean {
6227
        return rowData && rowData.summaries && (rowData.summaries instanceof Map);
2,784!
6228
    }
6229

6230
    /**
6231
     * @hidden @internal
6232
     */
6233
    public triggerPipes() {
UNCOV
6234
        this.pipeTrigger++;
×
UNCOV
6235
        this.cdr.detectChanges();
×
6236
    }
6237

6238
    /**
6239
     * @hidden
6240
     */
6241
    public rowEditingWheelHandler(event: WheelEvent) {
6242
        if (event.deltaY > 0) {
×
6243
            this.verticalScrollContainer.scrollNext();
×
6244
        } else {
6245
            this.verticalScrollContainer.scrollPrev();
×
6246
        }
6247
    }
6248

6249
    /**
6250
     * @hidden
6251
     */
6252
    public getUnpinnedIndexById(id) {
UNCOV
6253
        return this.unpinnedRecords.findIndex(x => x[this.primaryKey] === id);
×
6254
    }
6255

6256
    /**
6257
     * Finishes the row transactions on the current row and returns whether the grid editing was canceled.
6258
     *
6259
     * @remarks
6260
     * If `commit === true`, passes them from the pending state to the data (or transaction service)
6261
     * @example
6262
     * ```html
6263
     * <button type="button" igxButton (click)="grid.endEdit(true)">Commit Row</button>
6264
     * ```
6265
     * @param commit
6266
     */
6267
    // TODO: Facade for crud service refactoring. To be removed
6268
    // TODO: do not remove this, as it is used in rowEditTemplate, but mark is as internal and hidden
6269
    /* blazorCSSuppress */
6270
    public endEdit(commit = true, event?: Event): boolean {
×
UNCOV
6271
        const document = this.nativeElement?.getRootNode() as Document | ShadowRoot;
×
UNCOV
6272
        const focusWithin = this.nativeElement?.contains(document.activeElement);
×
6273

UNCOV
6274
        const success = this.crudService.endEdit(commit, event);
×
6275

UNCOV
6276
        if (focusWithin) {
×
6277
            // restore focus for navigation
UNCOV
6278
            this.navigation.restoreActiveNodeFocus();
×
UNCOV
6279
        } else if (this.navigation.activeNode) {
×
6280
            // grid already lost focus, clear active node
UNCOV
6281
            this.clearActiveNode();
×
6282
        }
6283

UNCOV
6284
        return success;
×
6285
    }
6286

6287
    /**
6288
     * Enters add mode by spawning the UI under the specified row by rowID.
6289
     *
6290
     * @remarks
6291
     * If null is passed as rowID, the row adding UI is spawned as the first record in the data view
6292
     * @remarks
6293
     * Spawning the UI to add a child for a record only works if you provide a rowID
6294
     * @example
6295
     * ```typescript
6296
     * this.grid.beginAddRowById('ALFKI');
6297
     * this.grid.beginAddRowById('ALFKI', true);
6298
     * this.grid.beginAddRowById(null);
6299
     * ```
6300
     * @param rowID - The rowID to spawn the add row UI for, or null to spawn it as the first record in the data view
6301
     * @param asChild - Whether the record should be added as a child. Only applicable to igxTreeGrid.
6302
     */
6303
    public beginAddRowById(rowID: any, asChild?: boolean): void {
UNCOV
6304
        let index = rowID;
×
UNCOV
6305
        if (rowID == null) {
×
UNCOV
6306
            if (asChild) {
×
6307
                console.warn('The record cannot be added as a child to an unspecified record.');
×
6308
                return;
×
6309
            }
UNCOV
6310
            index = null;
×
6311
        } else {
6312
            // find the index of the record with that PK
UNCOV
6313
            index = this.gridAPI.get_rec_index_by_id(rowID, this.dataView);
×
UNCOV
6314
            if (index === -1) {
×
6315
                console.warn('No row with the specified ID was found.');
×
6316
                return;
×
6317
            }
6318
        }
6319

UNCOV
6320
        this._addRowForIndex(index, asChild);
×
6321
    }
6322

6323
    protected _addRowForIndex(index: number, asChild?: boolean) {
UNCOV
6324
        if (!this.dataView.length) {
×
6325
            this.beginAddRowForIndex(index, asChild);
×
6326
            return;
×
6327
        }
6328
        // check if the index is valid - won't support anything outside the data view
UNCOV
6329
        if (index >= 0 && index < this.dataView.length) {
×
6330
            // check if the index is in the view port
UNCOV
6331
            if ((index < this.virtualizationState.startIndex ||
×
6332
                index >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) &&
6333
                !this.isRecordPinnedByViewIndex(index)) {
UNCOV
6334
                this.verticalScrollContainer.chunkLoad
×
6335
                    .pipe(first(), takeUntil(this.destroy$))
6336
                    .subscribe(() => {
UNCOV
6337
                        this.beginAddRowForIndex(index, asChild);
×
6338
                    });
UNCOV
6339
                this.navigateTo(index);
×
UNCOV
6340
                this.notifyChanges(true);
×
UNCOV
6341
                return;
×
6342
            }
UNCOV
6343
            this.beginAddRowForIndex(index, asChild);
×
6344
        } else {
6345
            console.warn('The row with the specified PK or index is outside of the current data view.');
×
6346
        }
6347
    }
6348

6349
    /* csSuppress */
6350
    /**
6351
     * Enters add mode by spawning the UI at the specified index.
6352
     *
6353
     * @remarks
6354
     * Accepted values for index are integers from 0 to this.grid.dataView.length
6355
     * @example
6356
     * ```typescript
6357
     * this.grid.beginAddRowByIndex(0);
6358
     * ```
6359
     * @param index - The index to spawn the UI at. Accepts integers from 0 to this.grid.dataView.length
6360
     */
6361
    public beginAddRowByIndex(index: number): void {
UNCOV
6362
        if (index === 0) {
×
UNCOV
6363
            return this.beginAddRowById(null);
×
6364
        }
6365
        return this._addRowForIndex(index - 1);
×
6366
    }
6367

6368
    /**
6369
     * @hidden
6370
     */
6371
    public preventHeaderScroll(args) {
6372
        if (args.target.scrollLeft !== 0) {
×
6373
            (this.navigation as any).forOfDir().getScroll().scrollLeft = args.target.scrollLeft;
×
6374
            args.target.scrollLeft = 0;
×
6375
        }
6376
    }
6377

6378
    protected beginAddRowForIndex(index: number, asChild = false) {
×
6379
        // TODO is row from rowList suitable for enterAddRowMode
UNCOV
6380
        const row = index == null ?
×
UNCOV
6381
            null : this.rowList.find(r => r.index === index);
×
UNCOV
6382
        if (row !== undefined) {
×
UNCOV
6383
            this.crudService.enterAddRowMode(row, asChild);
×
6384
        } else {
6385
            console.warn('No row with the specified PK or index was found.');
×
6386
        }
6387
    }
6388

6389
    protected switchTransactionService(val: boolean) {
UNCOV
6390
        if (val) {
×
UNCOV
6391
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.Base);
×
6392
        } else {
UNCOV
6393
            this._transactions = this.transactionFactory.create(TRANSACTION_TYPE.None);
×
6394
        }
6395

UNCOV
6396
        if (this.dataCloneStrategy) {
×
UNCOV
6397
            this._transactions.cloneStrategy = this.dataCloneStrategy;
×
6398
        }
6399
    }
6400

6401
    protected subscribeToTransactions(): void {
6402
        this.transactionChange$.next();
37✔
6403
        this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$)))
37✔
6404
            .subscribe(this.transactionStatusUpdate.bind(this));
6405
    }
6406

6407
    protected transactionStatusUpdate(event: StateUpdateEvent) {
UNCOV
6408
        let actions: Action<Transaction>[] = [];
×
UNCOV
6409
        if (event.origin === TransactionEventOrigin.REDO) {
×
UNCOV
6410
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.DELETE) : [];
×
UNCOV
6411
        } else if (event.origin === TransactionEventOrigin.UNDO) {
×
UNCOV
6412
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.ADD) : [];
×
6413
        }
UNCOV
6414
        if (actions.length > 0) {
×
UNCOV
6415
            for (const action of actions) {
×
UNCOV
6416
                if (this.selectionService.isRowSelected(action.transaction.id)) {
×
6417
                    this.selectionService.deselectRow(action.transaction.id);
×
6418
                }
6419
            }
6420
        }
UNCOV
6421
        if (event.origin === TransactionEventOrigin.REDO || event.origin === TransactionEventOrigin.UNDO) {
×
UNCOV
6422
            event.actions.forEach(x => {
×
UNCOV
6423
                if (x.transaction.type === TransactionType.UPDATE) {
×
UNCOV
6424
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
×
UNCOV
6425
                    this.validation.update(x.transaction.id, value ?? x.recordRef);
×
UNCOV
6426
                } else if (x.transaction.type === TransactionType.DELETE || x.transaction.type === TransactionType.ADD) {
×
UNCOV
6427
                    const value = this.transactions.getAggregatedValue(x.transaction.id, true);
×
UNCOV
6428
                    if (value) {
×
UNCOV
6429
                        this.validation.create(x.transaction.id, value ?? x.recordRef);
×
UNCOV
6430
                        this.validation.update(x.transaction.id, value ?? x.recordRef);
×
UNCOV
6431
                        this.validation.markAsTouched(x.transaction.id);
×
6432
                    } else {
UNCOV
6433
                        this.validation.clear(x.transaction.id);
×
6434
                    }
6435
                }
6436

6437
            });
6438
        }
6439

UNCOV
6440
        this.selectionService.clearHeaderCBState();
×
UNCOV
6441
        this.summaryService.clearSummaryCache();
×
UNCOV
6442
        this.pipeTrigger++;
×
UNCOV
6443
        this.notifyChanges();
×
6444
    }
6445

6446
    protected writeToData(rowIndex: number, value: any) {
6447
        mergeObjects(this.gridAPI.get_all_data()[rowIndex], value);
×
6448
    }
6449

6450
    protected _restoreVirtState(row) {
6451
        // check virtualization state of data record added from cache
6452
        // in case state is no longer valid - update it.
UNCOV
6453
        const rowForOf = row.virtDirRow;
×
UNCOV
6454
        const gridScrLeft = rowForOf.getScroll().scrollLeft;
×
UNCOV
6455
        rowForOf.onHScroll(gridScrLeft);
×
UNCOV
6456
        rowForOf.cdr.detectChanges();
×
6457
    }
6458

6459
    protected changeRowEditingOverlayStateOnScroll(row: RowType) {
UNCOV
6460
        if (!this.rowEditable || !this.rowEditingOverlay || this.rowEditingOverlay.collapsed) {
×
UNCOV
6461
            return;
×
6462
        }
UNCOV
6463
        if (!row) {
×
6464
            this.toggleRowEditingOverlay(false);
×
6465
        } else {
UNCOV
6466
            this.repositionRowEditingOverlay(row);
×
6467
        }
6468
    }
6469

6470
    /**
6471
     * Should be called when data and/or isLoading input changes so that the overlay can be
6472
     * hidden/shown based on the current value of shouldOverlayLoading
6473
     */
6474
    protected evaluateLoadingState() {
6475
        if (this.shouldOverlayLoading) {
27!
6476
            // a new overlay should be shown
UNCOV
6477
            const overlaySettings: OverlaySettings = {
×
6478
                outlet: this.loadingOutlet,
6479
                closeOnOutsideClick: false,
6480
                positionStrategy: new ContainerPositionStrategy()
6481
            };
UNCOV
6482
            this.loadingOverlay.open(overlaySettings);
×
6483
        } else {
6484
            this.loadingOverlay.close();
27✔
6485
        }
6486
    }
6487

6488
    /**
6489
     * @hidden
6490
     * Sets grid width i.e. this.calcWidth
6491
     */
6492
    protected calculateGridWidth() {
6493
        let width;
6494

6495
        if (this.isPercentWidth) {
110✔
6496
            /* width in %*/
6497
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('width');
106✔
6498
            width = computed.indexOf('%') === -1 ? parseFloat(computed) : null;
106!
6499
        } else {
6500
            width = parseInt(this.width, 10);
4✔
6501
        }
6502

6503
        if (!width && this.nativeElement) {
110!
UNCOV
6504
            width = this.nativeElement.offsetWidth;
×
6505
        }
6506

6507

6508
        if (this.width === null || !width) {
110!
UNCOV
6509
            this.isColumnWidthSum = true;
×
UNCOV
6510
            width = this.getColumnWidthSum();
×
6511
        } else {
6512
            this.isColumnWidthSum = false;
110✔
6513
        }
6514

6515
        if (this.hasVerticalScroll() && this.width !== null) {
110✔
6516
            width -= this.scrollSize;
22✔
6517
        }
6518
        if ((Number.isFinite(width) || width === null) && width !== this.calcWidth) {
110!
6519
            this.calcWidth = width;
63✔
6520
        }
6521
        this._derivePossibleWidth();
110✔
6522
    }
6523

6524
    /**
6525
     * @hidden
6526
     * Sets columns defaultWidth property
6527
     */
6528
    protected _derivePossibleWidth() {
6529
        if (!this.columnWidthSetByUser) {
110✔
6530
            this._columnWidth = this.width !== null ? this.getPossibleColumnWidth() : this.minColumnWidth + 'px';
110!
6531
        }
6532
        this._columns.forEach((column: IgxColumnComponent) => {
110✔
6533
            if (this.hasColumnLayouts && parseFloat(this._columnWidth)) {
735!
UNCOV
6534
                const columnWidthCombined = parseFloat(this._columnWidth) * (column.colEnd ? column.colEnd - column.colStart : 1);
×
UNCOV
6535
                column.defaultWidth = columnWidthCombined + 'px';
×
6536
            } else {
6537
                // D.K. March 29th, 2021 #9145 Consider min/max width when setting defaultWidth property
6538
                column.defaultWidth = this.getExtremumBasedColWidth(column);
735✔
6539
                column.resetCaches();
735✔
6540
            }
6541
        });
6542
        this.resetCachedWidths();
110✔
6543
    }
6544

6545
    /**
6546
     * @hidden
6547
     * @internal
6548
     */
6549
    protected getExtremumBasedColWidth(column: IgxColumnComponent): string {
6550
        let width = this._columnWidth;
735✔
6551
        if (width && typeof width !== 'string') {
735!
6552
            width = String(width);
×
6553
        }
6554
        const minWidth = width.indexOf('%') === -1 ? column.minWidthPx : column.minWidthPercent;
735!
6555
        const maxWidth = width.indexOf('%') === -1 ? column.maxWidthPx : column.maxWidthPercent;
735!
6556
        if (column.hidden) {
735!
UNCOV
6557
            return width;
×
6558
        }
6559

6560
        if (minWidth > parseFloat(width)) {
735!
6561
            width = String(column.minWidth);
×
6562
        } else if (maxWidth < parseFloat(width)) {
735!
UNCOV
6563
            width = String(column.maxWidth);
×
6564
        }
6565

6566
        // if no px or % are defined in maxWidth/minWidth consider it px
6567
        if (width.indexOf('%') === -1 && width.indexOf('px') === -1) {
735!
UNCOV
6568
            width += 'px';
×
6569
        }
6570
        return width;
735✔
6571
    }
6572

6573
    protected resetNotifyChanges() {
6574
        this._cdrRequestRepaint = false;
74✔
6575
        this._cdrRequests = false;
74✔
6576
    }
6577

6578
    /** @hidden @internal */
6579
    public resolveOutlet() {
6580
        return this._userOutletDirective ? this._userOutletDirective : this._outletDirective;
2,458!
6581
    }
6582

6583
    /**
6584
     * Reorder columns in the main columnList and _columns collections.
6585
     *
6586
     * @hidden
6587
     */
6588
    protected _moveColumns(from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
UNCOV
6589
        const orderedList = this._pinnedColumns.concat(this._unpinnedColumns);
×
UNCOV
6590
        const list = orderedList;
×
UNCOV
6591
        this._reorderColumns(from, to, pos, list);
×
UNCOV
6592
        const newList = this._resetColumnList(list);
×
UNCOV
6593
        this.updateColumns(newList);
×
6594
    }
6595

6596

6597
    /**
6598
     * Update internal column's collection.
6599
     * @hidden
6600
     */
6601
    public updateColumns(newColumns: IgxColumnComponent[]) {
6602
        // update internal collections to retain order.
6603
        this._pinnedColumns = newColumns
41✔
6604
            .filter((c) => c.pinned);
262✔
6605
        this._unpinnedColumns = newColumns.filter((c) => !c.pinned);
262✔
6606
        this._columns = newColumns;
41✔
6607
        if (this._columns && this._filteringExpressionsTree) {
41✔
6608
            this._filteringExpressionsTree = recreateTreeFromFields(this._filteringExpressionsTree, this.columns) as IFilteringExpressionsTree;
41✔
6609
        }
6610
        this.resetCaches();
41✔
6611
    }
6612

6613
    /**
6614
     * @hidden
6615
     */
6616
    protected _resetColumnList(list?) {
UNCOV
6617
        if (!list) {
×
6618
            list = this._columns;
×
6619
        }
UNCOV
6620
        let newList = [];
×
UNCOV
6621
        list.filter(c => c.level === 0).forEach(p => {
×
UNCOV
6622
            newList.push(p);
×
UNCOV
6623
            if (p.columnGroup) {
×
UNCOV
6624
                newList = newList.concat(p.allChildren);
×
6625
            }
6626
        });
UNCOV
6627
        return newList;
×
6628
    }
6629

6630
    /**
6631
     * Reorders columns inside the passed column collection.
6632
     * When reordering column group collection, the collection is not flattened.
6633
     * In all other cases, the columns collection is flattened, this is why adittional calculations on the dropIndex are done.
6634
     *
6635
     * @hidden
6636
     */
6637
    protected _reorderColumns(from: IgxColumnComponent, to: IgxColumnComponent, position: DropPosition, columnCollection: any[],
6638
        inGroup = false) {
×
UNCOV
6639
        const fromIndex = columnCollection.indexOf(from);
×
UNCOV
6640
        const childColumnsCount = inGroup ? 1 : from.allChildren.length + 1;
×
UNCOV
6641
        columnCollection.splice(fromIndex, childColumnsCount);
×
UNCOV
6642
        let dropIndex = columnCollection.indexOf(to);
×
UNCOV
6643
        if (position === DropPosition.AfterDropTarget) {
×
UNCOV
6644
            dropIndex++;
×
UNCOV
6645
            if (!inGroup && to.columnGroup) {
×
UNCOV
6646
                dropIndex += to.allChildren.length;
×
6647
            }
6648
        }
UNCOV
6649
        columnCollection.splice(dropIndex, 0, from);
×
6650
    }
6651

6652
    /**
6653
     * Reorder column group collection.
6654
     *
6655
     * @hidden
6656
     */
6657
    protected _moveChildColumns(parent: IgxColumnComponent, from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
UNCOV
6658
        const buffer = parent.children.toArray();
×
UNCOV
6659
        this._reorderColumns(from, to, pos, buffer, true);
×
UNCOV
6660
        parent.children.reset(buffer);
×
6661
    }
6662

6663
    /**
6664
     * @hidden @internal
6665
     */
6666
    protected setupColumns() {
6667
        if (this.autoGenerate) {
37!
UNCOV
6668
            this.autogenerateColumns();
×
6669
        } else {
6670
            this._columns = this.getColumnList();
37✔
6671
            if (this._columns && this._filteringExpressionsTree) {
37✔
6672
                this._filteringExpressionsTree = recreateTreeFromFields(this._filteringExpressionsTree, this._columns) as IFilteringExpressionsTree;
37✔
6673
            }
6674
        }
6675

6676
        this.initColumns(this._columns, (col: IgxColumnComponent) => this.columnInit.emit(col));
247✔
6677
        this.columnListDiffer.diff(this.columnList);
37✔
6678

6679
        this.columnList.changes
37✔
6680
            .pipe(takeUntil(this.destroy$))
6681
            .subscribe((change: QueryList<IgxColumnComponent>) => {
6682
                this.onColumnsChanged(change);
1✔
6683
            });
6684
    }
6685

6686
    protected getColumnList() {
6687
        return this.columnList.toArray();
36✔
6688
    }
6689

6690
    /**
6691
     * @hidden
6692
     */
6693
    protected deleteRowFromData(rowID: any, index: number) {
6694
        //  if there is a row (index !== 0) delete it
6695
        //  if there is a row in ADD or UPDATE state change it's state to DELETE
6696
        if (index !== -1) {
×
6697
            if (this.transactions.enabled) {
×
6698
                const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null };
×
6699
                this.transactions.add(transaction, this.data[index]);
×
6700
            } else {
6701
                this.data.splice(index, 1);
×
6702
            }
6703
        } else {
6704
            const state: State = this.transactions.getState(rowID);
×
6705
            this.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef);
×
6706
        }
6707
    }
6708

6709

6710
    /**
6711
     * @hidden @internal
6712
     */
6713
    protected getDataBasedBodyHeight(): number {
UNCOV
6714
        return !this.data || (this.data.length < this._defaultTargetRecordNumber) ?
×
6715
            0 : this.defaultTargetBodyHeight;
6716
    }
6717

6718
    /**
6719
     * @hidden @internal
6720
     */
6721
    protected onPinnedRowsChanged(change: QueryList<IgxGridRowComponent>) {
UNCOV
6722
        const diff = this.rowListDiffer.diff(change);
×
UNCOV
6723
        if (diff) {
×
UNCOV
6724
            this.notifyChanges(true);
×
6725
        }
6726
    }
6727

6728
    /**
6729
     * @hidden
6730
     */
6731
    protected onColumnsChanged(change: QueryList<IgxColumnComponent>) {
6732
        const diff = this.columnListDiffer.diff(change);
1✔
6733

6734
        if (this.autoGenerate && this._columns.length === 0 && this._autoGeneratedCols.length > 0) {
1!
6735
            // In Ivy if there are nested conditional templates the content children are re-evaluated
6736
            // hence autogenerated columns are cleared and need to be reset.
6737
            this.updateColumns(this._autoGeneratedCols);
×
6738
            return;
×
6739
        }
6740
        if (diff) {
1✔
6741
            let added = false;
1✔
6742
            let removed = false;
1✔
6743
            let pinning = false;
1✔
6744
            diff.forEachAddedItem((record: IterableChangeRecord<IgxColumnComponent>) => {
1✔
6745
                added = true;
6✔
6746
                if (record.item.pinned) {
6!
UNCOV
6747
                    this._pinnedColumns.push(record.item);
×
UNCOV
6748
                    pinning = true;
×
6749
                } else {
6750
                    this._unpinnedColumns.push(record.item);
6✔
6751
                }
6752
            });
6753

6754
            this.initColumns(this.columnList.toArray(), (col: IgxColumnComponent) => this.columnInit.emit(col));
6✔
6755
            if (pinning) {
1!
UNCOV
6756
                this.initPinning();
×
6757
            }
6758

6759
            diff.forEachRemovedItem((record: IterableChangeRecord<IgxColumnComponent | IgxColumnGroupComponent>) => {
1✔
6760
                const isColumnGroup = record.item instanceof IgxColumnGroupComponent;
6✔
6761
                if (!isColumnGroup) {
6✔
6762
                    // Clear Grouping
6763
                    this.gridAPI.clear_groupby(record.item.field);
6✔
6764

6765
                    // Clear Filtering
6766
                    this.filteringService.clear_filter(record.item.field);
6✔
6767

6768
                    // Close filter row
6769
                    if (this.filteringService.isFilterRowVisible
6!
6770
                        && this.filteringService.filteredColumn
6771
                        && this.filteringService.filteredColumn.field === record.item.field) {
6772
                        this.filteringRow.close();
×
6773
                    }
6774

6775
                    // Clear Sorting
6776
                    this.gridAPI.clear_sort(record.item.field);
6✔
6777

6778
                    // Remove column selection
6779
                    this.selectionService.deselectColumnsWithNoEvent([record.item.field]);
6✔
6780
                }
6781
                removed = true;
6✔
6782
            });
6783

6784
            this.resetCaches();
1✔
6785

6786
            if (added || removed) {
1!
6787
                this.onColumnsAddedOrRemoved();
1✔
6788
            }
6789
        }
6790
    }
6791

6792
    protected checkPrimaryKeyField() {
6793
        if (this.primaryKey && this.data?.length && !(this.primaryKey in this.data[0])) {
41!
UNCOV
6794
            console.warn(`Field "${this.primaryKey}" is not defined in the data. Set \`primaryKey\` to a valid field.`);
×
6795
        }
6796
    }
6797

6798
    /**
6799
     * @hidden @internal
6800
     */
6801
    protected onColumnsAddedOrRemoved() {
6802
        this.summaryService.clearSummaryCache();
1✔
6803
        Promise.resolve().then(() => {
1✔
6804
            // `onColumnsChanged` can be executed midway a current detectChange cycle and markForCheck will be ignored then.
6805
            // This ensures that we will wait for the current cycle to end so we can trigger a new one and ngDoCheck to fire.
6806
            this.notifyChanges(true);
1✔
6807
        });
6808
    }
6809

6810
    /**
6811
     * @hidden
6812
     */
6813
    protected calculateGridSizes(recalcFeatureWidth = true) {
70✔
6814
        /*
6815
            TODO: (R.K.) This layered lasagne should be refactored
6816
            ASAP. The reason I have to reset the caches so many times is because
6817
            after teach `detectChanges` call they are filled with invalid
6818
            state. Of course all of this happens midway through the grid
6819
            sizing process which of course, uses values from the caches, thus resulting
6820
            in a broken layout.
6821
        */
6822
        this.cdr.detectChanges();
70✔
6823
        this.resetCaches(recalcFeatureWidth);
70✔
6824
        const hasScroll = this.hasVerticalScroll();
70✔
6825
        const hasHScroll = !this.isHorizontalScrollHidden;
70✔
6826
        this.calculateGridWidth();
70✔
6827
        this.resetCaches(recalcFeatureWidth);
70✔
6828
        this.cdr.detectChanges();
70✔
6829
        this.calculateGridHeight();
70✔
6830

6831
        if (this.rowEditable) {
70!
UNCOV
6832
            this.repositionRowEditingOverlay(this.crudService.rowInEditMode);
×
6833
        }
6834

6835
        if (this.filteringService.isFilterRowVisible) {
70!
UNCOV
6836
            this.filteringRow.resetChipsArea();
×
6837
        }
6838

6839
        this.cdr.detectChanges();
70✔
6840
        // in case scrollbar has appeared recalc to size correctly.
6841
        if (hasScroll !== this.hasVerticalScroll()) {
70✔
6842
            this.calculateGridWidth();
3✔
6843
            this.cdr.detectChanges();
3✔
6844
        }
6845

6846
        // in case horizontal scrollbar has appeared recalc to size correctly.
6847
        if (hasHScroll !== this.hasHorizontalScroll()) {
70✔
6848
            this.isHorizontalScrollHidden = !this.hasHorizontalScroll();
36✔
6849
            this.cdr.detectChanges();
36✔
6850
            this.calculateGridHeight();
36✔
6851
            this.cdr.detectChanges();
36✔
6852
        }
6853
        if (this.zone.isStable) {
70!
UNCOV
6854
            this.zone.run(() => {
×
UNCOV
6855
                this._applyWidthHostBinding();
×
UNCOV
6856
                this.cdr.detectChanges();
×
6857
            });
6858
        } else {
6859
            this.zone.onStable.pipe(first()).subscribe(() => {
70✔
6860
                this.zone.run(() => {
70✔
6861
                    this._applyWidthHostBinding();
70✔
6862
                });
6863
            });
6864
        }
6865
        this.resetCaches(recalcFeatureWidth);
70✔
6866
        if (this.hasColumnsToAutosize) {
70!
UNCOV
6867
            this.cdr.detectChanges();
×
UNCOV
6868
            this.zone.onStable.pipe(first()).subscribe(() => {
×
UNCOV
6869
                this._autoSizeColumnsNotify.next();
×
6870
            });
6871
        }
6872
    }
6873

6874
    /**
6875
     * @hidden
6876
     * Sets TBODY height i.e. this.calcHeight
6877
     */
6878
    protected calculateGridHeight() {
6879

6880
        this.calcHeight = this._calculateGridBodyHeight();
106✔
6881
        if (this.pinnedRowHeight && this.calcHeight) {
106!
UNCOV
6882
            this.calcHeight -= this.pinnedRowHeight;
×
6883
        }
6884
    }
6885

6886
    /**
6887
     * @hidden
6888
     */
6889
    protected getGroupAreaHeight(): number {
6890
        return 0;
4✔
6891
    }
6892

6893
    /**
6894
     * @hidden
6895
     */
6896
    protected getComputedHeight(elem) {
6897
        return elem.offsetHeight ? parseFloat(this.document.defaultView.getComputedStyle(elem).getPropertyValue('height')) : 0;
421✔
6898
    }
6899
    /**
6900
     * @hidden
6901
     */
6902
    protected getFooterHeight(): number {
6903
        return this.summaryRowHeight || this.getComputedHeight(this.tfoot.nativeElement);
106✔
6904
    }
6905
    /**
6906
     * @hidden
6907
     */
6908
    protected getTheadRowHeight(): number {
6909
        // D.P.: Before CSS loads,theadRow computed height will be 'auto'->NaN, so use 0 fallback
6910
        const height = this.getComputedHeight(this.theadRow.nativeElement) || 0;
106!
6911
        return (!this.allowFiltering || (this.allowFiltering && this.filterMode !== FilterMode.quickFilter)) ?
106!
6912
            height - this.getFilterCellHeight() :
6913
            height;
6914
    }
6915

6916
    /**
6917
     * @hidden
6918
     */
6919
    protected getToolbarHeight(): number {
6920
        let toolbarHeight = 0;
106✔
6921
        if (this.toolbar.first) {
106✔
6922
            toolbarHeight = this.getComputedHeight(this.toolbar.first.nativeElement);
90✔
6923
        }
6924
        return toolbarHeight;
106✔
6925
    }
6926

6927
    /**
6928
     * @hidden
6929
     */
6930
    protected getPagingFooterHeight(): number {
6931
        let pagingHeight = 0;
106✔
6932
        if (this.footer) {
106✔
6933
            const height = this.getComputedHeight(this.footer.nativeElement);
106✔
6934
            pagingHeight = this.footer.nativeElement.firstElementChild ?
106!
6935
                height : 0;
6936
        }
6937
        return pagingHeight;
106✔
6938
    }
6939

6940
    /**
6941
     * @hidden
6942
     */
6943
    protected getFilterCellHeight(): number {
6944
        const headerGroupNativeEl = (this.headerGroupsList.length !== 0) ?
106!
6945
            this.headerGroupsList[0].nativeElement : null;
6946
        const filterCellNativeEl = (headerGroupNativeEl) ?
106!
6947
            headerGroupNativeEl.querySelector('igx-grid-filtering-cell') as HTMLElement : null;
6948
        return (filterCellNativeEl) ? filterCellNativeEl.offsetHeight : 0;
106!
6949
    }
6950

6951
    /**
6952
     * @hidden
6953
     */
6954
    protected _calculateGridBodyHeight(): number {
6955
        if (!this._height) {
106!
UNCOV
6956
            return null;
×
6957
        }
6958
        const actualTheadRow = this.getTheadRowHeight();
106✔
6959
        const footerHeight = this.getFooterHeight();
106✔
6960
        const toolbarHeight = this.getToolbarHeight();
106✔
6961
        const pagingHeight = this.getPagingFooterHeight();
106✔
6962
        const groupAreaHeight = this.getGroupAreaHeight();
106✔
6963
        const scrHeight = this.getComputedHeight(this.scr.nativeElement);
106✔
6964
        const renderedHeight = toolbarHeight + actualTheadRow +
106✔
6965
            footerHeight + pagingHeight + groupAreaHeight +
6966
            scrHeight;
6967

6968
        let gridHeight = 0;
106✔
6969

6970
        if (this.isPercentHeight) {
106!
UNCOV
6971
            const computed = this.document.defaultView.getComputedStyle(this.nativeElement).getPropertyValue('height');
×
UNCOV
6972
            const autoSize = this._shouldAutoSize(renderedHeight);
×
UNCOV
6973
            if (autoSize || computed.indexOf('%') !== -1) {
×
UNCOV
6974
                const bodyHeight = this.getDataBasedBodyHeight();
×
UNCOV
6975
                return bodyHeight > 0 ? bodyHeight : null;
×
6976
            }
UNCOV
6977
            gridHeight = parseFloat(computed);
×
6978
        } else {
6979
            gridHeight = parseInt(this._height, 10);
106✔
6980
        }
6981
        const height = Math.abs(gridHeight - renderedHeight);
106✔
6982

6983
        if (Math.round(height) === 0 || isNaN(gridHeight)) {
106!
UNCOV
6984
            const bodyHeight = this.defaultTargetBodyHeight;
×
UNCOV
6985
            return bodyHeight > 0 ? bodyHeight : null;
×
6986
        }
6987
        return height;
106✔
6988
    }
6989

6990
    protected checkContainerSizeChange() {
UNCOV
6991
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
×
UNCOV
6992
        const origHeight = parentElement.offsetHeight;
×
UNCOV
6993
        this.nativeElement.style.display = 'none';
×
UNCOV
6994
        const height = parentElement.offsetHeight;
×
UNCOV
6995
        this.nativeElement.style.display = '';
×
UNCOV
6996
        return origHeight !== height;
×
6997
    }
6998

6999
    protected _shouldAutoSize(renderedHeight) {
UNCOV
7000
        this.tbody.nativeElement.style.display = 'none';
×
UNCOV
7001
        const parentElement = this.nativeElement.parentElement || (this.nativeElement.getRootNode() as any).host;
×
UNCOV
7002
        let res = !parentElement ||
×
7003
            parentElement.clientHeight === 0 ||
7004
            parentElement.clientHeight === renderedHeight;
UNCOV
7005
        if (parentElement && (res || this._autoSize)) {
×
7006
            // If grid causes the parent container to extend (for example when container is flex)
7007
            // we should always auto-size since the actual size of the container will continuously change as the grid renders elements.
UNCOV
7008
            this._autoSize = false;
×
UNCOV
7009
            res = this.checkContainerSizeChange();
×
7010
        }
UNCOV
7011
        this.tbody.nativeElement.style.display = '';
×
UNCOV
7012
        return res;
×
7013
    }
7014

7015
    /**
7016
     * @hidden
7017
     * Gets calculated width of the unpinned area
7018
     * @param takeHidden If we should take into account the hidden columns in the pinned area.
7019
     */
7020
    protected getUnpinnedWidth(takeHidden = false) {
215✔
7021
        let width = this.isPercentWidth ?
215✔
7022
            this.calcWidth :
7023
            parseInt(this.width, 10) || parseInt(this.hostWidth, 10) || this.calcWidth;
8!
7024
        if (this.hasVerticalScroll() && !this.isPercentWidth) {
215✔
7025
            width -= this.scrollSize;
5✔
7026
        }
7027
        if (!this.isPinningToStart) {
215!
UNCOV
7028
            width -= this.featureColumnsWidth();
×
7029
        }
7030

7031
        return width - this.getPinnedWidth(takeHidden);
215✔
7032
    }
7033

7034
    /**
7035
     * @hidden
7036
     */
7037
    protected _summaries(fieldName: string, hasSummary: boolean, summaryOperand?: any) {
UNCOV
7038
        const column = this.gridAPI.get_column_by_name(fieldName);
×
UNCOV
7039
        if (column) {
×
UNCOV
7040
            column.hasSummary = hasSummary;
×
UNCOV
7041
            if (summaryOperand) {
×
UNCOV
7042
                if (this.rootSummariesEnabled) {
×
UNCOV
7043
                    this.summaryService.retriggerRootPipe++;
×
7044
                }
UNCOV
7045
                column.summaries = summaryOperand;
×
7046
            }
7047
        }
7048
    }
7049

7050
    /**
7051
     * @hidden
7052
     */
7053
    protected _multipleSummaries(expressions: ISummaryExpression[], hasSummary: boolean) {
UNCOV
7054
        expressions.forEach((element) => {
×
UNCOV
7055
            this._summaries(element.fieldName, hasSummary, element.customSummary);
×
7056
        });
7057
    }
7058
    /**
7059
     * @hidden
7060
     */
7061
    protected _disableMultipleSummaries(expressions) {
UNCOV
7062
        expressions.forEach((column) => {
×
UNCOV
7063
            const columnName = column && column.fieldName ? column.fieldName : column;
×
UNCOV
7064
            this._summaries(columnName, false);
×
7065
        });
7066
    }
7067

7068
    /**
7069
     * @hidden
7070
     */
7071
    public resolveDataTypes(rec) {
UNCOV
7072
        if (typeof rec === 'number') {
×
UNCOV
7073
            return GridColumnDataType.Number;
×
UNCOV
7074
        } else if (typeof rec === 'boolean') {
×
UNCOV
7075
            return GridColumnDataType.Boolean;
×
UNCOV
7076
        } else if (typeof rec === 'object' && rec instanceof Date) {
×
UNCOV
7077
            return GridColumnDataType.Date;
×
UNCOV
7078
        } else if (typeof rec === 'string' && (/\.(gif|jpe?g|tiff?|png|webp|bmp)$/i).test(rec)) {
×
UNCOV
7079
            return GridColumnDataType.Image;
×
7080
        }
UNCOV
7081
        return GridColumnDataType.String;
×
7082
    }
7083

7084
    /**
7085
     * @hidden
7086
     */
7087
    protected autogenerateColumns() {
UNCOV
7088
        const data = this.gridAPI.get_data();
×
UNCOV
7089
        const fields = this.generateDataFields(data);
×
UNCOV
7090
        const columns = [];
×
7091

UNCOV
7092
        fields.forEach((field) => {
×
UNCOV
7093
            const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
×
UNCOV
7094
            ref.instance.field = field;
×
UNCOV
7095
            ref.instance.dataType = this.resolveDataTypes(data[0][field]);
×
UNCOV
7096
            ref.changeDetectorRef.detectChanges();
×
UNCOV
7097
            columns.push(ref.instance);
×
7098
        });
UNCOV
7099
        this._autoGeneratedCols = columns;
×
7100

UNCOV
7101
        this.updateColumns(columns);
×
UNCOV
7102
        this.columnsAutogenerated.emit({ columns: this._autoGeneratedCols });
×
7103
    }
7104

7105
    protected generateDataFields(data: any[]): string[] {
UNCOV
7106
        return Object.keys(data && data.length !== 0 ? data[0] : [])
×
UNCOV
7107
            .filter(key => !this.autoGenerateExclude.includes(key));
×
7108
    }
7109

7110
    /**
7111
     * @hidden
7112
     */
7113
    protected initColumns(collection: IgxColumnComponent[], cb: (args: any) => void = null) {
×
7114
        this._columnGroups = collection.some(col => col.columnGroup);
252✔
7115
        if (this.hasColumnLayouts) {
39!
7116
            // Set overall row layout size
UNCOV
7117
            collection.forEach((col) => {
×
UNCOV
7118
                if (col.columnLayout) {
×
UNCOV
7119
                    const layoutSize = col.children ?
×
UNCOV
7120
                        col.children.reduce((acc, val) => Math.max(val.rowStart + val.gridRowSpan - 1, acc), 1) :
×
7121
                        1;
UNCOV
7122
                    this._multiRowLayoutRowSize = Math.max(layoutSize, this._multiRowLayoutRowSize);
×
7123
                }
7124
            });
7125
        }
7126
        if (this.hasColumnLayouts && this.hasColumnGroups) {
39!
7127
            // invalid configuration - multi-row and column groups
7128
            // remove column groups
UNCOV
7129
            const columnLayoutColumns = collection.filter((col) => col.columnLayout || col.columnLayoutChild);
×
UNCOV
7130
            collection = columnLayoutColumns;
×
7131
        }
7132
        this._maxLevelHeaderDepth = null;
39✔
7133
        collection.forEach((column: IgxColumnComponent) => {
39✔
7134
            column.defaultWidth = this.columnWidthSetByUser ? this._columnWidth : column.defaultWidth ? column.defaultWidth : '';
256!
7135

7136
            if (cb) {
256✔
7137
                cb(column);
256✔
7138
            }
7139
        });
7140

7141
        this.updateColumns(collection);
39✔
7142

7143
        if (this.hasColumnLayouts) {
39!
UNCOV
7144
            collection.forEach((column: IgxColumnComponent) => {
×
UNCOV
7145
                column.populateVisibleIndexes();
×
7146
            });
7147
        }
7148
    }
7149

7150
    /**
7151
     * @hidden
7152
     */
7153
    protected reinitPinStates() {
UNCOV
7154
        this._pinnedColumns = this._columns
×
UNCOV
7155
            .filter((c) => c.pinned).sort((a, b) => this._pinnedColumns.indexOf(a) - this._pinnedColumns.indexOf(b));
×
UNCOV
7156
        this._unpinnedColumns = this.hasColumnGroups ? this._columns.filter((c) => !c.pinned) :
×
UNCOV
7157
            this._columns.filter((c) => !c.pinned)
×
UNCOV
7158
                .sort((a, b) => this._unpinnedColumns.indexOf(a) - this._unpinnedColumns.indexOf(b));
×
7159
    }
7160

7161
    protected extractDataFromSelection(source: any[], formatters = false, headers = false, columnData?: any[]): any[] {
×
7162
        let columnsArray: IgxColumnComponent[];
UNCOV
7163
        let record = {};
×
UNCOV
7164
        let selectedData = [];
×
UNCOV
7165
        let keys = [];
×
UNCOV
7166
        const selectionCollection = new Map();
×
UNCOV
7167
        const keysAndData = [];
×
UNCOV
7168
        const activeEl = this.selectionService.activeElement;
×
7169

UNCOV
7170
        if (this.type === 'hierarchical') {
×
UNCOV
7171
            const expansionRowIndexes = [];
×
UNCOV
7172
            for (const [key, value] of this.expansionStates.entries()) {
×
7173
                if (value) {
×
7174
                    const rowIndex = this.gridAPI.get_rec_index_by_id(key, this.dataView);
×
7175
                    expansionRowIndexes.push(rowIndex);
×
7176
                }
7177
            }
UNCOV
7178
            if (this.selectionService.selection.size > 0) {
×
UNCOV
7179
                if (expansionRowIndexes.length > 0) {
×
7180
                    for (const [key, value] of this.selectionService.selection.entries()) {
×
7181
                        const updatedKey = key;
×
7182
                        let subtract = 0;
×
7183
                        expansionRowIndexes.forEach((row) => {
×
7184
                            if (updatedKey > Number(row)) {
×
7185
                                subtract++;
×
7186
                            }
7187
                        });
7188
                        selectionCollection.set(updatedKey - subtract, value);
×
7189
                    }
7190
                }
7191
            } else if (activeEl) {
×
7192
                let subtract = 0;
×
7193
                if (expansionRowIndexes.length > 0) {
×
7194
                    expansionRowIndexes.forEach(row => {
×
7195
                        if (activeEl.row > Number(row)) {
×
7196
                            subtract++;
×
7197
                        }
7198
                    });
7199
                    activeEl.row -= subtract;
×
7200
                }
7201
            }
7202
        }
7203

UNCOV
7204
        const totalItems = (this as any).totalItemCount ?? 0;
×
UNCOV
7205
        const isRemote = totalItems && totalItems > this.dataView.length;
×
7206
        let selectionMap;
UNCOV
7207
        if (this.type === 'hierarchical' && selectionCollection.size > 0) {
×
7208
            selectionMap = isRemote ? Array.from(selectionCollection) :
×
7209
                Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length);
×
7210
        } else {
UNCOV
7211
            selectionMap = isRemote ? Array.from(this.selectionService.selection) :
×
UNCOV
7212
                Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length);
×
7213
        }
7214

UNCOV
7215
        if (this.cellSelection === GridSelectionMode.single && activeEl) {
×
UNCOV
7216
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
×
7217
        }
7218

UNCOV
7219
        if (this.cellSelection === GridSelectionMode.none && activeEl) {
×
UNCOV
7220
            selectionMap.push([activeEl.row, new Set<number>().add(activeEl.column)]);
×
7221
        }
7222

UNCOV
7223
        if (columnData) {
×
7224
            selectedData = columnData;
×
7225
        }
7226

7227
        // eslint-disable-next-line prefer-const
UNCOV
7228
        for (let [row, set] of selectionMap) {
×
UNCOV
7229
            row = this.paginator && (this.pagingMode === GridPagingMode.Local && source === this.filteredSortedData) ? row + (this.perPage * this.page) : row;
×
UNCOV
7230
            row = isRemote ? row - this.virtualizationState.startIndex : row;
×
UNCOV
7231
            if (!source[row] || source[row].detailsData !== undefined) {
×
UNCOV
7232
                continue;
×
7233
            }
UNCOV
7234
            const temp = Array.from(set);
×
UNCOV
7235
            for (const each of temp) {
×
UNCOV
7236
                columnsArray = this.getSelectableColumnsAt(each);
×
UNCOV
7237
                columnsArray.forEach((col) => {
×
UNCOV
7238
                    if (col) {
×
UNCOV
7239
                        const key = this.type !== 'pivot' && headers ? col.header || col.field : col.field;
×
UNCOV
7240
                        const rowData = source[row].ghostRecord ? source[row].recordRef : source[row];
×
UNCOV
7241
                        const value = this.type === 'pivot' ? rowData.aggregationValues.get(col.field)
×
7242
                            : resolveNestedPath(rowData, col.field);
UNCOV
7243
                        record[key] = formatters && col.formatter ? col.formatter(value, rowData) : value;
×
UNCOV
7244
                        if (columnData) {
×
7245
                            if (!record[key]) {
×
7246
                                record[key] = '';
×
7247
                            }
7248
                            record[key] = record[key].toString().concat('recordRow-' + row);
×
7249
                        }
7250
                    }
7251
                });
7252
            }
UNCOV
7253
            if (Object.keys(record).length) {
×
UNCOV
7254
                if (columnData) {
×
7255
                    if (!keys.length) {
×
7256
                        keys = Object.keys(columnData[0]);
×
7257
                    }
7258
                    for (const [key, value] of Object.entries(record)) {
×
7259
                        if (!keys.includes(key)) {
×
7260
                            keys.push(key);
×
7261
                        }
7262
                        let c: any = value;
×
7263
                        const rowNumber = +c.split('recordRow-')[1];
×
7264
                        c = c.split('recordRow-')[0];
×
7265
                        record[key] = c;
×
7266
                        const mergedObj = Object.assign(selectedData[rowNumber], record);
×
7267
                        selectedData[rowNumber] = mergedObj;
×
7268
                    }
7269
                } else {
UNCOV
7270
                    selectedData.push(record);
×
7271
                }
7272
            }
UNCOV
7273
            record = {};
×
7274
        }
7275

UNCOV
7276
        if (keys.length) {
×
7277
            keysAndData.push(selectedData);
×
7278
            keysAndData.push(keys);
×
7279
            return keysAndData;
×
7280
        } else {
UNCOV
7281
            return selectedData;
×
7282
        }
7283
    }
7284

7285
    protected getSelectableColumnsAt(index) {
UNCOV
7286
        if (this.hasColumnLayouts) {
×
UNCOV
7287
            const visibleLayoutColumns = this.visibleColumns
×
UNCOV
7288
                .filter(col => col.columnLayout)
×
UNCOV
7289
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
×
UNCOV
7290
            const colLayout = visibleLayoutColumns[index];
×
UNCOV
7291
            return colLayout ? colLayout.children.toArray() : [];
×
7292
        } else {
UNCOV
7293
            const visibleColumns = this.visibleColumns
×
UNCOV
7294
                .filter(col => !col.columnGroup)
×
UNCOV
7295
                .sort((a, b) => a.visibleIndex - b.visibleIndex);
×
UNCOV
7296
            return [visibleColumns[index]];
×
7297
        }
7298
    }
7299

7300
    protected autoSizeColumnsInView() {
UNCOV
7301
        if (!this.hasColumnsToAutosize) return;
×
UNCOV
7302
        const vState = this.headerContainer.state;
×
UNCOV
7303
        let colResized = false;
×
UNCOV
7304
        const unpinnedInView = this.headerContainer.igxGridForOf.slice(vState.startIndex, vState.startIndex + vState.chunkSize).flatMap(x => x.columnGroup ? x.allChildren : x);
×
UNCOV
7305
        const columnsInView = this.pinnedColumns.concat(unpinnedInView as IgxColumnComponent[]);
×
UNCOV
7306
        for (const col of columnsInView) {
×
UNCOV
7307
            if (!col.autoSize && col.headerCell) {
×
UNCOV
7308
                const cellsContentWidths = [];
×
UNCOV
7309
                if (col._cells.length !== this.rowList.length) {
×
7310
                    this.rowList.forEach(x => x.cdr.detectChanges());
×
7311
                }
UNCOV
7312
                const cells = this._dataRowList.map(x => x.cells.find(c => c.column === col));
×
UNCOV
7313
                cells.forEach((cell) => cellsContentWidths.push(cell?.nativeElement?.offsetWidth || 0));
×
UNCOV
7314
                let maxForCells = Math.max(...cellsContentWidths);
×
UNCOV
7315
                const header = this.headerCellList.find(x => x.column === col);
×
UNCOV
7316
                cellsContentWidths.push(header.nativeElement.offsetWidth);
×
UNCOV
7317
                const max = Math.max(...cellsContentWidths);
×
7318
                // in cases with template contains something, like a webcomponent,
7319
                // that renders fully only after it is already injected in the DOM,
7320
                // and initially renders as empty, skip measuring it.
UNCOV
7321
                let emptyCellWithPaddingOnly = 0;
×
UNCOV
7322
                if (cells.length > 0 && !!col.bodyTemplate) {
×
UNCOV
7323
                    const cellStyle = this.document.defaultView.getComputedStyle(cells[0].nativeElement);
×
UNCOV
7324
                    emptyCellWithPaddingOnly = parseFloat(cellStyle.paddingLeft) + parseFloat(cellStyle.paddingRight);
×
7325
                } else {
UNCOV
7326
                    maxForCells = max;
×
7327
                }
7328

UNCOV
7329
                if (max === 0 || (maxForCells <= emptyCellWithPaddingOnly && this._firstAutoResize)) {
×
7330
                    // cells not in DOM yet or content not fully initialized.
UNCOV
7331
                    continue;
×
7332
                }
UNCOV
7333
                let maxSize = Math.ceil(Math.max(...cellsContentWidths)) + 1;
×
UNCOV
7334
                if (col.maxWidth && maxSize > col.maxWidthPx) {
×
UNCOV
7335
                    maxSize = col.maxWidthPx;
×
UNCOV
7336
                } else if (maxSize < col.minWidthPx) {
×
UNCOV
7337
                    maxSize = col.minWidthPx;
×
7338
                }
UNCOV
7339
                col.autoSize = maxSize;
×
UNCOV
7340
                col.resetCaches();
×
UNCOV
7341
                colResized = true;
×
7342
            }
7343
        }
UNCOV
7344
        if (colResized) {
×
UNCOV
7345
            this.resetCachedWidths();
×
UNCOV
7346
            this.cdr.detectChanges();
×
7347
        }
7348

UNCOV
7349
        if (this.isColumnWidthSum) {
×
UNCOV
7350
            this.calcWidth = this.getColumnWidthSum();
×
7351
        }
7352
    }
7353

7354
    protected extractDataFromColumnsSelection(source: any[], formatters = false, headers = false): any[] {
×
UNCOV
7355
        let record = {};
×
UNCOV
7356
        const selectedData = [];
×
UNCOV
7357
        const selectedColumns = this.selectedColumns();
×
UNCOV
7358
        if (selectedColumns.length === 0) {
×
UNCOV
7359
            return [];
×
7360
        }
7361

UNCOV
7362
        for (const data of source) {
×
UNCOV
7363
            selectedColumns.forEach((col) => {
×
UNCOV
7364
                const key = headers ? col.header || col.field : col.field;
×
UNCOV
7365
                record[key] = formatters && col.formatter ? col.formatter(data[col.field], data)
×
7366
                    : data[col.field];
7367
            });
7368

UNCOV
7369
            if (Object.keys(record).length) {
×
UNCOV
7370
                selectedData.push(record);
×
7371
            }
UNCOV
7372
            record = {};
×
7373
        }
UNCOV
7374
        return selectedData;
×
7375
    }
7376

7377
    /**
7378
     * @hidden
7379
     */
7380
    protected initPinning() {
7381
        this.calculateGridWidth();
37✔
7382
        this.resetCaches();
37✔
7383
        this.handleColumnPinningForGroups();
37✔
7384
        this.notifyChanges();
37✔
7385
    }
7386

7387
    /**
7388
     * @hidden
7389
     */
7390
    protected scrollTo(row: any | number, column: any | number, inCollection = this._filteredSortedUnpinnedData): void {
×
UNCOV
7391
        let delayScrolling = false;
×
7392

UNCOV
7393
        if (this.paginator && typeof (row) !== 'number') {
×
UNCOV
7394
            const rowIndex = inCollection.indexOf(row);
×
UNCOV
7395
            const page = Math.floor(rowIndex / this.perPage);
×
7396

UNCOV
7397
            if (this.page !== page) {
×
UNCOV
7398
                delayScrolling = true;
×
UNCOV
7399
                this.page = page;
×
7400
            }
7401
        }
7402

UNCOV
7403
        if (delayScrolling) {
×
UNCOV
7404
            this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
×
UNCOV
7405
                this.scrollDirective(this.verticalScrollContainer,
×
7406
                    typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
×
7407
            });
7408
        } else {
UNCOV
7409
            this.scrollDirective(this.verticalScrollContainer,
×
7410
                typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(row));
×
7411
        }
7412

UNCOV
7413
        this.scrollToHorizontally(column);
×
7414
    }
7415

7416
    /**
7417
     * @hidden
7418
     */
7419
    protected scrollToHorizontally(column: any | number) {
UNCOV
7420
        let columnIndex = typeof column === 'number' ? column : this.getColumnByName(column).visibleIndex;
×
UNCOV
7421
        const scrollRow = this.rowList.find(r => !!r.virtDirRow);
×
UNCOV
7422
        const virtDir = scrollRow ? scrollRow.virtDirRow : null;
×
UNCOV
7423
        if (this.isPinningToStart && this.pinnedColumns.length) {
×
UNCOV
7424
            if (columnIndex >= this.pinnedColumns.length) {
×
7425
                columnIndex -= this.pinnedColumns.length;
×
7426
                this.scrollDirective(virtDir, columnIndex);
×
7427
            }
7428
        } else {
UNCOV
7429
            this.scrollDirective(virtDir, columnIndex);
×
7430
        }
7431
    }
7432

7433
    /**
7434
     * @hidden
7435
     */
7436
    protected scrollDirective(directive: IgxGridForOfDirective<any, any[]>, goal: number): void {
UNCOV
7437
        if (!directive) {
×
UNCOV
7438
            return;
×
7439
        }
UNCOV
7440
        directive.scrollTo(goal);
×
7441
    }
7442

7443

7444
    /**
7445
     * @hidden
7446
     */
7447
    protected getColumnWidthSum(): number {
UNCOV
7448
        let colSum = 0;
×
UNCOV
7449
        const cols = this.hasColumnLayouts ?
×
UNCOV
7450
            this.visibleColumns.filter(x => x.columnLayout) : this.visibleColumns.filter(x => !x.columnGroup);
×
UNCOV
7451
        cols.forEach((item) => {
×
UNCOV
7452
            colSum += parseInt((item.calcWidth || item.defaultWidth), 10) || this.minColumnWidth;
×
7453
        });
UNCOV
7454
        if (!colSum) {
×
7455
            return null;
×
7456
        }
UNCOV
7457
        this.cdr.detectChanges();
×
UNCOV
7458
        colSum += this.featureColumnsWidth();
×
UNCOV
7459
        return colSum;
×
7460
    }
7461

7462
    /**
7463
     * Notify changes, reset cache and populateVisibleIndexes.
7464
     *
7465
     * @hidden
7466
     */
7467
    private _columnsReordered(column: IgxColumnComponent) {
UNCOV
7468
        this.notifyChanges();
×
7469
        // after reordering is done reset cached column collections.
UNCOV
7470
        this.resetColumnCollections();
×
UNCOV
7471
        column.resetCaches();
×
7472
    }
7473

7474
    protected buildDataView(_data: any[]) {
7475
        this._dataView = this.isRowPinningToTop ?
134!
7476
            [...this.pinnedDataView, ...this.unpinnedDataView] :
7477
            [...this.unpinnedDataView, ...this.pinnedDataView];
7478
    }
7479

7480
    private _applyWidthHostBinding() {
7481
        let width = this._width;
70✔
7482
        if (width === null) {
70!
UNCOV
7483
            let currentWidth = this.calcWidth;
×
UNCOV
7484
            if (this.hasVerticalScroll()) {
×
7485
                currentWidth += this.scrollSize;
×
7486
            }
UNCOV
7487
            width = currentWidth + 'px';
×
UNCOV
7488
            this.resetCaches();
×
7489
        }
7490
        this._hostWidth = width;
70✔
7491
        this.cdr.markForCheck();
70✔
7492
    }
7493

7494
    protected verticalScrollHandler(event) {
UNCOV
7495
        this.verticalScrollContainer.onScroll(event);
×
UNCOV
7496
        this.disableTransitions = true;
×
7497

UNCOV
7498
        this.zone.run(() => {
×
UNCOV
7499
            this.zone.onStable.pipe(first()).subscribe(() => {
×
UNCOV
7500
                this.verticalScrollContainer.chunkLoad.emit(this.verticalScrollContainer.state);
×
UNCOV
7501
                if (this.rowEditable) {
×
UNCOV
7502
                    this.changeRowEditingOverlayStateOnScroll(this.crudService.rowInEditMode);
×
7503
                }
7504
            });
7505
        });
UNCOV
7506
        this.disableTransitions = false;
×
7507

UNCOV
7508
        this.hideOverlays();
×
UNCOV
7509
        this.actionStrip?.hide();
×
UNCOV
7510
        if (this.actionStrip) {
×
UNCOV
7511
            this.actionStrip.context = null;
×
7512
        }
UNCOV
7513
        const args: IGridScrollEventArgs = {
×
7514
            direction: 'vertical',
7515
            event,
7516
            scrollPosition: this.verticalScrollContainer.scrollPosition
7517
        };
UNCOV
7518
        this.gridScroll.emit(args);
×
7519
    }
7520

7521
    protected horizontalScrollHandler(event) {
UNCOV
7522
        const scrollLeft = event.target.scrollLeft;
×
UNCOV
7523
        this.headerContainer.onHScroll(scrollLeft);
×
UNCOV
7524
        this._horizontalForOfs.forEach(vfor => vfor.onHScroll(scrollLeft));
×
UNCOV
7525
        this.cdr.markForCheck();
×
7526

UNCOV
7527
        this.zone.run(() => {
×
UNCOV
7528
            this.zone.onStable.pipe(first()).subscribe(() => {
×
UNCOV
7529
                this.parentVirtDir.chunkLoad.emit(this.headerContainer.state);
×
UNCOV
7530
                requestAnimationFrame(() => {
×
UNCOV
7531
                    this.autoSizeColumnsInView();
×
7532
                });
7533
            });
7534
        });
UNCOV
7535
        if (!this.navigation.isColumnFullyVisible(this.navigation.lastColumnIndex)) {
×
UNCOV
7536
            this.hideOverlays();
×
7537
        }
UNCOV
7538
        const args: IGridScrollEventArgs = { direction: 'horizontal', event, scrollPosition: this.headerContainer.scrollPosition };
×
UNCOV
7539
        this.gridScroll.emit(args);
×
7540
    }
7541

7542
    protected get renderedActualRowHeight() {
UNCOV
7543
        let border = 1;
×
UNCOV
7544
        if (this.rowList.toArray().length > 0) {
×
UNCOV
7545
            const rowStyles = this.document.defaultView.getComputedStyle(this.rowList.first.nativeElement);
×
UNCOV
7546
            border = rowStyles.borderBottomWidth ? Math.ceil(parseFloat(rowStyles.borderBottomWidth)) : border;
×
7547
        }
UNCOV
7548
        return this.rowHeight + border;
×
7549
    }
7550

7551
    private executeCallback(rowIndex, visibleColIndex = -1, cb: (args: any) => void = null) {
×
UNCOV
7552
        if (!cb) {
×
UNCOV
7553
            return;
×
7554
        }
UNCOV
7555
        let row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
×
UNCOV
7556
        if (!row) {
×
UNCOV
7557
            if ((this as any).totalItemCount) {
×
7558
                this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
×
7559
                    this.cdr.detectChanges();
×
7560
                    row = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).find(r => r.index === rowIndex);
×
7561
                    const cbArgs = this.getNavigationArguments(row, visibleColIndex);
×
7562
                    cb(cbArgs);
×
7563
                });
7564
            }
UNCOV
7565
            const dataViewIndex = this._getDataViewIndex(rowIndex);
×
UNCOV
7566
            if (this.dataView[dataViewIndex].detailsData) {
×
UNCOV
7567
                this.navigation.setActiveNode({ row: rowIndex });
×
UNCOV
7568
                this.cdr.detectChanges();
×
7569
            }
7570

UNCOV
7571
            return;
×
7572
        }
UNCOV
7573
        const args = this.getNavigationArguments(row, visibleColIndex);
×
UNCOV
7574
        cb(args);
×
7575
    }
7576

7577
    private getNavigationArguments(row, visibleColIndex) {
7578
        let targetType: GridKeydownTargetType; let target;
UNCOV
7579
        switch (row.nativeElement.tagName.toLowerCase()) {
×
7580
            case 'igx-grid-groupby-row':
UNCOV
7581
                targetType = 'groupRow';
×
UNCOV
7582
                target = row;
×
UNCOV
7583
                break;
×
7584
            case 'igx-grid-summary-row':
UNCOV
7585
                targetType = 'summaryCell';
×
UNCOV
7586
                target = visibleColIndex !== -1 ?
×
UNCOV
7587
                    row.summaryCells.find(c => c.visibleColumnIndex === visibleColIndex) : row.summaryCells.first;
×
UNCOV
7588
                break;
×
7589
            case 'igx-child-grid-row':
7590
                targetType = 'hierarchicalRow';
×
7591
                target = row;
×
7592
                break;
×
7593
            default:
UNCOV
7594
                targetType = 'dataCell';
×
UNCOV
7595
                target = visibleColIndex !== -1 ? row.cells.find(c => c.visibleColumnIndex === visibleColIndex) : row.cells.first;
×
UNCOV
7596
                break;
×
7597
        }
UNCOV
7598
        return { targetType, target };
×
7599
    }
7600

7601
    private getNextDataRowIndex(currentRowIndex, previous = false): number {
×
UNCOV
7602
        const resolvedIndex = this._getDataViewIndex(currentRowIndex);
×
UNCOV
7603
        if (currentRowIndex < 0 || (currentRowIndex === 0 && previous) || (resolvedIndex >= this.dataView.length - 1 && !previous)) {
×
UNCOV
7604
            return currentRowIndex;
×
7605
        }
7606
        // find next/prev record that is editable.
UNCOV
7607
        const nextRowIndex = previous ? this.findPrevEditableDataRowIndex(currentRowIndex) :
×
7608
            this.dataView.findIndex((rec, index) =>
UNCOV
7609
                index > resolvedIndex && this.isEditableDataRecordAtIndex(index));
×
UNCOV
7610
        const nextDataIndex = this.getDataIndex(nextRowIndex);
×
UNCOV
7611
        return nextDataIndex !== -1 ? nextDataIndex : currentRowIndex;
×
7612
    }
7613

7614
    /**
7615
     * Returns the previous editable row index or -1 if no such row is found.
7616
     *
7617
     * @param currentIndex The index of the current editable record.
7618
     */
7619
    private findPrevEditableDataRowIndex(currentIndex): number {
UNCOV
7620
        let i = this.dataView.length;
×
UNCOV
7621
        const resolvedIndex = this._getDataViewIndex(currentIndex);
×
UNCOV
7622
        while (i--) {
×
UNCOV
7623
            if (i < resolvedIndex && this.isEditableDataRecordAtIndex(i)) {
×
UNCOV
7624
                return i;
×
7625
            }
7626
        }
7627
        return -1;
×
7628
    }
7629

7630

7631
    /**
7632
     * Returns if the record at the specified data view index is a an editable data record.
7633
     * If record is group rec, summary rec, child rec, ghost rec. etc. it is not editable.
7634
     *
7635
     * @param dataViewIndex The index of that record in the data view.
7636
     *
7637
     */
7638
    // TODO: Consider moving it into CRUD
7639
    private isEditableDataRecordAtIndex(dataViewIndex) {
UNCOV
7640
        const rec = this.dataView[dataViewIndex];
×
UNCOV
7641
        return !rec.expression && !rec.summaries && !rec.childGridsData && !rec.detailsData &&
×
7642
            !this.isGhostRecordAtIndex(dataViewIndex);
7643
    }
7644

7645
    /**
7646
     * Returns if the record at the specified data view index is a ghost.
7647
     * If record is pinned but is not in pinned area then it is a ghost record.
7648
     *
7649
     * @param dataViewIndex The index of that record in the data view.
7650
     */
7651
    private isGhostRecordAtIndex(dataViewIndex) {
UNCOV
7652
        const isPinned = this.isRecordPinned(this.dataView[dataViewIndex]);
×
UNCOV
7653
        const isInPinnedArea = this.isRecordPinnedByViewIndex(dataViewIndex);
×
UNCOV
7654
        return isPinned && !isInPinnedArea;
×
7655
    }
7656

7657
    private isValidPosition(rowIndex, colIndex): boolean {
UNCOV
7658
        const rows = this.summariesRowList.filter(s => s.index !== 0).concat(this.rowList.toArray()).length;
×
UNCOV
7659
        const cols = this._columns.filter(col => !col.columnGroup && col.visibleIndex >= 0 && !col.hidden).length;
×
UNCOV
7660
        if (rows < 1 || cols < 1) {
×
UNCOV
7661
            return false;
×
7662
        }
UNCOV
7663
        if (rowIndex > -1 && rowIndex < this.dataView.length &&
×
UNCOV
7664
            colIndex > - 1 && colIndex <= Math.max(...this.visibleColumns.map(c => c.visibleIndex))) {
×
UNCOV
7665
            return true;
×
7666
        }
UNCOV
7667
        return false;
×
7668
    }
7669

7670
    private find(text: string, increment: number, caseSensitive?: boolean, exactMatch?: boolean, scroll?: boolean, endEdit = true) {
×
UNCOV
7671
        if (!this.rowList) {
×
7672
            return 0;
×
7673
        }
7674

UNCOV
7675
        if (endEdit) {
×
UNCOV
7676
            this.crudService.endEdit(false);
×
7677
        }
7678

UNCOV
7679
        if (!text) {
×
7680
            this.clearSearch();
×
7681
            return 0;
×
7682
        }
7683

UNCOV
7684
        const caseSensitiveResolved = caseSensitive ? true : false;
×
UNCOV
7685
        const exactMatchResolved = exactMatch ? true : false;
×
UNCOV
7686
        let rebuildCache = false;
×
7687

UNCOV
7688
        if (this._lastSearchInfo.searchText !== text ||
×
7689
            this._lastSearchInfo.caseSensitive !== caseSensitiveResolved ||
7690
            this._lastSearchInfo.exactMatch !== exactMatchResolved) {
UNCOV
7691
            this._lastSearchInfo = {
×
7692
                searchText: text,
7693
                activeMatchIndex: 0,
7694
                caseSensitive: caseSensitiveResolved,
7695
                exactMatch: exactMatchResolved,
7696
                matchInfoCache: [],
7697
                matchCount: 0,
7698
                content: ''
7699
            };
7700

UNCOV
7701
            rebuildCache = true;
×
7702
        } else {
UNCOV
7703
            this._lastSearchInfo.activeMatchIndex += increment;
×
7704
        }
7705

UNCOV
7706
        if (rebuildCache) {
×
UNCOV
7707
            this.rowList.forEach((row) => {
×
UNCOV
7708
                if (row.cells) {
×
UNCOV
7709
                    row.cells.forEach((c: IgxGridCellComponent) => {
×
UNCOV
7710
                        c.highlightText(text, caseSensitiveResolved, exactMatchResolved);
×
7711
                    });
7712
                }
7713
            });
7714

UNCOV
7715
            this.rebuildMatchCache();
×
7716
        }
7717

UNCOV
7718
        if (this._lastSearchInfo.activeMatchIndex >= this._lastSearchInfo.matchCount) {
×
UNCOV
7719
            this._lastSearchInfo.activeMatchIndex = 0;
×
UNCOV
7720
        } else if (this._lastSearchInfo.activeMatchIndex < 0) {
×
UNCOV
7721
            this._lastSearchInfo.activeMatchIndex = this._lastSearchInfo.matchCount - 1;
×
7722
        }
7723

UNCOV
7724
        if (this._lastSearchInfo.matchCount > 0) {
×
UNCOV
7725
            const matchInfo = this._lastSearchInfo.matchInfoCache[this._lastSearchInfo.activeMatchIndex];
×
UNCOV
7726
            this._lastSearchInfo = { ...this._lastSearchInfo };
×
7727

UNCOV
7728
            if (scroll !== false) {
×
UNCOV
7729
                this.scrollTo(matchInfo.row, matchInfo.column);
×
7730
            }
7731

UNCOV
7732
            this.textHighlightService.setActiveHighlight(this.id, {
×
7733
                column: matchInfo.column,
7734
                row: matchInfo.row,
7735
                index: matchInfo.index,
7736
                metadata: matchInfo.metadata,
7737
            });
7738

7739
        } else {
UNCOV
7740
            this.textHighlightService.clearActiveHighlight(this.id);
×
7741
        }
7742

UNCOV
7743
        return this._lastSearchInfo.matchCount;
×
7744
    }
7745

7746
    private rebuildMatchCache() {
UNCOV
7747
        this._lastSearchInfo.matchInfoCache = [];
×
7748

UNCOV
7749
        const caseSensitive = this._lastSearchInfo.caseSensitive;
×
UNCOV
7750
        const exactMatch = this._lastSearchInfo.exactMatch;
×
UNCOV
7751
        const searchText = caseSensitive ? this._lastSearchInfo.searchText : this._lastSearchInfo.searchText.toLowerCase();
×
UNCOV
7752
        const data = this.filteredSortedData;
×
UNCOV
7753
        const columnItems = this.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex);
×
7754

UNCOV
7755
        data.forEach((dataRow, rowIndex) => {
×
UNCOV
7756
            columnItems.forEach((c) => {
×
UNCOV
7757
                const pipeArgs = this.getColumnByName(c.field).pipeArgs;
×
UNCOV
7758
                const value = c.formatter ? c.formatter(resolveNestedPath(dataRow, c.field), dataRow) :
×
7759
                    c.dataType === 'number' ? formatNumber(resolveNestedPath(dataRow, c.field), this.locale, pipeArgs.digitsInfo) :
×
7760
                        c.dataType === 'date'
×
7761
                            ? formatDate(resolveNestedPath(dataRow, c.field), pipeArgs.format, this.locale, pipeArgs.timezone)
7762
                            : resolveNestedPath(dataRow, c.field);
UNCOV
7763
                if (value !== undefined && value !== null && c.searchable) {
×
UNCOV
7764
                    let searchValue = caseSensitive ? String(value) : String(value).toLowerCase();
×
7765

UNCOV
7766
                    if (exactMatch) {
×
UNCOV
7767
                        if (searchValue === searchText) {
×
UNCOV
7768
                            const mic: IMatchInfoCache = {
×
7769
                                row: dataRow,
7770
                                column: c.field,
7771
                                index: 0,
7772
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7773
                            };
7774

UNCOV
7775
                            this._lastSearchInfo.matchInfoCache.push(mic);
×
7776
                        }
7777
                    } else {
UNCOV
7778
                        let occurrenceIndex = 0;
×
UNCOV
7779
                        let searchIndex = searchValue.indexOf(searchText);
×
7780

UNCOV
7781
                        while (searchIndex !== -1) {
×
UNCOV
7782
                            const mic: IMatchInfoCache = {
×
7783
                                row: dataRow,
7784
                                column: c.field,
7785
                                index: occurrenceIndex++,
7786
                                metadata: new Map<string, boolean>([['pinned', this.isRecordPinnedByIndex(rowIndex)]])
7787
                            };
7788

UNCOV
7789
                            this._lastSearchInfo.matchInfoCache.push(mic);
×
7790

UNCOV
7791
                            searchValue = searchValue.substring(searchIndex + searchText.length);
×
UNCOV
7792
                            searchIndex = searchValue.indexOf(searchText);
×
7793
                        }
7794
                    }
7795
                }
7796
            });
7797
        });
7798

UNCOV
7799
        this._lastSearchInfo.matchCount = this._lastSearchInfo.matchInfoCache.length;
×
7800
    }
7801

7802
    protected updateDefaultRowHeight() {
7803
        if (this.dataRowList.length > 0 && this.dataRowList.first.cells && this.dataRowList.first.cells.length > 0) {
37!
UNCOV
7804
            const height = parseFloat(this.document.defaultView.getComputedStyle(this.dataRowList.first.cells.first.nativeElement)?.getPropertyValue('height'));
×
UNCOV
7805
            if (height) {
×
UNCOV
7806
                this._defaultRowHeight = height;
×
7807
            } else {
7808
                this._shouldRecalcRowHeight = true;
×
7809
            }
7810
        }
7811
    }
7812

7813
    // TODO: About to Move to CRUD
7814
    private configureRowEditingOverlay(rowID: any, useOuter = false) {
×
UNCOV
7815
        let settings = this.rowEditSettings;
×
UNCOV
7816
        const overlay = this.overlayService.getOverlayById(this.rowEditingOverlay.overlayId);
×
UNCOV
7817
        if (overlay) {
×
UNCOV
7818
            settings = overlay.settings;
×
7819
        }
UNCOV
7820
        settings.outlet = useOuter ? this.parentRowOutletDirective : this.rowOutletDirective;
×
UNCOV
7821
        this.rowEditPositioningStrategy.settings.container = this.tbody.nativeElement;
×
UNCOV
7822
        const pinned = this._pinnedRecordIDs.indexOf(rowID) !== -1;
×
UNCOV
7823
        const targetRow = !pinned ?
×
7824
            this.gridAPI.get_row_by_key(rowID) as IgxRowDirective
UNCOV
7825
            : this.pinnedRows.find(x => x.key === rowID) as IgxRowDirective;
×
UNCOV
7826
        if (!targetRow) {
×
7827
            return;
×
7828
        }
UNCOV
7829
        settings.target = targetRow.element.nativeElement;
×
UNCOV
7830
        this.toggleRowEditingOverlay(true);
×
7831
    }
7832

7833
    private handleColumnPinningForGroups(): void {
7834
        // When a column is a group or is inside a group, pin all related.
7835
        const pinnedColumns = [];
37✔
7836
        const unpinnedColumns = [];
37✔
7837

7838
        this._pinnedColumns.forEach(col => {
37✔
UNCOV
7839
            if (col.parent) {
×
UNCOV
7840
                col.parent.pinned = true;
×
7841
            }
UNCOV
7842
            if (col.columnGroup) {
×
UNCOV
7843
                col.children.forEach(child => child.pinned = true);
×
7844
            }
7845
        });
7846

7847
        // Make sure we don't exceed unpinned area min width and get pinned and unpinned col collections.
7848
        // We take into account top level columns (top level groups and non groups).
7849
        // If top level is unpinned the pinning handles all children to be unpinned as well.
7850
        for (const column of this._columns) {
37✔
7851
            if (column.pinned && !column.parent) {
247!
UNCOV
7852
                pinnedColumns.push(column);
×
7853
            } else if (column.pinned && column.parent) {
247!
UNCOV
7854
                if (column.topLevelParent.pinned) {
×
UNCOV
7855
                    pinnedColumns.push(column);
×
7856
                } else {
7857
                    column.pinned = false;
×
7858
                    unpinnedColumns.push(column);
×
7859
                }
7860
            } else {
7861
                unpinnedColumns.push(column);
247✔
7862
            }
7863
        }
7864
        // Assign the applicable collections.
7865
        this._pinnedColumns = pinnedColumns;
37✔
7866
        this._unpinnedColumns = unpinnedColumns;
37✔
7867
    }
7868

7869
    protected shouldRecreateColumns(oldData: any[] | null | undefined, newData: any[] | null | undefined): boolean {
UNCOV
7870
        if (!oldData || !oldData.length) return true;
×
UNCOV
7871
        if (!newData || !newData.length) return false;
×
UNCOV
7872
        return Object.keys(oldData[0]).join() !== Object.keys(newData[0]).join();
×
7873
    }
7874

7875
    /**
7876
     * Clears the current navigation service active node
7877
     */
7878
    private clearActiveNode() {
UNCOV
7879
        this.navigation.lastActiveNode = this.navigation.activeNode;
×
UNCOV
7880
        this.navigation.activeNode = {} as IActiveNode;
×
UNCOV
7881
        this.notifyChanges();
×
7882
    }
7883
}
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