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

IgniteUI / igniteui-angular / 13331632524

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

Pull #15372

github

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

1990 of 15592 branches covered (12.76%)

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

19956 existing lines in 307 files now uncovered.

6452 of 29307 relevant lines covered (22.02%)

249.17 hits per line

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

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

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

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

108

109
    /**
110
     * @hidden @internal
111
     */
112
    public validators: Validator[] = [];
259✔
113

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

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

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

225
        if (this.isPrimaryColumn && (rowEditable || hasTransactions)) {
19,876!
UNCOV
226
            return false;
×
227
        }
228

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

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

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

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

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

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

406
    /**
407
     * @hidden
408
     */
409
    @Output()
410
    public hiddenChange = new EventEmitter<boolean>();
259✔
411

412
    /** @hidden */
413
    @Output()
414
    public expandedChange = new EventEmitter<boolean>();
259✔
415

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

423
    /** @hidden */
424
    @Output()
425
    public columnChange = new EventEmitter<void>();
259✔
426

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

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

472
        }
473
        return this.widthSetByUser ? this._width : this.defaultWidth;
18,888✔
474
    }
475

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

856
    /**
857
     * @hidden
858
     */
859
    @Output()
860
    public widthChange = new EventEmitter<string>();
259✔
861

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

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

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

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

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

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

949

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

972
    }
973
    public get minWidth(): string {
974
        return !this._defaultMinWidth ? this.defaultMinWidth : this._defaultMinWidth;
2,940!
975
    }
976

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

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

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

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

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

1074
        if (this.grid) {
258✔
1075
            this.grid.summaryService.removeSummariesCachePerColumn(this.field);
258✔
1076
            this.grid.summaryPipeTrigger++;
258✔
1077
            this.grid.summaryService.resetSummaryHeight();
258✔
1078
        }
1079
    }
1080

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

1098
    public set disabledSummaries(value: string[]) {
UNCOV
1099
        this._disabledSummaries = value;
×
UNCOV
1100
        if (this.grid) {
×
UNCOV
1101
            this.grid.summaryService.removeSummariesCachePerColumn(this.field);
×
UNCOV
1102
            this.grid.summaryPipeTrigger++;
×
UNCOV
1103
            this.grid.summaryService.resetSummaryHeight();
×
1104
        }
1105
    }
1106

1107
    /**
1108
     * Gets the column `filters`.
1109
     * ```typescript
1110
     * let columnFilters = this.column.filters'
1111
     * ```
1112
     *
1113
     * @memberof IgxColumnComponent
1114
     */
1115
    @Input()
1116
    public get filters(): IgxFilteringOperand {
1117
        return this._filters;
4,476✔
1118
    }
1119
    /**
1120
     * Sets the column `filters`.
1121
     * ```typescript
1122
     * this.column.filters = IgxBooleanFilteringOperand.instance().
1123
     * ```
1124
     *
1125
     * @memberof IgxColumnComponent
1126
     */
1127
    public set filters(instance: IgxFilteringOperand) {
1128
        this._filters = instance;
258✔
1129
    }
1130
    /**
1131
     * Gets the column `sortStrategy`.
1132
     * ```typescript
1133
     * let sortStrategy = this.column.sortStrategy
1134
     * ```
1135
     *
1136
     * @memberof IgxColumnComponent
1137
     */
1138
    @Input()
1139
    public get sortStrategy(): ISortingStrategy {
UNCOV
1140
        return this._sortStrategy;
×
1141
    }
1142
    /**
1143
     * Sets the column `sortStrategy`.
1144
     * ```typescript
1145
     * this.column.sortStrategy = new CustomSortingStrategy().
1146
     * class CustomSortingStrategy extends SortingStrategy {...}
1147
     * ```
1148
     *
1149
     * @memberof IgxColumnComponent
1150
     */
1151
    public set sortStrategy(classRef: ISortingStrategy) {
UNCOV
1152
        this._sortStrategy = classRef;
×
1153
    }
1154

1155
    /* blazorSuppress */
1156
    /**
1157
     * Gets the function that compares values for grouping.
1158
     * ```typescript
1159
     * let groupingComparer = this.column.groupingComparer'
1160
     * ```
1161
     *
1162
     * @memberof IgxColumnComponent
1163
     */
1164
    @Input()
1165
    public get groupingComparer(): (a: any, b: any, currRec?: any, groupRec?: any) => number {
UNCOV
1166
        return this._groupingComparer;
×
1167
    }
1168

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

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

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

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

1398
    /**
1399
     * @hidden @internal
1400
     */
1401
    public get cells(): CellType[] {
UNCOV
1402
        return this.grid.dataView
×
1403
            .map((rec, index) => {
UNCOV
1404
                if (!this.grid.isGroupByRecord(rec) && !this.grid.isSummaryRow(rec)) {
×
UNCOV
1405
                    this.grid.pagingMode === 1 && this.grid.page !== 0 ? index = index + this.grid.perPage * this.grid.page : index = this.grid.dataRowList.first.index + index;
×
UNCOV
1406
                    const cell = new IgxGridCell(this.grid as any, index, this);
×
UNCOV
1407
                    return cell;
×
1408
                }
UNCOV
1409
            }).filter(cell => cell);
×
1410
    }
1411

1412

1413
    /**
1414
     * @hidden @internal
1415
     */
1416
    public get _cells(): CellType[] {
UNCOV
1417
        return this.grid.rowList.filter((row) => row instanceof IgxRowDirective)
×
1418
            .map((row) => {
UNCOV
1419
                if (row._cells) {
×
UNCOV
1420
                    return row._cells.filter((cell) => cell.columnIndex === this.index);
×
1421
                }
UNCOV
1422
            }).reduce((a, b) => a.concat(b), []);
×
1423
    }
