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

IgniteUI / igniteui-angular / 6146749308

11 Sep 2023 01:00PM CUT coverage: 92.266% (+0.003%) from 92.263%
6146749308

push

github

web-flow
Merge pull request #13406 from IgniteUI/bpachilova/fix-13392-16.0.x

fix(grid): round column width when set in percentages - 16.0.x

15312 of 17991 branches covered (0.0%)

26844 of 29094 relevant lines covered (92.27%)

29493.99 hits per line

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

91.23
/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts
1
import {
2
    AfterContentInit,
3
    AfterViewInit,
4
    ChangeDetectionStrategy,
5
    ChangeDetectorRef,
6
    Component,
7
    EventEmitter,
8
    ElementRef,
9
    HostBinding,
10
    Inject,
11
    Input,
12
    IterableDiffers,
13
    LOCALE_ID,
14
    NgZone,
15
    OnInit,
16
    Output,
17
    Optional,
18
    QueryList,
19
    TemplateRef,
20
    ViewChild,
21
    ViewChildren,
22
    ViewContainerRef,
23
    Injector,
24
    ApplicationRef,
25
    ContentChild,
26
    createComponent,
27
    EnvironmentInjector,
28
    CUSTOM_ELEMENTS_SCHEMA
29
} from '@angular/core';
30
import { DOCUMENT, NgTemplateOutlet, NgIf, NgClass, NgStyle, NgFor } from '@angular/common';
31

