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

IgniteUI / igniteui-angular / 13853162666

14 Mar 2025 08:58AM UTC coverage: 91.652% (+0.005%) from 91.647%
13853162666

Pull #15507

github

web-flow
Merge a041f31c1 into 99f71de9c
Pull Request #15507: fix(grid): fixed advancedFilteringExpressionsTree rehydration #15500

13347 of 15615 branches covered (85.48%)

11 of 12 new or added lines in 2 files covered. (91.67%)

15 existing lines in 1 file now uncovered.

26900 of 29350 relevant lines covered (91.65%)

33710.96 hits per line

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

97.22
/projects/igniteui-angular/src/lib/grids/columns/column.component.ts
1
import { Subject } from 'rxjs';
2
import { isEqual } from 'lodash-es';
3
import {
4
    AfterContentInit,
5
    ChangeDetectorRef,
6
    ChangeDetectionStrategy,
7
    Component,
8
    ContentChild,
9
    ContentChildren,
10
    Input,
11
    QueryList,
12
    TemplateRef,
13
    Output,
14
    EventEmitter,
15
    OnDestroy,
16
    Inject,
17
    Optional,
18
    Self,
19
    booleanAttribute,
20
} from '@angular/core';
21
import { notifyChanges } from '../watch-changes';
22
import { WatchColumnChanges } from '../watch-changes';
23
import { GridColumnDataType } from '../../data-operations/data-util';
24
import {
25
    IgxFilteringOperand,
26
    IgxBooleanFilteringOperand,
27
    IgxNumberFilteringOperand,
28
    IgxDateFilteringOperand,
29
    IgxStringFilteringOperand,
30
    IgxDateTimeFilteringOperand,
31
    IgxTimeFilteringOperand
32
} from '../../data-operations/filtering-condition';
33
import { ISortingStrategy, DefaultSortingStrategy } from '../../data-operations/sorting-strategy';
34
import { IgxRowDirective } from '../row.directive';
35
import { FilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
36
import { CellType, ColumnType, GridType, IgxCellTemplateContext, IgxColumnTemplateContext, IgxSummaryTemplateContext, IGX_GRID_BASE } from '../common/grid.interface';
37
import { IgxGridHeaderComponent } from '../headers/grid-header.component';
38
import { IgxGridFilteringCellComponent } from '../filtering/base/grid-filtering-cell.component';
39
import { IgxGridHeaderGroupComponent } from '../headers/grid-header-group.component';
40
import {
41
    IgxSummaryOperand, IgxNumberSummaryOperand, IgxDateSummaryOperand,
42
    IgxSummaryResult, IgxTimeSummaryOperand
43
} from '../summaries/grid-summary';
44
import {
45
    IgxCellTemplateDirective,
46
    IgxCellHeaderTemplateDirective,
47
    IgxCellEditorTemplateDirective,
48
    IgxCollapsibleIndicatorTemplateDirective,
49
    IgxFilterCellTemplateDirective,
50
    IgxSummaryTemplateDirective,
51
    IgxCellValidationErrorDirective
52
} from './templates.directive';
53
import { MRLResizeColumnInfo, MRLColumnSizeInfo, IColumnPipeArgs, IColumnEditorOptions } from './interfaces';
54
import { DropPosition } from '../moving/moving.service';
55
import { IColumnVisibilityChangingEventArgs, IPinColumnCancellableEventArgs, IPinColumnEventArgs } from '../common/events';
56
import { isConstructor, PlatformUtil } from '../../core/utils';
57
import { IgxGridCell } from '../grid-public-cell';
58
import { NG_VALIDATORS, Validator } from '@angular/forms';
59
import { Size } from '../common/enums';
60
import { ExpressionsTreeUtil } from '../../data-operations/expressions-tree-util';
61

62
const DEFAULT_DATE_FORMAT = 'mediumDate';
2✔
63
const DEFAULT_TIME_FORMAT = 'mediumTime';
2✔
64
const DEFAULT_DATE_TIME_FORMAT = 'medium';
2✔
65
const DEFAULT_DIGITS_INFO = '1.0-3';
2✔
66

67
/* blazorElement */
68
/* contentParent: ColumnGroup */
69
/* wcElementTag: igc-column */
70
/* additionalIdentifier: Field */
71
/* blazorIndirectRender */
72
/**
73
 * **Ignite UI for Angular Column** -
74
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid#columns-configuration)
75
 *
76
 * The Ignite UI Column is used within an `igx-grid` element to define what data the column will show. Features such as sorting,
77
 * filtering & editing are enabled at the column level.  You can also provide a template containing custom content inside
78
 * the column using `ng-template` which will be used for all cells within the column.
79
 *
80
 * @igxParent IgxGridComponent, IgxTreeGridComponent, IgxHierarchicalGridComponent, IgxPivotGridComponent, IgxRowIslandComponent, IgxColumnGroupComponent, IgxColumnLayoutComponent
81
 */
82
@Component({
83
    changeDetection: ChangeDetectionStrategy.OnPush,
84
    selector: 'igx-column',
85
    template: ``,
86
    standalone: true
87
})
88
export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnType {
2✔
89
    /**
90
     * Sets/gets the `field` value.
91
     * ```typescript
92
     * let columnField = this.column.field;
93
     * ```
94
     * ```html
95
     * <igx-column [field] = "'ID'"></igx-column>
96
     * ```
97
     *
98
     * @memberof IgxColumnComponent
99
     */
100
    @Input()
101
    public set field(value: string) {
102
        this._field = value;
28,099✔
103
        this.hasNestedPath = value?.includes('.');
28,099✔
104
    }
105
    public get field(): string {
106
        return this._field;
20,783,008✔
107
    }
108

109

110
    /**
111
     * @hidden @internal
112
     */
113
    public validators: Validator[] = [];
29,102✔
114

115
    /**
116
     * Sets/gets the `header` value.
117
     * ```typescript
118
     * let columnHeader = this.column.header;
119
     * ```
120
     * ```html
121
     * <igx-column [header] = "'ID'"></igx-column>
122
     * ```
123
     *
124
     * @memberof IgxColumnComponent
125
     */
126
    @notifyChanges()
127
    @WatchColumnChanges()
128
    @Input()
129
    public header = '';
29,102✔
130
    /**
131
     * Sets/gets the `title` value.
132
     * ```typescript
133
     * let title = this.column.title;
134
     * ```
135
     * ```html
136
     * <igx-column [title] = "'Some column tooltip'"></igx-column>
137
     * ```
138
     *
139
     * @memberof IgxColumnComponent
140
     */
141
    @notifyChanges()
142
    @WatchColumnChanges()
143
    @Input()
144
    public title = '';
29,102✔
145
    /**
146
     * Sets/gets whether the column is sortable.
147
     * Default value is `false`.
148
     * ```typescript
149
     * let isSortable = this.column.sortable;
150
     * ```
151
     * ```html
152
     * <igx-column [sortable] = "true"></igx-column>
153
     * ```
154
     *
155
     * @memberof IgxColumnComponent
156
     */
157
    @WatchColumnChanges()
158
    @Input({ transform: booleanAttribute })
159
    public sortable = false;
29,102✔
160
    /**
161
     * Returns if the column is selectable.
162
     * ```typescript
163
     * let columnSelectable = this.column.selectable;
164
     * ```
165
     *
166
     * @memberof IgxColumnComponent
167
     */
168
    @WatchColumnChanges()
169
    @Input()
170
    public get selectable(): boolean {
171
        return this._selectable;
119,886✔
172
    }
173

174
    /**
175
     * Sets if the column is selectable.
176
     * Default value is `true`.
177
     * ```html
178
     * <igx-column [selectable] = "false"></igx-column>
179
     * ```
180
     *
181
     * @memberof IgxColumnComponent
182
     */
183
    public set selectable(value: boolean) {
184
        this._selectable = value;
1,605✔
185
    }
186

187
    /**
188
     * Sets/gets whether the column is groupable.
189
     * Default value is `false`.
190
     * ```typescript
191
     * let isGroupable = this.column.groupable;
192
     * ```
193
     * ```html
194
     * <igx-column [groupable] = "true"></igx-column>
195
     * ```
196
     *
197
     * @memberof IgxColumnComponent
198
     */
199
    @notifyChanges(true)
200
    @WatchColumnChanges()
201
    @Input({ transform: booleanAttribute })
202
    public get groupable(): boolean {
203
        return this._groupable;
162,771✔
204
    }
205
    public set groupable(value: boolean) {
206
        this._groupable = value;
3,452✔
207
        this.grid.groupablePipeTrigger++;
3,452✔
208
    }
209
    /**
210
     * Gets whether the column is editable.
211
     * Default value is `false`.
212
     * ```typescript
213
     * let isEditable = this.column.editable;
214
     * ```
215
     *
216
     * @memberof IgxColumnComponent
217
     */
218
    @WatchColumnChanges()
219
    @Input({ transform: booleanAttribute })
220
    public get editable(): boolean {
221
        // Updating the primary key when grid has transactions (incl. row edit)
222
        // should not be allowed, as that can corrupt transaction state.
223
        const rowEditable = this.grid && this.grid.rowEditable;
2,403,624✔
224
        const hasTransactions = this.grid && this.grid.transactions.enabled;
2,403,624✔
225

226
        if (this.isPrimaryColumn && (rowEditable || hasTransactions)) {
2,403,624✔
227
            return false;
99,069✔
228
        }
229

230
        if (this._editable !== undefined) {
2,304,555✔
231
            return this._editable;
391,543✔
232
        } else {
233
            return rowEditable;
1,913,012✔
234
        }
235
    }
236
    /**
237
     * Sets whether the column is editable.
238
     * ```typescript
239
     * this.column.editable = true;
240
     * ```
241
     * ```html
242
     * <igx-column [editable] = "true"></igx-column>
243
     * ```
244
     *
245
     * @memberof IgxColumnComponent
246
     */
247
    public set editable(editable: boolean) {
248
        this._editable = editable;
4,429✔
249
    }
250
    /**
251
     * Sets/gets whether the column is filterable.
252
     * Default value is `true`.
253
     * ```typescript
254
     * let isFilterable = this.column.filterable;
255
     * ```
256
     * ```html
257
     * <igx-column [filterable] = "false"></igx-column>
258
     * ```
259
     *
260
     * @memberof IgxColumnComponent
261
     */
262
    @notifyChanges()
263
    @WatchColumnChanges()
264
    @Input({ transform: booleanAttribute })
265
    public filterable = true;
29,102✔
266
    /**
267
     * Sets/gets whether the column is resizable.
268
     * Default value is `false`.
269
     * ```typescript
270
     * let isResizable = this.column.resizable;
271
     * ```
272
     * ```html
273
     * <igx-column [resizable] = "true"></igx-column>
274
     * ```
275
     *
276
     * @memberof IgxColumnComponent
277
     */
278
    @WatchColumnChanges()
279
    @Input({ transform: booleanAttribute })
280
    public resizable = false;
29,102✔
281

282
    /**
283
     * Sets/gets whether the column header is included in autosize logic.
284
     * Useful when template for a column header is sized based on parent, for example a default `div`.
285
     * Default value is `false`.
286
     * ```typescript
287
     * let isResizable = this.column.resizable;
288
     * ```
289
     * ```html
290
     * <igx-column [resizable] = "true"></igx-column>
291
     * ```
292
     *
293
     * @memberof IgxColumnComponent
294
     */
295
    @WatchColumnChanges()
296
    @Input({ transform: booleanAttribute })
297
    public autosizeHeader = true;
29,102✔
298

299
    /**
300
     * Gets a value indicating whether the summary for the column is enabled.
301
     * ```typescript
302
     * let hasSummary = this.column.hasSummary;
303
     * ```
304
     *
305
     * @memberof IgxColumnComponent
306
     */
307
    @notifyChanges(true)
308
    @WatchColumnChanges()
309
    @Input({ transform: booleanAttribute })
310
    public get hasSummary() {
311
        return this._hasSummary;
2,317,759✔
312
    }
313
    /**
314
     * Sets a value indicating whether the summary for the column is enabled.
315
     * Default value is `false`.
316
     * ```html
317
     * <igx-column [hasSummary] = "true"></igx-column>
318
     * ```
319
     *
320
     * @memberof IgxColumnComponent
321
     */
322
    public set hasSummary(value) {
323
        this._hasSummary = value;
3,283✔
324

325
        if (this.grid) {
3,283✔
326
            this.grid.summaryService.resetSummaryHeight();
3,283✔
327
        }
328
    }
329
    /**
330
     * Gets whether the column is hidden.
331
     * ```typescript
332
     * let isHidden = this.column.hidden;
333
     * ```
334
     *
335
     * @memberof IgxColumnComponent
336
     */
337
    @notifyChanges(true)
338
    @WatchColumnChanges()
339
    @Input({ transform: booleanAttribute })
340
    public get hidden(): boolean {
341
        return this._hidden;
1,318,553✔
342
    }
343
    /**
344
     * Sets the column hidden property.
345
     * Default value is `false`.
346
     * ```html
347
     * <igx-column [hidden] = "true"></igx-column>
348
     * ```
349
     *
350
     * Two-way data binding.
351
     * ```html
352
     * <igx-column [(hidden)] = "model.isHidden"></igx-column>
353
     * ```
354
     *
355
     * @memberof IgxColumnComponent
356
     */
357
    public set hidden(value: boolean) {
358
        if (this._hidden !== value) {
5,846✔
359
            this._hidden = value;
1,133✔
360
            this.hiddenChange.emit(this._hidden);
1,133✔
361
            if (this.columnLayoutChild && this.parent.hidden !== value) {
1,133✔
362
                this.parent.hidden = value;
5✔
363
                return;
5✔
364
            }
365
            if (this.grid) {
1,128✔
366
                this.grid.crudService.endEdit(false);
1,128✔
367
                this.grid.summaryService.resetSummaryHeight();
1,128✔
368
                this.grid.filteringService.refreshExpressions();
1,128✔
369
                this.grid.filteringService.hideFilteringRowOnColumnVisibilityChange(this);
1,128✔
370
                this.grid.notifyChanges();
1,128✔
371
            }
372
        }
373
    }
374

375
    /**
376
     * Returns if the column is selected.
377
     * ```typescript
378
     * let isSelected = this.column.selected;
379
     * ```
380
     *
381
     * @memberof IgxColumnComponent
382
     */
383
    public get selected(): boolean {
384
        return this.grid.selectionService.isColumnSelected(this.field);
1,691,636✔
385
    }
386

387
    /**
388
     * Select/deselect a column.
389
     * Default value is `false`.
390
     * ```typescript
391
     * this.column.selected = true;
392
     * ```
393
     *
394
     * @memberof IgxColumnComponent
395
     */
396
    public set selected(value: boolean) {
397
        if (this.selectable && value !== this.selected) {
46✔
398
            if (value) {
42✔
399
                this.grid.selectionService.selectColumnsWithNoEvent([this.field]);
37✔
400
            } else {
401
                this.grid.selectionService.deselectColumnsWithNoEvent([this.field]);
5✔
402
            }
403
            this.grid.notifyChanges();
42✔
404
        }
405
    }
406

407
    /**
408
     * @hidden
409
     */
410
    @Output()
411
    public hiddenChange = new EventEmitter<boolean>();
29,102✔
412

413
    /** @hidden */
414
    @Output()
415
    public expandedChange = new EventEmitter<boolean>();
29,102✔
416

417
    /** @hidden */
418
    @Output()
419
    public collapsibleChange = new EventEmitter<boolean>();
29,102✔
420
    /** @hidden */
421
    @Output()
422
    public visibleWhenCollapsedChange = new EventEmitter<boolean>();
29,102✔
423

424
    /** @hidden */
425
    @Output()
426
    public columnChange = new EventEmitter<void>();
29,102✔
427

428
    /**
429
     * Gets whether the hiding is disabled.
430
     * ```typescript
431
     * let isHidingDisabled =  this.column.disableHiding;
432
     * ```
433
     *
434
     * @memberof IgxColumnComponent
435
     */
436
    @notifyChanges()
437
    @WatchColumnChanges()
438
    @Input({ transform: booleanAttribute })
439
    public disableHiding = false;
29,102✔
440
    /**
441
     * Gets whether the pinning is disabled.
442
     * ```typescript
443
     * let isPinningDisabled =  this.column.disablePinning;
444
     * ```
445
     *
446
     * @memberof IgxColumnComponent
447
     */
448
    @notifyChanges()
449
    @WatchColumnChanges()
450
    @Input({ transform: booleanAttribute })
451
    public disablePinning = false;
29,102✔
452

453
    /**
454
     * Gets the `width` of the column.
455
     * ```typescript
456
     * let columnWidth = this.column.width;
457
     * ```
458
     *
459
     * @memberof IgxColumnComponent
460
     */
461
    @notifyChanges(true)
462
    @WatchColumnChanges()
463
    @Input()
464
    public get width(): string {
465
        const isAutoWidth = this._width && typeof this._width === 'string' && this._width === 'auto';
2,133,112✔
466
        if (isAutoWidth) {
2,133,112✔
467
            if (!this.autoSize) {
21,290✔
468
                return 'fit-content';
11,774✔
469
            } else {
470
                return this.autoSize + 'px';
9,516✔
471
            }
472

473
        }
474
        return this.widthSetByUser ? this._width : this.defaultWidth;
2,111,822✔
475
    }
476

477
    /**
478
     * Sets the `width` of the column.
479
     * ```html
480
     * <igx-column [width] = "'25%'"></igx-column>
481
     * ```
482
     *
483
     * Two-way data binding.
484
     * ```html
485
     * <igx-column [(width)]="model.columns[0].width"></igx-column>
486
     * ```
487
     *
488
     * @memberof IgxColumnComponent
489
     */
490
    public set width(value: string) {
491
        if (value) {
15,318✔
492
            this._calcWidth = null;
13,396✔
493
            this.calcPixelWidth = NaN;
13,396✔
494
            this.widthSetByUser = true;
13,396✔
495
            // width could be passed as number from the template
496
            // host bindings are not px affixed so we need to ensure we affix simple number strings
497
            if (typeof (value) === 'number' || value.match(/^[0-9]*$/)) {
13,396✔
498
                value = value + 'px';
877✔
499
            }
500
            if (value === 'fit-content') {
13,396✔
501
                value = 'auto';
1✔
502
            }
503
            this._width = value;
13,396✔
504
            if (this.grid) {
13,396✔
505
                this.cacheCalcWidth();
13,396✔
506
            }
507
            this.widthChange.emit(this._width);
13,396✔
508
        }
509
    }
510

511
    /** @hidden @internal **/
512
    public autoSize: number;
513

514
    /**
515
     * Sets/gets the maximum `width` of the column.
516
     * ```typescript
517
     * let columnMaxWidth = this.column.width;
518
     * ```
519
     * ```html
520
     * <igx-column [maxWidth] = "'150px'"></igx-column>
521
     * ```
522
     *
523
     * @memberof IgxColumnComponent
524
     */
525
    @WatchColumnChanges()
526
    @Input()
527
    public maxWidth: string;
528

529
    /**
530
     * Sets/gets the class selector of the column header.
531
     * ```typescript
532
     * let columnHeaderClass = this.column.headerClasses;
533
     * ```
534
     * ```html
535
     * <igx-column [headerClasses] = "'column-header'"></igx-column>
536
     * ```
537
     *
538
     * @memberof IgxColumnComponent
539
     */
540
    @notifyChanges()
541
    @WatchColumnChanges()
542
    @Input()
543
    public headerClasses = '';
29,102✔
544

545
    /**
546
     * Sets conditional style properties on the column header.
547
     * Similar to `ngStyle` it accepts an object literal where the keys are
548
     * the style properties and the value is the expression to be evaluated.
549
     * ```typescript
550
     * styles = {
551
     *  background: 'royalblue',
552
     *  color: (column) => column.pinned ? 'red': 'inherit'
553
     * }
554
     * ```
555
     * ```html
556
     * <igx-column [headerStyles]="styles"></igx-column>
557
     * ```
558
     *
559
     * @memberof IgxColumnComponent
560
     */
561
    @notifyChanges()
562
    @WatchColumnChanges()
563
    @Input()
564
    public headerStyles = null;
29,102✔
565

566
    /**
567
     * Sets/gets the class selector of the column group header.
568
     * ```typescript
569
     * let columnHeaderClass = this.column.headerGroupClasses;
570
     * ```
571
     * ```html
572
     * <igx-column [headerGroupClasses] = "'column-group-header'"></igx-column>
573
     * ```
574
     *
575
     * @memberof IgxColumnComponent
576
     */
577
    @notifyChanges()
578
    @WatchColumnChanges()
579
    @Input()
580
    public headerGroupClasses = '';
29,102✔
581

582
    /**
583
     * Sets conditional style properties on the column header group wrapper.
584
     * Similar to `ngStyle` it accepts an object literal where the keys are
585
     * the style properties and the value is the expression to be evaluated.
586
     * ```typescript
587
     * styles = {
588
     *  background: 'royalblue',
589
     *  color: (column) => column.pinned ? 'red': 'inherit'
590
     * }
591
     * ```
592
     * ```html
593
     * <igx-column [headerGroupStyles]="styles"></igx-column>
594
     * ```
595
     *
596
     * @memberof IgxColumnComponent
597
     */
598
    @notifyChanges()
599
    @WatchColumnChanges()
600
    @Input()
601
    public headerGroupStyles = null;
29,102✔
602

603
    /* treatAsRef */
604
    /**
605
     * Sets a conditional class selector of the column cells.
606
     * Accepts an object literal, containing key-value pairs,
607
     * where the key is the name of the CSS class, while the
608
     * value is either a callback function that returns a boolean,
609
     * or boolean, like so:
610
     * ```typescript
611
     * callback = (rowData, columnKey, cellValue, rowIndex) => { return rowData[columnKey] > 6; }
612
     * cellClasses = { 'className' : this.callback };
613
     * ```
614
     * ```html
615
     * <igx-column [cellClasses] = "cellClasses"></igx-column>
616
     * <igx-column [cellClasses] = "{'class1' : true }"></igx-column>
617
     * ```
618
     *
619
     * @memberof IgxColumnComponent
620
     */
621
    @notifyChanges()
622
    @WatchColumnChanges()
623
    @Input()
624
    public cellClasses: any;
625

626
    /* treatAsRef */
627
    /**
628
     * Sets conditional style properties on the column cells.
629
     * Similar to `ngStyle` it accepts an object literal where the keys are
630
     * the style properties and the value is the expression to be evaluated.
631
     * As with `cellClasses` it accepts a callback function.
632
     * ```typescript
633
     * styles = {
634
     *  background: 'royalblue',
635
     *  color: (rowData, columnKey, cellValue, rowIndex) => value.startsWith('Important') ? 'red': 'inherit'
636
     * }
637
     * ```
638
     * ```html
639
     * <igx-column [cellStyles]="styles"></igx-column>
640
     * ```
641
     *
642
     * @memberof IgxColumnComponent
643
     */
644
    @notifyChanges()
645
    @WatchColumnChanges()
646
    @Input()
647
    public cellStyles = null;
29,102✔
648

649
    /* blazorAlternateType: CellValueFormatterEventHandler */
650
    /* blazorOnlyScript */
651
    /**
652
     * Applies display format to cell values in the column. Does not modify the underlying data.
653
     *
654
     * @remarks
655
     * Note: As the formatter is used in places like the Excel style filtering dialog, in certain
656
     * scenarios (remote filtering for example), the row data argument can be `undefined`.
657
     *
658
     *
659
     * In this example, we check to see if the column name is Salary, and then provide a method as the column formatter
660
     * to format the value into a currency string.
661
     *
662
     * @example
663
     * ```typescript
664
     * columnInit(column: IgxColumnComponent) {
665
     *   if (column.field == "Salary") {
666
     *     column.formatter = (salary => this.format(salary));
667
     *   }
668
     * }
669
     *
670
     * format(value: number) : string {
671
     *   return formatCurrency(value, "en-us", "$");
672
     * }
673
     * ```
674
     *
675
     * @example
676
     * ```typescript
677
     * const column = this.grid.getColumnByName('Address');
678
     * const addressFormatter = (address: string, rowData: any) => data.privacyEnabled ? 'unknown' : address;
679
     * column.formatter = addressFormatter;
680
     * ```
681
     *
682
     * @memberof IgxColumnComponent
683
     */
684
    @notifyChanges()
685
    @WatchColumnChanges()
686
    @Input()
687
    public formatter: (value: any, rowData?: any) => any;
688

689
    /* blazorAlternateType: SummaryValueFormatterEventHandler */
690
    /* blazorOnlyScript */
691
    /* forceCastDelegate */
692
    /**
693
     * The summaryFormatter is used to format the display of the column summaries.
694
     *
695
     * In this example, we check to see if the column name is OrderDate, and then provide a method as the summaryFormatter
696
     * to change the locale for the dates to 'fr-FR'. The summaries with the count key are skipped so they are displayed as numbers.
697
     *
698
     * ```typescript
699
     * columnInit(column: IgxColumnComponent) {
700
     *   if (column.field == "OrderDate") {
701
     *     column.summaryFormatter = this.summaryFormat;
702
     *   }
703
     * }
704
     *
705
     * summaryFormat(summary: IgxSummaryResult, summaryOperand: IgxSummaryOperand): string {
706
     *   const result = summary.summaryResult;
707
     *   if(summaryResult.key !== 'count' && result !== null && result !== undefined) {
708
     *      const pipe = new DatePipe('fr-FR');
709
     *      return pipe.transform(result,'mediumDate');
710
     *   }
711
     *   return result;
712
     * }
713
     * ```
714
     *
715
     * @memberof IgxColumnComponent
716
     */
717
    @notifyChanges()
718
    @WatchColumnChanges()
719
    @Input()
720
    public summaryFormatter: (summary: IgxSummaryResult, summaryOperand: IgxSummaryOperand) => any;
721

722
    /**
723
     * Sets/gets whether the column filtering should be case sensitive.
724
     * Default value is `true`.
725
     * ```typescript
726
     * let filteringIgnoreCase = this.column.filteringIgnoreCase;
727
     * ```
728
     * ```html
729
     * <igx-column [filteringIgnoreCase] = "false"></igx-column>
730
     * ```
731
     *
732
     * @memberof IgxColumnComponent
733
     */
734
    @WatchColumnChanges()
735
    @Input({ transform: booleanAttribute })
736
    public filteringIgnoreCase = true;
29,102✔
737
    /**
738
     * Sets/gets whether the column sorting should be case sensitive.
739
     * Default value is `true`.
740
     * ```typescript
741
     * let sortingIgnoreCase = this.column.sortingIgnoreCase;
742
     * ```
743
     * ```html
744
     * <igx-column [sortingIgnoreCase] = "false"></igx-column>
745
     * ```
746
     *
747
     * @memberof IgxColumnComponent
748
     */
749
    @WatchColumnChanges()
750
    @Input({ transform: booleanAttribute })
751
    public sortingIgnoreCase = true;
29,102✔
752
    /**
753
     * Sets/gets whether the column is `searchable`.
754
     * Default value is `true`.
755
     * ```typescript
756
     * let isSearchable =  this.column.searchable';
757
     * ```
758
     * ```html
759
     *  <igx-column [searchable] = "false"></igx-column>
760
     * ```
761
     *
762
     * @memberof IgxColumnComponent
763
     */
764
    @notifyChanges()
765
    @WatchColumnChanges()
766
    @Input({ transform: booleanAttribute })
767
    public searchable = true;
29,102✔
768
    /**
769
     * Sets/gets the data type of the column values.
770
     * Default value is `string`.
771
     * ```typescript
772
     * let columnDataType = this.column.dataType;
773
     * ```
774
     * ```html
775
     * <igx-column [dataType] = "'number'"></igx-column>
776
     * ```
777
     *
778
     * @memberof IgxColumnComponent
779
     */
780
    @Input()
781
    public dataType: GridColumnDataType = GridColumnDataType.String;
29,102✔
782

783
    /** @hidden */
784
    @Input()
785
    public collapsibleIndicatorTemplate: TemplateRef<IgxColumnTemplateContext>;
786

787
    /**
788
     * Row index where the current field should end.
789
     * The amount of rows between rowStart and rowEnd will determine the amount of spanning rows to that field
790
     * ```html
791
     * <igx-column-layout>
792
     *   <igx-column [rowEnd]="2" [rowStart]="1" [colStart]="1"></igx-column>
793
     * </igx-column-layout>
794
     * ```
795
     *
796
     * @memberof IgxColumnComponent
797
     */
798
    @Input()
799
    public rowEnd: number;
800

801
    /**
802
     * Column index where the current field should end.
803
     * The amount of columns between colStart and colEnd will determine the amount of spanning columns to that field
804
     * ```html
805
     * <igx-column-layout>
806
     *   <igx-column [colEnd]="3" [rowStart]="1" [colStart]="1"></igx-column>
807
     * </igx-column-layout>
808
     * ```
809
     *
810
     * @memberof IgxColumnComponent
811
     */
812
    @Input()
813
    public colEnd: number;
814

815
    /**
816
     * Row index from which the field is starting.
817
     * ```html
818
     * <igx-column-layout>
819
     *   <igx-column [rowStart]="1" [colStart]="1"></igx-column>
820
     * </igx-column-layout>
821
     * ```
822
     *
823
     * @memberof IgxColumnComponent
824
     */
825
    @Input()
826
    public rowStart: number;
827

828
    /**
829
     * Column index from which the field is starting.
830
     * ```html
831
     * <igx-column-layout>
832
     *   <igx-column [colStart]="1" [rowStart]="1"></igx-column>
833
     * </igx-column-layout>
834
     * ```
835
     *
836
     * @memberof IgxColumnComponent
837
     */
838
    @Input()
839
    public colStart: number;
840

841
    /**
842
     * Sets/gets custom properties provided in additional template context.
843
     *
844
     * ```html
845
     * <igx-column [additionalTemplateContext]="contextObject">
846
     *   <ng-template igxCell let-cell="cell" let-props="additionalTemplateContext">
847
     *      {{ props }}
848
     *   </ng-template>
849
     * </igx-column>
850
     * ```
851
     *
852
     * @memberof IgxColumnComponent
853
     */
854
    @Input()
855
    public additionalTemplateContext: any;
856

857
    /**
858
     * @hidden
859
     */
860
    @Output()
861
    public widthChange = new EventEmitter<string>();
29,102✔
862

863
    /**
864
     * @hidden
865
     */
866
    @Output()
867
    public pinnedChange = new EventEmitter<boolean>();
29,102✔
868
    /**
869
     * @hidden
870
     */
871
    @ContentChild(IgxFilterCellTemplateDirective, { read: IgxFilterCellTemplateDirective })
872
    public filterCellTemplateDirective: IgxFilterCellTemplateDirective;
873
    /**
874
     * @hidden
875
     */
876
    @ContentChild(IgxSummaryTemplateDirective, { read: IgxSummaryTemplateDirective })
877
    protected summaryTemplateDirective: IgxSummaryTemplateDirective;
878
    /**
879
     * @hidden
880
     * @see {@link bodyTemplate}
881
     */
882
    @ContentChild(IgxCellTemplateDirective, { read: IgxCellTemplateDirective })
883
    protected cellTemplate: IgxCellTemplateDirective;
884
    /**
885
     * @hidden
886
     */
887
    @ContentChild(IgxCellValidationErrorDirective, { read: IgxCellValidationErrorDirective })
888
    protected cellValidationErrorTemplate: IgxCellValidationErrorDirective;
889
    /**
890
     * @hidden
891
     */
892
    @ContentChildren(IgxCellHeaderTemplateDirective, { read: IgxCellHeaderTemplateDirective, descendants: false })
893
    protected headTemplate: QueryList<IgxCellHeaderTemplateDirective>;
894
    /**
895
     * @hidden
896
     */
897
    @ContentChild(IgxCellEditorTemplateDirective, { read: IgxCellEditorTemplateDirective })
898
    protected editorTemplate: IgxCellEditorTemplateDirective;
899
    /**
900
     * @hidden
901
     */
902
    @ContentChild(IgxCollapsibleIndicatorTemplateDirective, { read: IgxCollapsibleIndicatorTemplateDirective, static: false })
903
    protected collapseIndicatorTemplate: IgxCollapsibleIndicatorTemplateDirective;
904
    /**
905
     * @hidden
906
     */
907
    public get calcWidth(): any {
908
        return this.getCalcWidth();
237,531✔
909
    }
910

911
    /** @hidden @internal **/
912
    public calcPixelWidth: number;
913

914
    /**
915
     * @hidden
916
     */
917
    public get maxWidthPx() {
918
        const gridAvailableSize = this.grid.calcWidth;
70,516✔
919
        const isPercentageWidth = this.maxWidth && typeof this.maxWidth === 'string' && this.maxWidth.indexOf('%') !== -1;
70,516✔
920
        return isPercentageWidth ? parseFloat(this.maxWidth) / 100 * gridAvailableSize : parseFloat(this.maxWidth);
70,516✔
921
    }
922

923
    /**
924
     * @hidden
925
     */
926
    public get maxWidthPercent() {
927
        const gridAvailableSize = this.grid.calcWidth;
17✔
928
        const isPercentageWidth = this.maxWidth && typeof this.maxWidth === 'string' && this.maxWidth.indexOf('%') !== -1;
17✔
929
        return isPercentageWidth ? parseFloat(this.maxWidth) : parseFloat(this.maxWidth) / gridAvailableSize * 100;
17✔
930
    }
931

932
    /**
933
     * @hidden
934
     */
935
    public get minWidthPx() {
936
        const gridAvailableSize = this.grid.calcWidth;
70,626✔
937
        const isPercentageWidth = this.minWidth && typeof this.minWidth === 'string' && this.minWidth.indexOf('%') !== -1;
70,626✔
938
        return isPercentageWidth ? parseFloat(this.minWidth) / 100 * gridAvailableSize : parseFloat(this.minWidth);
70,626✔
939
    }
940

941
    /**
942
     * @hidden
943
     */
944
    public get minWidthPercent() {
945
        const gridAvailableSize = this.grid.calcWidth;
17✔
946
        const isPercentageWidth = this.minWidth && typeof this.minWidth === 'string' && this.minWidth.indexOf('%') !== -1;
17✔
947
        return isPercentageWidth ? parseFloat(this.minWidth) : parseFloat(this.minWidth) / gridAvailableSize * 100;
17✔
948
    }
949

950

951
    /**
952
     * Sets/gets the minimum `width` of the column.
953
     * Default value is `88`;
954
     * ```typescript
955
     * let columnMinWidth = this.column.minWidth;
956
     * ```
957
     * ```html
958
     * <igx-column [minWidth] = "'100px'"></igx-column>
959
     * ```
960
     *
961
     * @memberof IgxColumnComponent
962
     */
963
    @notifyChanges()
964
    @WatchColumnChanges()
965
    @Input()
966
    public set minWidth(value: string) {
967
        const minVal = parseFloat(value);
1,480✔
968
        if (Number.isNaN(minVal)) {
1,480✔
969
            return;
45✔
970
        }
971
        this._defaultMinWidth = value;
1,435✔
972

973
    }
974
    public get minWidth(): string {
975
        return !this._defaultMinWidth ? this.defaultMinWidth : this._defaultMinWidth;
286,827✔
976
    }
977

978
    /** @hidden @internal **/
979
    public get resolvedWidth(): string {
980
        if (this.columnLayoutChild) {
4,096,280!
UNCOV
981
            return '';
×
982
        }
983
        const isAutoWidth = this._width && typeof this._width === 'string' && this._width === 'auto';
4,096,280✔
984
        return isAutoWidth ? this.width : this.calcPixelWidth + 'px';
4,096,280✔
985
    }
986

987
    /**
988
     * Gets the column index.
989
     * ```typescript
990
     * let columnIndex = this.column.index;
991
     * ```
992
     *
993
     * @memberof IgxColumnComponent
994
     */
995
    public get index(): number {
996
        return (this.grid as any)._columns.indexOf(this);
240,090✔
997
    }
998

999
    /**
1000
     * Gets whether the column is `pinned`.
1001
     * ```typescript
1002
     * let isPinned = this.column.pinned;
1003
     * ```
1004
     *
1005
     * @memberof IgxColumnComponent
1006
     */
1007
    @WatchColumnChanges()
1008
    @Input({ transform: booleanAttribute })
1009
    public get pinned(): boolean {
1010
        return this._pinned;
2,257,006✔
1011
    }
1012
    /**
1013
     * Sets whether the column is pinned.
1014
     * Default value is `false`.
1015
     * ```html
1016
     * <igx-column [pinned] = "true"></igx-column>
1017
     * ```
1018
     *
1019
     * Two-way data binding.
1020
     * ```html
1021
     * <igx-column [(pinned)] = "model.columns[0].isPinned"></igx-column>
1022
     * ```
1023
     *
1024
     * @memberof IgxColumnComponent
1025
     */
1026
    public set pinned(value: boolean) {
1027
        if (this._pinned !== value) {
2,823✔
1028
            const isAutoWidth = this.width && typeof this.width === 'string' && this.width === 'fit-content';
564✔
1029
            if (this.grid && this.width && (isAutoWidth || !isNaN(parseInt(this.width, 10)))) {
564✔
1030
                if (value) {
392✔
1031
                    this.pin();
354✔
1032
                } else {
1033
                    this.unpin();
38✔
1034
                }
1035
                return;
392✔
1036
            }
1037
            /* No grid/width available at initialization. `initPinning` in the grid
1038
               will re-init the group (if present)
1039
            */
1040
            this._pinned = value;
172✔
1041
            this.pinnedChange.emit(this._pinned);
172✔
1042
        }
1043
    }
1044

1045
    /* treatAsRef */
1046
    /**
1047
     * Gets the column `summaries`.
1048
     * ```typescript
1049
     * let columnSummaries = this.column.summaries;
1050
     * ```
1051
     *
1052
     * @memberof IgxColumnComponent
1053
     */
1054
    @notifyChanges(true)
1055
    @WatchColumnChanges()
1056
    @Input()
1057
    public get summaries(): any {
1058
        return this._summaries;
64,472✔
1059
    }
1060

1061
    /* treatAsRef */
1062
    /**
1063
     * Sets the column `summaries`.
1064
     * ```typescript
1065
     * this.column.summaries = IgxNumberSummaryOperand;
1066
     * ```
1067
     *
1068
     * @memberof IgxColumnComponent
1069
     */
1070
    public set summaries(classRef: any) {
1071
        if (isConstructor(classRef)) {
24,066✔
1072
            this._summaries = new classRef();
24,060✔
1073
        }
1074

1075
        if (this.grid) {
24,066✔
1076
            this.grid.summaryService.removeSummariesCachePerColumn(this.field);
24,066✔
1077
            this.grid.summaryPipeTrigger++;
24,066✔
1078
            this.grid.summaryService.resetSummaryHeight();
24,066✔
1079
        }
1080
    }
1081

1082
    /**
1083
     * Sets/gets the summary operands to exclude from display.
1084
     * Accepts an array of string keys representing the summary types to disable, such as 'Min', 'Max', 'Count' etc.
1085
     * ```typescript
1086
     * let disabledSummaries = this.column.disabledSummaries;
1087
     * ```
1088
     * ```html
1089
     * <igx-column [disabledSummaries]="['min', 'max', 'average']"></igx-column>
1090
     * ```
1091
     *
1092
     * @memberof IgxColumnComponent
1093
     */
1094
    @WatchColumnChanges()
1095
    @Input()
1096
    public get disabledSummaries(): string[] {
1097
        return this._disabledSummaries;
17,984✔
1098
    }
1099

1100
    public set disabledSummaries(value: string[]) {
1101
        if (isEqual(this._disabledSummaries, value)) {
1,418✔
1102
            return;
1,411✔
1103
        }
1104
        this._disabledSummaries = value;
7✔
1105
        if (this.grid) {
7✔
1106
            this.grid.summaryService.removeSummariesCachePerColumn(this.field);
7✔
1107
            this.grid.summaryPipeTrigger++;
7✔
1108
            this.grid.summaryService.resetSummaryHeight();
7✔
1109
        }
1110
    }
1111

1112
    /**
1113
     * Gets the column `filters`.
1114
     * ```typescript
1115
     * let columnFilters = this.column.filters'
1116
     * ```
1117
     *
1118
     * @memberof IgxColumnComponent
1119
     */
1120
    @Input()
1121
    public get filters(): IgxFilteringOperand {
1122
        return this._filters;
74,577✔
1123
    }
1124
    /**
1125
     * Sets the column `filters`.
1126
     * ```typescript
1127
     * this.column.filters = IgxBooleanFilteringOperand.instance().
1128
     * ```
1129
     *
1130
     * @memberof IgxColumnComponent
1131
     */
1132
    public set filters(instance: IgxFilteringOperand) {
1133
        this._filters = instance;
24,052✔
1134
    }
1135
    /**
1136
     * Gets the column `sortStrategy`.
1137
     * ```typescript
1138
     * let sortStrategy = this.column.sortStrategy
1139
     * ```
1140
     *
1141
     * @memberof IgxColumnComponent
1142
     */
1143
    @Input()
1144
    public get sortStrategy(): ISortingStrategy {
1145
        return this._sortStrategy;
2,852✔
1146
    }
1147
    /**
1148
     * Sets the column `sortStrategy`.
1149
     * ```typescript
1150
     * this.column.sortStrategy = new CustomSortingStrategy().
1151
     * class CustomSortingStrategy extends SortingStrategy {...}
1152
     * ```
1153
     *
1154
     * @memberof IgxColumnComponent
1155
     */
1156
    public set sortStrategy(classRef: ISortingStrategy) {
1157
        this._sortStrategy = classRef;
1,414✔
1158
    }
1159

1160
    /* blazorSuppress */
1161
    /**
1162
     * Gets the function that compares values for grouping.
1163
     * ```typescript
1164
     * let groupingComparer = this.column.groupingComparer'
1165
     * ```
1166
     *
1167
     * @memberof IgxColumnComponent
1168
     */
1169
    @Input()
1170
    public get groupingComparer(): (a: any, b: any, currRec?: any, groupRec?: any) => number {
1171
        return this._groupingComparer;
3,283✔
1172
    }
1173

1174
    /* blazorSuppress */
1175
    /**
1176
     * Sets a custom function to compare values for grouping.
1177
     * Subsequent values in the sorted data that the function returns 0 for are grouped.
1178
     * ```typescript
1179
     * this.column.groupingComparer = (a: any, b: any, currRec?: any, groupRec?: any) => { return a === b ? 0 : -1; }
1180
     * ```
1181
     *
1182
     * @memberof IgxColumnComponent
1183
     */
1184
    public set groupingComparer(funcRef: (a: any, b: any, currRec?: any, groupRec?: any) => number) {
1185
        this._groupingComparer = funcRef;
1,414✔
1186
    }
1187
    /**
1188
     * @hidden @internal
1189
     */
1190
    public get defaultMinWidth(): string {
1191
        if (!this.grid) {
265,991!
UNCOV
1192
            return '80';
×
1193
        }
1194
        switch (this.grid.gridSize) {
265,991✔
1195
            case Size.Medium:
1196
                return '64';
12,321✔
1197
            case Size.Small:
1198
                return '56';
1,173✔
1199
            default:
1200
                return '80';
252,497✔
1201
        }
1202
    }
1203
    /**
1204
     * Returns a reference to the `summaryTemplate`.
1205
     * ```typescript
1206
     * let summaryTemplate = this.column.summaryTemplate;
1207
     * ```
1208
     *
1209
     * @memberof IgxColumnComponent
1210
     */
1211
    @notifyChanges()
1212
    @WatchColumnChanges()
1213
    @Input()
1214
    public get summaryTemplate(): TemplateRef<IgxSummaryTemplateContext> {
1215
        return this._summaryTemplate;
79,670✔
1216
    }
1217
    /**
1218
     * Sets the summary template.
1219
     * ```html
1220
     * <ng-template #summaryTemplate igxSummary let-summaryResults>
1221
     *    <p>{{ summaryResults[0].label }}: {{ summaryResults[0].summaryResult }}</p>
1222
     *    <p>{{ summaryResults[1].label }}: {{ summaryResults[1].summaryResult }}</p>
1223
     * </ng-template>
1224
     * ```
1225
     * ```typescript
1226
     * @ViewChild("'summaryTemplate'", {read: TemplateRef })
1227
     * public summaryTemplate: TemplateRef<any>;
1228
     * this.column.summaryTemplate = this.summaryTemplate;
1229
     * ```
1230
     *
1231
     * @memberof IgxColumnComponent
1232
     */
1233
    public set summaryTemplate(template: TemplateRef<IgxSummaryTemplateContext>) {
1234
        this._summaryTemplate = template;
1,417✔
1235
    }
1236

1237
    /**
1238
     * Returns a reference to the `bodyTemplate`.
1239
     * ```typescript
1240
     * let bodyTemplate = this.column.bodyTemplate;
1241
     * ```
1242
     *
1243
     * @memberof IgxColumnComponent
1244
     */
1245
    @notifyChanges()
1246
    @WatchColumnChanges()
1247
    @Input('cellTemplate')
1248
    public get bodyTemplate(): TemplateRef<IgxCellTemplateContext> {
1249
        return this._bodyTemplate;
1,201,216✔
1250
    }
1251
    /**
1252
     * Sets the body template.
1253
     * ```html
1254
     * <ng-template #bodyTemplate igxCell let-val>
1255
     *    <div style = "background-color: yellowgreen" (click) = "changeColor(val)">
1256
     *       <span> {{val}} </span>
1257
     *    </div>
1258
     * </ng-template>
1259
     * ```
1260
     * ```typescript
1261
     * @ViewChild("'bodyTemplate'", {read: TemplateRef })
1262
     * public bodyTemplate: TemplateRef<any>;
1263
     * this.column.bodyTemplate = this.bodyTemplate;
1264
     * ```
1265
     *
1266
     * @memberof IgxColumnComponent
1267
     */
1268
    public set bodyTemplate(template: TemplateRef<IgxCellTemplateContext>) {
1269
        this._bodyTemplate = template;
1,381✔
1270
    }
1271
    /**
1272
     * Returns a reference to the header template.
1273
     * ```typescript
1274
     * let headerTemplate = this.column.headerTemplate;
1275
     * ```
1276
     *
1277
     * @memberof IgxColumnComponent
1278
     */
1279
    @notifyChanges()
1280
    @WatchColumnChanges()
1281
    @Input()
1282
    public get headerTemplate(): TemplateRef<IgxColumnTemplateContext> {
1283
        return this._headerTemplate;
259,545✔
1284
    }
1285
    /**
1286
     * Sets the header template.
1287
     * Note that the column header height is fixed and any content bigger than it will be cut off.
1288
     * ```html
1289
     * <ng-template #headerTemplate>
1290
     *   <div style = "background-color:black" (click) = "changeColor(val)">
1291
     *       <span style="color:red" >{{column.field}}</span>
1292
     *   </div>
1293
     * </ng-template>
1294
     * ```
1295
     * ```typescript
1296
     * @ViewChild("'headerTemplate'", {read: TemplateRef })
1297
     * public headerTemplate: TemplateRef<any>;
1298
     * this.column.headerTemplate = this.headerTemplate;
1299
     * ```
1300
     *
1301
     * @memberof IgxColumnComponent
1302
     */
1303
    public set headerTemplate(template: TemplateRef<IgxColumnTemplateContext>) {
1304
        this._headerTemplate = template;
5,127✔
1305
    }
1306
    /**
1307
     * Returns a reference to the inline editor template.
1308
     * ```typescript
1309
     * let inlineEditorTemplate = this.column.inlineEditorTemplate;
1310
     * ```
1311
     *
1312
     * @memberof IgxColumnComponent
1313
     */
1314
    @notifyChanges()
1315
    @WatchColumnChanges()
1316
    @Input('cellEditorTemplate')
1317
    public get inlineEditorTemplate(): TemplateRef<IgxCellTemplateContext> {
1318
        return this._inlineEditorTemplate;
5,477✔
1319
    }
1320
    /**
1321
     * Sets the inline editor template.
1322
     * ```html
1323
     * <ng-template #inlineEditorTemplate igxCellEditor let-cell="cell">
1324
     *     <input type="string" [(ngModel)]="cell.value"/>
1325
     * </ng-template>
1326
     * ```
1327
     * ```typescript
1328
     * @ViewChild("'inlineEditorTemplate'", {read: TemplateRef })
1329
     * public inlineEditorTemplate: TemplateRef<any>;
1330
     * this.column.inlineEditorTemplate = this.inlineEditorTemplate;
1331
     * ```
1332
     *
1333
     * @memberof IgxColumnComponent
1334
     */
1335
    public set inlineEditorTemplate(template: TemplateRef<IgxCellTemplateContext>) {
1336
        this._inlineEditorTemplate = template;
1,378✔
1337
    }
1338

1339
    /**
1340
     * Returns a reference to the validation error template.
1341
     * ```typescript
1342
     * let errorTemplate = this.column.errorTemplate;
1343
     * ```
1344
     */
1345
    @notifyChanges()
1346
    @WatchColumnChanges()
1347
    @Input('errorTemplate')
1348
    public get errorTemplate(): TemplateRef<IgxCellTemplateContext> {
1349
        return this._errorTemplate;
1,145,773✔
1350
    }
1351
    /**
1352
     * Sets the error template.
1353
     * ```html
1354
     * <ng-template igxCellValidationError let-cell="cell" #errorTemplate >
1355
     *     <div *ngIf="cell.validation.errors?.['forbiddenName']">
1356
     *      This name is forbidden.
1357
     *     </div>
1358
     * </ng-template>
1359
     * ```
1360
     * ```typescript
1361
     * @ViewChild("'errorTemplate'", {read: TemplateRef })
1362
     * public errorTemplate: TemplateRef<any>;
1363
     * this.column.errorTemplate = this.errorTemplate;
1364
     * ```
1365
     */
1366
    public set errorTemplate(template: TemplateRef<IgxCellTemplateContext>) {
1367
        this._errorTemplate = template;
1,411✔
1368
    }
1369

1370
    /**
1371
     * Returns a reference to the `filterCellTemplate`.
1372
     * ```typescript
1373
     * let filterCellTemplate = this.column.filterCellTemplate;
1374
     * ```
1375
     *
1376
     * @memberof IgxColumnComponent
1377
     */
1378
    @notifyChanges()
1379
    @WatchColumnChanges()
1380
    @Input('filterCellTemplate')
1381
    public get filterCellTemplate(): TemplateRef<IgxColumnTemplateContext> {
1382
        return this._filterCellTemplate;
41,636✔
1383
    }
1384
    /**
1385
     * Sets the quick filter template.
1386
     * ```html
1387
     * <ng-template #filterCellTemplate IgxFilterCellTemplate let-column="column">
1388
     *    <input (input)="onInput()">
1389
     * </ng-template>
1390
     * ```
1391
     * ```typescript
1392
     * @ViewChild("'filterCellTemplate'", {read: TemplateRef })
1393
     * public filterCellTemplate: TemplateRef<any>;
1394
     * this.column.filterCellTemplate = this.filterCellTemplate;
1395
     * ```
1396
     *
1397
     * @memberof IgxColumnComponent
1398
     */
1399
    public set filterCellTemplate(template: TemplateRef<IgxColumnTemplateContext>) {
1400
        this._filterCellTemplate = template;
1,427✔
1401
    }
1402

1403
    /**
1404
     * @hidden @internal
1405
     */
1406
    public get cells(): CellType[] {
1407
        return this.grid.dataView
23✔
1408
            .map((rec, index) => {
1409
                if (!this.grid.isGroupByRecord(rec) && !this.grid.isSummaryRow(rec)) {
1,273✔
1410
                    this.grid.pagingMode === 1 && this.grid.page !== 0 ? index = index + this.grid.perPage * this.grid.page : index = this.grid.dataRowList.first.index + index;
1,273!
1411
                    const cell = new IgxGridCell(this.grid as any, index, this);
1,273✔
1412
                    return cell;
1,273✔
1413
                }
1414
            }).filter(cell => cell);
1,273✔
1415
    }
1416

1417

1418
    /**
1419
     * @hidden @internal
1420
     */
1421
    public get _cells(): CellType[] {
1422
        return this.grid.rowList.filter((row) => row instanceof IgxRowDirective)
2,865✔
1423
            .map((row) => {
1424
                if (row._cells) {
2,865✔
1425
                    return row._cells.filter((cell) => cell.columnIndex === this.index);
17,213✔
1426
                }
1427
            }).reduce((a, b) => a.concat(b), []);
2,865✔
1428
    }
1429

1430
    /**
1431
     * Gets the column visible index.
1432
     * If the column is not visible, returns `-1`.
1433
     * ```typescript
1434
     * let visibleColumnIndex =  this.column.visibleIndex;
1435
     * ```
1436
     *
1437
     * @memberof IgxColumnComponent
1438
     */
1439
    public get visibleIndex(): number {
1440
        if (!isNaN(this._vIndex)) {
5,404,723✔
1441
            return this._vIndex;
4,961,059✔
1442
        }
1443
        const unpinnedColumns = this.grid.unpinnedColumns.filter(c => !c.columnGroup);
4,389,799✔
1444
        const pinnedColumns = this.grid.pinnedColumns.filter(c => !c.columnGroup);
443,664✔
1445

1446
        let col = this;
443,664✔
1447
        let vIndex = -1;
443,664✔
1448

1449
        if (this.columnGroup) {
443,664✔
1450
            col = this.allChildren.filter(c => !c.columnGroup && !c.hidden)[0] as any;
24,367✔
1451
        }
1452
        if (this.columnLayoutChild) {
443,664✔
1453
            return this.parent.childrenVisibleIndexes.find(x => x.column === this).index;
859,737✔
1454
        }
1455

1456
        if (!this.pinned) {
111,467✔
1457
            const indexInCollection = unpinnedColumns.indexOf(col);
109,278✔
1458
            vIndex = indexInCollection === -1 ?
109,278✔
1459
                -1 :
1460
                (this.grid.isPinningToStart ?
108,662✔
1461
                    pinnedColumns.length + indexInCollection :
1462
                    indexInCollection);
1463
        } else {
1464
            const indexInCollection = pinnedColumns.indexOf(col);
2,189✔
1465
            vIndex = this.grid.isPinningToStart ?
2,189✔
1466
                indexInCollection :
1467
                unpinnedColumns.length + indexInCollection;
1468
        }
1469
        this._vIndex = vIndex;
111,467✔
1470
        return vIndex;
111,467✔
1471
    }
1472

1473
    /* blazorCSSuppress - Blazor doesn't carry over the ColumnType interface + should translate as static bool value */
1474
    /**
1475
     * Returns a boolean indicating if the column is a `ColumnGroup`.
1476
     * ```typescript
1477
     * let columnGroup =  this.column.columnGroup;
1478
     * ```
1479
     *
1480
     * @memberof IgxColumnComponent
1481
     */
1482
    public get columnGroup() {
1483
        return false;
8,047,235✔
1484
    }
1485

1486
    /* blazorCSSuppress - Blazor doesn't carry over the ColumnType interface + should translate as static bool value */
1487
    /**
1488
     * Returns a boolean indicating if the column is a `ColumnLayout` for multi-row layout.
1489
     * ```typescript
1490
     * let columnGroup =  this.column.columnGroup;
1491
     * ```
1492
     *
1493
     * @memberof IgxColumnComponent
1494
     */
1495
    public get columnLayout() {
1496
        return false;
19,461,509✔
1497
    }
1498

1499
    /**
1500
     * Returns a boolean indicating if the column is a child of a `ColumnLayout` for multi-row layout.
1501
     * ```typescript
1502
     * let columnLayoutChild =  this.column.columnLayoutChild;
1503
     * ```
1504
     *
1505
     * @memberof IgxColumnComponent
1506
     */
1507
    public get columnLayoutChild(): boolean {
1508
        return this.parent && this.parent.columnLayout;
16,895,350✔
1509
    }
1510

1511
    /**
1512
     * A list containing all the child columns under this column (if any).
1513
     * Empty without children or if this column is not Group or Layout.
1514
     */
1515
    public get childColumns(): ColumnType[] {
UNCOV
1516
        return [];
×
1517
    }
1518

1519
    /** @hidden @internal **/
1520
    public get allChildren(): IgxColumnComponent[] {
1521
        return [];
310✔
1522
    }
1523
    /**
1524
     * Returns the level of the column in a column group.
1525
     * Returns `0` if the column doesn't have a `parent`.
1526
     * ```typescript
1527
     * let columnLevel =  this.column.level;
1528
     * ```
1529
     *
1530
     * @memberof IgxColumnComponent
1531
     */
1532
    public get level() {
1533
        let ptr = this.parent;
471,851✔
1534
        let lvl = 0;
471,851✔
1535

1536
        while (ptr) {
471,851✔
1537
            lvl++;
144,880✔
1538
            ptr = ptr.parent;
144,880✔
1539
        }
1540
        return lvl;
471,851✔
1541
    }
1542

1543
    /** @hidden @internal **/
1544
    public get isLastPinned(): boolean {
1545
        return this.grid.isPinningToStart &&
1,089,872✔
1546
            this.grid.pinnedColumns[this.grid.pinnedColumns.length - 1] === this;
1547
    }
1548

1549
    /** @hidden @internal **/
1550
    public get isFirstPinned(): boolean {
1551
        const pinnedCols = this.grid.pinnedColumns.filter(x => !x.columnGroup);
1,005,725✔
1552
        return !this.grid.isPinningToStart && pinnedCols[0] === this;
1,005,725✔
1553
    }
1554

1555
    /** @hidden @internal **/
1556
    public get rightPinnedOffset(): string {
1557
        return this.pinned && !this.grid.isPinningToStart ?
725,029✔
1558
            - this.grid.pinnedWidth - this.grid.headerFeaturesWidth + 'px' :
1559
            null;
1560
    }
1561

1562
    /** @hidden @internal **/
1563
    public get gridRowSpan(): number {
1564
        return this.rowEnd && this.rowStart ? this.rowEnd - this.rowStart : 1;
1,274✔
1565
    }
1566
    /** @hidden @internal **/
1567
    public get gridColumnSpan(): number {
1568
        return this.colEnd && this.colStart ? this.colEnd - this.colStart : 1;
686,983✔
1569
    }
1570

1571
    /**
1572
     * Indicates whether the column will be visible when its parent is collapsed.
1573
     * ```html
1574
     * <igx-column-group>
1575
     *   <igx-column [visibleWhenCollapsed]="true"></igx-column>
1576
     * </igx-column-group>
1577
     * ```
1578
     *
1579
     * @memberof IgxColumnComponent
1580
     */
1581
    @notifyChanges(true)
1582
    @Input({ transform: booleanAttribute })
1583
    public set visibleWhenCollapsed(value: boolean) {
1584
        this._visibleWhenCollapsed = value;
1,760✔
1585
        this.visibleWhenCollapsedChange.emit(this._visibleWhenCollapsed);
1,760✔
1586
        if (this.parent) {
1,760✔
1587
            this.parent?.setExpandCollapseState?.();
26✔
1588
        }
1589
    }
1590

1591
    public get visibleWhenCollapsed(): boolean {
1592
        return this._visibleWhenCollapsed;
9,479✔
1593
    }
1594

1595
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
1596
    /**
1597
     * @remarks
1598
     * Pass optional parameters for DatePipe and/or DecimalPipe to format the display value for date and numeric columns.
1599
     * Accepts an `IColumnPipeArgs` object with any of the `format`, `timezone` and `digitsInfo` properties.
1600
     * For more details see https://angular.io/api/common/DatePipe and https://angular.io/api/common/DecimalPipe
1601
     * @example
1602
     * ```typescript
1603
     * const pipeArgs: IColumnPipeArgs = {
1604
     *      format: 'longDate',
1605
     *      timezone: 'UTC',
1606
     *      digitsInfo: '1.1-2'
1607
     * }
1608
     * ```
1609
     * ```html
1610
     * <igx-column dataType="date" [pipeArgs]="pipeArgs"></igx-column>
1611
     * <igx-column dataType="number" [pipeArgs]="pipeArgs"></igx-column>
1612
     * ```
1613
     * @memberof IgxColumnComponent
1614
     */
1615
    @notifyChanges()
1616
    @WatchColumnChanges()
1617
    @Input()
1618
    public set pipeArgs(value: IColumnPipeArgs) {
1619
        this._columnPipeArgs = Object.assign(this._columnPipeArgs, value);
1,450✔
1620
        this.grid.summaryService.clearSummaryCache();
1,450✔
1621
        this.grid.pipeTrigger++;
1,450✔
1622
    }
1623
    /* mustSetInCodePlatforms: WebComponents;Blazor */
1624
    public get pipeArgs(): IColumnPipeArgs {
1625
        return this._columnPipeArgs;
1,654,990✔
1626
    }
1627

1628
    /**
1629
     * Pass optional properties for the default column editors.
1630
     * @remarks
1631
     * Options may be applicable only to specific column type editors.
1632
     * @example
1633
     * ```typescript
1634
     * const editorOptions: IColumnEditorOptions = {
1635
     *      dateTimeFormat: 'MM/dd/YYYY',
1636
     * }
1637
     * ```
1638
     * ```html
1639
     * <igx-column dataType="date" [editorOptions]="editorOptions"></igx-column>
1640
     * ```
1641
     * @memberof IgxColumnComponent
1642
     */
1643
    @notifyChanges()
1644
    @WatchColumnChanges()
1645
    @Input()
1646
    public set editorOptions(value: IColumnEditorOptions) {
1647
        this._editorOptions = value;
1,421✔
1648
    }
1649
    public get editorOptions(): IColumnEditorOptions {
1650
        return this._editorOptions;
4,571✔
1651
    }
1652

1653
    /**
1654
     * @hidden
1655
     * @internal
1656
     */
1657
    public get collapsible() {
UNCOV
1658
        return false;
×
1659
    }
1660
    public set collapsible(_value: boolean) { }
1661

1662
    /**
1663
     * @hidden
1664
     * @internal
1665
     */
1666
    public get expanded() {
UNCOV
1667
        return true;
×
1668
    }
1669
    public set expanded(_value: boolean) { }
1670

1671
    /**
1672
     * @hidden
1673
     */
1674
    public defaultWidth: string;
1675

1676
    /**
1677
     * @hidden
1678
     */
1679
    public widthSetByUser: boolean;
1680

1681
    /**
1682
     * @hidden
1683
     */
1684
    public hasNestedPath: boolean;
1685

1686
    /**
1687
     * @hidden
1688
     * @internal
1689
     */
1690
    public defaultTimeFormat = 'hh:mm:ss a';
29,102✔
1691

1692
    /**
1693
     * @hidden
1694
     * @internal
1695
     */
1696
    public defaultDateTimeFormat = 'dd/MM/yyyy HH:mm:ss a';
29,102✔
1697

1698

1699
    /**
1700
     * Returns the filteringExpressionsTree of the column.
1701
     * ```typescript
1702
     * let tree =  this.column.filteringExpressionsTree;
1703
     * ```
1704
     *
1705
     * @memberof IgxColumnComponent
1706
     */
1707
    public get filteringExpressionsTree(): FilteringExpressionsTree {
1708
        return ExpressionsTreeUtil.find(this.grid.filteringExpressionsTree, this.field) as FilteringExpressionsTree;
63,988✔
1709
    }
1710

1711
    /* alternateName: parentColumn */
1712
    /**
1713
     * Sets/gets the parent column.
1714
     * ```typescript
1715
     * let parentColumn = this.column.parent;
1716
     * ```
1717
     * ```typescript
1718
     * this.column.parent = higherLevelColumn;
1719
     * ```
1720
     *
1721
     * @memberof IgxColumnComponent
1722
     */
1723
    public parent = null;
29,102✔
1724

1725
    /* blazorSuppress */
1726
    /**
1727
     * Sets/gets the children columns.
1728
     * ```typescript
1729
     * let columnChildren = this.column.children;
1730
     * ```
1731
     *
1732
     * @deprecated in version 18.1.0. Use the `childColumns` property instead.
1733
     */
1734
    public children: QueryList<IgxColumnComponent>;
1735
    /**
1736
     * @hidden
1737
     */
1738
    public destroy$ = new Subject<any>();
29,102✔
1739

1740
    /**
1741
     * @hidden
1742
     */
1743
    protected _applySelectableClass = false;
29,102✔
1744

1745
    protected _vIndex = NaN;
29,102✔
1746
    /**
1747
     * @hidden
1748
     */
1749
    protected _pinned = false;
29,102✔
1750
    /**
1751
     * @hidden
1752
     */
1753
    protected _bodyTemplate: TemplateRef<IgxCellTemplateContext>;
1754
    /**
1755
     * @hidden
1756
     */
1757
    protected _errorTemplate: TemplateRef<IgxCellTemplateContext>;
1758
    /**
1759
     * @hidden
1760
     */
1761
    protected _headerTemplate: TemplateRef<IgxColumnTemplateContext>;
1762
    /**
1763
     * @hidden
1764
     */
1765
    protected _summaryTemplate: TemplateRef<IgxSummaryTemplateContext>;
1766
    /**
1767
     * @hidden
1768
     */
1769
    protected _inlineEditorTemplate: TemplateRef<IgxCellTemplateContext>;
1770
    /**
1771
     * @hidden
1772
     */
1773
    protected _filterCellTemplate: TemplateRef<IgxColumnTemplateContext>;
1774
    /**
1775
     * @hidden
1776
     */
1777
    protected _summaries = null;
29,102✔
1778
    /**
1779
     * @hidden
1780
     */
1781
    private _disabledSummaries: string[] = [];
29,102✔
1782
    /**
1783
     * @hidden
1784
     */
1785
    protected _filters = null;
29,102✔
1786
    /**
1787
     * @hidden
1788
     */
1789
    protected _sortStrategy: ISortingStrategy = DefaultSortingStrategy.instance();
29,102✔
1790
    /**
1791
     * @hidden
1792
     */
1793
    protected _groupingComparer: (a: any, b: any, currRec?: any, groupRec?: any) => number;
1794
    /**
1795
     * @hidden
1796
     */
1797
    protected _hidden = false;
29,102✔
1798
    /**
1799
     * @hidden
1800
     */
1801
    protected _index: number;
1802
    /**
1803
     * @hidden
1804
     */
1805
    protected _disablePinning = false;
29,102✔
1806
    /**
1807
     * @hidden
1808
     */
1809
    protected _width: string;
1810
    /**
1811
     * @hidden
1812
     */
1813
    protected _defaultMinWidth = '';
29,102✔
1814
    /**
1815
     * @hidden
1816
     */
1817
    protected _hasSummary = false;
29,102✔
1818
    /**
1819
     * @hidden
1820
     */
1821
    protected _editable: boolean;
1822
    /**
1823
     * @hidden
1824
     */
1825
    protected _groupable = false;
29,102✔
1826
    /**
1827
     *  @hidden
1828
     */
1829
    protected _visibleWhenCollapsed;
1830
    /**
1831
     * @hidden
1832
     */
1833
    protected _collapsible = false;
29,102✔
1834
    /**
1835
     * @hidden
1836
     */
1837
    protected _expanded = true;
29,102✔
1838
    /**
1839
     * @hidden
1840
     */
1841
    protected _selectable = true;
29,102✔
1842
    /**
1843
     * @hidden
1844
     */
1845
    protected get isPrimaryColumn(): boolean {
1846
        return this.field !== undefined && this.grid !== undefined && this.field === this.grid.primaryKey;
2,403,624✔
1847
    }
1848

1849
    private _field: string;
1850
    private _calcWidth = null;
29,102✔
1851
    private _columnPipeArgs: IColumnPipeArgs = { digitsInfo: DEFAULT_DIGITS_INFO };
29,102✔
1852
    private _editorOptions: IColumnEditorOptions = { };
29,102✔
1853

1854
    constructor(
1855
        @Inject(IGX_GRID_BASE) public grid: GridType,
29,102✔
1856
        @Optional() @Self() @Inject(NG_VALIDATORS) private _validators: Validator[],
29,102✔
1857
        /** @hidden @internal **/
1858
        public cdr: ChangeDetectorRef,
29,102✔
1859
        protected platform: PlatformUtil,
29,102✔
1860
    ) {
1861
        this.validators = _validators;
29,102✔
1862
    }
1863

1864
    /**
1865
     * @hidden
1866
     * @internal
1867
     */
1868
    public resetCaches() {
1869
        this._vIndex = NaN;
285,189✔
1870
        if (this.grid) {
285,189✔
1871
            this.cacheCalcWidth();
285,189✔
1872
        }
1873
    }
1874

1875
    /**
1876
     * @hidden
1877
     */
1878
    public ngOnDestroy() {
1879
        this.destroy$.next(true);
16,130✔
1880
        this.destroy$.complete();
16,130✔
1881
    }
1882
    /**
1883
     * @hidden
1884
     */
1885
    public ngAfterContentInit(): void {
1886
        if (this.summaryTemplateDirective) {
23,965✔
1887
            this._summaryTemplate = this.summaryTemplateDirective.template;
6✔
1888
        }
1889
        if (this.cellTemplate) {
23,965✔
1890
            this._bodyTemplate = this.cellTemplate.template;
44✔
1891
        }
1892
        if (this.cellValidationErrorTemplate) {
23,965✔
1893
            this._errorTemplate = this.cellValidationErrorTemplate.template;
16✔
1894
        }
1895
        if (this.headTemplate && this.headTemplate.length) {
23,965✔
1896
            this._headerTemplate = this.headTemplate.toArray()[0].template;
81✔
1897
        }
1898
        if (this.editorTemplate) {
23,965✔
1899
            this._inlineEditorTemplate = this.editorTemplate.template;
4✔
1900
        }
1901
        if (this.filterCellTemplateDirective) {
23,965!
UNCOV
1902
            this._filterCellTemplate = this.filterCellTemplateDirective.template;
×
1903
        }
1904
        if (!this._columnPipeArgs.format) {
23,965✔
1905
            this._columnPipeArgs.format = this.dataType === GridColumnDataType.Time ?
22,681✔
1906
                DEFAULT_TIME_FORMAT : this.dataType === GridColumnDataType.DateTime ?
22,411✔
1907
                    DEFAULT_DATE_TIME_FORMAT : DEFAULT_DATE_FORMAT;
1908
        }
1909
        if (!this.summaries) {
23,965✔
1910
            switch (this.dataType) {
22,628✔
1911
                case GridColumnDataType.Number:
1912
                case GridColumnDataType.Currency:
1913
                case GridColumnDataType.Percent:
1914
                    this.summaries = IgxNumberSummaryOperand;
6,899✔
1915
                    break;
6,899✔
1916
                case GridColumnDataType.Date:
1917
                case GridColumnDataType.DateTime:
1918
                    this.summaries = IgxDateSummaryOperand;
1,767✔
1919
                    break;
1,767✔
1920
                case GridColumnDataType.Time:
1921
                    this.summaries = IgxTimeSummaryOperand;
270✔
1922
                    break;
270✔
1923

1924
                case GridColumnDataType.String:
1925
                case GridColumnDataType.Boolean:
1926
                default:
1927
                    this.summaries = IgxSummaryOperand;
13,692✔
1928
                    break;
13,692✔
1929
            }
1930
        }
1931
        if (!this.filters) {
23,965✔
1932
            switch (this.dataType) {
22,388✔
1933
                case GridColumnDataType.Boolean:
1934
                    this.filters = IgxBooleanFilteringOperand.instance();
1,157✔
1935
                    break;
1,157✔
1936
                case GridColumnDataType.Number:
1937
                case GridColumnDataType.Currency:
1938
                case GridColumnDataType.Percent:
1939
                    this.filters = IgxNumberFilteringOperand.instance();
6,942✔
1940
                    break;
6,942✔
1941
                case GridColumnDataType.Date:
1942
                    this.filters = IgxDateFilteringOperand.instance();
1,507✔
1943
                    break;
1,507✔
1944
                case GridColumnDataType.Time:
1945
                    this.filters = IgxTimeFilteringOperand.instance();
270✔
1946
                    break;
270✔
1947
                case GridColumnDataType.DateTime:
1948
                    this.filters = IgxDateTimeFilteringOperand.instance();
266✔
1949
                    break;
266✔
1950
                case GridColumnDataType.Image:
1951
                    this.filterable = false;
1✔
1952
                    break;
1✔
1953
                case GridColumnDataType.String:
1954
                default:
1955
                    this.filters = IgxStringFilteringOperand.instance();
12,245✔
1956
                    break;
12,245✔
1957
            }
1958
        }
1959
    }
1960

1961
    /**
1962
     * @hidden
1963
     */
1964
    public getGridTemplate(isRow: boolean): string {
1965
        if (isRow) {
26,856✔
1966
            const rowsCount = this.grid.type !== 'pivot' ? this.grid.multiRowLayoutRowSize : this.children.length - 1;
13,429!
1967
            return `repeat(${rowsCount},1fr)`;
13,429✔
1968
        } else {
1969
            return this.getColumnSizesString(this.children);
13,427✔
1970
        }
1971
    }
1972

1973
    /** @hidden @internal **/
1974
    public getInitialChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<MRLColumnSizeInfo> {
1975
        const columnSizes: MRLColumnSizeInfo[] = [];
148,163✔
1976
        // find the smallest col spans
1977
        children.forEach(col => {
148,163✔
1978
            if (!col.colStart) {
462,028✔
1979
                return;
106,166✔
1980
            }
1981
            const newWidthSet = col.widthSetByUser && columnSizes[col.colStart - 1] && !columnSizes[col.colStart - 1].widthSetByUser;
355,862✔
1982
            const newSpanSmaller = columnSizes[col.colStart - 1] && columnSizes[col.colStart - 1].colSpan > col.gridColumnSpan;
355,862✔
1983
            const bothWidthsSet = col.widthSetByUser && columnSizes[col.colStart - 1] && columnSizes[col.colStart - 1].widthSetByUser;
355,862✔
1984
            const bothWidthsNotSet = !col.widthSetByUser && columnSizes[col.colStart - 1] && !columnSizes[col.colStart - 1].widthSetByUser;
355,862✔
1985

1986
            if (columnSizes[col.colStart - 1] === undefined) {
355,862✔
1987
                // If nothing is defined yet take any column at first
1988
                // We use colEnd to know where the column actually ends, because not always it starts where we have it set in columnSizes.
1989
                columnSizes[col.colStart - 1] = {
218,519✔
1990
                    ref: col,
1991
                    width: col.width === 'fit-content' ? col.autoSize :
218,519!
1992
                        col.widthSetByUser || this.grid.columnWidthSetByUser ? parseFloat(col.calcWidth) : null,
638,942✔
1993
                    colSpan: col.gridColumnSpan,
1994
                    colEnd: col.colStart + col.gridColumnSpan,
1995
                    widthSetByUser: col.widthSetByUser
1996
                };
1997
            } else if (newWidthSet || (newSpanSmaller && ((bothWidthsSet) || (bothWidthsNotSet)))) {
137,343✔
1998
                // If a column is set already it should either not have width defined or have width with bigger span than the new one.
1999

2000
                /**
2001
                 *  If replaced column has bigger span, we want to fill the remaining columns
2002
                 *  that the replacing column does not fill with the old one.
2003
                 */
2004
                if (bothWidthsSet && newSpanSmaller) {
49,190✔
2005
                    // Start from where the new column set would end and apply the old column to the rest depending on how much it spans.
2006
                    // We have not yet replaced it so we can use it directly from the columnSizes collection.
2007
                    // This is where colEnd is used because the colStart of the old column is not actually i + 1.
2008
                    for (let i = col.colStart - 1 + col.gridColumnSpan; i < columnSizes[col.colStart - 1].colEnd - 1; i++) {
4,784✔
2009
                        if (!columnSizes[i] || !columnSizes[i].widthSetByUser) {
2,737✔
2010
                            columnSizes[i] = columnSizes[col.colStart - 1];
2,687✔
2011
                        } else {
2012
                            break;
50✔
2013
                        }
2014
                    }
2015
                }
2016

2017
                // Replace the old column with the new one.
2018
                columnSizes[col.colStart - 1] = {
49,190✔
2019
                    ref: col,
2020
                    width: col.width === 'fit-content' ? col.autoSize :
49,190!
2021
                        col.widthSetByUser || this.grid.columnWidthSetByUser ? parseFloat(col.calcWidth) : null,
139,148✔
2022
                    colSpan: col.gridColumnSpan,
2023
                    colEnd: col.colStart + col.gridColumnSpan,
2024
                    widthSetByUser: col.widthSetByUser
2025
                };
2026
            } else if (bothWidthsSet && columnSizes[col.colStart - 1].colSpan < col.gridColumnSpan) {
88,153✔
2027
                // If the column already in the columnSizes has smaller span, we still need to fill any empty places with the current col.
2028
                // Start from where the smaller column set would end and apply the bigger column to the rest depending on how much it spans.
2029
                // Since here we do not have it in columnSizes we set it as a new column keeping the same colSpan.
2030
                for (let i = col.colStart - 1 + columnSizes[col.colStart - 1].colSpan; i < col.colStart - 1 + col.gridColumnSpan; i++) {
3,481✔
2031
                    if (!columnSizes[i] || !columnSizes[i].widthSetByUser) {
3,481✔
2032
                        columnSizes[i] = {
47✔
2033
                            ref: col,
2034
                            width: col.width === 'fit-content' ? col.autoSize :
47!
2035
                                col.widthSetByUser || this.grid.columnWidthSetByUser ? parseFloat(col.calcWidth) : null,
94!
2036
                            colSpan: col.gridColumnSpan,
2037
                            colEnd: col.colStart + col.gridColumnSpan,
2038
                            widthSetByUser: col.widthSetByUser
2039
                        };
2040
                    } else {
2041
                        break;
3,434✔
2042
                    }
2043
                }
2044
            }
2045
        });
2046

2047
        // Flatten columnSizes so there are not columns with colSpan > 1
2048
        for (let i = 0; i < columnSizes.length; i++) {
148,163✔
2049
            if (columnSizes[i] && columnSizes[i].colSpan > 1) {
216,659✔
2050
                let j = 1;
10,805✔
2051

2052
                // Replace all empty places depending on how much the current column spans starting from next col.
2053
                for (; j < columnSizes[i].colSpan && i + j + 1 < columnSizes[i].colEnd; j++) {
10,805✔
2054
                    if (columnSizes[i + j] &&
10,414✔
2055
                        ((!columnSizes[i].width && columnSizes[i + j].width) ||
2056
                            (!columnSizes[i].width && !columnSizes[i + j].width && columnSizes[i + j].colSpan <= columnSizes[i].colSpan) ||
2057
                            (!!columnSizes[i + j].width && columnSizes[i + j].colSpan <= columnSizes[i].colSpan))) {
2058
                        // If we reach an already defined column that has width and the current doesn't have or
2059
                        // if the reached column has bigger colSpan we stop.
2060
                        break;
4,393✔
2061
                    } else {
2062
                        const width = columnSizes[i].widthSetByUser ?
6,021✔
2063
                            columnSizes[i].width / columnSizes[i].colSpan :
2064
                            columnSizes[i].width;
2065
                        columnSizes[i + j] = {
6,021✔
2066
                            ref: columnSizes[i].ref,
2067
                            width,
2068
                            colSpan: 1,
2069
                            colEnd: columnSizes[i].colEnd,
2070
                            widthSetByUser: columnSizes[i].widthSetByUser
2071
                        };
2072
                    }
2073
                }
2074

2075
                // Update the current column width so it is divided between all columns it spans and set it to 1.
2076
                columnSizes[i].width = columnSizes[i].widthSetByUser ?
10,805✔
2077
                    columnSizes[i].width / columnSizes[i].colSpan :
2078
                    columnSizes[i].width;
2079
                columnSizes[i].colSpan = 1;
10,805✔
2080

2081
                // Update the index based on how much we have replaced. Subtract 1 because we started from 1.
2082
                i += j - 1;
10,805✔
2083
            }
2084
        }
2085

2086
        return columnSizes;
148,163✔
2087
    }
2088

2089
    /** @hidden @internal **/
2090
    public getFilledChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<string> {
2091
        const columnSizes = this.getInitialChildColumnSizes(children);
25,885✔
2092

2093
        // fill the gaps if there are any
2094
        const result: string[] = [];
25,885✔
2095
        for (const size of columnSizes) {
25,885✔
2096
            if (size && !!size.width) {
57,677✔
2097
                result.push(size.width + 'px');
22,563✔
2098
            } else {
2099
                result.push(parseFloat(this.grid.getPossibleColumnWidth()) + 'px');
35,114✔
2100
            }
2101
        }
2102
        return result;
25,885✔
2103
    }
2104

2105
    /** @hidden @internal **/
2106
    public getResizableColUnderEnd(): MRLResizeColumnInfo[] {
2107
        if (this.columnLayout || !this.columnLayoutChild || this.columnGroup) {
6!
UNCOV
2108
            return [{ target: this, spanUsed: 1 }];
×
2109
        }
2110

2111
        const columnSized = this.getInitialChildColumnSizes(this.parent.children);
6✔
2112
        const targets: MRLResizeColumnInfo[] = [];
6✔
2113
        const colEnd = this.colEnd ? this.colEnd : this.colStart + 1;
6!
2114

2115
        for (let i = 0; i < columnSized.length; i++) {
6✔
2116
            if (this.colStart <= i + 1 && i + 1 < colEnd) {
36✔
2117
                targets.push({ target: columnSized[i].ref, spanUsed: 1 });
10✔
2118
            }
2119
        }
2120

2121
        const targetsSquashed: MRLResizeColumnInfo[] = [];
6✔
2122
        for (const target of targets) {
6✔
2123
            if (targetsSquashed.length && targetsSquashed[targetsSquashed.length - 1].target.field === target.target.field) {
10!
UNCOV
2124
                targetsSquashed[targetsSquashed.length - 1].spanUsed++;
×
2125
            } else {
2126
                targetsSquashed.push(target);
10✔
2127
            }
2128
        }
2129

2130
        return targetsSquashed;
6✔
2131
    }
2132

2133
    /**
2134
     * Pins the column at the provided index in the pinned area.
2135
     * Defaults to index `0` if not provided, or to the initial index in the pinned area.
2136
     * Returns `true` if the column is successfully pinned. Returns `false` if the column cannot be pinned.
2137
     * Column cannot be pinned if:
2138
     * - Is already pinned
2139
     * - index argument is out of range
2140
     * - The pinned area exceeds 80% of the grid width
2141
     * ```typescript
2142
     * let success = this.column.pin();
2143
     * ```
2144
     *
2145
     * @memberof IgxColumnComponent
2146
     */
2147
    public pin(index?: number): boolean {
2148
        // TODO: Probably should the return type of the old functions
2149
        // should be moved as a event parameter.
2150
        const grid = (this.grid as any);
613✔
2151
        if (this._pinned) {
613✔
2152
            return false;
56✔
2153
        }
2154

2155
        if (this.parent && !this.parent.pinned) {
557✔
2156
            return this.topLevelParent.pin(index);
10✔
2157
        }
2158

2159
        const hasIndex = index !== undefined;
547✔
2160
        if (hasIndex && (index < 0 || index > grid.pinnedColumns.length)) {
547✔
2161
            return false;
2✔
2162
        }
2163

2164
        if (!this.parent && !this.pinnable) {
545!
UNCOV
2165
            return false;
×
2166
        }
2167

2168
        const rootPinnedCols = grid._pinnedColumns.filter((c) => c.level === 0);
1,050✔
2169
        index = hasIndex ? index : rootPinnedCols.length;
545✔
2170
        const args: IPinColumnCancellableEventArgs = { column: this, insertAtIndex: index, isPinned: false, cancel: false };
545✔
2171
        this.grid.columnPin.emit(args);
545✔
2172

2173
        if (args.cancel) {
545!
UNCOV
2174
            return;
×
2175
        }
2176

2177
        this.grid.crudService.endEdit(false);
545✔
2178

2179
        this._pinned = true;
545✔
2180
        this.pinnedChange.emit(this._pinned);
545✔
2181
        // it is possible that index is the last position, so will need to find target column by [index-1]
2182
        const targetColumn = args.insertAtIndex === grid._pinnedColumns.length ?
545✔
2183
            grid._pinnedColumns[args.insertAtIndex - 1] : grid._pinnedColumns[args.insertAtIndex];
2184

2185
        if (grid._pinnedColumns.indexOf(this) === -1) {
545✔
2186
            if (!grid.hasColumnGroups) {
420✔
2187
                grid._pinnedColumns.splice(args.insertAtIndex, 0, this);
371✔
2188
            } else {
2189
                // insert based only on root collection
2190
                if (this.level === 0) {
49✔
2191
                    rootPinnedCols.splice(args.insertAtIndex, 0, this);
42✔
2192
                }
2193
                let allPinned = [];
49✔
2194
                // FIX: this is duplicated on every step in the hierarchy....
2195
                // re-create hierarchy
2196
                rootPinnedCols.forEach(group => {
49✔
2197
                    allPinned.push(group);
79✔
2198
                    allPinned = allPinned.concat(group.allChildren);
79✔
2199
                });
2200
                grid._pinnedColumns = allPinned;
49✔
2201
            }
2202

2203
            if (grid._unpinnedColumns.indexOf(this) !== -1) {
420✔
2204
                const childrenCount = this.allChildren.length;
189✔
2205
                grid._unpinnedColumns.splice(grid._unpinnedColumns.indexOf(this), 1 + childrenCount);
189✔
2206
            }
2207
        }
2208

2209
        if (hasIndex) {
545✔
2210
            index === grid._pinnedColumns.length - 1 ?
10✔
2211
                grid._moveColumns(this, targetColumn, DropPosition.AfterDropTarget) : grid._moveColumns(this, targetColumn, DropPosition.BeforeDropTarget);
2212
        }
2213

2214
        if (this.columnGroup) {
545✔
2215
            this.allChildren.forEach(child => child.pin());
186✔
2216
            grid.reinitPinStates();
59✔
2217
        }
2218

2219
        grid.resetCaches();
545✔
2220
        grid.notifyChanges();
545✔
2221
        if (this.columnLayoutChild) {
545✔
2222
            this.grid.columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
407✔
2223
        }
2224
        this.grid.filteringService.refreshExpressions();
545✔
2225
        const eventArgs: IPinColumnEventArgs = { column: this, insertAtIndex: index, isPinned: true };
545✔
2226
        this.grid.columnPinned.emit(eventArgs);
545✔
2227
        return true;
545✔
2228
    }
2229
    /**
2230
     * Unpins the column and place it at the provided index in the unpinned area.
2231
     * Defaults to index `0` if not provided, or to the initial index in the unpinned area.
2232
     * Returns `true` if the column is successfully unpinned. Returns `false` if the column cannot be unpinned.
2233
     * Column cannot be unpinned if:
2234
     * - Is already unpinned
2235
     * - index argument is out of range
2236
     * ```typescript
2237
     * let success = this.column.unpin();
2238
     * ```
2239
     *
2240
     * @memberof IgxColumnComponent
2241
     */
2242
    public unpin(index?: number): boolean {
2243
        const grid = (this.grid as any);
188✔
2244
        if (!this._pinned) {
188✔
2245
            return false;
32✔
2246
        }
2247

2248
        if (this.parent && this.parent.pinned) {
156✔
2249
            return this.topLevelParent.unpin(index);
7✔
2250
        }
2251
        const hasIndex = index !== undefined;
149✔
2252
        if (hasIndex && (index < 0 || index > grid._unpinnedColumns.length)) {
149✔
2253
            return false;
2✔
2254
        }
2255

2256
        // estimate the exact index at which column will be inserted
2257
        // takes into account initial unpinned index of the column
2258
        if (!hasIndex) {
147✔
2259
            const indices = grid.unpinnedColumns.map(col => col.index);
1,184✔
2260
            indices.push(this.index);
143✔
2261
            indices.sort((a, b) => a - b);
1,560✔
2262
            index = indices.indexOf(this.index);
143✔
2263
        }
2264

2265
        const args: IPinColumnCancellableEventArgs = { column: this, insertAtIndex: index, isPinned: true, cancel: false };
147✔
2266
        this.grid.columnPin.emit(args);
147✔
2267

2268
        if (args.cancel) {
147!
UNCOV
2269
            return;
×
2270
        }
2271

2272
        this.grid.crudService.endEdit(false);
147✔
2273

2274
        this._pinned = false;
147✔
2275
        this.pinnedChange.emit(this._pinned);
147✔
2276

2277
        // it is possible that index is the last position, so will need to find target column by [index-1]
2278
        const targetColumn = args.insertAtIndex === grid._unpinnedColumns.length ?
147✔
2279
            grid._unpinnedColumns[args.insertAtIndex - 1] : grid._unpinnedColumns[args.insertAtIndex];
2280

2281
        if (!hasIndex) {
147✔
2282
            grid._unpinnedColumns.splice(index, 0, this);
143✔
2283
            if (grid._pinnedColumns.indexOf(this) !== -1) {
143✔
2284
                grid._pinnedColumns.splice(grid._pinnedColumns.indexOf(this), 1);
143✔
2285
            }
2286
        }
2287

2288
        if (hasIndex) {
147✔
2289
            grid.moveColumn(this, targetColumn);
4✔
2290
        }
2291

2292
        if (this.columnGroup) {
147✔
2293
            this.allChildren.forEach(child => child.unpin());
106✔
2294
        }
2295

2296
        grid.reinitPinStates();
147✔
2297
        grid.resetCaches();
147✔
2298

2299
        grid.notifyChanges();
147✔
2300
        if (this.columnLayoutChild) {
147✔
2301
            this.grid.columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
356✔
2302
        }
2303
        this.grid.filteringService.refreshExpressions();
147✔
2304

2305
        this.grid.columnPinned.emit({ column: this, insertAtIndex: index, isPinned: false });
147✔
2306

2307
        return true;
147✔
2308
    }
2309

2310
    /**
2311
     * Moves a column to the specified visible index.
2312
     * If passed index is invalid, or if column would receive a different visible index after moving, moving is not performed.
2313
     * If passed index would move the column to a different column group. moving is not performed.
2314
     *
2315
     * @example
2316
     * ```typescript
2317
     * column.move(index);
2318
     * ```
2319
     * @memberof IgxColumnComponent
2320
     */
2321
    public move(index: number) {
2322
        let target;
2323
        let columns = this.grid.columns.filter(c => c.visibleIndex > -1);
349✔
2324
        // grid last visible index
2325
        const li = columns.map(c => c.visibleIndex).reduce((a, b) => Math.max(a, b));
343✔
2326
        const parent = this.parent;
36✔
2327
        const isPreceding = this.visibleIndex < index;
36✔
2328

2329
        if (index === this.visibleIndex || index < 0 || index > li) {
36✔
2330
            return;
3✔
2331
        }
2332

2333
        if (parent) {
33✔
2334
            columns = columns.filter(c => c.level >= this.level && c !== this && c.parent !== this &&
84✔
2335
                c.topLevelParent === this.topLevelParent);
2336
        }
2337

2338
        // If isPreceding, find a target such that when the current column is placed after it, current colummn will receive a visibleIndex === index. This takes into account visible children of the columns.
2339
        // If !isPreceding, finds a column of the same level and visible index that equals the passed index agument (c.visibleIndex === index). No need to consider the children here.
2340

2341
        if (isPreceding) {
33✔
2342
            columns = columns.filter(c => c.visibleIndex > this.visibleIndex);
200✔
2343
            target = columns.find(c => c.level === this.level && c.visibleIndex + (c as any).calcChildren() - this.calcChildren() === index);
80✔
2344
        } else {
2345
            columns = columns.filter(c => c.visibleIndex < this.visibleIndex);
65✔
2346
            target = columns.find(c => c.level === this.level && c.visibleIndex === index);
15✔
2347
        }
2348

2349
        if (!target || (target.pinned && this.disablePinning)) {
33✔
2350
            return;
9✔
2351
        }
2352

2353
        const pos = isPreceding ? DropPosition.AfterDropTarget : DropPosition.BeforeDropTarget;
24✔
2354
        this.grid.moveColumn(this, target as IgxColumnComponent, pos);
24✔
2355
    }
2356

2357
    /**
2358
     * No children for the column, so will returns 1 or 0, if the column is hidden.
2359
     *
2360
     * @hidden
2361
     */
2362
    public calcChildren(): number {
2363
        const children = this.hidden ? 0 : 1;
70✔
2364
        return children;
70✔
2365
    }
2366

2367
    /**
2368
     * Toggles column vibisility and emits the respective event.
2369
     *
2370
     * @hidden
2371
     */
2372
    public toggleVisibility(value?: boolean) {
2373
        const newValue = value ?? !this.hidden;
65✔
2374
        const eventArgs: IColumnVisibilityChangingEventArgs = { column: this, newValue, cancel: false };
65✔
2375
        this.grid.columnVisibilityChanging.emit(eventArgs);
65✔
2376

2377
        if (eventArgs.cancel) {
65!
UNCOV
2378
            return;
×
2379
        }
2380
        this.hidden = newValue;
65✔
2381
        this.grid.columnVisibilityChanged.emit({ column: this, newValue });
65✔
2382
    }
2383

2384
    /**
2385
     * Returns a reference to the top level parent column.
2386
     * ```typescript
2387
     * let topLevelParent =  this.column.topLevelParent;
2388
     * ```
2389
     */
2390
    public get topLevelParent(): ColumnType | undefined {
2391
        let parent = this.parent;
490✔
2392
        while (parent && parent.parent) {
490✔
2393
            parent = parent.parent;
34✔
2394
        }
2395
        return parent ?? undefined;
490✔
2396
    }
2397

2398
    /**
2399
     * @hidden @internal
2400
     */
2401
    public get headerCell(): IgxGridHeaderComponent {
2402
        return this.grid.headerCellList.find((header) => header.column === this);
1,574✔
2403
    }
2404

2405
    /**
2406
     * @hidden @internal
2407
     */
2408
    public get filterCell(): IgxGridFilteringCellComponent {
2409
        return this.grid.filterCellList.find((filterCell) => filterCell.column === this);
6,929✔
2410
    }
2411

2412
    /**
2413
     * @hidden @internal
2414
     */
2415
    public get headerGroup(): IgxGridHeaderGroupComponent {
2416
        return this.grid.headerGroupsList.find(group => group.column === this);
6,275✔
2417
    }
2418

2419
    /**
2420
     * Autosize the column to the longest currently visible cell value, including the header cell.
2421
     * ```typescript
2422
     * @ViewChild('grid') grid: IgxGridComponent;
2423
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2424
     * column.autosize();
2425
     * ```
2426
     *
2427
     * @memberof IgxColumnComponent
2428
     * @param byHeaderOnly Set if column should be autosized based only on the header content.
2429
     */
2430
    public autosize(byHeaderOnly = false) {
17✔
2431
        if (!this.columnGroup) {
18✔
2432
            this.width = this.getAutoSize(byHeaderOnly);
18✔
2433
            this.grid.reflow();
18✔
2434
        }
2435
    }
2436

2437
    /**
2438
     * @hidden
2439
     */
2440
    public getAutoSize(byHeader = false): string {
8✔
2441
        const size = !byHeader ? this.getLargestCellWidth() :
26✔
2442
            (Object.values(this.getHeaderCellWidths()).reduce((a, b) => a + b) + 'px');
1✔
2443
        const isPercentageWidth = this.width && typeof this.width === 'string' && this.width.indexOf('%') !== -1;
26✔
2444

2445
        let newWidth;
2446
        if (isPercentageWidth) {
26✔
2447
            const gridAvailableSize = this.grid.calcWidth;
2✔
2448
            const percentageSize = parseFloat(size) / gridAvailableSize * 100;
2✔
2449
            newWidth = percentageSize + '%';
2✔
2450
        } else {
2451
            newWidth = size;
24✔
2452
        }
2453

2454
        const maxWidth = isPercentageWidth ? this.maxWidthPercent : this.maxWidthPx;
26✔
2455
        const minWidth = isPercentageWidth ? this.minWidthPercent : this.minWidthPx;
26✔
2456
        if (this.maxWidth && (parseFloat(newWidth) > maxWidth)) {
26✔
2457
            newWidth = isPercentageWidth ? maxWidth + '%' : maxWidth + 'px';
1!
2458
        } else if (parseFloat(newWidth) < minWidth) {
25✔
2459
            newWidth = isPercentageWidth ? minWidth + '%' : minWidth + 'px';
1!
2460
        }
2461

2462
        return newWidth;
26✔
2463
    }
2464

2465
    /**
2466
     * @hidden
2467
     */
2468
    public getCalcWidth(): any {
2469
        if (this._calcWidth && !isNaN(this.calcPixelWidth)) {
237,531✔
2470
            return this._calcWidth;
231,284✔
2471
        }
2472
        this.cacheCalcWidth();
6,247✔
2473
        return this._calcWidth;
6,247✔
2474
    }
2475

2476

2477
    /**
2478
     * @hidden
2479
     * Returns the width and padding of a header cell.
2480
     */
2481
    public getHeaderCellWidths() {
2482
        return this.grid.getHeaderCellWidth(this.headerCell.nativeElement);
25✔
2483
    }
2484

2485
    /**
2486
     * @hidden
2487
     * Returns the size (in pixels) of the longest currently visible cell, including the header cell.
2488
     * ```typescript
2489
     * @ViewChild('grid') grid: IgxGridComponent;
2490
     *
2491
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2492
     * let size = column.getLargestCellWidth();
2493
     * ```
2494
     * @memberof IgxColumnComponent
2495
     */
2496
    public getLargestCellWidth(): string {
2497
        const range = this.grid.document.createRange();
25✔
2498
        const largest = new Map<number, number>();
25✔
2499

2500
        if (this._cells.length > 0) {
25✔
2501
            const cellsContentWidths = [];
25✔
2502
            this._cells.forEach((cell) => cellsContentWidths.push(cell.calculateSizeToFit(range)));
270✔
2503

2504
            const index = cellsContentWidths.indexOf(Math.max(...cellsContentWidths));
25✔
2505
            const cellStyle = this.grid.document.defaultView.getComputedStyle(this._cells[index].nativeElement);
25✔
2506
            const cellPadding = parseFloat(cellStyle.paddingLeft) + parseFloat(cellStyle.paddingRight) +
25✔
2507
                parseFloat(cellStyle.borderLeftWidth) + parseFloat(cellStyle.borderRightWidth);
2508

2509
            largest.set(Math.max(...cellsContentWidths), cellPadding);
25✔
2510
        }
2511

2512
        if (this.headerCell && this.autosizeHeader) {
25✔
2513
            const headerCellWidths = this.getHeaderCellWidths();
24✔
2514
            largest.set(headerCellWidths.width, headerCellWidths.padding);
24✔
2515
        }
2516

2517
        const largestCell = Math.max(...Array.from(largest.keys()));
25✔
2518
        const width = Math.ceil(largestCell + largest.get(largestCell));
25✔
2519

2520
        if (Number.isNaN(width)) {
25!
UNCOV
2521
            return this.width;
×
2522
        } else {
2523
            return width + 'px';
25✔
2524
        }
2525
    }
2526

2527
    /**
2528
     * @hidden
2529
     */
2530
    public getCellWidth() {
2531
        const colWidth = this.width;
1,159,698✔
2532
        const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1;
1,159,698✔
2533

2534
        if (this.columnLayoutChild) {
1,159,698!
UNCOV
2535
            return '';
×
2536
        }
2537

2538
        if (colWidth && !isPercentageWidth) {
1,159,698✔
2539

2540
            let cellWidth = colWidth;
1,148,273✔
2541
            if (typeof cellWidth !== 'string' || cellWidth.endsWith('px') === false) {
1,148,273✔
2542
                cellWidth += 'px';
2,389✔
2543
            }
2544

2545
            return cellWidth;
1,148,273✔
2546
        } else {
2547
            return colWidth;
11,425✔
2548
        }
2549
    }
2550

2551
    /**
2552
     * @hidden
2553
     */
2554
    public populateVisibleIndexes() { }
2555

2556
    protected getColumnSizesString(children: QueryList<IgxColumnComponent>): string {
2557
        const res = this.getFilledChildColumnSizes(children);
13,427✔
2558
        return res.join(' ');
13,427✔
2559
    }
2560

2561
    /**
2562
     * @hidden
2563
     * @internal
2564
     */
2565
    protected cacheCalcWidth(): any {
2566
        const colWidth = this.width;
304,832✔
2567
        const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1;
304,832✔
2568
        const isAutoWidth = colWidth && typeof colWidth === 'string' && colWidth === 'fit-content';
304,832✔
2569
        if (isPercentageWidth && this.grid.isColumnWidthSum) {
304,832✔
2570
            this._calcWidth = this.grid.minColumnWidth;
32✔
2571
        } else if (isPercentageWidth) {
304,800✔
2572
            this._calcWidth = parseFloat(colWidth) / 100 * this.grid.calcWidth;
894✔
2573
        } else if (!colWidth || isAutoWidth && !this.autoSize) {
303,906✔
2574
            // no width
2575
            this._calcWidth = this.defaultWidth || this.grid.getPossibleColumnWidth();
26,851✔
2576
        } else {
2577
            this._calcWidth = this.width;
277,055✔
2578
        }
2579
        this.calcPixelWidth = parseFloat(this._calcWidth);
304,832✔
2580
    }
2581

2582
    /**
2583
     * @hidden
2584
     * @internal
2585
     */
2586
    protected setExpandCollapseState() {
2587
        this.children.filter(col => (col.visibleWhenCollapsed !== undefined)).forEach(c => {
295✔
2588
            if (!this.collapsible) {
270✔
2589
                c.hidden = this.hidden; return;
2✔
2590
            }
2591
            c.hidden = this._expanded ? c.visibleWhenCollapsed : !c.visibleWhenCollapsed;
268✔
2592
        });
2593
    }
2594
    /**
2595
     * @hidden
2596
     * @internal
2597
     */
2598
    protected checkCollapsibleState() {
2599
        if (!this.children) {
2,481!
UNCOV
2600
            return false;
×
2601
        }
2602
        const cols = this.children.map(child => child.visibleWhenCollapsed);
6,113✔
2603
        return (cols.some(c => c === true) && cols.some(c => c === false));
3,756✔
2604
    }
2605

2606
    /**
2607
     * @hidden
2608
     */
2609
    public get pinnable() {
2610
        return (this.grid as any)._init || !this.pinned;
401✔
2611
    }
2612

2613
    /**
2614
     * @hidden
2615
     */
2616
    public get applySelectableClass(): boolean {
2617
        return this._applySelectableClass;
10,986✔
2618
    }
2619

2620
    /**
2621
     * @hidden
2622
     */
2623
    public set applySelectableClass(value: boolean) {
2624
        if (this.selectable) {
15✔
2625
            this._applySelectableClass = value;
13✔
2626
        }
2627
    }
2628
}
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

© 2026 Coveralls, Inc