1424

1425
    /**
1426
     * Gets the column visible index.
1427
     * If the column is not visible, returns `-1`.
1428
     * ```typescript
1429
     * let visibleColumnIndex =  this.column.visibleIndex;
1430
     * ```
1431
     *
1432
     * @memberof IgxColumnComponent
1433
     */
1434
    public get visibleIndex(): number {
1435
        if (!isNaN(this._vIndex)) {
48,939✔
1436
            return this._vIndex;
47,744✔
1437
        }
1438
        const unpinnedColumns = this.grid.unpinnedColumns.filter(c => !c.columnGroup);
8,131✔
1439
        const pinnedColumns = this.grid.pinnedColumns.filter(c => !c.columnGroup);
1,195✔
1440

1441
        let col = this;
1,195✔
1442
        let vIndex = -1;
1,195✔
1443

1444
        if (this.columnGroup) {
1,195✔
1445
            col = this.allChildren.filter(c => !c.columnGroup && !c.hidden)[0] as any;
10✔
1446
        }
1447
        if (this.columnLayoutChild) {
1,195!
UNCOV
1448
            return this.parent.childrenVisibleIndexes.find(x => x.column === this).index;
×
1449
        }
1450

1451
        if (!this.pinned) {
1,195!
1452
            const indexInCollection = unpinnedColumns.indexOf(col);
1,195✔
1453
            vIndex = indexInCollection === -1 ?
1,195!
1454
                -1 :
1455
                (this.grid.isPinningToStart ?
1,195!
1456
                    pinnedColumns.length + indexInCollection :
1457
                    indexInCollection);
1458
        } else {
UNCOV
1459
            const indexInCollection = pinnedColumns.indexOf(col);
×
UNCOV
1460
            vIndex = this.grid.isPinningToStart ?
×
1461
                indexInCollection :
1462
                unpinnedColumns.length + indexInCollection;
1463
        }
1464
        this._vIndex = vIndex;
1,195✔
1465
        return vIndex;
1,195✔
1466
    }
1467

1468
    /* blazorCSSuppress - Blazor doesn't carry over the ColumnType interface + should translate as static bool value */
1469
    /**
1470
     * Returns a boolean indicating if the column is a `ColumnGroup`.
1471
     * ```typescript
1472
     * let columnGroup =  this.column.columnGroup;
1473
     * ```
1474
     *
1475
     * @memberof IgxColumnComponent
1476
     */
1477
    public get columnGroup() {
1478
        return false;
36,325✔
1479
    }
1480

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

1494
    /**
1495
     * Returns a boolean indicating if the column is a child of a `ColumnLayout` for multi-row layout.
1496
     * ```typescript
1497
     * let columnLayoutChild =  this.column.columnLayoutChild;
1498
     * ```
1499
     *
1500
     * @memberof IgxColumnComponent
1501
     */
1502
    public get columnLayoutChild(): boolean {
1503
        return this.parent && this.parent.columnLayout;
151,332✔
1504
    }
1505

1506
    /**
1507
     * A list containing all the child columns under this column (if any).
1508
     * Empty without children or if this column is not Group or Layout.
1509
     */
1510
    public get childColumns(): ColumnType[] {
1511
        return [];
×
1512
    }
1513

1514
    /** @hidden @internal **/
1515
    public get allChildren(): IgxColumnComponent[] {
UNCOV
1516
        return [];
×
1517
    }
1518
    /**
1519
     * Returns the level of the column in a column group.
1520
     * Returns `0` if the column doesn't have a `parent`.
1521
     * ```typescript
1522
     * let columnLevel =  this.column.level;
1523
     * ```
1524
     *
1525
     * @memberof IgxColumnComponent
1526
     */
1527
    public get level() {
1528
        let ptr = this.parent;
4,201✔
1529
        let lvl = 0;
4,201✔
1530

1531
        while (ptr) {
4,201✔
1532
            lvl++;
34✔
1533
            ptr = ptr.parent;
34✔
1534
        }
1535
        return lvl;
4,201✔
1536
    }
1537

1538
    /** @hidden @internal **/
1539
    public get isLastPinned(): boolean {
1540
        return this.grid.isPinningToStart &&
14,355✔
1541
            this.grid.pinnedColumns[this.grid.pinnedColumns.length - 1] === this;
1542
    }
1543

1544
    /** @hidden @internal **/
1545
    public get isFirstPinned(): boolean {
1546
        const pinnedCols = this.grid.pinnedColumns.filter(x => !x.columnGroup);
12,130✔
1547
        return !this.grid.isPinningToStart && pinnedCols[0] === this;
12,130!
1548
    }
1549

1550
    /** @hidden @internal **/
1551
    public get rightPinnedOffset(): string {
1552
        return this.pinned && !this.grid.isPinningToStart ?
9,776!
1553
            - this.grid.pinnedWidth - this.grid.headerFeaturesWidth + 'px' :
1554
            null;
1555
    }
1556

1557
    /** @hidden @internal **/
1558
    public get gridRowSpan(): number {
UNCOV
1559
        return this.rowEnd && this.rowStart ? this.rowEnd - this.rowStart : 1;
×
1560
    }
1561
    /** @hidden @internal **/
1562
    public get gridColumnSpan(): number {
UNCOV
1563
        return this.colEnd && this.colStart ? this.colEnd - this.colStart : 1;
×
1564
    }
1565