32
import { IgxGridBaseDirective } from '../grid-base.directive';
33
import { IgxFilteringService } from '../filtering/grid-filtering.service';
34
import { IgxGridSelectionService } from '../selection/selection.service';
35
import { IgxForOfSyncService, IgxForOfScrollSyncService } from '../../directives/for-of/for_of.sync.service';
36
import { ColumnType, GridType, IGX_GRID_BASE, RowType } from '../common/grid.interface';
37
import { IgxGridCRUDService } from '../common/crud.service';
38
import { IgxGridSummaryService } from '../summaries/grid-summary.service';
39
import { DEFAULT_PIVOT_KEYS, IDimensionsChange, IgxPivotGridValueTemplateContext, IPivotConfiguration, IPivotConfigurationChangedEventArgs, IPivotDimension, IPivotValue, IValuesChange, PivotDimensionType } from './pivot-grid.interface';
40
import { IgxPivotHeaderRowComponent } from './pivot-header-row.component';
41
import { IgxColumnGroupComponent } from '../columns/column-group.component';
42
import { IgxColumnComponent } from '../columns/column.component';
43
import { PivotUtil } from './pivot-util';
44
import { FilterMode, GridPagingMode, GridSummaryCalculationMode, GridSummaryPosition } from '../common/enums';
45
import { WatchChanges } from '../watch-changes';
46
import { OverlaySettings } from '../../services/public_api';
47
import {
48
    ICellPosition,
49
    IColumnMovingEndEventArgs, IColumnMovingEventArgs, IColumnMovingStartEventArgs,
50
    IColumnVisibilityChangedEventArgs, IGridEditDoneEventArgs, IGridEditEventArgs,
51
    IGridToolbarExportEventArgs,
52
    IPinColumnCancellableEventArgs, IPinColumnEventArgs, IPinRowEventArgs, IRowDataEventArgs, IRowDragEndEventArgs, IRowDragStartEventArgs
2✔
53
} from '../common/events';
2✔
54
import { IgxGridRowComponent } from '../grid/grid-row.component';
2✔
55
import { DropPosition } from '../moving/moving.service';
56
import { DimensionValuesFilteringStrategy, NoopPivotDimensionsStrategy } from '../../data-operations/pivot-strategy';
57
import { IgxGridExcelStyleFilteringComponent, IgxExcelStyleColumnOperationsTemplateDirective, IgxExcelStyleFilterOperationsTemplateDirective } from '../filtering/excel-style/excel-style-filtering.component';
58
import { IgxPivotGridNavigationService } from './pivot-grid-navigation.service';
59
import { IgxPivotColumnResizingService } from '../resizing/pivot-grid/pivot-resizing.service';
60
import { IgxFlatTransactionFactory, IgxOverlayService, State, Transaction, TransactionService } from '../../services/public_api';
61
import { DisplayDensity, DisplayDensityToken, IDensityChangedEventArgs, IDisplayDensityOptions } from '../../core/density';
62
import { cloneArray, PlatformUtil } from '../../core/utils';
63
import { IgxPivotFilteringService } from './pivot-filtering.service';
64
import { DataUtil } from '../../data-operations/data-util';
65
import { IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
66
import { IgxGridTransaction } from '../common/types';
67
import { GridBaseAPIService } from '../api.service';
68
import { IgxGridForOfDirective } from '../../directives/for-of/for_of.directive';
69
import { IgxPivotRowDimensionContentComponent } from './pivot-row-dimension-content.component';
70
import { IgxPivotGridColumnResizerComponent } from '../resizing/pivot-grid/pivot-resizer.component';
71
import { IgxActionStripComponent } from '../../action-strip/action-strip.component';
2✔
72
import { ISortingExpression, SortingDirection } from '../../data-operations/sorting-strategy';
73
import { PivotSortUtil } from './pivot-sort-util';
74
import { IFilteringStrategy } from '../../data-operations/filtering-strategy';
75
import { IgxPivotValueChipTemplateDirective } from './pivot-grid.directives';
76
import { IFilteringOperation } from '../../data-operations/filtering-condition';
77
import { IgxGridValidationService } from '../grid/grid-validation.service';
78
import { IgxPivotRowPipe, IgxPivotRowExpansionPipe, IgxPivotAutoTransform, IgxPivotColumnPipe, IgxPivotGridFilterPipe, IgxPivotGridSortingPipe, IgxPivotGridColumnSortingPipe, IgxPivotCellMergingPipe } from './pivot-grid.pipes';
79
import { IgxGridRowClassesPipe, IgxGridRowStylesPipe } from '../common/pipes';
80
import { IgxExcelStyleSearchComponent } from '../filtering/excel-style/excel-style-search.component';
81
import { IgxIconComponent } from '../../icon/icon.component';
20✔
82
import { IgxSnackbarComponent } from '../../snackbar/snackbar.component';
20✔
83
import { IgxCircularProgressBarComponent } from '../../progressbar/progressbar.component';
20✔
84
import { IgxToggleDirective, IgxOverlayOutletDirective } from '../../directives/toggle/toggle.directive';
85
import { IgxPivotRowComponent } from './pivot-row.component';
86
import { IgxTemplateOutletDirective } from '../../directives/template-outlet/template_outlet.directive';
121✔
87
import { IgxColumnMovingDropDirective } from '../moving/moving.drop.directive';
121✔
88
import { IgxGridDragSelectDirective } from '../selection/drag-select.directive';
121✔
89
import { IgxGridBodyDirective } from '../grid.common';
121✔
90
import { IgxColumnResizingService } from '../resizing/resizing.service';
16✔
91
import { DefaultDataCloneStrategy, IDataCloneStrategy } from '../../data-operations/data-clone-strategy';
92

121✔
93
let NEXT_ID = 0;
94
const MINIMUM_COLUMN_WIDTH = 200;
95
const MINIMUM_COLUMN_WIDTH_SUPER_COMPACT = 104;
700,638✔
96

97
/**
98
 * Pivot Grid provides a way to present and manipulate data in a pivot table view.
300,612✔
99
 *
100
 * @igxModule IgxPivotGridModule
101
 * @igxGroup Grids & Lists
2✔
102
 * @igxKeywords pivot, grid, table
103
 * @igxTheme igx-grid-theme
2✔
104
 * @remarks
2✔
105
 * The Ignite UI Pivot Grid is used for grouping and aggregating simple flat data into a pivot table.  Once data
106
 * has been bound and the dimensions and values configured it can be manipulated via sorting and filtering.
107
 * @example
108
 * ```html
202,563✔
109
 * <igx-pivot-grid [data]="data" [pivotConfiguration]="configuration">
323✔
110
 * </igx-pivot-grid>
111
 * ```
202,240✔
112
 */
113
@Component({
114
    changeDetection: ChangeDetectionStrategy.OnPush,
115
    preserveWhitespaces: false,
116
    selector: 'igx-pivot-grid',
117
    templateUrl: 'pivot-grid.component.html',
2✔
118
    providers: [
2✔
119
        IgxGridCRUDService,
2!
120
        IgxGridValidationService,
2✔
121
        IgxGridSummaryService,
122
        IgxGridSelectionService,
123
        IgxColumnResizingService,
124
        GridBaseAPIService,
2✔
125
        { provide: IGX_GRID_BASE, useExisting: IgxPivotGridComponent },
126
        { provide: IgxFilteringService, useClass: IgxPivotFilteringService },
127
        IgxPivotGridNavigationService,
128
        IgxPivotColumnResizingService,
3,304✔
129
        IgxForOfSyncService,
130
        IgxForOfScrollSyncService
131
    ],
×
132
    standalone: true,
×
133
    imports: [
134
        NgIf,
135
        NgFor,
136
        NgClass,
137
        NgStyle,
138
        NgTemplateOutlet,
139
        IgxPivotHeaderRowComponent,
4,790✔
140
        IgxGridBodyDirective,
1✔
141
        IgxGridDragSelectDirective,
142
        IgxColumnMovingDropDirective,
143
        IgxGridForOfDirective,
4,789✔
144
        IgxTemplateOutletDirective,
145
        IgxPivotRowComponent,
146
        IgxToggleDirective,
147
        IgxCircularProgressBarComponent,
148
        IgxSnackbarComponent,
119,087✔
149
        IgxOverlayOutletDirective,
150
        IgxPivotGridColumnResizerComponent,
151
        IgxIconComponent,
152
        IgxPivotRowDimensionContentComponent,
153
        IgxGridExcelStyleFilteringComponent,
154
        IgxExcelStyleColumnOperationsTemplateDirective,
4,456✔
155
        IgxExcelStyleFilterOperationsTemplateDirective,
156
        IgxExcelStyleSearchComponent,
157
        IgxGridRowClassesPipe,
158
        IgxGridRowStylesPipe,
159
        IgxPivotRowPipe,
160
        IgxPivotRowExpansionPipe,
407✔
161
        IgxPivotAutoTransform,
162
        IgxPivotColumnPipe,
163
        IgxPivotGridFilterPipe,
1,544✔
164
        IgxPivotGridSortingPipe,
165
        IgxPivotGridColumnSortingPipe,
166
        IgxPivotCellMergingPipe
95✔
167
    ],
168
    schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
169
})
×
170
export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnInit, AfterContentInit,
171
    GridType, AfterViewInit {
172

173
    /**
174
     * Emitted when the dimension collection is changed via the grid chip area.
×
175
     *
176
     * @remarks
177
     * Returns the new dimension collection and its type:
178
     * @example
179
     * ```html
×
180
     * <igx-pivot-grid #grid [data]="localData" [height]="'305px'"
181
     *              (dimensionsChange)="dimensionsChange($event)"></igx-grid>
182
     * ```
183
     */
184
    @Output()
×
185
    public dimensionsChange = new EventEmitter<IDimensionsChange>();
186

187
    /**
188
     * Emitted when any of the pivotConfiguration properties is changed via the grid chip area.
189
     *
×
190
     * @example
191
     * ```html
192
     * <igx-pivot-grid #grid [data]="localData" [height]="'305px'"
193
     *              (pivotConfigurationChanged)="configurationChanged($event)"></igx-grid>
194
     * ```
36,063✔
195
     */
196
    @Output()
197
    public pivotConfigurationChange = new EventEmitter<IPivotConfigurationChangedEventArgs>();
198

199

×
200
    /**
201
     * Emitted when the dimension is initialized.
202
     * @remarks
203
     * Emits the dimension that is about to be initialized.
204
     * @example
×
205
     * ```html
206
     * <igx-pivot-grid #grid [data]="localData" [height]="'305px'"
207
     *              (dimensionInit)="dimensionInit($event)"></igx-pivot-grid>
208
     * ```
209
     */
210
    @Output()
211
    public dimensionInit = new EventEmitter<IPivotDimension>();
212

106,685✔
213
    /**
214
     * Emitted when the value is initialized.
215
     * @remarks
216
     * Emits the value that is about to be initialized.
217
     * @example
218
     * ```html
20,891✔
219
     * <igx-pivot-grid #grid [data]="localData" [height]="'305px'"
220
     *              (valueInit)="valueInit($event)"></igx-pivot-grid>
221
     * ```
222
     */
223
    @Output()
224
    public valueInit = new EventEmitter<IPivotValue>();
×
225

226

227
    /**
228
     * Emitted when a dimension is sorted.
229
     *
230
     * @example
17,744✔
231
     * ```html
232
     * <igx-pivot-grid #grid [data]="localData" [height]="'305px'"
233
     *              (dimensionsSortingExpressionsChange)="dimensionsSortingExpressionsChange($event)"></igx-pivot-grid>
234
     * ```
235
     */
236
    @Output()
6,145✔
237
    public dimensionsSortingExpressionsChange = new EventEmitter<ISortingExpression[]>();
238

239
    /**
240
     * Emitted when the values collection is changed via the grid chip area.
241
     *
381✔
242
     * @remarks
243
     * Returns the new dimension
244
     * @example
245
     * ```html
246
     * <igx-pivot-grid #grid [data]="localData" [height]="'305px'"
247
     *              (valuesChange)="valuesChange($event)"></igx-grid>
385,673✔
248
     * ```
249
    */
250
    @Output()
251
    public valuesChange = new EventEmitter<IValuesChange>();
252

253

×
254
    /**
255
     * Gets the sorting expressions generated for the dimensions.
256
     *
257
     * @example
258
     * ```typescript
163,905✔
259
     * const expressions = this.grid.dimensionsSortingExpressions;
260
     * ```
261
     */
262
    public get dimensionsSortingExpressions() {
263
        const allEnabledDimensions = this.rowDimensions.concat(this.columnDimensions);
135,336✔
264
        const dimensionsSortingExpressions = PivotSortUtil.generateDimensionSortingExpressions(allEnabledDimensions);
265
        return dimensionsSortingExpressions;
266
    }
267

268
    /** @hidden @internal */
×
269
    @ViewChild(IgxPivotHeaderRowComponent, { static: true })
270
    public override theadRow: IgxPivotHeaderRowComponent;
271

272
    /**
273
    * @hidden @internal
×
274
    */
275
    @ContentChild(IgxPivotValueChipTemplateDirective, { read: IgxPivotValueChipTemplateDirective })
276
    protected valueChipTemplateDirective: IgxPivotValueChipTemplateDirective;
277

278
    /**
×
279
     * Gets/Sets a custom template for the value chips.
280
     *
281
     * @example
282
     * ```html
283
     * <igx-pivot-grid [valueChipTemplate]="myTemplate"><igx-pivot-grid>
284
     * ```
285
     */
286
     @Input()
×
287
     public valueChipTemplate: TemplateRef<IgxPivotGridValueTemplateContext>;
288

289
    @Input()
290
    /**
291
     * Gets/Sets the pivot configuration with all related dimensions and values.
292
     *
×
293
     * @example
294
     * ```html
295
     * <igx-pivot-grid [pivotConfiguration]="config"></igx-pivot-grid>
×
296
     * ```
297
     */
298
    public set pivotConfiguration(value: IPivotConfiguration) {
299
        this._pivotConfiguration = value;
300
        this.emitInitEvents(this._pivotConfiguration);
12✔
301
        this.filteringExpressionsTree = PivotUtil.buildExpressionTree(value);
3✔
302
        if (!this._init) {
303
            this.setupColumns();
9✔
304
        }
9✔
305
        this.notifyChanges(true);
140✔
306
    }
140✔
307

261✔
308
    public get pivotConfiguration() {
261✔
309
        return this._pivotConfiguration || { rows: null, columns: null, values: null, filters: null };
261✔
310
    }
483✔
311

483✔
312
    @Input()
23✔
313
    /**
23✔
314
     * Gets/Sets the pivot configuration ui for the pivot grid - chips and their
23✔
315
     * corresponding containers for row, filter, column dimensions and values
316
     *
460✔
317
     * @example
318
     * ```html
261✔
319
     * <igx-pivot-grid [showPivotConfigurationUI]="false"></igx-pivot-grid>
261✔
320
     * ```
23✔
321
     */
322
    public showPivotConfigurationUI = true;
323

324
    /**
9✔
325
     * @hidden @internal
326
     */
327
    @HostBinding('attr.role')
328
    public role = 'grid';
329

330

331
    /**
332
     * Enables a super compact theme for the component.
333
     * @remarks
334
     * Overrides the displayDensity option if one is set.
335
     * @example
91,847✔
336
     * ```html
316✔
337
     * <igx-pivot-grid [superCompactMode]="true"></igx-pivot-grid>
338
     * ```
91,531✔
339
     */
340
    @HostBinding('class.igx-grid__pivot--super-compact')
341
    @Input()
105✔
342
    public get superCompactMode() {
105✔
343
        return this._superCompactMode;
105✔
344
    }
105✔
345

105✔
346
    public set superCompactMode(value) {
105✔
347
        Promise.resolve().then(() => {
105✔
348
            // wait for the current detection cycle to end before triggering a new one.
105✔
349
            this._superCompactMode = value;
105✔
350
            this.cdr.detectChanges();
105✔
351
        });
105✔
352
    }
105✔
353

105✔
354
    /**
105✔
355
    * Returns the theme of the component.
105✔
356
    * The default theme is `comfortable`.
105✔
357
    * Available options are `comfortable`, `cosy`, `compact`.
105✔
358
    * @remarks
105✔
359
    * If set while superCompactMode is enabled will have no affect.
105✔
360
    * ```typescript
105✔
361
    * let componentTheme = this.component.displayDensity;
105✔
362
    * ```
105✔
363
    */
105✔
364
    @Input()
105✔
365
    public override get displayDensity(): DisplayDensity {
105✔
366
        if (this.superCompactMode) {
105✔
367
            return DisplayDensity.compact;
105✔
368
        }
105✔
369
        return super.displayDensity;
105✔
370
    }
105✔
371

105✔
372
    /**
105✔
373
    * Sets the theme of the component.
374
    */
105✔
375
    public override set displayDensity(val: DisplayDensity) {
376
        const currentDisplayDensity = this._displayDensity;
105✔
377
        this._displayDensity = val as DisplayDensity;
378

379
        if (currentDisplayDensity !== this._displayDensity) {
380
            const densityChangedArgs: IDensityChangedEventArgs = {
105✔
381
                oldDensity: currentDisplayDensity,
382
                newDensity: this._displayDensity
383
            };
384

105✔
385
            this.densityChanged.emit(densityChangedArgs);
105✔
386
        }
105✔
387
    }
105✔
388

105✔
389
    /**
105✔
390
     * Gets/Sets the values clone strategy of the pivot grid when assigning them to different dimensions.
105✔
391
     *
105✔
392
     * @example
393
     * ```html
394
     *  <igx-pivot-grid #grid [data]="localData" [pivotValueCloneStrategy]="customCloneStrategy"></igx-pivot-grid>
395
     * ```
105✔
396
     * @hidden @internal
397
     */
398
    @Input()
399
    public get pivotValueCloneStrategy(): IDataCloneStrategy {
105✔
400
        return this._pivotValueCloneStrategy;
401
    }
402

403
    public set pivotValueCloneStrategy(strategy: IDataCloneStrategy) {
105✔
404
        if (strategy) {
405
            this._pivotValueCloneStrategy = strategy;
406
        }
407
    }
408

409
    /**
410
     * @hidden @internal
105✔
411
     */
105✔
412
    @ViewChild('record_template', { read: TemplateRef, static: true })
413
    public recordTemplate: TemplateRef<any>;
414

415
    /**
416
     * @hidden @internal
417
     */
418
    @ViewChild('headerTemplate', { read: TemplateRef, static: true })
105✔
419
    public headerTemplate: TemplateRef<any>;
105✔
420

105✔
421
    /**
422
     * @hidden @internal
105!
423
     */
×
424
    @ViewChild(IgxPivotGridColumnResizerComponent)
425
    public override resizeLine: IgxPivotGridColumnResizerComponent;
426

427
    /**
428
     * @hidden @internal
429
     */
430
    @ViewChildren(IgxGridExcelStyleFilteringComponent, { read: IgxGridExcelStyleFilteringComponent })
105✔
431
    public override excelStyleFilteringComponents: QueryList<IgxGridExcelStyleFilteringComponent>;
105✔
432

433
    /**
434
     * @hidden @internal
435
     */
436
    @ViewChildren(IgxPivotRowDimensionContentComponent)
437
    protected rowDimensionContentCollection: QueryList<IgxPivotRowDimensionContentComponent>;
×
438

19!
439
    /**
19✔
440
     * @hidden @internal
441
     */
19✔
442
    protected override get minColumnWidth() {
19✔
443
        if (this.superCompactMode) {
444
            return MINIMUM_COLUMN_WIDTH_SUPER_COMPACT;
445
        } else {
446
            return MINIMUM_COLUMN_WIDTH;
447
        }
448
    }
449

450
    /**
451
     * @hidden @internal
452
     */
453
    @ViewChildren('verticalRowDimScrollContainer', { read: IgxGridForOfDirective })
1,545✔
454
    public verticalRowDimScrollContainers: QueryList<IgxGridForOfDirective<any, any[]>>;
1,545✔
455

8✔
456
    /**
3,812!
457
     * @hidden @internal
458
     */
459
    @Input()
460
    public override addRowEmptyTemplate: TemplateRef<void>;
5✔
461

5!
462
    /**
5✔
463
     * @hidden @internal
5✔
464
     */
3✔
465
    @Input()
3✔
466
    public override autoGenerateExclude: string[] = [];
3✔
467

468
    /**
2✔
469
     * @hidden @internal
470
     */
471
    @Input()
472
    public override snackbarDisplayTime = 6000;
473

7,203✔
474
    /**
475
     * @hidden @internal
476
     */
63,916✔
477
    @Output()
478
    public override cellEdit = new EventEmitter<IGridEditEventArgs>();
479

×
480
    /**
481
     * @hidden @internal
482
     */
109!
483
    @Output()
109✔
484
    public override cellEditDone = new EventEmitter<IGridEditDoneEventArgs>();
4✔
485

4✔
486
    /**
487
     * @hidden @internal
109✔
488
     */
109✔
489
    @Output()
490
    public override cellEditEnter = new EventEmitter<IGridEditEventArgs>();
36✔
491

492
    /**
493
     * @hidden @internal
494
     */
495
    @Output()
496
    public override cellEditExit = new EventEmitter<IGridEditDoneEventArgs>();
497

498
    /**
499
     * @hidden @internal
500
     */
6,624✔
501
    @Output()
502
    public override columnMovingStart = new EventEmitter<IColumnMovingStartEventArgs>();
503

504
    /**
505
     * @hidden @internal
506
     */
8,023✔
507
    @Output()
508
    public override columnMoving = new EventEmitter<IColumnMovingEventArgs>();
509

510
    /**
511
     * @hidden @internal
512
     */
513
    @Output()
514
    public override columnMovingEnd = new EventEmitter<IColumnMovingEndEventArgs>();
515

516
    /**
517
     * @hidden @internal
518
     */
519
    @Output()
17,036✔
520
    public override columnPin = new EventEmitter<IPinColumnCancellableEventArgs>();
521

522
    /**
523
     * @hidden @internal
524
     */
525
    @Output()
56,276✔
526
    public override columnPinned = new EventEmitter<IPinColumnEventArgs>();
56,276✔
527

11,292✔
528
    /**
529
     * @hidden @internal
44,984✔
530
     */
43,298✔
531
    @Output()
532
    public override rowAdd = new EventEmitter<IGridEditEventArgs>();
1,686✔
533

1,686✔
534
    /**
478✔
535
     * @hidden @internal
536
     */
537
    @Output()
1,208✔
538
    public override rowAdded = new EventEmitter<IRowDataEventArgs>();
539

540
    /**
541
     * @hidden @internal
542
     */
543
    @Output()
544
    public override rowDeleted = new EventEmitter<IRowDataEventArgs>();
1✔
545

546
    /**
547
     * @hidden @internal
548
     */
1,544✔
549
    @Output()
2,172✔
550
    public override rowDelete = new EventEmitter<IGridEditEventArgs>();
551

1,544✔
552
    /**
553
     * @hidden @internal
554
     */
555
    @Output()
7,720!
556
    public override rowDragStart = new EventEmitter<IRowDragStartEventArgs>();
557

558
    /**
559
     * @hidden @internal
1,544✔
560
     */
561
    @Output()
562
    public override rowDragEnd = new EventEmitter<IRowDragEndEventArgs>();
563

131,450✔
564
    /**
565
     * @hidden @internal
566
     */
567
    @Output()
223,744✔
568
    public override rowEditEnter = new EventEmitter<IGridEditEventArgs>();
569

570
    /**
571
     * @hidden @internal
4,757✔
572
     */
573
    @Output()
574
    public override rowEdit = new EventEmitter<IGridEditEventArgs>();
575

494,719✔
576
    /**
577
     * @hidden @internal
578
     */
4✔
579
    @Output()
4✔
580
    public override rowEditDone = new EventEmitter<IGridEditDoneEventArgs>();
4✔
581

4✔
582
    /**
4✔
583
     * @hidden @internal
584
     */
585
    @Output()
×
586
    public override rowEditExit = new EventEmitter<IGridEditDoneEventArgs>();
×
587

×
588
    /**
589
     * @hidden @internal
590
     */
591
    @Output()
592
    public override rowPinning = new EventEmitter<IPinRowEventArgs>();
593

×
594
    /**
595
     * @hidden @internal
596
     */
597
    @Output()
598
    public override rowPinned = new EventEmitter<IPinRowEventArgs>();
599

×
600
    /** @hidden @internal */
601
    public columnGroupStates = new Map<string, boolean>();
602
    /** @hidden @internal */
603
    public dimensionDataColumns;
604
    /** @hidden @internal */
605
    public get pivotKeys() {
606
        return this.pivotConfiguration.pivotKeys || DEFAULT_PIVOT_KEYS;
607
    }
608
    /** @hidden @internal */
609
    public override isPivot = true;
610

611
    /**
612
     * @hidden @internal
613
     */
614
    public override dragRowID = null;
615

616
    /**
617
    * @hidden @internal
618
    */
619
    public override get rootSummariesEnabled(): boolean {
620
        return false;
621
    }
622

623
    /**
624
     * @hidden @internal
625
     */
2✔
626
    public rowDimensionResizing = true;
627

628
    private _emptyRowDimension: IPivotDimension = { memberName: '', enabled: true, level: 0 };
×
629
    /**
630
     * @hidden @internal
631
     */
632
    public get emptyRowDimension(): IPivotDimension {
633
        return this._emptyRowDimension;
634
    }
635

×
636
    protected _pivotValueCloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy();
637
    protected override _defaultExpandState = false;
638
    protected override _filterStrategy: IFilteringStrategy = new DimensionValuesFilteringStrategy();
639
    private _data;
640
    private _pivotConfiguration: IPivotConfiguration = { rows: null, columns: null, values: null, filters: null };
641
    private p_id = `igx-pivot-grid-${NEXT_ID++}`;
642
    private _superCompactMode = false;
643

644
    /**
645
    * Gets/Sets the default expand state for all rows.
646
    */
647
    @Input()
648
    public get defaultExpandState() {
649
        return this._defaultExpandState;
650
    }
651

652
    public set defaultExpandState(val: boolean) {
653
        this._defaultExpandState = val;
654
    }
655

656
    /**
657
     * @hidden @internal
658
     */
659
    @Input()
660
    public override get pagingMode() {
661
        return;
662
    }
663

664
    public override set pagingMode(_val: GridPagingMode) {
665
    }
666

667
    /**
668
     * @hidden @internal
669
     */
670
    @WatchChanges()
671
    @Input()
×
672
    public override get hideRowSelectors() {
673
        return;
674
    }
675

676
    public override set hideRowSelectors(_value: boolean) {
677
    }
×
678

679
    /**
680
     * @hidden @internal
681
     */
682
    public override autoGenerate = true;
683

×
684
    /**
685
     * @hidden @internal
686
     */
687
    public override actionStrip: IgxActionStripComponent;
688

689
    /**
×
690
     * @hidden @internal
691
     */
692
    public override shouldGenerate: boolean;
693

694
    /**
695
     * @hidden @internal
1,925✔
696
     */
697
    public override moving = false;
698

699
    /**
700
     * @hidden @internal
701
     */
×
702
    public override toolbarExporting = new EventEmitter<IGridToolbarExportEventArgs>();
703

704
    /**
705
     * @hidden @internal
706
     */
707
    @Input()
7✔
708
    public override get rowDraggable(): boolean {
709
        return;
710
    }
711

712

713
    public override set rowDraggable(_val: boolean) {
26,270✔
714
    }
715

716
    /**
717
     * @hidden @internal
718
     */
719
    @Input()
8,023✔
720
    public override get allowAdvancedFiltering() {
721
        return false;
722
    }
723

724
    public override set allowAdvancedFiltering(_value) {
725
    }
726

727
    /**
728
     * @hidden @internal
729
     */
730
    @Input()
731
    public override get filterMode() {
732
        return FilterMode.quickFilter;
733
    }
734

×
735
    public override set filterMode(_value: FilterMode) {
736
    }
737

738
    /**
739
     * @hidden @internal
740
     */
741
    @Input()
742
    public override get allowFiltering() {
743
        return false;
744
    }
745

746
    public override set allowFiltering(_value) {
747
    }
748

749
    /**
750
     * @hidden @internal
751
     */
752
    @Input()
753
    public override get page(): number {
53✔
754
        return 0;
320✔
755
    }
756

757
    public override set page(_val: number) {
758
    }
759

760
    /**
×
761
     * @hidden @internal
762
     */
763
    @Input()
764
    public override get perPage(): number {
765
        return;
766
    }
×
767

768
    public override set perPage(_val: number) {
769
    }
770

771
    /**
×
772
     * @hidden @internal
×
773
     */
774
    public override get pinnedColumns(): IgxColumnComponent[] {
775
        return [];
776
    }
777

×
778
    /**
×
779
    * @hidden @internal
780
    */
781
    public override get unpinnedColumns(): IgxColumnComponent[] {
782
        return super.unpinnedColumns;
783
    }
695✔
784

2,401✔
785
    /**
786
    * @hidden @internal
787
    */
788
    public override get unpinnedDataView(): any[] {
789
        return super.unpinnedDataView;
790
    }
3,716✔
791

792
    /**
793
    * @hidden @internal
145✔
794
    */
795
    public override get unpinnedWidth() {
145✔
796
        return super.unpinnedWidth;
797
    }
798

5!
799
    /**
×
800
     * @hidden @internal
5✔
801
     */
88!
802
    public override get pinnedWidth() {
8✔
803
        return super.pinnedWidth;
4✔
804
    }
4✔
805

4✔
806
    /**
807
     * @hidden @internal
3✔
808
     */
3✔
809
    @Input()
810
    public override set summaryRowHeight(_value: number) {
811
    }
812

1✔
813
    public override get summaryRowHeight(): number {
1✔
814
        return 0;
815
    }
816

817
    /**
5!
818
     * @hidden @internal
2✔
819
     */
2✔
820
    public override get transactions(): TransactionService<Transaction, State> {
1✔
821
        return this._transactions;
1!
822
    }
1✔
823

824

825

×
826
    /**
827
     * @hidden @internal
828
     */
829
    public override get dragIndicatorIconTemplate(): TemplateRef<any> {
830
        return;
831
    }
832

833
    public override set dragIndicatorIconTemplate(_val: TemplateRef<any>) {
206✔
834
    }
835

836
    /**
837
     * @hidden @internal
838
     */
839
    @WatchChanges()
840
    @Input()
841
    public override get rowEditable(): boolean {
842
        return;
843
    }
844

845
    public override set rowEditable(_val: boolean) {
846
    }
847

2!
848
    /**
4✔
849
     * @hidden @internal
19✔
850
     */
10✔
851
    @Input()
2✔
852
    public override get pinning() {
2✔
853
        return {};
2✔
854
    }
2✔
855
    public override set pinning(_value) {
856
    }
857

858
    /**
859
     * @hidden @internal
860
     */
861
    @Input()
862
    public override get summaryPosition() {
863
        return;
864
    }
865

866
    public override set summaryPosition(_value: GridSummaryPosition) {
867
    }
868

869
    /**
870
     * @hidden @interal
12✔
871
     */
12✔
872
    @Input()
11✔
873
    public override get summaryCalculationMode() {
874
        return;
875
    }
1✔
876

877
    public override set summaryCalculationMode(_value: GridSummaryCalculationMode) {
12✔
878
    }
4✔
879

880
    /**
12✔
881
     * @hidden @interal
12✔
882
     */
12✔
883
    @Input()
3✔
884
    public override get showSummaryOnCollapse() {
3✔
885
        return;
886
    }
12✔
887

888
    public override set showSummaryOnCollapse(_value: boolean) {
889
    }
890

891
    /**
892
     * @hidden @internal
893
     */
894
    public override get hiddenColumnsCount() {
895
        return null;
896
    }
897

898
    /**
899
     * @hidden @internal
900
     */
901
    public override get pinnedColumnsCount() {
9✔
902
        return null;
9✔
903
    }
1✔
904

905
    /**
8✔
906
     * @hidden @internal
907
     */
8✔
908
    @Input()
8✔
909
    public override get batchEditing(): boolean {
3✔
910
        return;
911
    }
912

913
    public override set batchEditing(_val: boolean) {
914
    }
915

916
    public override get selectedRows(): any[] {
917
        if (this.selectionService.getSelectedRows().length === 0) {
918
            return [];
919
        }
920
        const selectedRowIds = [];
921
        this.dataView.forEach(record => {
922
            const prev = [];
923
            for (const dim of this.rowDimensions) {
924
                let currDim = dim;
4✔
925
                let shouldBreak = false;
4✔
926
                do {
4✔
927
                    const key = PivotUtil.getRecordKey(record, currDim);
1✔
928
                    if (this.selectionService.isPivotRowSelected(key) && !selectedRowIds.find(x => x === record)) {
929
                        selectedRowIds.push(record);
4✔
930
                        shouldBreak = true;
1✔
931
                        break;
932
                    }
4✔
933
                    currDim = currDim.childLevel;
4✔
934
                } while (currDim);
935
                prev.push(dim);
936
                if (shouldBreak) {
937
                    break;
938
                }
939
            }
940

941
        });
942

943
        return selectedRowIds;
944
    }
945

946
    /**
9✔
947
     * Gets the default row height.
9✔
948
     *
1✔
949
     * @example
8✔
950
     * ```typescript
8✔
951
     * const rowHeigh = this.grid.defaultRowHeight;
8✔
952
     * ```
3✔
953
     */
954
    public override get defaultRowHeight(): number {
8✔
955
        if (this.superCompactMode) {
1✔
956
            return 24;
957
        }
8✔
958
        return super.defaultRowHeight;
8✔
959
    }
8✔
960

8✔
961
    constructor(
3✔
962
        validationService: IgxGridValidationService,
963
        selectionService: IgxGridSelectionService,
8✔
964
        colResizingService: IgxPivotColumnResizingService,
965
        gridAPI: GridBaseAPIService<IgxGridBaseDirective & GridType>,
966
        transactionFactory: IgxFlatTransactionFactory,
967
        elementRef: ElementRef<HTMLElement>,
968
        zone: NgZone,
969
        @Inject(DOCUMENT) document,
970
        cdr: ChangeDetectorRef,
971
        differs: IterableDiffers,
972
        viewRef: ViewContainerRef,
973
        appRef: ApplicationRef,
974
        injector: Injector,
975
        envInjector: EnvironmentInjector,
976
        navigation: IgxPivotGridNavigationService,
977
        filteringService: IgxFilteringService,
7✔
978
        @Inject(IgxOverlayService) overlayService: IgxOverlayService,
1✔
979
        summaryService: IgxGridSummaryService,
980
        @Optional() @Inject(DisplayDensityToken) _displayDensityOptions: IDisplayDensityOptions,
7✔
981
        @Inject(LOCALE_ID) localeId: string,
7✔
982
        platform: PlatformUtil,
6✔
983
        @Optional() @Inject(IgxGridTransaction) _diTransactions?: TransactionService<Transaction, State>) {
984
        super(
985
            validationService,
1✔
986
            selectionService,
987
            colResizingService,
7✔
988
            gridAPI,
7✔
989
            transactionFactory,
7✔
990
            elementRef,
7✔
991
            zone,
7✔
992
            document,
993
            cdr,
994
            differs,
995
            viewRef,
996
            appRef,
997
            injector,
998
            envInjector,
999
            navigation,
1000
            filteringService,
1001
            overlayService,
1002
            summaryService,
1003
            _displayDensityOptions,
1004
            localeId,
1005
            platform,
6✔
1006
            _diTransactions);
1✔
1007
    }
1008

5✔
1009
    /**
1010
     * @hidden
5✔
1011
     */
1012
    public override ngOnInit() {
1013
        // pivot grid always generates columns automatically.
1014
        this.autoGenerate = true;
1015
        super.ngOnInit();
1016
    }
1017

1018
    /**
1019
     * @hidden
1020
     */
1021
    public override ngAfterContentInit() {
1022
        // ignore any user defined columns and auto-generate based on pivot config.
1023
        this.updateColumns([]);
1024
        Promise.resolve().then(() => {
6✔
1025
            this.setupColumns();
6✔
1026
        });
6!
1027
        if (this.valueChipTemplateDirective) {
6✔
1028
            this.valueChipTemplate = this.valueChipTemplateDirective.template;
6✔
1029
        }
6✔
1030
    }
6✔
1031

6✔
1032
    /**
1033
     * @hidden @internal
1034
     */
1035
    public override ngAfterViewInit() {
1036
        Promise.resolve().then(() => {
1037
            super.ngAfterViewInit();
1038
        });
1039
    }
1040

1041
    /**
1042
     * Notifies for dimension change.
1043
     */
1044
    public notifyDimensionChange(regenerateColumns = false) {
1045
        if (regenerateColumns) {
5✔
1046
            this.setupColumns();
1✔
1047
        }
4✔
1048
        this.pipeTrigger++;
4✔
1049
        this.cdr.detectChanges();
4✔
1050
    }
4✔
1051

4✔
1052
    /**
4✔
1053
     * Gets the full list of dimensions.
1054
     *
1055
     * @example
1056
     * ```typescript
1057
     * const dimensions = this.grid.allDimensions;
1058
     * ```
1059
     */
1060
    public get allDimensions() {
1061
        const config = this._pivotConfiguration;
1062
        if (!config) return [];
1063
        return (config.rows || []).concat((config.columns || [])).concat(config.filters || []).filter(x => x !== null && x !== undefined);
18✔
1064
    }
18✔
1065

1066
    /** @hidden @internal */
18✔
1067
    public createFilterESF(dropdown: any, column: ColumnType, options: OverlaySettings, shouldReatach: boolean) {
18✔
1068
        options.outlet = this.outlet;
6✔
1069
        if (dropdown) {
6✔
1070
            dropdown.initialize(column, this.overlayService);
1071
            if (shouldReatach) {
18✔
1072
                const id = this.overlayService.attach(dropdown.element, options);
18✔
1073
                dropdown.overlayComponentId = id;
18✔
1074
                return { id, ref: undefined };
13✔
1075
            }
1076
            return { id: dropdown.overlayComponentId, ref: undefined };
18✔
1077
        }
18✔
1078
    }
1079

1080
    /** @hidden */
1081
    public override featureColumnsWidth() {
1082
        return this.pivotRowWidths;
1083
    }
1084

1085
    /**
1086
     * Gets/Sets the value of the `id` attribute.
1087
     *
1088
     * @remarks
1089
     * If not provided it will be automatically generated.
1090
     * @example
1091
     * ```html
1092
     * <igx-pivot-grid [id]="'igx-pivot-1'" [data]="Data"></igx-pivot-grid>
1093
     * ```
2✔
1094
     */
2✔
1095
    @HostBinding('attr.id')
2✔
1096
    @Input()
1✔
1097
    public get id(): string {
1098
        return this.p_id;
2✔
1099
    }
1100
    public set id(value: string) {
1101
        this.p_id = value;
1102
    }
1103

1104
    /**
40✔
1105
     * An @Input property that lets you fill the `IgxPivotGridComponent` with an array of data.
1106
     * ```html
14!
1107
     * <igx-pivot-grid [data]="Data"></igx-pivot-grid>
×
1108
     * ```
1109
     */
14✔
1110
    @Input()
1111
    public set data(value: any[] | null) {
14!
1112
        this._data = value || [];
×
1113
        if (!this._init) {
1114
            this.setupColumns();
14✔
1115
            this.reflow();
1116
        }
11✔
1117
        this.cdr.markForCheck();
2✔
1118
        if (this.height === null || this.height.indexOf('%') !== -1) {
1119
            // If the height will change based on how much data there is, recalculate sizes in igxForOf.
11✔
1120
            this.notifyChanges(true);
1121
        }
1✔
1122
    }
1123

1124
    /**
1125
     * Returns an array of data set to the component.
1126
     * ```typescript
1127
     * let data = this.grid.data;
1128
     * ```
4✔
1129
     */
4✔
1130
    public get data(): any[] | null {
1✔
1131
        return this._data;
1132
    }
1133

3✔
1134
    /**
1135
     * @hidden
1136
     */
4✔
1137
    public getContext(rowData, rowIndex): any {
4✔
1138
        return {
1139
            $implicit: rowData,
1140
            templateID: {
1141
                type: 'dataRow',
1142
                id: null
1143
            },
1144
            index: this.getDataViewIndex(rowIndex, false)
12✔
1145
        };
12✔
1146
    }
1✔
1147

11✔
1148
    /**
11✔
1149
     * @hidden @internal
11✔
1150
     */
11✔
1151
    public get pivotRowWidths() {
11✔
1152
        return this.rowDimensions.length ? this.rowDimensions.reduce((accumulator, dim) => accumulator + this.rowDimensionWidthToPixels(dim), 0) :
1153
            this.rowDimensionWidthToPixels(this.emptyRowDimension);
1154
    }
56✔
1155

38✔
1156
    /**
39✔
1157
     * @hidden @internal
1158
     */
1159
    public rowDimensionWidthToPixels(dim: IPivotDimension, ignoreBeforeInit = false): number {
1160
        if (!ignoreBeforeInit && this.shouldGenerate) {
2✔
1161
            return 0;
2!
1162
        }
2✔
1163

10✔
1164
        if (!dim.width) {
2✔
1165
            return MINIMUM_COLUMN_WIDTH;
2✔
1166
        }
2✔
1167
        const isPercent = dim.width && dim.width.indexOf('%') !== -1;
1168
        if (isPercent) {
2✔
1169
            return parseFloat(dim.width) / 100 * this.calcWidth;
1170
        } else {
2✔
1171
            return parseInt(dim.width, 10);
2✔
1172
        }
2!
1173
    }
×
1174

1175
    /**
1176
     * @hidden @internal
2✔
1177
     */
1178
    public reverseDimensionWidthToPercent(width: number): number {
1179
        return (width * 100 / this.calcWidth);
1180
    }
1181

1182
    /** @hidden @internal */
1183
    public get pivotContentCalcWidth() {
126,406✔
1184
        const totalDimWidth = this.rowDimensions.length > 0 ?
1185
            this.rowDimensions.map((dim) => this.rowDimensionWidthToPixels(dim)).reduce((prev, cur) => prev + cur) :
1186
            0;
1187
        return this.calcWidth - totalDimWidth;
1188
    }
1189

1190
    /** @hidden @internal */
3,080✔
1191
    public get pivotPinnedWidth() {
3,080✔
1192
        return !this.shouldGenerate ? (this.isPinningToStart ? this.pinnedWidth : this.headerFeaturesWidth) : 0;
1193
    }
1194

1195
    /** @hidden @internal */
1196
    public get pivotUnpinnedWidth() {
1197
        return !this.shouldGenerate ? this.unpinnedWidth : 0;
1198
    }
3,080✔
1199

3,080✔
1200
    /** @hidden @internal */
1201
    public get rowDimensions() {
1202
        return this.pivotConfiguration.rows?.filter(x => x.enabled) || [];
8!
1203
    }
×
1204

8✔
1205
    /** @hidden @internal */
8✔
1206
    public get columnDimensions() {
8✔
1207
        return this.pivotConfiguration.columns?.filter(x => x.enabled) || [];
44✔
1208
    }
3✔
1209

8✔
1210
    /** @hidden @internal */
44✔
1211
    public get filterDimensions() {
3✔
1212
        return this.pivotConfiguration.filters?.filter(x => x.enabled) || [];
8✔
1213
    }
18✔
1214

18✔
1215
    /** @hidden @internal */
11✔
1216
    public get values() {
1217
        return this.pivotConfiguration.values?.filter(x => x.enabled) || [];
1218
    }
7✔
1219

1220
    public toggleColumn(col: IgxColumnComponent) {
1221
        const state = this.columnGroupStates.get(col.field);
8✔
1222
        const newState = !state;
1✔
1223
        this.columnGroupStates.set(col.field, newState);
3✔
1224
        this.toggleRowGroup(col, newState);
3✔
1225
        this.reflow();
1226
    }
1227

1228
    protected override getColumnWidthSum(): number {
1229
        let colSum = super.getColumnWidthSum();
1230
        colSum += this.rowDimensions.map(dim => this.rowDimensionWidthToPixels(dim, true)).reduce((prev, cur) => prev + cur, 0);
1231
        return colSum;
1232
    }
1233

1234
    /**
1235
     * @hidden @internal
267✔
1236
     */
1237
    public override isRecordPinnedByIndex(_rowIndex: number) {
1238
        return null;
1239
    }
1240

1241
    /**
45!
1242
     * @hidden @internal
45✔
1243
     */
1244
    public override toggleColumnVisibility(_args: IColumnVisibilityChangedEventArgs) {
1245
        return;
4✔
1246
    }
4✔
1247

8✔
1248
    /**
1249
     * @hidden @internal
4✔
1250
     */
1251
    public override expandAll() {
1252
    }
×
1253

×
1254
    /**
1255
     * @hidden @internal
×
1256
     */
1257
    public override collapseAll() {
1258
    }
1259

1260
    /**
1261
     * @hidden @internal
206✔
1262
     */
206✔
1263
    public override expandRow(_rowID: any) {
206✔
1264
    }
215✔
1265

206✔
1266
    /**
206✔
1267
     * @hidden @internal
206✔
1268
     */
12✔
1269
    public override collapseRow(_rowID: any) {
1270
    }
1271

206✔
1272
    /**
4✔
1273
     * @hidden @internal
4✔
1274
     */
2✔
1275
    public override get pinnedRows(): IgxGridRowComponent[] {
4✔
1276
        return;
2✔
1277
    }
12✔
1278

1279
    /**
1280
     * @hidden @internal
2✔
1281
     */
1282
    @Input()
1283
    public override get totalRecords(): number {
202✔
1284
        return;
1285
    }
204✔
1286

204✔
1287
    public override set totalRecords(_total: number) {
1288
    }
204✔
1289

1!
1290
    /**
1✔
1291
     * @hidden @internal
14✔
1292
     */
1✔
1293
    public override moveColumn(_column: IgxColumnComponent, _target: IgxColumnComponent, _pos: DropPosition = DropPosition.AfterDropTarget) {
1!
1294
    }
1✔
1295

1296
    /**
1297
     * @hidden @internal
1298
     */
204✔
1299
    public override addRow(_data: any): void {
204✔
1300
    }
204!
1301

204✔
1302
    /**
1303
     * @hidden @internal
1304
     */
1305
    public override deleteRow(_rowSelector: any): any {
706✔
1306
    }
4✔
1307

1308
    /**
702✔
1309
     * @hidden @internal
1310
     */
1311
    public override updateCell(_value: any, _rowSelector: any, _column: string): void {
524✔
1312
    }
209✔
1313

209✔
1314
    /**
524✔
1315
     * @hidden @internal
524✔
1316
     */
524✔
1317
    public override updateRow(_value: any, _rowSelector: any): void {
524✔
1318
    }
1319

209✔
1320
    /**
1321
     * @hidden @internal
1322
     */
2✔
1323
    public override enableSummaries(..._rest) {
8✔
1324
    }
2✔
1325

2✔
1326
    /**
8✔
1327
     * @hidden @internal
8✔
1328
     */
8✔
1329
    public override disableSummaries(..._rest) {
8✔
1330
    }
8✔
1331

8✔
1332
    /**
8!
1333
     * @hidden @internal
8✔
1334
     */
8✔
1335
    public override pinColumn(_columnName: string | IgxColumnComponent, _index?): boolean {
1336
        return;
8✔
1337
    }
1338

1339
    /**
2✔
1340
     * @hidden @internal
1341
     */
204✔
1342
    public override unpinColumn(_columnName: string | IgxColumnComponent, _index?): boolean {
268✔
1343
        return;
268✔
1344
    }
12✔
1345

12✔
1346
    /**
12✔
1347
     * @hidden @internal
12✔
1348
     */
12✔
1349
    public override pinRow(_rowID: any, _index?: number, _row?: RowType): boolean {
12✔
1350
        return;
12✔
1351
    }
12✔
1352

12✔
1353
    /**
1354
     * @hidden @internal
12✔
1355
     */
1356
    public override unpinRow(_rowID: any, _row?: RowType): boolean {
256✔
1357
        return;
256✔
1358
    }
861✔
1359

861!
1360
    /**
×
1361
     * @hidden @internal
1362
     */
861✔
1363
    public override get pinnedRowHeight() {
797✔
1364
        return;
797✔
1365
    }
797✔
1366

641✔
1367
    /**
641✔
1368
     * @hidden @internal
641✔
1369
     */
1370
    public override get hasEditableColumns(): boolean {
1371
        return;
64!
1372
    }
64✔
1373

64✔
1374
    /**
21✔
1375
     * @hidden @internal
1376
     */
64✔
1377
    public override get hasSummarizedColumns(): boolean {
456✔
1378
        return;
64✔
1379
    }
64✔
1380

63✔
1381
    /**
63✔
1382
     * @hidden @internal
1383
     */
63✔
1384
    public override get hasMovableColumns(): boolean {
63✔
1385
        return;
63✔
1386
    }
20✔
1387

20✔
1388
    /**
20✔
1389
     * @hidden @internal
20✔
1390
     */
20✔
1391
    public override get pinnedDataView(): any[] {
1392
        return [];
1393
    }
1394

1✔
1395
    /**
1✔
1396
     * @hidden @internal
1!
1397
     */
1✔
1398
    public override openAdvancedFilteringDialog(_overlaySettings?: OverlaySettings) {
1✔
1399
    }
1400

1401
    /**
1402
     * @hidden @internal
1403
     */
256✔
1404
    public override closeAdvancedFilteringDialog(_applyChanges: boolean) {
1405
    }
1406

882✔
1407
    /**
882✔
1408
     * @hidden @internal
1409
     */
1410
    public override endEdit(_commit = true, _event?: Event) {
882✔
1411
    }
882✔
1412

882✔
1413
    /**
882!
1414
     * @hidden @internal
×
1415
     */
1416
    public override beginAddRowById(_rowID: any, _asChild?: boolean): void {
882✔
1417
    }
882✔
1418

882✔
1419
    /**
882✔
1420
     * @hidden @internal
882✔
1421
     */
882✔
1422
    public override beginAddRowByIndex(_index: number): void {
1423
    }
1424

×
1425
    /**
×
1426
     * @hidden @internal
1427
     */
×
1428
    public override clearSearch() { }
1429

1430
    /**
724✔
1431
    * @hidden @internal
724✔
1432
    */
724✔
1433
    public override refreshSearch(_updateActiveInfo?: boolean, _endEdit = true): number {
724!
1434
        return 0;
724✔
1435
    }
1,453✔
1436

1,453✔
1437
    /**
1,453✔
1438
    * @hidden @internal
1,453✔
1439
    */
1,453!
1440
    public override findNext(_text: string, _caseSensitive?: boolean, _exactMatch?: boolean): number {
×
1441
        return 0;
1442
    }
1,453✔
1443

1,453✔
1444
    /**
1,453✔
1445
    * @hidden @internal
1,453✔
1446
    */
1,453✔
1447
    public override findPrev(_text: string, _caseSensitive?: boolean, _exactMatch?: boolean): number {
1,453✔
1448
        return 0;
1449
    }
724✔
1450

1451
    /**
1452
    * @hidden @internal
1453
    */
1454
    public override getNextCell(currRowIndex: number, curVisibleColIndex: number,
1455
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
1,544✔
1456
        return super.getNextCell(currRowIndex, curVisibleColIndex, callback);
1,544✔
1457
    }
1458

28✔
1459
    /**
1460
    * @hidden @internal
1,516✔
1461
    */
1462
    public override getPreviousCell(currRowIndex: number, curVisibleColIndex: number,
1463
        callback: (IgxColumnComponent) => boolean = null): ICellPosition {
121✔
1464
        return super.getPreviousCell(currRowIndex, curVisibleColIndex, callback);
121✔
1465
    }
413✔
1466

1467
    /**
121✔
1468
    * @hidden @internal
121✔
1469
    */
211✔
1470
    public override getPinnedWidth(takeHidden = false) {
1471
        return super.getPinnedWidth(takeHidden);
1472
    }
2✔
1473

1474
    /**
1475
     * @hidden @internal
1476
     */
1477
    public override get totalHeight() {
1478
        return this.calcHeight;
1479
    }
1480

1481
    public getColumnGroupExpandState(col: IgxColumnComponent) {
1482
        const state = this.columnGroupStates.get(col.field);
1483
        // columns are expanded by default?
1484
        return state !== undefined && state !== null ? state : false;
1485
    }
1486

1487
    public toggleRowGroup(col: IgxColumnComponent, newState: boolean) {
1488
        if (!col) return;
1489
        if (this.hasMultipleValues) {
1490
            const parentCols = col.parent ? col.parent.children.toArray() : this._autoGeneratedCols.filter(x => x.level === 0);
1491
            const siblingCol = parentCols.filter(x => x.header === col.header && x !== col)[0];
1492
            const currIndex = parentCols.indexOf(col);
1493
            const siblingIndex = parentCols.indexOf(siblingCol);
1494
            if (currIndex < siblingIndex) {
1495
                // clicked on the full hierarchy header
1496
                this.resolveToggle(col, newState);
2✔
1497
                siblingCol.headerTemplate = this.headerTemplate;
1498
            } else {
1499
                // clicked on summary parent column that contains just the measures
1500
                col.headerTemplate = undefined;
1501
                this.resolveToggle(siblingCol, newState);
1502
            }
1503
        } else {
1504
            const parentCols = col.parent ? col.parent.children : this._autoGeneratedCols.filter(x => x.level === 0);
1505
            const fieldColumn = parentCols.filter(x => x.header === col.header && !x.columnGroup)[0];
1506
            const groupColumn = parentCols.filter(x => x.header === col.header && x.columnGroup)[0];
1507
            this.resolveToggle(groupColumn, newState);
1508
            if (newState) {
1509
                fieldColumn.headerTemplate = this.headerTemplate;
1510
            } else {
1511
                fieldColumn.headerTemplate = undefined;
1512
            }
1513
        }
1514
    }
1515

1516
    /**
1517
    * @hidden @internal
1518
    */
1519
    public override setupColumns() {
1520
        super.setupColumns();
1521
    }
1522

1523
    /**
1524
     * Auto-sizes row dimension cells.
1525
     *
1526
     * @remarks
1527
     * Only sizes based on the dimension cells in view.
1528
     * @example
1529
     * ```typescript
1530
     * this.grid.autoSizeRowDimension(dimension);
1531
     * ```
1532
     * @param dimension The row dimension to size.
1533
     */
1534
    public autoSizeRowDimension(dimension: IPivotDimension) {
1535
        if (this.getDimensionType(dimension) === PivotDimensionType.Row) {
1536
            const relatedDims = PivotUtil.flatten([dimension]).map(x => x.memberName);
1537
            const content = this.rowDimensionContentCollection.filter(x => relatedDims.indexOf(x.dimension.memberName) !== -1);
1538
            const headers = content.map(x => x.headerGroups.toArray()).flat().map(x => x.header && x.header.refInstance);
1539
            const autoWidth = this.getLargesContentWidth(headers);
1540
            dimension.width = autoWidth;
1541
            this.pipeTrigger++;
1542
            this.cdr.detectChanges();
1543
        }
1544
    }
1545

1546
    /**
1547
     * Inserts dimension in target collection by type at specified index or at the collection's end.
1548
     *
1549
     * @example
1550
     * ```typescript
1551
     * this.grid.insertDimensionAt(dimension, PivotDimensionType.Row, 1);
1552
     * ```
1553
     * @param dimension The dimension that will be added.
1554
     * @param targetCollectionType The target collection type to add to. Can be Row, Column or Filter.
1555
     * @param index The index in the collection at which to add.
1556
     * This parameter is optional. If not set it will add it to the end of the collection.
1557
     */
1558
    public insertDimensionAt(dimension: IPivotDimension, targetCollectionType: PivotDimensionType, index?: number) {
1559
        const targetCollection = this.getDimensionsByType(targetCollectionType);
1560
        if (index !== undefined) {
1561
            targetCollection.splice(index, 0, dimension);
1562
        } else {
1563
            targetCollection.push(dimension);
1564
        }
1565
        if (targetCollectionType === PivotDimensionType.Column) {
2✔
1566
            this.setupColumns();
1567
        }
1568
        this.pipeTrigger++;
2✔
1569
        this.dimensionsChange.emit({ dimensions: targetCollection, dimensionCollectionType: targetCollectionType });
1570
        if (targetCollectionType === PivotDimensionType.Filter) {
1571
            this.dimensionDataColumns = this.generateDimensionColumns();
2✔
1572
            this.reflow();
1573
        }
1574
        this.pivotConfigurationChange.emit({ pivotConfiguration: this.pivotConfiguration });
1575
    }
1576

1577
    /**
1578
     * Move dimension from its currently collection to the specified target collection by type at specified index or at the collection's end.
1579
     *
1580
     * @example
1581
     * ```typescript
1582
     * this.grid.moveDimension(dimension, PivotDimensionType.Row, 1);
1583
     * ```
1584
     * @param dimension The dimension that will be moved.
1585
     * @param targetCollectionType The target collection type to move it to. Can be Row, Column or Filter.
1586
     * @param index The index in the collection at which to add.
1587
     * This parameter is optional. If not set it will add it to the end of the collection.
1588
     */
1589
    public moveDimension(dimension: IPivotDimension, targetCollectionType: PivotDimensionType, index?: number) {
1590
        const prevCollectionType = this.getDimensionType(dimension);
1591
        if (prevCollectionType === null) return;
1592
        // remove from old collection
1593
        this._removeDimensionInternal(dimension);
1594
        // add to target
1595
        this.insertDimensionAt(dimension, targetCollectionType, index);
1596

1597
        if (prevCollectionType === PivotDimensionType.Column) {
1598
            this.setupColumns();
1599
        }
1600
    }
1601

1602
    /**
1603
     * Removes dimension from its currently collection.
1604
     * @remarks
1605
     * This is different than toggleDimension that enabled/disables the dimension.
1606
     * This completely removes the specified dimension from the collection.
1607
     * @example
1608
     * ```typescript
1609
     * this.grid.removeDimension(dimension);
1610
     * ```
1611
     * @param dimension The dimension to be removed.
1612
     */
1613
    public removeDimension(dimension: IPivotDimension) {
1614
        const prevCollectionType = this.getDimensionType(dimension);
1615
        this._removeDimensionInternal(dimension);
1616
        if (prevCollectionType === PivotDimensionType.Column) {
1617
            this.setupColumns();
1618
        }
1619
        if (prevCollectionType === PivotDimensionType.Filter) {
1620
            this.reflow();
1621
        }
1622
        this.pipeTrigger++;
1623
        this.cdr.detectChanges();
1624
    }
1625

1626
    /**
1627
     * Toggles the dimension's enabled state on or off.
1628
     * @remarks
1629
     * The dimension remains in its current collection. This just changes its enabled state.
1630
     * @example
1631
     * ```typescript
1632
     * this.grid.toggleDimension(dimension);
1633
     * ```
1634
     * @param dimension The dimension to be toggled.
1635
     */
1636
    public toggleDimension(dimension: IPivotDimension) {
1637
        const dimType = this.getDimensionType(dimension);
1638
        if (dimType === null) return;
1639
        const collection = this.getDimensionsByType(dimType);
1640
        dimension.enabled = !dimension.enabled;
1641
        if (dimType === PivotDimensionType.Column) {
1642
            this.setupColumns();
1643
        }
1644
        if (!dimension.enabled && dimension.filter) {
1645
            this.filteringService.clearFilter(dimension.memberName);
1646
        }
1647
        this.pipeTrigger++;
1648
        this.dimensionsChange.emit({ dimensions: collection, dimensionCollectionType: dimType });
1649
        this.cdr.detectChanges();
1650
        if (dimType === PivotDimensionType.Filter) {
1651
            this.reflow();
1652
        }
1653
        this.pivotConfigurationChange.emit({ pivotConfiguration: this.pivotConfiguration });
1654
    }
1655

1656
    /**
1657
     * Inserts value at specified index or at the end.
1658
     *
1659
     * @example
1660
     * ```typescript
1661
     * this.grid.insertValueAt(value, 1);
1662
     * ```
1663
     * @param value The value definition that will be added.
1664
     * @param index The index in the collection at which to add.
1665
     * This parameter is optional. If not set it will add it to the end of the collection.
1666
     */
1667
    public insertValueAt(value: IPivotValue, index?: number) {
1668
        if (!this.pivotConfiguration.values) {
1669
            this.pivotConfiguration.values = [];
1670
        }
1671
        const values = this.pivotConfiguration.values;
1672
        if (index !== undefined) {
1673
            values.splice(index, 0, value);
1674
        } else {
1675
            values.push(value);
1676
        }
1677
        this.setupColumns();
1678
        this.pipeTrigger++;
1679
        this.cdr.detectChanges();
1680
        this.valuesChange.emit({ values });
1681
        this.pivotConfigurationChange.emit({ pivotConfiguration: this.pivotConfiguration });
1682
    }
1683

1684
    /**
1685
     * Move value from its currently at specified index or at the end.
1686
     *
1687
     * @example
1688
     * ```typescript
1689
     * this.grid.moveValue(value, 1);
1690
     * ```
1691
     * @param value The value that will be moved.
1692
     * @param index The index in the collection at which to add.
1693
     * This parameter is optional. If not set it will add it to the end of the collection.
1694
     */
1695
    public moveValue(value: IPivotValue, index?: number) {
1696
        if (this.pivotConfiguration.values.indexOf(value) === -1) return;
1697
        // remove from old index
1698
        this.removeValue(value);
1699
        // add to new
1700
        this.insertValueAt(value, index);
1701
    }
1702

1703
    /**
1704
     * Removes value from collection.
1705
     * @remarks
1706
     * This is different than toggleValue that enabled/disables the value.
1707
     * This completely removes the specified value from the collection.
1708
     * @example
1709
     * ```typescript
1710
     * this.grid.removeValue(dimension);
1711
     * ```
1712
     * @param value The value to be removed.
1713
     */
1714
    public removeValue(value: IPivotValue,) {
1715
        const values = this.pivotConfiguration.values;
1716
        const currentIndex = values.indexOf(value);
1717
        if (currentIndex !== -1) {
1718
            values.splice(currentIndex, 1);
1719
            this.setupColumns();
1720
            this.pipeTrigger++;
1721
            this.valuesChange.emit({ values });
1722
            this.pivotConfigurationChange.emit({ pivotConfiguration: this.pivotConfiguration });
1723
        }
1724
    }
1725

1726
    /**
1727
     * Toggles the value's enabled state on or off.
1728
     * @remarks
1729
     * The value remains in its current collection. This just changes its enabled state.
1730
     * @example
1731
     * ```typescript
1732
     * this.grid.toggleValue(value);
1733
     * ```
1734
     * @param value The value to be toggled.
1735
     */
1736
    public toggleValue(value: IPivotValue) {
1737
        if (this.pivotConfiguration.values.indexOf(value) === -1) return;
1738
        value.enabled = !value.enabled;
1739
        this.setupColumns();
1740
        this.pipeTrigger++;
1741
        this.valuesChange.emit({ values: this.pivotConfiguration.values });
1742
        this.reflow();
1743
        this.pivotConfigurationChange.emit({ pivotConfiguration: this.pivotConfiguration });
1744
    }
1745

1746
    /**
1747
     * Sort the dimension and its children in the provided direction.
1748
     * @example
1749
     * ```typescript
1750
     * this.grid.sortDimension(dimension, SortingDirection.Asc);
1751
     * ```
1752
     * @param value The value to be toggled.
1753
     */
1754
    public sortDimension(dimension: IPivotDimension, sortDirection: SortingDirection) {
1755
        const dimensionType = this.getDimensionType(dimension);
1756
        dimension.sortDirection = sortDirection;
1757
        // apply same sort direction to children.
1758
        let dim = dimension;
1759
        while (dim.childLevel) {
1760
            dim.childLevel.sortDirection = dimension.sortDirection;
1761
            dim = dim.childLevel;
1762
        }
1763
        this.pipeTrigger++;
1764
        this.dimensionsSortingExpressionsChange.emit(this.dimensionsSortingExpressions);
1765
        if (dimensionType === PivotDimensionType.Column) {
1766
            this.setupColumns();
1767
        }
1768
        this.cdr.detectChanges();
1769
        this.pivotConfigurationChange.emit({ pivotConfiguration: this.pivotConfiguration });
1770
    }
1771

1772
    /**
1773
     * Filters a single `IPivotDimension`.
1774
     *
1775
     * @example
1776
     * ```typescript
1777
     * public filter() {
1778
     *      const set = new Set();
1779
     *      set.add('Value 1');
1780
     *      set.add('Value 2');
1781
     *      this.grid1.filterDimension(this.pivotConfigHierarchy.rows[0], set, IgxStringFilteringOperand.instance().condition('in'));
1782
     * }
1783
     * ```
1784
     */
1785
    public filterDimension(dimension: IPivotDimension, value: any, conditionOrExpressionTree?: IFilteringOperation | IFilteringExpressionsTree ) {
1786
        this.filteringService.filter(dimension.memberName, value, conditionOrExpressionTree);
1787
        const dimensionType = this.getDimensionType(dimension);
1788
        if (dimensionType === PivotDimensionType.Column) {
1789
            this.setupColumns();
1790
        }
1791
        this.cdr.detectChanges();
1792
    }
1793

1794
    /**
1795
     * @hidden @internal
1796
     */
1797
    public getDimensionsByType(dimension: PivotDimensionType) {
1798
        switch (dimension) {
1799
            case PivotDimensionType.Row:
1800
                if (!this.pivotConfiguration.rows) {
1801
                    this.pivotConfiguration.rows = [];
1802
                }
1803
                return this.pivotConfiguration.rows;
1804
            case PivotDimensionType.Column:
1805
                if (!this.pivotConfiguration.columns) {
1806
                    this.pivotConfiguration.columns = [];
1807
                }
1808
                return this.pivotConfiguration.columns;
1809
            case PivotDimensionType.Filter:
1810
                if (!this.pivotConfiguration.filters) {
1811
                    this.pivotConfiguration.filters = [];
1812
                }
1813
                return this.pivotConfiguration.filters;
1814
            default:
1815
                return null;
1816
        }
1817
    }
1818

1819
    /**
1820
     * @hidden @internal
1821
     */
1822
    public resizeRowDimensionPixels(dimension: IPivotDimension, newWidth: number) {
1823
        const isPercentageWidth = dimension.width && typeof dimension.width === 'string' && dimension.width.indexOf('%') !== -1;
1824
        if (isPercentageWidth) {
1825
            dimension.width = this.reverseDimensionWidthToPercent(newWidth).toFixed(2) + '%';
1826
        } else {
1827
            dimension.width = newWidth + 'px';
1828
        }
1829

1830
        // Notify the grid to reflow, to update if horizontal scrollbar needs to be rendered/removed.
1831
        this.pipeTrigger++;
1832
        this.cdr.detectChanges();
1833
    }
1834

1835
    /*
1836
    * @hidden
1837
    * @internal
1838
    */
1839
    protected _removeDimensionInternal(dimension) {
1840
        const prevCollectionType = this.getDimensionType(dimension);
1841
        if (prevCollectionType === null) return;
1842
        const prevCollection = this.getDimensionsByType(prevCollectionType);
1843
        const currentIndex = prevCollection.indexOf(dimension);
1844
        prevCollection.splice(currentIndex, 1);
1845
        this.pipeTrigger++;
1846
        this.cdr.detectChanges();
1847
    }
1848

1849
    protected getDimensionType(dimension: IPivotDimension): PivotDimensionType {
1850
        return PivotUtil.flatten(this.pivotConfiguration.rows).indexOf(dimension) !== -1 ? PivotDimensionType.Row :
1851
            PivotUtil.flatten(this.pivotConfiguration.columns).indexOf(dimension) !== -1 ? PivotDimensionType.Column :
1852
                (!!this.pivotConfiguration.filters && PivotUtil.flatten(this.pivotConfiguration.filters).indexOf(dimension) !== -1) ?
1853
                    PivotDimensionType.Filter : null;
1854
    }
1855

1856
    protected getLargesContentWidth(contents: ElementRef[]): string {
1857
        const largest = new Map<number, number>();
1858
        if (contents.length > 0) {
1859
            const cellsContentWidths = [];
1860
            contents.forEach((elem) => cellsContentWidths.push(this.getHeaderCellWidth(elem.nativeElement).width));
1861
            const index = cellsContentWidths.indexOf(Math.max(...cellsContentWidths));
1862
            const cellStyle = this.document.defaultView.getComputedStyle(contents[index].nativeElement);
1863
            const cellPadding = parseFloat(cellStyle.paddingLeft) + parseFloat(cellStyle.paddingRight) +
1864
                parseFloat(cellStyle.borderLeftWidth) + parseFloat(cellStyle.borderRightWidth);
1865
            largest.set(Math.max(...cellsContentWidths), cellPadding);
1866
        }
1867
        const largestCell = Math.max(...Array.from(largest.keys()));
1868
        const width = Math.ceil(largestCell + largest.get(largestCell));
1869

1870
        if (Number.isNaN(width)) {
1871
            return null;
1872
        } else {
1873
            return width + 'px';
1874
        }
1875
    }
1876

1877
    /**
1878
    * @hidden
1879
    */
1880
    public get hasMultipleValues() {
1881
        return this.values.length > 1;
1882
    }
1883

1884
    /**
1885
    * @hidden
1886
    */
1887
    public get excelStyleFilterMaxHeight() {
1888
        // max 10 rows, row size depends on density
1889
        const maxHeight = this.renderedRowHeight * 10;
1890
        return `${maxHeight}px`;
1891
    }
1892

1893
    /**
1894
    * @hidden
1895
    */
1896
    public get excelStyleFilterMinHeight(): string {
1897
        // min 5 rows, row size depends on density
1898
        const minHeight = this.renderedRowHeight * 5;
1899
        return `${minHeight}px`;
1900
    }
1901

1902
    protected resolveToggle(groupColumn: IgxColumnComponent, state: boolean) {
1903
        if (!groupColumn) return;
1904
        groupColumn.hidden = state;
1905
        this.columnGroupStates.set(groupColumn.field, state);
1906
        const childrenTotal = this.hasMultipleValues ?
1907
            groupColumn.children.filter(x => x.columnGroup && x.children.filter(y => !y.columnGroup).length === this.values.length) :
1908
            groupColumn.children.filter(x => !x.columnGroup);
1909
        const childrenSubgroups = this.hasMultipleValues ?
1910
            groupColumn.children.filter(x => x.columnGroup && x.children.filter(y => !y.columnGroup).length === 0) :
1911
            groupColumn.children.filter(x => x.columnGroup);
1912
        childrenTotal.forEach(group => {
1913
            const newState = this.columnGroupStates.get(group.field) || state;
1914
            if (newState) {
1915
                group.headerTemplate = this.headerTemplate;
1916
            } else {
1917
                group.headerTemplate = undefined;
1918
            }
1919
        });
1920
        if (!groupColumn.hidden && childrenSubgroups.length > 0) {
1921
            childrenSubgroups.forEach(group => {
1922
                const newState = this.columnGroupStates.get(group.field) || state;
1923
                this.resolveToggle(group, newState);
1924
            });
1925
        }
1926
    }
1927

1928
    /**
1929
     * @hidden
1930
     * @internal
1931
     */
1932
    protected override calcGridHeadRow() {
1933
    }
1934

1935
    protected override buildDataView(data: any[]) {
1936
        this._dataView = data;
1937
    }
1938

1939
    /**
1940
     * @hidden @internal
1941
     */
1942
    protected override getDataBasedBodyHeight(): number {
1943
        const dvl = this.dataView?.length || 0;
1944
        return dvl < this._defaultTargetRecordNumber ? 0 : this.defaultTargetBodyHeight;
1945
    }
1946

1947
    protected override horizontalScrollHandler(event) {
1948
        const scrollLeft = event.target.scrollLeft;
1949
        this.theadRow.headerContainers.forEach(headerForOf => {
1950
            headerForOf.onHScroll(scrollLeft);
1951
        });
1952
        super.horizontalScrollHandler(event);
1953
    }
1954

1955
    protected override verticalScrollHandler(event) {
1956
        this.verticalRowDimScrollContainers.forEach(x => {
1957
            x.onScroll(event);
1958
        });
1959
        super.verticalScrollHandler(event);
1960
    }
1961

1962
    /**
1963
     * @hidden
1964
     */
1965
    protected override autogenerateColumns() {
1966
        let columns = [];
1967
        const data = this.gridAPI.filterDataByExpressions(this.filteringExpressionsTree);
1968
        this.dimensionDataColumns = this.generateDimensionColumns();
1969
        const flattenedColumnsWithSorting = PivotUtil.flatten(this.columnDimensions).filter(dim => dim.sortDirection);
1970
        const expressions =  flattenedColumnsWithSorting.length > 0? PivotSortUtil.generateDimensionSortingExpressions(flattenedColumnsWithSorting) : [];
1971
        let sortedData = data;
1972
        if (expressions.length > 0) {
1973
            sortedData = DataUtil.sort(cloneArray(data), expressions, this.sortStrategy, this);
1974
        }
1975
        let fieldsMap;
1976
        if (this.pivotConfiguration.columnStrategy && this.pivotConfiguration.columnStrategy instanceof NoopPivotDimensionsStrategy) {
1977
            const fields = this.generateDataFields(sortedData);
1978
            if (fields.length === 0) return;
1979
            const rowFields = PivotUtil.flatten(this.pivotConfiguration.rows).map(x => x.memberName);
1980
            const keyFields = Object.values(this.pivotKeys);
1981
            const filteredFields = fields.filter(x => rowFields.indexOf(x) === -1 && keyFields.indexOf(x) === -1 &&
1982
                x.indexOf(this.pivotKeys.rowDimensionSeparator + this.pivotKeys.level) === -1 &&
1983
                x.indexOf(this.pivotKeys.rowDimensionSeparator + this.pivotKeys.records) === -1);
1984
            fieldsMap = this.generateFromData(filteredFields);
1985
        } else {
1986
            fieldsMap = PivotUtil.getFieldsHierarchy(
1987
                sortedData,
1988
                this.columnDimensions,
1989
                PivotDimensionType.Column,
1990
                this.pivotKeys,
1991
                this.pivotValueCloneStrategy
1992
            );
1993
        }
1994
        columns = this.generateColumnHierarchy(fieldsMap, sortedData);
1995
        this._autoGeneratedCols = columns;
1996
        // reset expansion states if any are stored.
1997
        this.columnGroupStates.forEach((value, key) => {
1998
            if (value) {
1999
                const primaryColumn = columns.find(x => x.field === key && x.headerTemplate === this.headerTemplate);
2000
                const groupSummaryColumn = columns.find(x => x.field === key && x.headerTemplate !== this.headerTemplate);
2001
                this.toggleRowGroup(primaryColumn, value);
2002
                if (groupSummaryColumn) {
2003
                    groupSummaryColumn.headerTemplate = this.headerTemplate;
2004
                }
2005
            }
2006
        });
2007

2008
        this.updateColumns(columns);
2009
        this.reflow();
2010
        if (data && data.length > 0) {
2011
            this.shouldGenerate = false;
2012
        }
2013
    }
2014

2015
    protected override getComponentDensityClass(baseStyleClass: string): string {
2016
        if (this.superCompactMode) {
2017
            return `${baseStyleClass}--${DisplayDensity.compact} igx-grid__pivot--super-compact`;
2018
        }
2019
        return super.getComponentDensityClass(baseStyleClass);
2020
    }
2021

2022
    protected generateDimensionColumns(): IgxColumnComponent[] {
2023
        const rootFields = this.allDimensions.map(x => x.memberName);
2024
        const columns = [];
2025
        rootFields.forEach((field) => {
2026
            const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
2027
            ref.instance.field = field;
2028
            ref.changeDetectorRef.detectChanges();
2029
            columns.push(ref.instance);
2030
        });
2031
        return columns;
2032
    }
2033

2034
    protected generateFromData(fields: string[]) {
2035
        const separator = this.pivotKeys.columnDimensionSeparator;
2036
        const dataArr = fields.map(x => x.split(separator)).sort(x => x.length);
2037
        const hierarchy = new Map<string, any>();
2038
        dataArr.forEach(arr => {
2039
            let currentHierarchy = hierarchy;
2040
            const path = [];
2041
            for (const val of arr) {
2042
                path.push(val);
2043
                const newPath = path.join(separator);
2044
                let targetHierarchy = currentHierarchy.get(newPath);
2045
                if (!targetHierarchy) {
2046
                    currentHierarchy.set(newPath, { value: newPath, expandable: true, children: new Map<string, any>(), dimension: this.columnDimensions[0] });
2047
                    targetHierarchy = currentHierarchy.get(newPath);
2048
                }
2049
                currentHierarchy = targetHierarchy.children;
2050
            }
2051
        });
2052
        return hierarchy;
2053
    }
2054

2055
    protected generateColumnHierarchy(fields: Map<string, any>, data, parent = null): IgxColumnComponent[] {
2056
        let columns = [];
2057
        if (fields.size === 0) {
2058
            this.values.forEach((value) => {
2059
                const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
2060
                ref.instance.header = value.displayName;
2061
                ref.instance.field = value.member;
2062
                ref.instance.parent = parent;
2063
                ref.instance.sortable = true;
2064
                ref.instance.dataType = value.dataType || this.resolveDataTypes(data[0][value.member]);
2065
                ref.instance.formatter = value.formatter;
2066
                columns.push(ref.instance);
2067
            });
2068
            return columns;
2069
        }
2070
        const currentFields = fields;
2071
        currentFields.forEach((value) => {
2072
            let shouldGenerate = true;
2073
            if (data.length === 0) {
2074
                shouldGenerate = false;
2075
            }
2076
            if (shouldGenerate && (value.children == null || value.children.length === 0 || value.children.size === 0)) {
2077
                const col = this.createColumnForDimension(value, data, parent, this.hasMultipleValues);
2078
                columns.push(col);
2079
                if (this.hasMultipleValues) {
2080
                    const measureChildren = this.getMeasureChildren(data, col, false, value.dimension.width);
2081
                    col.children.reset(measureChildren);
2082
                    columns = columns.concat(measureChildren);
2083
                }
2084

2085
            } else if (shouldGenerate) {
2086
                const col = this.createColumnForDimension(value, data, parent, true);
2087
                if (value.expandable) {
2088
                    col.headerTemplate = this.headerTemplate;
2089
                }
2090
                const children = this.generateColumnHierarchy(value.children, data, col);
2091
                const filteredChildren = children.filter(x => x.level === col.level + 1);
2092
                columns.push(col);
2093
                if (this.hasMultipleValues) {
2094
                    let measureChildren = this.getMeasureChildren(data, col, true, value.dimension.width);
2095
                    const nestedChildren = filteredChildren;
2096
                    //const allChildren = children.concat(measureChildren);
2097
                    col.children.reset(nestedChildren);
2098
                    columns = columns.concat(children);
2099
                    if (value.dimension.childLevel) {
2100
                        const sibling = this.createColumnForDimension(value, data, parent, true);
2101
                        columns.push(sibling);
2102

2103
                        measureChildren = this.getMeasureChildren(data, sibling, false, value.dimension?.width);
2104
                        sibling.children.reset(measureChildren);
2105
                        columns = columns.concat(measureChildren);
2106
                    }
2107

2108
                } else {
2109
                    col.children.reset(filteredChildren);
2110
                    columns = columns.concat(children);
2111
                    if (value.dimension.childLevel) {
2112
                        const sibling = this.createColumnForDimension(value, data, parent, false);
2113
                        columns.push(sibling);
2114
                    }
2115
                }
2116
            }
2117
        });
2118

2119
        return columns;
2120
    }
2121

2122
    protected createColumnForDimension(value: any, data: any, parent: ColumnType, isGroup: boolean) {
2123
        const key = value.value;
2124
        const ref = isGroup ?
2125
            createComponent(IgxColumnGroupComponent, { environmentInjector: this.envInjector, elementInjector: this.injector }) :
2126
            createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
2127
        ref.instance.header = parent != null ? key.split(parent.header + this.pivotKeys.columnDimensionSeparator)[1] : key;
2128
        ref.instance.field = key;
2129
        ref.instance.parent = parent;
2130
        if (value.dimension.width) {
2131
            ref.instance.width = value.dimension.width;
2132
        }
2133
        const valueDefinition = this.values[0];
2134
        ref.instance.dataType = valueDefinition?.dataType || this.resolveDataTypes(data[0][valueDefinition?.member]);
2135
        ref.instance.formatter = valueDefinition?.formatter;
2136
        ref.instance.sortable = true;
2137
        ref.changeDetectorRef.detectChanges();
2138
        return ref.instance;
2139
    }
2140

2141
    protected resolveColumnDimensionWidth(dim: IPivotDimension) {
2142
        if (dim.width) {
2143
            return dim.width;
2144
        }
2145
        return this.minColumnWidth + 'px';
2146
    }
2147

2148
    protected getMeasureChildren(data, parent, hidden, parentWidth) {
2149
        const cols = [];
2150
        const count = this.values.length;
2151
        const childWidth = parseInt(parentWidth, 10) / count;
2152
        const isPercent = parentWidth && parentWidth.indexOf('%') !== -1;
2153
        this.values.forEach(val => {
2154
            const ref = createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector});
2155
            ref.instance.header = val.displayName || val.member;
2156
            ref.instance.field = parent.field + this.pivotKeys.columnDimensionSeparator + val.member;
2157
            ref.instance.parent = parent;
2158
            if (parentWidth) {
2159
                ref.instance.width = isPercent ? childWidth + '%' : childWidth + 'px';
2160
            }
2161
            ref.instance.hidden = hidden;
2162
            ref.instance.sortable = true;
2163
            ref.instance.dataType = val.dataType || this.resolveDataTypes(data[0][val.member]);
2164
            ref.instance.formatter = val.formatter;
2165
            ref.changeDetectorRef.detectChanges();
2166
            cols.push(ref.instance);
2167
        });
2168
        return cols;
2169
    }
2170

2171
    /**
2172
    * @hidden @internal
2173
    */
2174
    @ViewChild('emptyPivotGridTemplate', { read: TemplateRef, static: true })
2175
    public defaultEmptyPivotGridTemplate: TemplateRef<any>;
2176

2177
    /**
2178
     * Gets/Sets a custom template when pivot grid is empty.
2179
     *
2180
     * @example
2181
     * ```html
2182
     * <igx-pivot-grid [emptyPivotGridTemplate]="myTemplate"><igx-pivot-grid>
2183
     * ```
2184
     */
2185
    @Input()
2186
    public emptyPivotGridTemplate: TemplateRef<void>;
2187

2188
    /**
2189
    * @hidden @internal
2190
    */
2191
    public override get template(): TemplateRef<any> {
2192
        const allEnabledDimensions = this.rowDimensions.concat(this.columnDimensions);
2193
        if (allEnabledDimensions.length === 0 && this.values.length === 0) {
2194
            // no enabled values and dimensions
2195
            return this.emptyPivotGridTemplate || this.defaultEmptyPivotGridTemplate;
2196
        }
2197
        super.template;
2198
    }
2199

2200
    private emitInitEvents(pivotConfig: IPivotConfiguration) {
2201
        const dimensions = PivotUtil.flatten(this.allDimensions);
2202
        dimensions.forEach(dim => {
2203
            this.dimensionInit.emit(dim);
2204
        });
2205
        const values = pivotConfig?.values;
2206
        values?.forEach(val => {
2207
            this.valueInit.emit(val);
2208
        });
2209
    }
2210
}
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