1566
    /**
1567
     * Indicates whether the column will be visible when its parent is collapsed.
1568
     * ```html
1569
     * <igx-column-group>
1570
     *   <igx-column [visibleWhenCollapsed]="true"></igx-column>
1571
     * </igx-column-group>
1572
     * ```
1573
     *
1574
     * @memberof IgxColumnComponent
1575
     */
1576
    @notifyChanges(true)
1577
    @Input({ transform: booleanAttribute })
1578
    public set visibleWhenCollapsed(value: boolean) {
UNCOV
1579
        this._visibleWhenCollapsed = value;
×
UNCOV
1580
        this.visibleWhenCollapsedChange.emit(this._visibleWhenCollapsed);
×
UNCOV
1581
        if (this.parent) {
×
UNCOV
1582
            this.parent?.setExpandCollapseState?.();
×
1583
        }
1584
    }
1585

1586
    public get visibleWhenCollapsed(): boolean {
UNCOV
1587
        return this._visibleWhenCollapsed;
×
1588
    }
1589

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

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

1648
    /**
1649
     * @hidden
1650
     * @internal
1651
     */
1652
    public get collapsible() {
1653
        return false;
×
1654
    }
1655
    public set collapsible(_value: boolean) { }
1656

1657
    /**
1658
     * @hidden
1659
     * @internal
1660
     */
1661
    public get expanded() {
1662
        return true;
×
1663
    }
1664
    public set expanded(_value: boolean) { }
1665

1666
    /**
1667
     * @hidden
1668
     */
1669
    public defaultWidth: string;
1670

1671
    /**
1672
     * @hidden
1673
     */
1674
    public widthSetByUser: boolean;
1675

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

1681
    /**
1682
     * @hidden
1683
     * @internal
1684
     */
1685
    public defaultTimeFormat = 'hh:mm:ss a';
259✔
1686

1687
    /**
1688
     * @hidden
1689
     * @internal
1690
     */
1691
    public defaultDateTimeFormat = 'dd/MM/yyyy HH:mm:ss a';
259✔
1692

1693

1694
    /**
1695
     * Returns the filteringExpressionsTree of the column.
1696
     * ```typescript
1697
     * let tree =  this.column.filteringExpressionsTree;
1698
     * ```
1699
     *
1700
     * @memberof IgxColumnComponent
1701
     */
1702
    public get filteringExpressionsTree(): FilteringExpressionsTree {
UNCOV
1703
        return ExpressionsTreeUtil.find(this.grid.filteringExpressionsTree, this.field) as FilteringExpressionsTree;
×
1704
    }
1705

1706
    /* alternateName: parentColumn */
1707
    /**
1708
     * Sets/gets the parent column.
1709
     * ```typescript
1710
     * let parentColumn = this.column.parent;
1711
     * ```
1712
     * ```typescript
1713
     * this.column.parent = higherLevelColumn;
1714
     * ```
1715
     *
1716
     * @memberof IgxColumnComponent
1717
     */
1718
    public parent = null;
259✔
1719

1720
    /* blazorSuppress */
1721
    /**
1722
     * Sets/gets the children columns.
1723
     * ```typescript
1724
     * let columnChildren = this.column.children;
1725
     * ```
1726
     *
1727
     * @deprecated in version 18.1.0. Use the `childColumns` property instead.
1728
     */
1729
    public children: QueryList<IgxColumnComponent>;
1730
    /**
1731
     * @hidden
1732
     */
1733
    public destroy$ = new Subject<any>();
259✔
1734

1735
    /**
1736
     * @hidden
1737
     */
1738
    protected _applySelectableClass = false;
259✔
1739

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

1844
    private _field: string;
1845
    private _calcWidth = null;
259✔
1846
    private _columnPipeArgs: IColumnPipeArgs = { digitsInfo: DEFAULT_DIGITS_INFO };
259✔
1847
    private _editorOptions: IColumnEditorOptions = { };
259✔
1848

1849
    constructor(
1850
        @Inject(IGX_GRID_BASE) public grid: GridType,
259✔
1851
        @Optional() @Self() @Inject(NG_VALIDATORS) private _validators: Validator[],
259✔
1852
        /** @hidden @internal **/
1853
        public cdr: ChangeDetectorRef,
259✔
1854
        protected platform: PlatformUtil,
259✔
1855
    ) {
1856
        this.validators = _validators;
259✔
1857
    }
1858

1859
    /**
1860
     * @hidden
1861
     * @internal
1862
     */
1863
    public resetCaches() {
1864
        this._vIndex = NaN;
2,651✔
1865
        if (this.grid) {
2,651✔
1866
            this.cacheCalcWidth();
2,651✔
1867
        }
1868
    }
1869

1870
    /**
1871
     * @hidden
1872
     */
1873
    public ngOnDestroy() {
1874
        this.destroy$.next(true);
133✔
1875
        this.destroy$.complete();
133✔
1876
    }
1877
    /**
1878
     * @hidden
1879
     */
1880
    public ngAfterContentInit(): void {
1881
        if (this.summaryTemplateDirective) {
258!
UNCOV
1882
            this._summaryTemplate = this.summaryTemplateDirective.template;
×
1883
        }
1884
        if (this.cellTemplate) {
258!
UNCOV
1885
            this._bodyTemplate = this.cellTemplate.template;
×
1886
        }
1887
        if (this.cellValidationErrorTemplate) {
258!
UNCOV
1888
            this._errorTemplate = this.cellValidationErrorTemplate.template;
×
1889
        }
1890
        if (this.headTemplate && this.headTemplate.length) {
258!
UNCOV
1891
            this._headerTemplate = this.headTemplate.toArray()[0].template;
×
1892
        }
1893
        if (this.editorTemplate) {
258!
UNCOV
1894
            this._inlineEditorTemplate = this.editorTemplate.template;
×
1895
        }
1896
        if (this.filterCellTemplateDirective) {
258!
1897
            this._filterCellTemplate = this.filterCellTemplateDirective.template;
×
1898
        }
1899
        if (!this._columnPipeArgs.format) {
258✔
1900
            this._columnPipeArgs.format = this.dataType === GridColumnDataType.Time ?
258✔
1901
                DEFAULT_TIME_FORMAT : this.dataType === GridColumnDataType.DateTime ?
228✔
1902
                    DEFAULT_DATE_TIME_FORMAT : DEFAULT_DATE_FORMAT;
1903
        }
1904
        if (!this.summaries) {
258✔
1905
            switch (this.dataType) {
258✔
1906
                case GridColumnDataType.Number:
1907
                case GridColumnDataType.Currency:
1908
                case GridColumnDataType.Percent:
1909
                    this.summaries = IgxNumberSummaryOperand;
37✔
1910
                    break;
37✔
1911
                case GridColumnDataType.Date:
1912
                case GridColumnDataType.DateTime:
1913
                    this.summaries = IgxDateSummaryOperand;
35✔
1914
                    break;
35✔
1915
                case GridColumnDataType.Time:
1916
                    this.summaries = IgxTimeSummaryOperand;
30✔
1917
                    break;
30✔
1918

1919
                case GridColumnDataType.String:
1920
                case GridColumnDataType.Boolean:
1921
                default:
1922
                    this.summaries = IgxSummaryOperand;
156✔
1923
                    break;
156✔
1924
            }
1925
        }
1926
        if (!this.filters) {
258✔
1927
            switch (this.dataType) {
225!
1928
                case GridColumnDataType.Boolean:
1929
                    this.filters = IgxBooleanFilteringOperand.instance();
36✔
1930
                    break;
36✔
1931
                case GridColumnDataType.Number:
1932
                case GridColumnDataType.Currency:
1933
                case GridColumnDataType.Percent:
1934
                    this.filters = IgxNumberFilteringOperand.instance();
37✔
1935
                    break;
37✔
1936
                case GridColumnDataType.Date:
1937
                    this.filters = IgxDateFilteringOperand.instance();
34✔
1938
                    break;
34✔
1939
                case GridColumnDataType.Time:
1940
                    this.filters = IgxTimeFilteringOperand.instance();
30✔
1941
                    break;
30✔
1942
                case GridColumnDataType.DateTime:
1943
                    this.filters = IgxDateTimeFilteringOperand.instance();
1✔
1944
                    break;
1✔
1945
                case GridColumnDataType.Image:
UNCOV
1946
                    this.filterable = false;
×
UNCOV
1947
                    break;
×
1948
                case GridColumnDataType.String:
1949
                default:
1950
                    this.filters = IgxStringFilteringOperand.instance();
87✔
1951
                    break;
87✔
1952
            }
1953
        }
1954
    }
1955

1956
    /**
1957
     * @hidden
1958
     */
1959
    public getGridTemplate(isRow: boolean): string {
UNCOV
1960
        if (isRow) {
×
UNCOV
1961
            const rowsCount = this.grid.type !== 'pivot' ? this.grid.multiRowLayoutRowSize : this.children.length - 1;
×
UNCOV
1962
            return `repeat(${rowsCount},1fr)`;
×
1963
        } else {
UNCOV
1964
            return this.getColumnSizesString(this.children);
×
1965
        }
1966
    }
1967

1968
    /** @hidden @internal **/
1969
    public getInitialChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<MRLColumnSizeInfo> {
1970
        const columnSizes: MRLColumnSizeInfo[] = [];
3✔
1971
        // find the smallest col spans
1972
        children.forEach(col => {
3✔
1973
            if (!col.colStart) {
6✔
1974
                return;
6✔
1975
            }
UNCOV
1976
            const newWidthSet = col.widthSetByUser && columnSizes[col.colStart - 1] && !columnSizes[col.colStart - 1].widthSetByUser;
×
UNCOV
1977
            const newSpanSmaller = columnSizes[col.colStart - 1] && columnSizes[col.colStart - 1].colSpan > col.gridColumnSpan;
×
UNCOV
1978
            const bothWidthsSet = col.widthSetByUser && columnSizes[col.colStart - 1] && columnSizes[col.colStart - 1].widthSetByUser;
×
UNCOV
1979
            const bothWidthsNotSet = !col.widthSetByUser && columnSizes[col.colStart - 1] && !columnSizes[col.colStart - 1].widthSetByUser;
×
1980

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

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

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

2042
        // Flatten columnSizes so there are not columns with colSpan > 1
2043
        for (let i = 0; i < columnSizes.length; i++) {
3✔
UNCOV
2044
            if (columnSizes[i] && columnSizes[i].colSpan > 1) {
×
UNCOV
2045
                let j = 1;
×
2046

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

2070
                // Update the current column width so it is divided between all columns it spans and set it to 1.
UNCOV
2071
                columnSizes[i].width = columnSizes[i].widthSetByUser ?
×
2072
                    columnSizes[i].width / columnSizes[i].colSpan :
2073
                    columnSizes[i].width;
UNCOV
2074
                columnSizes[i].colSpan = 1;
×
2075

2076
                // Update the index based on how much we have replaced. Subtract 1 because we started from 1.
UNCOV
2077
                i += j - 1;
×
2078
            }
2079
        }
2080

2081
        return columnSizes;
3✔
2082
    }
2083

2084
    /** @hidden @internal **/
2085
    public getFilledChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<string> {
UNCOV
2086
        const columnSizes = this.getInitialChildColumnSizes(children);
×
2087

2088
        // fill the gaps if there are any
UNCOV
2089
        const result: string[] = [];
×
UNCOV
2090
        for (const size of columnSizes) {
×
UNCOV
2091
            if (size && !!size.width) {
×
UNCOV
2092
                result.push(size.width + 'px');
×
2093
            } else {
UNCOV
2094
                result.push(parseFloat(this.grid.getPossibleColumnWidth()) + 'px');
×
2095
            }
2096
        }
UNCOV
2097
        return result;
×
2098
    }
2099

2100
    /** @hidden @internal **/
2101
    public getResizableColUnderEnd(): MRLResizeColumnInfo[] {
UNCOV
2102
        if (this.columnLayout || !this.columnLayoutChild || this.columnGroup) {
×
2103
            return [{ target: this, spanUsed: 1 }];
×
2104
        }
2105

UNCOV
2106
        const columnSized = this.getInitialChildColumnSizes(this.parent.children);
×
UNCOV
2107
        const targets: MRLResizeColumnInfo[] = [];
×
UNCOV
2108
        const colEnd = this.colEnd ? this.colEnd : this.colStart + 1;
×
2109

UNCOV
2110
        for (let i = 0; i < columnSized.length; i++) {
×
UNCOV
2111
            if (this.colStart <= i + 1 && i + 1 < colEnd) {
×
UNCOV
2112
                targets.push({ target: columnSized[i].ref, spanUsed: 1 });
×
2113
            }
2114
        }
2115

UNCOV
2116
        const targetsSquashed: MRLResizeColumnInfo[] = [];
×
UNCOV
2117
        for (const target of targets) {
×
UNCOV
2118
            if (targetsSquashed.length && targetsSquashed[targetsSquashed.length - 1].target.field === target.target.field) {
×
2119
                targetsSquashed[targetsSquashed.length - 1].spanUsed++;
×
2120
            } else {
UNCOV
2121
                targetsSquashed.push(target);
×
2122
            }
2123
        }
2124

UNCOV
2125
        return targetsSquashed;
×
2126
    }
2127

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

UNCOV
2150
        if (this.parent && !this.parent.pinned) {
×
UNCOV
2151
            return this.topLevelParent.pin(index);
×
2152
        }
2153

UNCOV
2154
        const hasIndex = index !== undefined;
×
UNCOV
2155
        if (hasIndex && (index < 0 || index > grid.pinnedColumns.length)) {
×
UNCOV
2156
            return false;
×
2157
        }
2158

UNCOV
2159
        if (!this.parent && !this.pinnable) {
×
2160
            return false;
×
2161
        }
2162

UNCOV
2163
        const rootPinnedCols = grid._pinnedColumns.filter((c) => c.level === 0);
×
UNCOV
2164
        index = hasIndex ? index : rootPinnedCols.length;
×
UNCOV
2165
        const args: IPinColumnCancellableEventArgs = { column: this, insertAtIndex: index, isPinned: false, cancel: false };
×
UNCOV
2166
        this.grid.columnPin.emit(args);
×
2167

UNCOV
2168
        if (args.cancel) {
×
2169
            return;
×
2170
        }
2171

UNCOV
2172
        this.grid.crudService.endEdit(false);
×
2173

UNCOV
2174
        this._pinned = true;
×
UNCOV
2175
        this.pinnedChange.emit(this._pinned);
×
2176
        // it is possible that index is the last position, so will need to find target column by [index-1]
UNCOV
2177
        const targetColumn = args.insertAtIndex === grid._pinnedColumns.length ?
×
2178
            grid._pinnedColumns[args.insertAtIndex - 1] : grid._pinnedColumns[args.insertAtIndex];
2179

UNCOV
2180
        if (grid._pinnedColumns.indexOf(this) === -1) {
×
UNCOV
2181
            if (!grid.hasColumnGroups) {
×
UNCOV
2182
                grid._pinnedColumns.splice(args.insertAtIndex, 0, this);
×
2183
            } else {
2184
                // insert based only on root collection
UNCOV
2185
                rootPinnedCols.splice(args.insertAtIndex, 0, this);
×
UNCOV
2186
                let allPinned = [];
×
2187
                // re-create hierarchy
UNCOV
2188
                rootPinnedCols.forEach(group => {
×
UNCOV
2189
                    allPinned.push(group);
×
UNCOV
2190
                    allPinned = allPinned.concat(group.allChildren);
×
2191
                });
UNCOV
2192
                grid._pinnedColumns = allPinned;
×
2193
            }
2194

UNCOV
2195
            if (grid._unpinnedColumns.indexOf(this) !== -1) {
×
UNCOV
2196
                const childrenCount = this.allChildren.length;
×
UNCOV
2197
                grid._unpinnedColumns.splice(grid._unpinnedColumns.indexOf(this), 1 + childrenCount);
×
2198
            }
2199
        }
2200

UNCOV
2201
        if (hasIndex) {
×
UNCOV
2202
            index === grid._pinnedColumns.length - 1 ?
×
2203
                grid._moveColumns(this, targetColumn, DropPosition.AfterDropTarget) : grid._moveColumns(this, targetColumn, DropPosition.BeforeDropTarget);
2204
        }
2205

UNCOV
2206
        if (this.columnGroup) {
×
UNCOV
2207
            this.allChildren.forEach(child => child.pin());
×
UNCOV
2208
            grid.reinitPinStates();
×
2209
        }
2210

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

UNCOV
2240
        if (this.parent && this.parent.pinned) {
×
UNCOV
2241
            return this.topLevelParent.unpin(index);
×
2242
        }
UNCOV
2243
        const hasIndex = index !== undefined;
×
UNCOV
2244
        if (hasIndex && (index < 0 || index > grid._unpinnedColumns.length)) {
×
UNCOV
2245
            return false;
×
2246
        }
2247

2248
        // estimate the exact index at which column will be inserted
2249
        // takes into account initial unpinned index of the column
UNCOV
2250
        if (!hasIndex) {
×
UNCOV
2251
            const indices = grid.unpinnedColumns.map(col => col.index);
×
UNCOV
2252
            indices.push(this.index);
×
UNCOV
2253
            indices.sort((a, b) => a - b);
×
UNCOV
2254
            index = indices.indexOf(this.index);
×
2255
        }
2256

UNCOV
2257
        const args: IPinColumnCancellableEventArgs = { column: this, insertAtIndex: index, isPinned: true, cancel: false };
×
UNCOV
2258
        this.grid.columnPin.emit(args);
×
2259

UNCOV
2260
        if (args.cancel) {
×
2261
            return;
×
2262
        }
2263

UNCOV
2264
        this.grid.crudService.endEdit(false);
×
2265

UNCOV
2266
        this._pinned = false;
×
UNCOV
2267
        this.pinnedChange.emit(this._pinned);
×
2268

2269
        // it is possible that index is the last position, so will need to find target column by [index-1]
UNCOV
2270
        const targetColumn = args.insertAtIndex === grid._unpinnedColumns.length ?
×
2271
            grid._unpinnedColumns[args.insertAtIndex - 1] : grid._unpinnedColumns[args.insertAtIndex];
2272

UNCOV
2273
        if (!hasIndex) {
×
UNCOV
2274
            grid._unpinnedColumns.splice(index, 0, this);
×
UNCOV
2275
            if (grid._pinnedColumns.indexOf(this) !== -1) {
×
UNCOV
2276
                grid._pinnedColumns.splice(grid._pinnedColumns.indexOf(this), 1);
×
2277
            }
2278
        }
2279

UNCOV
2280
        if (hasIndex) {
×
UNCOV
2281
            grid.moveColumn(this, targetColumn);
×
2282
        }
2283

UNCOV
2284
        if (this.columnGroup) {
×
UNCOV
2285
            this.allChildren.forEach(child => child.unpin());
×
2286
        }
2287

UNCOV
2288
        grid.reinitPinStates();
×
UNCOV
2289
        grid.resetCaches();
×
2290

UNCOV
2291
        grid.notifyChanges();
×
UNCOV
2292
        if (this.columnLayoutChild) {
×
UNCOV
2293
            this.grid.columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
×
2294
        }
UNCOV
2295
        this.grid.filteringService.refreshExpressions();
×
2296

UNCOV
2297
        this.grid.columnPinned.emit({ column: this, insertAtIndex: index, isPinned: false });
×
2298

UNCOV
2299
        return true;
×
2300
    }
2301

2302
    /**
2303
     * Moves a column to the specified visible index.
2304
     * If passed index is invalid, or if column would receive a different visible index after moving, moving is not performed.
2305
     * If passed index would move the column to a different column group. moving is not performed.
2306
     *
2307
     * @example
2308
     * ```typescript
2309
     * column.move(index);
2310
     * ```
2311
     * @memberof IgxColumnComponent
2312
     */
2313
    public move(index: number) {
2314
        let target;
UNCOV
2315
        let columns = this.grid.columns.filter(c => c.visibleIndex > -1);
×
2316
        // grid last visible index
UNCOV
2317
        const li = columns.map(c => c.visibleIndex).reduce((a, b) => Math.max(a, b));
×
UNCOV
2318
        const parent = this.parent;
×
UNCOV
2319
        const isPreceding = this.visibleIndex < index;
×
2320

UNCOV
2321
        if (index === this.visibleIndex || index < 0 || index > li) {
×
UNCOV
2322
            return;
×
2323
        }
2324

UNCOV
2325
        if (parent) {
×
UNCOV
2326
            columns = columns.filter(c => c.level >= this.level && c !== this && c.parent !== this &&
×
2327
                c.topLevelParent === this.topLevelParent);
2328
        }
2329

2330
        // 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.
2331
        // 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.
2332

UNCOV
2333
        if (isPreceding) {
×
UNCOV
2334
            columns = columns.filter(c => c.visibleIndex > this.visibleIndex);
×
UNCOV
2335
            target = columns.find(c => c.level === this.level && c.visibleIndex + (c as any).calcChildren() - this.calcChildren() === index);
×
2336
        } else {
UNCOV
2337
            columns = columns.filter(c => c.visibleIndex < this.visibleIndex);
×
UNCOV
2338
            target = columns.find(c => c.level === this.level && c.visibleIndex === index);
×
2339
        }
2340

UNCOV
2341
        if (!target || (target.pinned && this.disablePinning)) {
×
UNCOV
2342
            return;
×
2343
        }
2344

UNCOV
2345
        const pos = isPreceding ? DropPosition.AfterDropTarget : DropPosition.BeforeDropTarget;
×
UNCOV
2346
        this.grid.moveColumn(this, target as IgxColumnComponent, pos);
×
2347
    }
2348

2349
    /**
2350
     * No children for the column, so will returns 1 or 0, if the column is hidden.
2351
     *
2352
     * @hidden
2353
     */
2354
    public calcChildren(): number {
UNCOV
2355
        const children = this.hidden ? 0 : 1;
×
UNCOV
2356
        return children;
×
2357
    }
2358

2359
    /**
2360
     * Toggles column vibisility and emits the respective event.
2361
     *
2362
     * @hidden
2363
     */
2364
    public toggleVisibility(value?: boolean) {
UNCOV
2365
        const newValue = value ?? !this.hidden;
×
UNCOV
2366
        const eventArgs: IColumnVisibilityChangingEventArgs = { column: this, newValue, cancel: false };
×
UNCOV
2367
        this.grid.columnVisibilityChanging.emit(eventArgs);
×
2368

UNCOV
2369
        if (eventArgs.cancel) {
×
2370
            return;
×
2371
        }
UNCOV
2372
        this.hidden = newValue;
×
UNCOV
2373
        this.grid.columnVisibilityChanged.emit({ column: this, newValue });
×
2374
    }
2375

2376
    /**
2377
     * Returns a reference to the top level parent column.
2378
     * ```typescript
2379
     * let topLevelParent =  this.column.topLevelParent;
2380
     * ```
2381
     */
2382
    public get topLevelParent(): ColumnType | undefined {
UNCOV
2383
        let parent = this.parent;
×
UNCOV
2384
        while (parent && parent.parent) {
×
UNCOV
2385
            parent = parent.parent;
×
2386
        }
UNCOV
2387
        return parent ?? undefined;
×
2388
    }
2389

2390
    /**
2391
     * @hidden @internal
2392
     */
2393
    public get headerCell(): IgxGridHeaderComponent {
UNCOV
2394
        return this.grid.headerCellList.find((header) => header.column === this);
×
2395
    }
2396

2397
    /**
2398
     * @hidden @internal
2399
     */
2400
    public get filterCell(): IgxGridFilteringCellComponent {
UNCOV
2401
        return this.grid.filterCellList.find((filterCell) => filterCell.column === this);
×
2402
    }
2403

2404
    /**
2405
     * @hidden @internal
2406
     */
2407
    public get headerGroup(): IgxGridHeaderGroupComponent {
UNCOV
2408
        return this.grid.headerGroupsList.find(group => group.column === this);
×
2409
    }
2410

2411
    /**
2412
     * Autosize the column to the longest currently visible cell value, including the header cell.
2413
     * ```typescript
2414
     * @ViewChild('grid') grid: IgxGridComponent;
2415
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2416
     * column.autosize();
2417
     * ```
2418
     *
2419
     * @memberof IgxColumnComponent
2420
     * @param byHeaderOnly Set if column should be autosized based only on the header content.
2421
     */
2422
    public autosize(byHeaderOnly = false) {
×
UNCOV
2423
        if (!this.columnGroup) {
×
UNCOV
2424
            this.width = this.getAutoSize(byHeaderOnly);
×
UNCOV
2425
            this.grid.reflow();
×
2426
        }
2427
    }
2428

2429
    /**
2430
     * @hidden
2431
     */
2432
    public getAutoSize(byHeader = false): string {
×
UNCOV
2433
        const size = !byHeader ? this.getLargestCellWidth() :
×
UNCOV
2434
            (Object.values(this.getHeaderCellWidths()).reduce((a, b) => a + b) + 'px');
×
UNCOV
2435
        const isPercentageWidth = this.width && typeof this.width === 'string' && this.width.indexOf('%') !== -1;
×
2436

2437
        let newWidth;
UNCOV
2438
        if (isPercentageWidth) {
×
UNCOV
2439
            const gridAvailableSize = this.grid.calcWidth;
×
UNCOV
2440
            const percentageSize = parseFloat(size) / gridAvailableSize * 100;
×
UNCOV
2441
            newWidth = percentageSize + '%';
×
2442
        } else {
UNCOV
2443
            newWidth = size;
×
2444
        }
2445

UNCOV
2446
        const maxWidth = isPercentageWidth ? this.maxWidthPercent : this.maxWidthPx;
×
UNCOV
2447
        const minWidth = isPercentageWidth ? this.minWidthPercent : this.minWidthPx;
×
UNCOV
2448
        if (this.maxWidth && (parseFloat(newWidth) > maxWidth)) {
×
UNCOV
2449
            newWidth = isPercentageWidth ? maxWidth + '%' : maxWidth + 'px';
×
UNCOV
2450
        } else if (parseFloat(newWidth) < minWidth) {
×
UNCOV
2451
            newWidth = isPercentageWidth ? minWidth + '%' : minWidth + 'px';
×
2452
        }
2453

UNCOV
2454
        return newWidth;
×
2455
    }
2456

2457
    /**
2458
     * @hidden
2459
     */
2460
    public getCalcWidth(): any {
2461
        if (this._calcWidth && !isNaN(this.calcPixelWidth)) {
551✔
2462
            return this._calcWidth;
551✔
2463
        }
UNCOV
2464
        this.cacheCalcWidth();
×
UNCOV
2465
        return this._calcWidth;
×
2466
    }
2467

2468

2469
    /**
2470
     * @hidden
2471
     * Returns the width and padding of a header cell.
2472
     */
2473
    public getHeaderCellWidths() {
UNCOV
2474
        return this.grid.getHeaderCellWidth(this.headerCell.nativeElement);
×
2475
    }
2476

2477
    /**
2478
     * @hidden
2479
     * Returns the size (in pixels) of the longest currently visible cell, including the header cell.
2480
     * ```typescript
2481
     * @ViewChild('grid') grid: IgxGridComponent;
2482
     *
2483
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2484
     * let size = column.getLargestCellWidth();
2485
     * ```
2486
     * @memberof IgxColumnComponent
2487
     */
2488
    public getLargestCellWidth(): string {
UNCOV
2489
        const range = this.grid.document.createRange();
×
UNCOV
2490
        const largest = new Map<number, number>();
×
2491

UNCOV
2492
        if (this._cells.length > 0) {
×
UNCOV
2493
            const cellsContentWidths = [];
×
UNCOV
2494
            this._cells.forEach((cell) => cellsContentWidths.push(cell.calculateSizeToFit(range)));
×
2495

UNCOV
2496
            const index = cellsContentWidths.indexOf(Math.max(...cellsContentWidths));
×
UNCOV
2497
            const cellStyle = this.grid.document.defaultView.getComputedStyle(this._cells[index].nativeElement);
×
UNCOV
2498
            const cellPadding = parseFloat(cellStyle.paddingLeft) + parseFloat(cellStyle.paddingRight) +
×
2499
                parseFloat(cellStyle.borderLeftWidth) + parseFloat(cellStyle.borderRightWidth);
2500

UNCOV
2501
            largest.set(Math.max(...cellsContentWidths), cellPadding);
×
2502
        }
2503

UNCOV
2504
        if (this.headerCell && this.autosizeHeader) {
×
UNCOV
2505
            const headerCellWidths = this.getHeaderCellWidths();
×
UNCOV
2506
            largest.set(headerCellWidths.width, headerCellWidths.padding);
×
2507
        }
2508

UNCOV
2509
        const largestCell = Math.max(...Array.from(largest.keys()));
×
UNCOV
2510
        const width = Math.ceil(largestCell + largest.get(largestCell));
×
2511

UNCOV
2512
        if (Number.isNaN(width)) {
×
2513
            return this.width;
×
2514
        } else {
UNCOV
2515
            return width + 'px';
×
2516
        }
2517
    }
2518

2519
    /**
2520
     * @hidden
2521
     */
2522
    public getCellWidth() {
2523
        const colWidth = this.width;
9,938✔
2524
        const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1;
9,938✔
2525

2526
        if (this.columnLayoutChild) {
9,938!
2527
            return '';
×
2528
        }
2529

2530
        if (colWidth && !isPercentageWidth) {
9,938!
2531

2532
            let cellWidth = colWidth;
9,938✔
2533
            if (typeof cellWidth !== 'string' || cellWidth.endsWith('px') === false) {
9,938!
UNCOV
2534
                cellWidth += 'px';
×
2535
            }
2536

2537
            return cellWidth;
9,938✔
2538
        } else {
UNCOV
2539
            return colWidth;
×
2540
        }
2541
    }
2542

2543
    /**
2544
     * @hidden
2545
     */
2546
    public populateVisibleIndexes() { }
2547

2548
    protected getColumnSizesString(children: QueryList<IgxColumnComponent>): string {
UNCOV
2549
        const res = this.getFilledChildColumnSizes(children);
×
UNCOV
2550
        return res.join(' ');
×
2551
    }
2552

2553
    /**
2554
     * @hidden
2555
     * @internal
2556
     */
2557
    protected cacheCalcWidth(): any {
2558
        const colWidth = this.width;
2,900✔
2559
        const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1;
2,900✔
2560
        const isAutoWidth = colWidth && typeof colWidth === 'string' && colWidth === 'fit-content';
2,900✔
2561
        if (isPercentageWidth && this.grid.isColumnWidthSum) {
2,900!
UNCOV
2562
            this._calcWidth = this.grid.minColumnWidth;
×
2563
        } else if (isPercentageWidth) {
2,900!
UNCOV
2564
            this._calcWidth = parseFloat(colWidth) / 100 * this.grid.calcWidth;
×
2565
        } else if (!colWidth || isAutoWidth && !this.autoSize) {
2,900!
2566
            // no width
2567
            this._calcWidth = this.defaultWidth || this.grid.getPossibleColumnWidth();
12✔
2568
        } else {
2569
            this._calcWidth = this.width;
2,888✔
2570
        }
2571
        this.calcPixelWidth = parseFloat(this._calcWidth);
2,900✔
2572
    }
2573

2574
    /**
2575
     * @hidden
2576
     * @internal
2577
     */
2578
    protected setExpandCollapseState() {
UNCOV
2579
        this.children.filter(col => (col.visibleWhenCollapsed !== undefined)).forEach(c => {
×
UNCOV
2580
            if (!this.collapsible) {
×
UNCOV
2581
                c.hidden = this.hidden; return;
×
2582
            }
UNCOV
2583
            c.hidden = this._expanded ? c.visibleWhenCollapsed : !c.visibleWhenCollapsed;
×
2584
        });
2585
    }
2586
    /**
2587
     * @hidden
2588
     * @internal
2589
     */
2590
    protected checkCollapsibleState() {
UNCOV
2591
        if (!this.children) {
×
2592
            return false;
×
2593
        }
UNCOV
2594
        const cols = this.children.map(child => child.visibleWhenCollapsed);
×
UNCOV
2595
        return (cols.some(c => c === true) && cols.some(c => c === false));
×
2596
    }
2597

2598
    /**
2599
     * @hidden
2600
     */
2601
    public get pinnable() {
UNCOV
2602
        return (this.grid as any)._init || !this.pinned;
×
2603
    }
2604

2605
    /**
2606
     * @hidden
2607
     */
2608
    public get applySelectableClass(): boolean {
UNCOV
2609
        return this._applySelectableClass;
×
2610
    }
2611

2612
    /**
2613
     * @hidden
2614
     */
2615
    public set applySelectableClass(value: boolean) {
UNCOV
2616
        if (this.selectable) {
×
UNCOV
2617
            this._applySelectableClass = value;
×
2618
        }
2619
    }
2620
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc