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

IgniteUI / igniteui-angular / 20960087204

13 Jan 2026 02:19PM UTC coverage: 12.713% (-78.8%) from 91.5%
20960087204

Pull #16746

github

web-flow
Merge 9afce6e5d into a967f087e
Pull Request #16746: fix(csv): export summaries - master

1008 of 16803 branches covered (6.0%)

19 of 23 new or added lines in 2 files covered. (82.61%)

24693 existing lines in 336 files now uncovered.

3985 of 31345 relevant lines covered (12.71%)

2.49 hits per line

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

30.2
/projects/igniteui-angular/grids/core/src/columns/column.component.ts
1
import { Subject } from 'rxjs';
2
import { isEqual } from 'lodash-es';
3
import { AfterContentInit, ChangeDetectorRef, ChangeDetectionStrategy, Component, ContentChild, ContentChildren, Input, QueryList, TemplateRef, Output, EventEmitter, OnDestroy, booleanAttribute, inject } from '@angular/core';
4
import { notifyChanges } from '../watch-changes';
5
import { WatchColumnChanges } from '../watch-changes';
6
import { IgxRowDirective } from '../row.directive';
7
import { CellType, GridType, IgxCellTemplateContext, IgxColumnTemplateContext, IgxSummaryTemplateContext, IGX_GRID_BASE } from '../common/grid.interface';
8
import { IgxGridHeaderComponent } from '../headers/grid-header.component';
9
import { IgxGridFilteringCellComponent } from '../filtering/base/grid-filtering-cell.component';
10
import { IgxGridHeaderGroupComponent } from '../headers/grid-header-group.component';
11
import {
12
    IgxSummaryOperand, IgxNumberSummaryOperand, IgxDateSummaryOperand, IgxTimeSummaryOperand
13
} from '../summaries/grid-summary';
14
import {
15
    IgxCellTemplateDirective,
16
    IgxCellHeaderTemplateDirective,
17
    IgxCellEditorTemplateDirective,
18
    IgxCollapsibleIndicatorTemplateDirective,
19
    IgxFilterCellTemplateDirective,
20
    IgxSummaryTemplateDirective,
21
    IgxCellValidationErrorDirective
22
} from './templates.directive';
23
import { DropPosition } from '../moving/moving.service';
24
import { IColumnVisibilityChangingEventArgs, IPinColumnCancellableEventArgs, IPinColumnEventArgs } from '../common/events';
25
import { IgxGridCell } from '../grid-public-cell';
26
import { NG_VALIDATORS, Validator } from '@angular/forms';
27
import { ColumnPinningPosition, ColumnType, DefaultSortingStrategy, ExpressionsTreeUtil, FilteringExpressionsTree, GridColumnDataType, IColumnEditorOptions, IColumnPipeArgs, IgxBooleanFilteringOperand, IgxDateFilteringOperand, IgxDateTimeFilteringOperand, IgxFilteringOperand, IgxNumberFilteringOperand, IgxStringFilteringOperand, IgxSummaryResult, IgxTimeFilteringOperand, isConstructor, ISortingStrategy, MRLColumnSizeInfo, MRLResizeColumnInfo, PlatformUtil, ɵSize } from 'igniteui-angular/core';
28
import type { IgxColumnLayoutComponent } from './column-layout.component';
29

30
const DEFAULT_DATE_FORMAT = 'mediumDate';
3✔
31
const DEFAULT_TIME_FORMAT = 'mediumTime';
3✔
32
const DEFAULT_DATE_TIME_FORMAT = 'medium';
3✔
33
const DEFAULT_DIGITS_INFO = '1.0-3';
3✔
34

35
/* blazorElement */
36
/* contentParent: ColumnGroup */
37
/* wcElementTag: igc-column */
38
/* additionalIdentifier: Field */
39
/* blazorIndirectRender */
40
/**
41
 * **Ignite UI for Angular Column** -
42
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid#columns-configuration)
43
 *
44
 * The Ignite UI Column is used within an `igx-grid` element to define what data the column will show. Features such as sorting,
45
 * filtering & editing are enabled at the column level.  You can also provide a template containing custom content inside
46
 * the column using `ng-template` which will be used for all cells within the column.
47
 *
48
 * @igxParent IgxGridComponent, IgxTreeGridComponent, IgxHierarchicalGridComponent, IgxPivotGridComponent, IgxRowIslandComponent, IgxColumnGroupComponent, IgxColumnLayoutComponent
49
 */
50
@Component({
51
    changeDetection: ChangeDetectionStrategy.OnPush,
52
    selector: 'igx-column',
53
    template: ``,
54
    styles: `:host { display: none }`,
55
    standalone: true
56
})
57
export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnType {
3✔
58
    public grid = inject<GridType>(IGX_GRID_BASE);
3✔
59
    private _validators = inject<Validator[]>(NG_VALIDATORS, { optional: true, self: true });
3✔
60

61
    /** @hidden @internal **/
62
    public cdr = inject(ChangeDetectorRef);
3✔
63
    protected platform = inject(PlatformUtil);
3✔
64

65
    /**
66
     * Sets/gets the `field` value.
67
     * ```typescript
68
     * let columnField = this.column.field;
69
     * ```
70
     * ```html
71
     * <igx-column [field] = "'ID'"></igx-column>
72
     * ```
73
     *
74
     * @memberof IgxColumnComponent
75
     */
76
    @Input()
77
    public set field(value: string) {
78
        this._field = value;
3✔
79
        this.hasNestedPath = value?.includes('.');
3✔
80
    }
81
    public get field(): string {
82
        return this._field;
1,699✔
83
    }
84

85
    /**
86
     * Sets/gets whether to merge cells in this column.
87
     * ```html
88
     * <igx-column [merge]="true"></igx-column>
89
     * ```
90
     *
91
     */
92
    @Input()
93
    public get merge() {
94
        return this._merge;
474✔
95
    }
96

97
    public set merge(value) {
UNCOV
98
        if (this.grid.hasColumnLayouts) {
×
UNCOV
99
            console.warn('Merging is not supported with multi-row layouts.');
×
UNCOV
100
            return;
×
101
        }
UNCOV
102
        if (value !== this._merge) {
×
UNCOV
103
            this._merge = value;
×
UNCOV
104
            if (this.grid) {
×
UNCOV
105
                this.grid.resetColumnCollections();
×
UNCOV
106
                this.grid.notifyChanges();
×
107
            }
108
        }
109
    }
110

111
    /**
112
     * @hidden @internal
113
     */
114
    public validators: Validator[] = this._validators;
3✔
115

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

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

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

227
        if (this.isPrimaryColumn && (rowEditable || hasTransactions)) {
204!
UNCOV
228
            return false;
×
229
        }
230

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

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

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

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

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

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

408
    /**
409
     * Emitted when the column is hidden or shown.
410
     *
411
     * ```html
412
     * <igx-column (hiddenChange)="hiddenChange($event)">
413
     * </igx-column>
414
     * ```
415
     *
416
     */
417
    @Output()
418
    public hiddenChange = new EventEmitter<boolean>();
3✔
419

420
    /**
421
     * Emitted when the column expanded or collapsed.
422
     *
423
     * ```html
424
     * <igx-column (expandedChange)="expandedChange($event)">
425
     * </igx-column>
426
     * ```
427
     *
428
     */
429
    @Output()
430
    public expandedChange = new EventEmitter<boolean>();
3✔
431

432
    /** @hidden */
433
    @Output()
434
    public collapsibleChange = new EventEmitter<boolean>();
3✔
435

436
    /** @hidden */
437
    @Output()
438
    public visibleWhenCollapsedChange = new EventEmitter<boolean>();
3✔
439

440
    /** @hidden @internal */
441
    @Output()
442
    public columnChange = new EventEmitter<void>();
3✔
443

444
    /**
445
     * Gets whether the hiding is disabled.
446
     * ```typescript
447
     * let isHidingDisabled =  this.column.disableHiding;
448
     * ```
449
     *
450
     * @memberof IgxColumnComponent
451
     */
452
    @notifyChanges()
453
    @WatchColumnChanges()
454
    @Input({ transform: booleanAttribute })
455
    public disableHiding = false;
3✔
456
    /**
457
     * Gets whether the pinning is disabled.
458
     * ```typescript
459
     * let isPinningDisabled =  this.column.disablePinning;
460
     * ```
461
     *
462
     * @memberof IgxColumnComponent
463
     */
464
    @notifyChanges()
465
    @WatchColumnChanges()
466
    @Input({ transform: booleanAttribute })
467
    public disablePinning = false;
3✔
468

469
    /**
470
     * Gets the `width` of the column.
471
     * ```typescript
472
     * let columnWidth = this.column.width;
473
     * ```
474
     *
475
     * @memberof IgxColumnComponent
476
     */
477
    @notifyChanges(true)
478
    @WatchColumnChanges()
479
    @Input()
480
    public get width(): string {
481
        const isAutoWidth = this._width && typeof this._width === 'string' && this._width === 'auto';
153!
482
        if (isAutoWidth) {
153!
UNCOV
483
            if (!this.autoSize) {
×
UNCOV
484
                return 'fit-content';
×
485
            } else {
UNCOV
486
                return this.autoSize + 'px';
×
487
            }
488

489
        }
490
        return this.widthSetByUser ? this._width : this.defaultWidth;
153!
491
    }
492

493
    /**
494
     * Sets the `width` of the column.
495
     * ```html
496
     * <igx-column [width] = "'25%'"></igx-column>
497
     * ```
498
     *
499
     * Two-way data binding.
500
     * ```html
501
     * <igx-column [(width)]="model.columns[0].width"></igx-column>
502
     * ```
503
     *
504
     * @memberof IgxColumnComponent
505
     */
506
    public set width(value: string) {
UNCOV
507
        if (value) {
×
UNCOV
508
            this._calcWidth = null;
×
UNCOV
509
            this.calcPixelWidth = NaN;
×
UNCOV
510
            this.widthSetByUser = true;
×
511
            // width could be passed as number from the template
512
            // host bindings are not px affixed so we need to ensure we affix simple number strings
UNCOV
513
            if (typeof (value) === 'number' || value.match(/^[0-9]*$/)) {
×
UNCOV
514
                value = value + 'px';
×
515
            }
UNCOV
516
            if (value === 'fit-content') {
×
UNCOV
517
                value = 'auto';
×
518
            }
UNCOV
519
            this._width = value;
×
UNCOV
520
            if (this.grid) {
×
UNCOV
521
                this.cacheCalcWidth();
×
522
            }
UNCOV
523
            this.widthChange.emit(this._width);
×
524
        }
525
    }
526

527
    /** @hidden @internal **/
528
    public autoSize: number;
529

530
    /**
531
     * Sets/gets the maximum `width` of the column.
532
     * ```typescript
533
     * let columnMaxWidth = this.column.width;
534
     * ```
535
     * ```html
536
     * <igx-column [maxWidth] = "'150px'"></igx-column>
537
     * ```
538
     *
539
     * @memberof IgxColumnComponent
540
     */
541
    @WatchColumnChanges()
542
    @Input()
543
    public set maxWidth(value: string) {
UNCOV
544
        this._maxWidth = value;
×
545

UNCOV
546
        this.grid.notifyChanges(true);
×
547
    }
548
    public get maxWidth(): string {
549
        return this._maxWidth;
21✔
550
    }
551
    /**
552
     * Sets/gets the class selector of the column header.
553
     * ```typescript
554
     * let columnHeaderClass = this.column.headerClasses;
555
     * ```
556
     * ```html
557
     * <igx-column [headerClasses] = "'column-header'"></igx-column>
558
     * ```
559
     *
560
     * @memberof IgxColumnComponent
561
     */
562
    @notifyChanges()
563
    @WatchColumnChanges()
564
    @Input()
565
    public headerClasses = '';
3✔
566

567
    /**
568
     * Sets conditional style properties on the column header.
569
     * Similar to `ngStyle` it accepts an object literal where the keys are
570
     * the style properties and the value is the expression to be evaluated.
571
     * ```typescript
572
     * styles = {
573
     *  background: 'royalblue',
574
     *  color: (column) => column.pinned ? 'red': 'inherit'
575
     * }
576
     * ```
577
     * ```html
578
     * <igx-column [headerStyles]="styles"></igx-column>
579
     * ```
580
     *
581
     * @memberof IgxColumnComponent
582
     */
583
    @notifyChanges()
584
    @WatchColumnChanges()
585
    @Input()
586
    public headerStyles = null;
3✔
587

588
    /**
589
     * Sets/gets the class selector of the column group header.
590
     * ```typescript
591
     * let columnHeaderClass = this.column.headerGroupClasses;
592
     * ```
593
     * ```html
594
     * <igx-column [headerGroupClasses] = "'column-group-header'"></igx-column>
595
     * ```
596
     *
597
     * @memberof IgxColumnComponent
598
     */
599
    @notifyChanges()
600
    @WatchColumnChanges()
601
    @Input()
602
    public headerGroupClasses = '';
3✔
603

604
    /**
605
     * Sets conditional style properties on the column header group wrapper.
606
     * Similar to `ngStyle` it accepts an object literal where the keys are
607
     * the style properties and the value is the expression to be evaluated.
608
     * ```typescript
609
     * styles = {
610
     *  background: 'royalblue',
611
     *  color: (column) => column.pinned ? 'red': 'inherit'
612
     * }
613
     * ```
614
     * ```html
615
     * <igx-column [headerGroupStyles]="styles"></igx-column>
616
     * ```
617
     *
618
     * @memberof IgxColumnComponent
619
     */
620
    @notifyChanges()
621
    @WatchColumnChanges()
622
    @Input()
623
    public headerGroupStyles = null;
3✔
624

625
    /* treatAsRef */
626
    /**
627
     * Sets a conditional class selector of the column cells.
628
     * Accepts an object literal, containing key-value pairs,
629
     * where the key is the name of the CSS class, while the
630
     * value is either a callback function that returns a boolean,
631
     * or boolean, like so:
632
     * ```typescript
633
     * callback = (rowData, columnKey, cellValue, rowIndex) => { return rowData[columnKey] > 6; }
634
     * cellClasses = { 'className' : this.callback };
635
     * ```
636
     * ```html
637
     * <igx-column [cellClasses] = "cellClasses"></igx-column>
638
     * <igx-column [cellClasses] = "{'class1' : true }"></igx-column>
639
     * ```
640
     *
641
     * @memberof IgxColumnComponent
642
     */
643
    @notifyChanges()
644
    @WatchColumnChanges()
645
    @Input()
646
    public cellClasses: any;
647

648
    /* treatAsRef */
649
    /**
650
     * Sets conditional style properties on the column cells.
651
     * Similar to `ngStyle` it accepts an object literal where the keys are
652
     * the style properties and the value is the expression to be evaluated.
653
     * As with `cellClasses` it accepts a callback function.
654
     * ```typescript
655
     * styles = {
656
     *  background: 'royalblue',
657
     *  color: (rowData, columnKey, cellValue, rowIndex) => value.startsWith('Important') ? 'red': 'inherit'
658
     * }
659
     * ```
660
     * ```html
661
     * <igx-column [cellStyles]="styles"></igx-column>
662
     * ```
663
     *
664
     * @memberof IgxColumnComponent
665
     */
666
    @notifyChanges()
667
    @WatchColumnChanges()
668
    @Input()
669
    public cellStyles = null;
3✔
670

671
    /* blazorAlternateType: CellValueFormatterEventHandler */
672
    /* blazorOnlyScript */
673
    /**
674
     * Applies display format to cell values in the column. Does not modify the underlying data.
675
     *
676
     * @remarks
677
     * Note: As the formatter is used in places like the Excel style filtering dialog, in certain
678
     * scenarios (remote filtering for example), the row data argument can be `undefined`.
679
     *
680
     *
681
     * In this example, we check to see if the column name is Salary, and then provide a method as the column formatter
682
     * to format the value into a currency string.
683
     *
684
     * @example
685
     * ```typescript
686
     * columnInit(column: IgxColumnComponent) {
687
     *   if (column.field == "Salary") {
688
     *     column.formatter = (salary => this.format(salary));
689
     *   }
690
     * }
691
     *
692
     * format(value: number) : string {
693
     *   return formatCurrency(value, "en-us", "$");
694
     * }
695
     * ```
696
     *
697
     * @example
698
     * ```typescript
699
     * const column = this.grid.getColumnByName('Address');
700
     * const addressFormatter = (address: string, rowData: any) => data.privacyEnabled ? 'unknown' : address;
701
     * column.formatter = addressFormatter;
702
     * ```
703
     *
704
     * @memberof IgxColumnComponent
705
     */
706
    @notifyChanges()
707
    @WatchColumnChanges()
708
    @Input()
709
    public formatter: (value: any, rowData?: any) => any;
710

711
    /* blazorAlternateType: SummaryValueFormatterEventHandler */
712
    /* blazorOnlyScript */
713
    /* forceCastDelegate */
714
    /**
715
     * The summaryFormatter is used to format the display of the column summaries.
716
     *
717
     * In this example, we check to see if the column name is OrderDate, and then provide a method as the summaryFormatter
718
     * to change the locale for the dates to 'fr-FR'. The summaries with the count key are skipped so they are displayed as numbers.
719
     *
720
     * ```typescript
721
     * columnInit(column: IgxColumnComponent) {
722
     *   if (column.field == "OrderDate") {
723
     *     column.summaryFormatter = this.summaryFormat;
724
     *   }
725
     * }
726
     *
727
     * summaryFormat(summary: IgxSummaryResult, summaryOperand: IgxSummaryOperand): string {
728
     *   const result = summary.summaryResult;
729
     *   if(summaryResult.key !== 'count' && result !== null && result !== undefined) {
730
     *      const pipe = new DatePipe('fr-FR');
731
     *      return pipe.transform(result,'mediumDate');
732
     *   }
733
     *   return result;
734
     * }
735
     * ```
736
     *
737
     * @memberof IgxColumnComponent
738
     */
739
    @notifyChanges()
740
    @WatchColumnChanges()
741
    @Input()
742
    public summaryFormatter: (summary: IgxSummaryResult, summaryOperand: IgxSummaryOperand) => any;
743

744
    /**
745
     * Sets/gets whether the column filtering should be case sensitive.
746
     * Default value is `true`.
747
     * ```typescript
748
     * let filteringIgnoreCase = this.column.filteringIgnoreCase;
749
     * ```
750
     * ```html
751
     * <igx-column [filteringIgnoreCase] = "false"></igx-column>
752
     * ```
753
     *
754
     * @memberof IgxColumnComponent
755
     */
756
    @WatchColumnChanges()
757
    @Input({ transform: booleanAttribute })
758
    public filteringIgnoreCase = true;
3✔
759
    /**
760
     * Sets/gets whether the column sorting should be case sensitive.
761
     * Default value is `true`.
762
     * ```typescript
763
     * let sortingIgnoreCase = this.column.sortingIgnoreCase;
764
     * ```
765
     * ```html
766
     * <igx-column [sortingIgnoreCase] = "false"></igx-column>
767
     * ```
768
     *
769
     * @memberof IgxColumnComponent
770
     */
771
    @WatchColumnChanges()
772
    @Input({ transform: booleanAttribute })
773
    public sortingIgnoreCase = true;
3✔
774
    /**
775
     * Sets/gets whether the column is `searchable`.
776
     * Default value is `true`.
777
     * ```typescript
778
     * let isSearchable =  this.column.searchable';
779
     * ```
780
     * ```html
781
     *  <igx-column [searchable] = "false"></igx-column>
782
     * ```
783
     *
784
     * @memberof IgxColumnComponent
785
     */
786
    @notifyChanges()
787
    @WatchColumnChanges()
788
    @Input({ transform: booleanAttribute })
789
    public searchable = true;
3✔
790
    /**
791
     * Sets/gets the data type of the column values.
792
     * Default value is `string`.
793
     * ```typescript
794
     * let columnDataType = this.column.dataType;
795
     * ```
796
     * ```html
797
     * <igx-column [dataType] = "'number'"></igx-column>
798
     * ```
799
     *
800
     * @memberof IgxColumnComponent
801
     */
802
    @Input()
803
    public dataType: GridColumnDataType = GridColumnDataType.String;
3✔
804

805
    /** @hidden */
806
    @Input()
807
    public collapsibleIndicatorTemplate: TemplateRef<IgxColumnTemplateContext>;
808

809
    /**
810
     * Row index where the current field should end.
811
     * The amount of rows between rowStart and rowEnd will determine the amount of spanning rows to that field
812
     * ```html
813
     * <igx-column-layout>
814
     *   <igx-column [rowEnd]="2" [rowStart]="1" [colStart]="1"></igx-column>
815
     * </igx-column-layout>
816
     * ```
817
     *
818
     * @memberof IgxColumnComponent
819
     */
820
    @Input()
821
    public rowEnd: number;
822

823
    /**
824
     * Column index where the current field should end.
825
     * The amount of columns between colStart and colEnd will determine the amount of spanning columns to that field
826
     * ```html
827
     * <igx-column-layout>
828
     *   <igx-column [colEnd]="3" [rowStart]="1" [colStart]="1"></igx-column>
829
     * </igx-column-layout>
830
     * ```
831
     *
832
     * @memberof IgxColumnComponent
833
     */
834
    @Input()
835
    public colEnd: number;
836

837
    /**
838
     * Row index from which the field is starting.
839
     * ```html
840
     * <igx-column-layout>
841
     *   <igx-column [rowStart]="1" [colStart]="1"></igx-column>
842
     * </igx-column-layout>
843
     * ```
844
     *
845
     * @memberof IgxColumnComponent
846
     */
847
    @Input()
848
    public rowStart: number;
849

850
    /**
851
     * Column index from which the field is starting.
852
     * ```html
853
     * <igx-column-layout>
854
     *   <igx-column [colStart]="1" [rowStart]="1"></igx-column>
855
     * </igx-column-layout>
856
     * ```
857
     *
858
     * @memberof IgxColumnComponent
859
     */
860
    @Input()
861
    public colStart: number;
862

863
    /**
864
     * Sets/gets custom properties provided in additional template context.
865
     *
866
     * ```html
867
     * <igx-column [additionalTemplateContext]="contextObject">
868
     *   <ng-template igxCell let-cell="cell" let-props="additionalTemplateContext">
869
     *      {{ props }}
870
     *   </ng-template>
871
     * </igx-column>
872
     * ```
873
     *
874
     * @memberof IgxColumnComponent
875
     */
876
    @Input()
877
    public additionalTemplateContext: any;
878

879
    /**
880
     * Emitted when the column width changes.
881
     *
882
     * ```html
883
     * <igx-column (widthChange)="widthChange($event)">
884
     * </igx-column>
885
     * ```
886
     *
887
     */
888
    @Output()
889
    public widthChange = new EventEmitter<string>();
3✔
890

891
    /**
892
     * Emitted when the column is pinned/unpinned.
893
     *
894
     * ```html
895
     * <igx-column (pinnedChange)="pinnedChange($event)">
896
     * </igx-column>
897
     * ```
898
     *
899
     */
900
    @Output()
901
    public pinnedChange = new EventEmitter<boolean>();
3✔
902
    /**
903
     * @hidden
904
     */
905
    @ContentChild(IgxFilterCellTemplateDirective, { read: IgxFilterCellTemplateDirective })
906
    public filterCellTemplateDirective: IgxFilterCellTemplateDirective;
907
    /**
908
     * @hidden
909
     */
910
    @ContentChild(IgxSummaryTemplateDirective, { read: IgxSummaryTemplateDirective })
911
    protected summaryTemplateDirective: IgxSummaryTemplateDirective;
912
    /**
913
     * @hidden
914
     * @see {@link bodyTemplate}
915
     */
916
    @ContentChild(IgxCellTemplateDirective, { read: IgxCellTemplateDirective })
917
    protected cellTemplate: IgxCellTemplateDirective;
918
    /**
919
     * @hidden
920
     */
921
    @ContentChild(IgxCellValidationErrorDirective, { read: IgxCellValidationErrorDirective })
922
    protected cellValidationErrorTemplate: IgxCellValidationErrorDirective;
923
    /**
924
     * @hidden
925
     */
926
    @ContentChildren(IgxCellHeaderTemplateDirective, { read: IgxCellHeaderTemplateDirective, descendants: false })
927
    protected headTemplate: QueryList<IgxCellHeaderTemplateDirective>;
928
    /**
929
     * @hidden
930
     */
931
    @ContentChild(IgxCellEditorTemplateDirective, { read: IgxCellEditorTemplateDirective })
932
    protected editorTemplate: IgxCellEditorTemplateDirective;
933
    /**
934
     * @hidden
935
     */
936
    @ContentChild(IgxCollapsibleIndicatorTemplateDirective, { read: IgxCollapsibleIndicatorTemplateDirective, static: false })
937
    protected collapseIndicatorTemplate: IgxCollapsibleIndicatorTemplateDirective;
938
    /**
939
     * @hidden
940
     */
941
    public get calcWidth(): any {
942
        return this.getCalcWidth();
3✔
943
    }
944

945
    /** @hidden @internal **/
946
    public calcPixelWidth: number;
947

948
    /**
949
     * @hidden
950
     */
951
    public get maxWidthPx() {
UNCOV
952
        const gridAvailableSize = this.grid.calcWidth;
×
UNCOV
953
        const isPercentageWidth = this.maxWidth && typeof this.maxWidth === 'string' && this.maxWidth.indexOf('%') !== -1;
×
UNCOV
954
        return isPercentageWidth ? parseFloat(this.maxWidth) / 100 * gridAvailableSize : parseFloat(this.maxWidth);
×
955
    }
956

957
    /**
958
     * @hidden
959
     */
960
    public get maxWidthPercent() {
UNCOV
961
        const gridAvailableSize = this.grid.calcWidth;
×
UNCOV
962
        const isPercentageWidth = this.maxWidth && typeof this.maxWidth === 'string' && this.maxWidth.indexOf('%') !== -1;
×
UNCOV
963
        return isPercentageWidth ? parseFloat(this.maxWidth) : parseFloat(this.maxWidth) / gridAvailableSize * 100;
×
964
    }
965

966
    /**
967
     * @hidden
968
     */
969
    public get minWidthPx() {
UNCOV
970
        const gridAvailableSize = this.grid.calcWidth;
×
UNCOV
971
        const minWidth = this.minWidth || this.defaultMinWidth;
×
UNCOV
972
        const isPercentageWidth = minWidth && typeof minWidth === 'string' && minWidth.indexOf('%') !== -1;
×
UNCOV
973
        return isPercentageWidth ? parseFloat(minWidth) / 100 * gridAvailableSize : parseFloat(minWidth);
×
974
    }
975

976
    /**
977
     * @hidden
978
     */
979
    public get userSetMinWidthPx() {
980
        const gridAvailableSize = this.grid.calcWidth;
18✔
981
        const isPercentageWidth = this._defaultMinWidth && typeof this._defaultMinWidth === 'string' && this._defaultMinWidth.indexOf('%') !== -1;
18!
982
        return isPercentageWidth ? parseFloat(this._defaultMinWidth) / 100 * gridAvailableSize : parseFloat(this._defaultMinWidth);
18!
983
    }
984

985
    /**
986
     * @hidden
987
     */
988
    public get minWidthPercent() {
UNCOV
989
        const gridAvailableSize = this.grid.calcWidth;
×
UNCOV
990
        const minWidth = this.minWidth || this.defaultMinWidth;
×
UNCOV
991
        const isPercentageWidth = minWidth && typeof minWidth === 'string' && minWidth.indexOf('%') !== -1;
×
UNCOV
992
        return isPercentageWidth ? parseFloat(minWidth) : parseFloat(minWidth) / gridAvailableSize * 100;
×
993
    }
994

995

996
    /**
997
     * Sets/gets the minimum `width` of the column.
998
     * Default value is `88`;
999
     * ```typescript
1000
     * let columnMinWidth = this.column.minWidth;
1001
     * ```
1002
     * ```html
1003
     * <igx-column [minWidth] = "'100px'"></igx-column>
1004
     * ```
1005
     *
1006
     * @memberof IgxColumnComponent
1007
     */
1008
    @notifyChanges()
1009
    @WatchColumnChanges()
1010
    @Input()
1011
    public set minWidth(value: string) {
UNCOV
1012
        const minVal = parseFloat(value);
×
UNCOV
1013
        if (Number.isNaN(minVal)) {
×
UNCOV
1014
            return;
×
1015
        }
UNCOV
1016
        this._defaultMinWidth = value;
×
UNCOV
1017
        this.grid.notifyChanges(true);
×
1018
    }
1019
    public get minWidth(): string {
1020
        return this._defaultMinWidth;
42✔
1021
    }
1022

1023
    /** @hidden @internal **/
1024
    public get resolvedWidth(): string {
1025
        if (this.columnLayoutChild) {
366!
UNCOV
1026
            return '';
×
1027
        }
1028
        const isAutoWidth = this._width && typeof this._width === 'string' && this._width === 'auto';
366!
1029
        return isAutoWidth ? this.width : this.calcPixelWidth + 'px';
366!
1030
    }
1031

1032
    /**
1033
     * Gets the column index.
1034
     * ```typescript
1035
     * let columnIndex = this.column.index;
1036
     * ```
1037
     *
1038
     * @memberof IgxColumnComponent
1039
     */
1040
    public get index(): number {
1041
        return (this.grid as any)._columns.indexOf(this);
117✔
1042
    }
1043

1044
    /* mustCoerceToInt */
1045
    /**
1046
     * Gets the pinning position of the column.
1047
     * ```typescript
1048
     * let pinningPosition = this.column.pinningPosition;
1049
     */
1050
    @WatchColumnChanges()
1051
    @Input()
1052
    public get pinningPosition(): ColumnPinningPosition {
1053
        const userSet = this._pinningPosition !== null && this._pinningPosition !== undefined;
249!
1054
        return userSet ? this._pinningPosition : this.grid.pinning.columns;
249!
1055
    }
1056

1057
    /**
1058
     * Sets the pinning position of the column.
1059
     *```html
1060
     * <igx-column [pinningPosition]="1"></igx-column>
1061
     * ```
1062
     */
1063
    public set pinningPosition(value: ColumnPinningPosition) {
UNCOV
1064
        this._pinningPosition = value;
×
1065
    }
1066

1067
    /**
1068
     * Gets whether the column is `pinned`.
1069
     * ```typescript
1070
     * let isPinned = this.column.pinned;
1071
     * ```
1072
     *
1073
     * @memberof IgxColumnComponent
1074
     */
1075
    @WatchColumnChanges()
1076
    @Input({ transform: booleanAttribute })
1077
    public get pinned(): boolean {
1078
        return this._pinned;
183✔
1079
    }
1080
    /**
1081
     * Sets whether the column is pinned.
1082
     * Default value is `false`.
1083
     * ```html
1084
     * <igx-column [pinned] = "true"></igx-column>
1085
     * ```
1086
     *
1087
     * Two-way data binding.
1088
     * ```html
1089
     * <igx-column [(pinned)] = "model.columns[0].isPinned"></igx-column>
1090
     * ```
1091
     *
1092
     * @memberof IgxColumnComponent
1093
     */
1094
    public set pinned(value: boolean) {
UNCOV
1095
        if (this._pinned !== value) {
×
UNCOV
1096
            const isAutoWidth = this.width && typeof this.width === 'string' && this.width === 'fit-content';
×
UNCOV
1097
            if (this.grid && this.width && (isAutoWidth || !isNaN(parseInt(this.width, 10)))) {
×
UNCOV
1098
                if (value) {
×
UNCOV
1099
                    this.pin();
×
1100
                } else {
UNCOV
1101
                    this.unpin();
×
1102
                }
UNCOV
1103
                return;
×
1104
            }
1105
            /* No grid/width available at initialization. `initPinning` in the grid
1106
               will re-init the group (if present)
1107
            */
UNCOV
1108
            this._pinned = value;
×
UNCOV
1109
            this.pinnedChange.emit(this._pinned);
×
1110
        }
1111
    }
1112

1113
    /* treatAsRef */
1114
    /**
1115
     * Gets the column `summaries`.
1116
     * ```typescript
1117
     * let columnSummaries = this.column.summaries;
1118
     * ```
1119
     *
1120
     * @memberof IgxColumnComponent
1121
     */
1122
    @notifyChanges(true)
1123
    @WatchColumnChanges()
1124
    @Input()
1125
    public get summaries(): any {
1126
        return this._summaries;
21✔
1127
    }
1128

1129
    /* treatAsRef */
1130
    /**
1131
     * Sets the column `summaries`.
1132
     * ```typescript
1133
     * this.column.summaries = IgxNumberSummaryOperand;
1134
     * ```
1135
     *
1136
     * @memberof IgxColumnComponent
1137
     */
1138
    public set summaries(classRef: any) {
1139
        if (isConstructor(classRef)) {
3✔
1140
            this._summaries = new classRef();
3✔
1141
        }
1142

1143
        if (this.grid) {
3✔
1144
            this.grid.summaryService.removeSummariesCachePerColumn(this.field);
3✔
1145
            this.grid.summaryPipeTrigger++;
3✔
1146
            this.grid.summaryService.resetSummaryHeight();
3✔
1147
        }
1148
    }
1149

1150
    /**
1151
     * Sets/gets the summary operands to exclude from display.
1152
     * Accepts an array of string keys representing the summary types to disable, such as 'Min', 'Max', 'Count' etc.
1153
     * ```typescript
1154
     * let disabledSummaries = this.column.disabledSummaries;
1155
     * ```
1156
     * ```html
1157
     * <igx-column [disabledSummaries]="['min', 'max', 'average']"></igx-column>
1158
     * ```
1159
     *
1160
     * @memberof IgxColumnComponent
1161
     */
1162
    @WatchColumnChanges()
1163
    @Input()
1164
    public get disabledSummaries(): string[] {
1165
        return this._disabledSummaries;
15✔
1166
    }
1167

1168
    public set disabledSummaries(value: string[]) {
UNCOV
1169
        if (isEqual(this._disabledSummaries, value)) {
×
UNCOV
1170
            return;
×
1171
        }
UNCOV
1172
        this._disabledSummaries = value;
×
UNCOV
1173
        if (this.grid) {
×
UNCOV
1174
            this.grid.summaryService.removeSummariesCachePerColumn(this.field);
×
UNCOV
1175
            this.grid.summaryPipeTrigger++;
×
UNCOV
1176
            this.grid.summaryService.resetSummaryHeight();
×
1177
        }
1178
    }
1179

1180
    /**
1181
     * Gets the column `filters`.
1182
     * ```typescript
1183
     * let columnFilters = this.column.filters'
1184
     * ```
1185
     *
1186
     * @memberof IgxColumnComponent
1187
     */
1188
    @Input()
1189
    public get filters(): IgxFilteringOperand {
1190
        return this._filters;
3✔
1191
    }
1192
    /**
1193
     * Sets the column `filters`.
1194
     * ```typescript
1195
     * this.column.filters = IgxBooleanFilteringOperand.instance().
1196
     * ```
1197
     *
1198
     * @memberof IgxColumnComponent
1199
     */
1200
    public set filters(instance: IgxFilteringOperand) {
1201
        this._filters = instance;
3✔
1202
    }
1203
    /**
1204
     * Gets the column `sortStrategy`.
1205
     * ```typescript
1206
     * let sortStrategy = this.column.sortStrategy
1207
     * ```
1208
     *
1209
     * @memberof IgxColumnComponent
1210
     */
1211
    @Input()
1212
    public get sortStrategy(): ISortingStrategy {
UNCOV
1213
        return this._sortStrategy;
×
1214
    }
1215
    /**
1216
     * Sets the column `sortStrategy`.
1217
     * ```typescript
1218
     * this.column.sortStrategy = new CustomSortingStrategy().
1219
     * class CustomSortingStrategy extends SortingStrategy {...}
1220
     * ```
1221
     *
1222
     * @memberof IgxColumnComponent
1223
     */
1224
    public set sortStrategy(classRef: ISortingStrategy) {
UNCOV
1225
        this._sortStrategy = classRef;
×
1226
    }
1227

1228
    /* blazorSuppress */
1229
    /**
1230
     * Gets the function that compares values for merging.
1231
     * ```typescript
1232
     * let mergingComparer = this.column.mergingComparer'
1233
     * ```
1234
     */
1235
    @Input()
1236
    public get mergingComparer(): (prevRecord: any, record: any, field: string) => boolean {
UNCOV
1237
        return this._mergingComparer;
×
1238
    }
1239

1240
    /* blazorSuppress */
1241
    /**
1242
     * Sets a custom function to compare values for merging.
1243
     * ```typescript
1244
     * this.column.mergingComparer = (prevRecord: any, record: any, field: string) => { return prevRecord[field] === record[field]; }
1245
     * ```
1246
     */
1247
    public set mergingComparer(funcRef: (prevRecord: any, record: any, field: string) => boolean) {
UNCOV
1248
        this._mergingComparer = funcRef;
×
1249
    }
1250

1251

1252
    /* blazorSuppress */
1253
    /**
1254
     * Gets the function that compares values for grouping.
1255
     * ```typescript
1256
     * let groupingComparer = this.column.groupingComparer'
1257
     * ```
1258
     *
1259
     * @memberof IgxColumnComponent
1260
     */
1261
    @Input()
1262
    public get groupingComparer(): (a: any, b: any, currRec?: any, groupRec?: any) => number {
UNCOV
1263
        return this._groupingComparer;
×
1264
    }
1265

1266
    /* blazorSuppress */
1267
    /**
1268
     * Sets a custom function to compare values for grouping.
1269
     * Subsequent values in the sorted data that the function returns 0 for are grouped.
1270
     * ```typescript
1271
     * this.column.groupingComparer = (a: any, b: any, currRec?: any, groupRec?: any) => { return a === b ? 0 : -1; }
1272
     * ```
1273
     *
1274
     * @memberof IgxColumnComponent
1275
     */
1276
    public set groupingComparer(funcRef: (a: any, b: any, currRec?: any, groupRec?: any) => number) {
UNCOV
1277
        this._groupingComparer = funcRef;
×
1278
    }
1279
    /**
1280
     * @hidden @internal
1281
     */
1282
    public get defaultMinWidth(): string {
UNCOV
1283
        if (!this.grid) {
×
1284
            return '80';
×
1285
        }
UNCOV
1286
        switch (this.grid.gridSize) {
×
1287
            case ɵSize.Medium:
UNCOV
1288
                return '64';
×
1289
            case ɵSize.Small:
UNCOV
1290
                return '56';
×
1291
            default:
UNCOV
1292
                return '80';
×
1293
        }
1294
    }
1295
    /**
1296
     * Returns a reference to the `summaryTemplate`.
1297
     * ```typescript
1298
     * let summaryTemplate = this.column.summaryTemplate;
1299
     * ```
1300
     *
1301
     * @memberof IgxColumnComponent
1302
     */
1303
    @notifyChanges()
1304
    @WatchColumnChanges()
1305
    @Input()
1306
    public get summaryTemplate(): TemplateRef<IgxSummaryTemplateContext> {
1307
        return this._summaryTemplate;
15✔
1308
    }
1309
    /**
1310
     * Sets the summary template.
1311
     * ```html
1312
     * <ng-template #summaryTemplate igxSummary let-summaryResults>
1313
     *    <p>{{ summaryResults[0].label }}: {{ summaryResults[0].summaryResult }}</p>
1314
     *    <p>{{ summaryResults[1].label }}: {{ summaryResults[1].summaryResult }}</p>
1315
     * </ng-template>
1316
     * ```
1317
     * ```typescript
1318
     * @ViewChild("'summaryTemplate'", {read: TemplateRef })
1319
     * public summaryTemplate: TemplateRef<any>;
1320
     * this.column.summaryTemplate = this.summaryTemplate;
1321
     * ```
1322
     *
1323
     * @memberof IgxColumnComponent
1324
     */
1325
    public set summaryTemplate(template: TemplateRef<IgxSummaryTemplateContext>) {
UNCOV
1326
        this._summaryTemplate = template;
×
1327
    }
1328

1329
    /**
1330
     * Returns a reference to the `bodyTemplate`.
1331
     * ```typescript
1332
     * let bodyTemplate = this.column.bodyTemplate;
1333
     * ```
1334
     *
1335
     * @memberof IgxColumnComponent
1336
     */
1337
    @notifyChanges()
1338
    @WatchColumnChanges()
1339
    @Input('cellTemplate')
1340
    public get bodyTemplate(): TemplateRef<IgxCellTemplateContext> {
1341
        return this._bodyTemplate;
102✔
1342
    }
1343
    /**
1344
     * Sets the body template.
1345
     * ```html
1346
     * <ng-template #bodyTemplate igxCell let-val>
1347
     *    <div style = "background-color: yellowgreen" (click) = "changeColor(val)">
1348
     *       <span> {{val}} </span>
1349
     *    </div>
1350
     * </ng-template>
1351
     * ```
1352
     * ```typescript
1353
     * @ViewChild("'bodyTemplate'", {read: TemplateRef })
1354
     * public bodyTemplate: TemplateRef<any>;
1355
     * this.column.bodyTemplate = this.bodyTemplate;
1356
     * ```
1357
     *
1358
     * @memberof IgxColumnComponent
1359
     */
1360
    public set bodyTemplate(template: TemplateRef<IgxCellTemplateContext>) {
UNCOV
1361
        this._bodyTemplate = template;
×
1362
    }
1363
    /**
1364
     * Returns a reference to the header template.
1365
     * ```typescript
1366
     * let headerTemplate = this.column.headerTemplate;
1367
     * ```
1368
     *
1369
     * @memberof IgxColumnComponent
1370
     */
1371
    @notifyChanges()
1372
    @WatchColumnChanges()
1373
    @Input()
1374
    public get headerTemplate(): TemplateRef<IgxColumnTemplateContext> {
1375
        return this._headerTemplate;
15✔
1376
    }
1377
    /**
1378
     * Sets the header template.
1379
     * Note that the column header height is fixed and any content bigger than it will be cut off.
1380
     * ```html
1381
     * <ng-template #headerTemplate>
1382
     *   <div style = "background-color:black" (click) = "changeColor(val)">
1383
     *       <span style="color:red" >{{column.field}}</span>
1384
     *   </div>
1385
     * </ng-template>
1386
     * ```
1387
     * ```typescript
1388
     * @ViewChild("'headerTemplate'", {read: TemplateRef })
1389
     * public headerTemplate: TemplateRef<any>;
1390
     * this.column.headerTemplate = this.headerTemplate;
1391
     * ```
1392
     *
1393
     * @memberof IgxColumnComponent
1394
     */
1395
    public set headerTemplate(template: TemplateRef<IgxColumnTemplateContext>) {
UNCOV
1396
        this._headerTemplate = template;
×
1397
    }
1398
    /**
1399
     * Returns a reference to the inline editor template.
1400
     * ```typescript
1401
     * let inlineEditorTemplate = this.column.inlineEditorTemplate;
1402
     * ```
1403
     *
1404
     * @memberof IgxColumnComponent
1405
     */
1406
    @notifyChanges()
1407
    @WatchColumnChanges()
1408
    @Input('cellEditorTemplate')
1409
    public get inlineEditorTemplate(): TemplateRef<IgxCellTemplateContext> {
UNCOV
1410
        return this._inlineEditorTemplate;
×
1411
    }
1412
    /**
1413
     * Sets the inline editor template.
1414
     * ```html
1415
     * <ng-template #inlineEditorTemplate igxCellEditor let-cell="cell">
1416
     *     <input type="string" [(ngModel)]="cell.value"/>
1417
     * </ng-template>
1418
     * ```
1419
     * ```typescript
1420
     * @ViewChild("'inlineEditorTemplate'", {read: TemplateRef })
1421
     * public inlineEditorTemplate: TemplateRef<any>;
1422
     * this.column.inlineEditorTemplate = this.inlineEditorTemplate;
1423
     * ```
1424
     *
1425
     * @memberof IgxColumnComponent
1426
     */
1427
    public set inlineEditorTemplate(template: TemplateRef<IgxCellTemplateContext>) {
UNCOV
1428
        this._inlineEditorTemplate = template;
×
1429
    }
1430

1431
    /**
1432
     * Returns a reference to the validation error template.
1433
     * ```typescript
1434
     * let errorTemplate = this.column.errorTemplate;
1435
     * ```
1436
     */
1437
    @notifyChanges()
1438
    @WatchColumnChanges()
1439
    @Input('errorTemplate')
1440
    public get errorTemplate(): TemplateRef<IgxCellTemplateContext> {
1441
        return this._errorTemplate;
102✔
1442
    }
1443
    /**
1444
     * Sets the error template.
1445
     * ```html
1446
     * <ng-template igxCellValidationError let-cell="cell" #errorTemplate >
1447
     *     <div *ngIf="cell.validation.errors?.['forbiddenName']">
1448
     *      This name is forbidden.
1449
     *     </div>
1450
     * </ng-template>
1451
     * ```
1452
     * ```typescript
1453
     * @ViewChild("'errorTemplate'", {read: TemplateRef })
1454
     * public errorTemplate: TemplateRef<any>;
1455
     * this.column.errorTemplate = this.errorTemplate;
1456
     * ```
1457
     */
1458
    public set errorTemplate(template: TemplateRef<IgxCellTemplateContext>) {
UNCOV
1459
        this._errorTemplate = template;
×
1460
    }
1461

1462
    /**
1463
     * Returns a reference to the `filterCellTemplate`.
1464
     * ```typescript
1465
     * let filterCellTemplate = this.column.filterCellTemplate;
1466
     * ```
1467
     *
1468
     * @memberof IgxColumnComponent
1469
     */
1470
    @notifyChanges()
1471
    @WatchColumnChanges()
1472
    @Input('filterCellTemplate')
1473
    public get filterCellTemplate(): TemplateRef<IgxColumnTemplateContext> {
UNCOV
1474
        return this._filterCellTemplate;
×
1475
    }
1476
    /**
1477
     * Sets the quick filter template.
1478
     * ```html
1479
     * <ng-template #filterCellTemplate IgxFilterCellTemplate let-column="column">
1480
     *    <input (input)="onInput()">
1481
     * </ng-template>
1482
     * ```
1483
     * ```typescript
1484
     * @ViewChild("'filterCellTemplate'", {read: TemplateRef })
1485
     * public filterCellTemplate: TemplateRef<any>;
1486
     * this.column.filterCellTemplate = this.filterCellTemplate;
1487
     * ```
1488
     *
1489
     * @memberof IgxColumnComponent
1490
     */
1491
    public set filterCellTemplate(template: TemplateRef<IgxColumnTemplateContext>) {
UNCOV
1492
        this._filterCellTemplate = template;
×
1493
    }
1494

1495
    /**
1496
     * @hidden @internal
1497
     */
1498
    public get cells(): CellType[] {
UNCOV
1499
        return this.grid.dataView
×
1500
            .map((rec, index) => {
UNCOV
1501
                if (!this.grid.isGroupByRecord(rec) && !this.grid.isSummaryRow(rec)) {
×
UNCOV
1502
                    this.grid.pagingMode === 'remote' && this.grid.page !== 0 ?
×
1503
                        index = index + this.grid.perPage * this.grid.page : index = this.grid.dataRowList.first.index + index;
UNCOV
1504
                    const cell = new IgxGridCell(this.grid as any, index, this);
×
UNCOV
1505
                    return cell;
×
1506
                }
UNCOV
1507
            }).filter(cell => cell);
×
1508
    }
1509

1510

1511
    /**
1512
     * @hidden @internal
1513
     */
1514
    public get _cells(): CellType[] {
UNCOV
1515
        return this.grid.rowList.filter((row) => row instanceof IgxRowDirective)
×
1516
            .map((row) => {
UNCOV
1517
                if (row._cells) {
×
UNCOV
1518
                    return row._cells.filter((cell) => cell.columnIndex === this.index);
×
1519
                }
UNCOV
1520
            }).reduce((a, b) => a.concat(b), []);
×
1521
    }
1522

1523
    /**
1524
     * Gets the column visible index.
1525
     * If the column is not visible, returns `-1`.
1526
     * ```typescript
1527
     * let visibleColumnIndex =  this.column.visibleIndex;
1528
     * ```
1529
     */
1530
    public get visibleIndex(): number {
1531
        if (!isNaN(this._vIndex)) {
369✔
1532
            return this._vIndex;
360✔
1533
        }
1534
        const unpinnedColumns = this.grid.unpinnedColumns.filter(c => !c.columnGroup);
27✔
1535
        const pinnedStartColumns = this.grid.pinnedStartColumns.filter(c => !c.columnGroup);
9✔
1536
        const pinnedEndColumns = this.grid.pinnedEndColumns.filter(c => !c.columnGroup);
9✔
1537

1538
        let col = this;
9✔
1539
        let vIndex = -1;
9✔
1540

1541
        if (this.columnGroup) {
9!
UNCOV
1542
            col = this.allChildren.filter(c => !c.columnGroup && !c.hidden)[0] as any;
×
1543
        }
1544
        if (this.columnLayoutChild) {
9!
1545
            // TODO: Refactor/redo/remove this
UNCOV
1546
            return (this.parent as IgxColumnLayoutComponent).childrenVisibleIndexes.find(x => x.column === this).index;
×
1547
        }
1548

1549
        if (!this.pinned) {
9!
1550
            const indexInCollection = unpinnedColumns.indexOf(col);
9✔
1551
            vIndex = indexInCollection === -1 ?
9!
1552
                -1 : pinnedStartColumns.length + indexInCollection;
1553
        } else {
UNCOV
1554
            const indexInCollection = this.pinningPosition === ColumnPinningPosition.Start ?
×
1555
            pinnedStartColumns.indexOf(col) : pinnedEndColumns.indexOf(col);
UNCOV
1556
            vIndex = this.pinningPosition === ColumnPinningPosition.Start ?
×
1557
                indexInCollection :
1558
                pinnedStartColumns.length + unpinnedColumns.length + indexInCollection;
1559
        }
1560
        this._vIndex = vIndex;
9✔
1561
        return vIndex;
9✔
1562
    }
1563

1564
    /* blazorCSSuppress - Blazor doesn't carry over the ColumnType interface + should translate as static bool value */
1565
    /**
1566
     * Returns a boolean indicating if the column is a `ColumnGroup`.
1567
     * ```typescript
1568
     * let columnGroup =  this.column.columnGroup;
1569
     * ```
1570
     *
1571
     * @memberof IgxColumnComponent
1572
     */
1573
    public get columnGroup() {
1574
        return false;
255✔
1575
    }
1576

1577
    /* blazorCSSuppress - Blazor doesn't carry over the ColumnType interface + should translate as static bool value */
1578
    /**
1579
     * Returns a boolean indicating if the column is a `ColumnLayout` for multi-row layout.
1580
     * ```typescript
1581
     * let columnGroup =  this.column.columnGroup;
1582
     * ```
1583
     *
1584
     * @memberof IgxColumnComponent
1585
     */
1586
    public get columnLayout() {
1587
        return false;
738✔
1588
    }
1589

1590
    /**
1591
     * Returns a boolean indicating if the column is a child of a `ColumnLayout` for multi-row layout.
1592
     * ```typescript
1593
     * let columnLayoutChild =  this.column.columnLayoutChild;
1594
     * ```
1595
     *
1596
     * @memberof IgxColumnComponent
1597
     */
1598
    public get columnLayoutChild(): boolean {
1599
        return this.parent && this.parent.columnLayout;
1,497!
1600
    }
1601

1602
    /**
1603
     * A list containing all the child columns under this column (if any).
1604
     * Empty without children or if this column is not Group or Layout.
1605
     */
1606
    public get childColumns(): ColumnType[] {
1607
        return [];
×
1608
    }
1609

1610
    /** @hidden @internal **/
1611
    public get allChildren(): IgxColumnComponent[] {
UNCOV
1612
        return [];
×
1613
    }
1614
    /**
1615
     * Returns the level of the column in a column group.
1616
     * Returns `0` if the column doesn't have a `parent`.
1617
     * ```typescript
1618
     * let columnLevel =  this.column.level;
1619
     * ```
1620
     *
1621
     * @memberof IgxColumnComponent
1622
     */
1623
    public get level() {
1624
        let ptr = this.parent;
36✔
1625
        let lvl = 0;
36✔
1626

1627
        while (ptr) {
36✔
UNCOV
1628
            lvl++;
×
UNCOV
1629
            ptr = ptr.parent;
×
1630
        }
1631
        return lvl;
36✔
1632
    }
1633

1634
    /** @hidden @internal **/
1635
    public get isLastPinned(): boolean {
1636
        return this.pinningPosition === ColumnPinningPosition.Start &&
132✔
1637
            this.grid.pinnedStartColumns[this.grid.pinnedStartColumns.length - 1] === this;
1638
    }
1639

1640
    /** @hidden @internal **/
1641
    public get isFirstPinned(): boolean {
1642
        const pinnedCols = this.grid.pinnedEndColumns.filter(x => !x.columnGroup);
117✔
1643
        return this.pinningPosition === ColumnPinningPosition.End && pinnedCols[0] === this;
117!
1644
    }
1645

1646
    /** @hidden @internal **/
1647
    public get gridRowSpan(): number {
UNCOV
1648
        return this.rowEnd && this.rowStart ? this.rowEnd - this.rowStart : 1;
×
1649
    }
1650
    /** @hidden @internal **/
1651
    public get gridColumnSpan(): number {
UNCOV
1652
        return this.colEnd && this.colStart ? this.colEnd - this.colStart : 1;
×
1653
    }
1654

1655
    /**
1656
     * Indicates whether the column will be visible when its parent is collapsed.
1657
     * ```html
1658
     * <igx-column-group>
1659
     *   <igx-column [visibleWhenCollapsed]="true"></igx-column>
1660
     * </igx-column-group>
1661
     * ```
1662
     *
1663
     * @memberof IgxColumnComponent
1664
     */
1665
    @notifyChanges(true)
1666
    @Input({ transform: booleanAttribute })
1667
    public set visibleWhenCollapsed(value: boolean) {
UNCOV
1668
        this._visibleWhenCollapsed = value;
×
UNCOV
1669
        this.visibleWhenCollapsedChange.emit(this._visibleWhenCollapsed);
×
UNCOV
1670
        if (this.parent) {
×
1671
            // TODO: Refactor/redo/remove this
UNCOV
1672
            (this.parent as IgxColumnLayoutComponent)?.setExpandCollapseState?.();
×
1673
        }
1674
    }
1675

1676
    public get visibleWhenCollapsed(): boolean {
UNCOV
1677
        return this._visibleWhenCollapsed;
×
1678
    }
1679

1680
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
1681
    /**
1682
     * @remarks
1683
     * Pass optional parameters for DatePipe and/or DecimalPipe to format the display value for date and numeric columns.
1684
     * Accepts an `IColumnPipeArgs` object with any of the `format`, `timezone` and `digitsInfo` properties.
1685
     * For more details see https://angular.io/api/common/DatePipe and https://angular.io/api/common/DecimalPipe
1686
     * @example
1687
     * ```typescript
1688
     * const pipeArgs: IColumnPipeArgs = {
1689
     *      format: 'longDate',
1690
     *      timezone: 'UTC',
1691
     *      digitsInfo: '1.1-2'
1692
     * }
1693
     * ```
1694
     * ```html
1695
     * <igx-column dataType="date" [pipeArgs]="pipeArgs"></igx-column>
1696
     * <igx-column dataType="number" [pipeArgs]="pipeArgs"></igx-column>
1697
     * ```
1698
     * @memberof IgxColumnComponent
1699
     */
1700
    @notifyChanges()
1701
    @WatchColumnChanges()
1702
    @Input()
1703
    public set pipeArgs(value: IColumnPipeArgs) {
UNCOV
1704
        this._columnPipeArgs = Object.assign(this._columnPipeArgs, value);
×
UNCOV
1705
        this.grid.summaryService.clearSummaryCache();
×
UNCOV
1706
        this.grid.pipeTrigger++;
×
1707
    }
1708
    /* mustSetInCodePlatforms: WebComponents;Blazor */
1709
    public get pipeArgs(): IColumnPipeArgs {
1710
        return this._columnPipeArgs;
149✔
1711
    }
1712

1713
    /**
1714
     * Pass optional properties for the default column editors.
1715
     * @remarks
1716
     * Options may be applicable only to specific column type editors.
1717
     * @example
1718
     * ```typescript
1719
     * const editorOptions: IColumnEditorOptions = {
1720
     *      dateTimeFormat: 'MM/dd/YYYY',
1721
     * }
1722
     * ```
1723
     * ```html
1724
     * <igx-column dataType="date" [editorOptions]="editorOptions"></igx-column>
1725
     * ```
1726
     * @memberof IgxColumnComponent
1727
     */
1728
    @notifyChanges()
1729
    @WatchColumnChanges()
1730
    @Input()
1731
    public set editorOptions(value: IColumnEditorOptions) {
UNCOV
1732
        this._editorOptions = value;
×
1733
    }
1734
    public get editorOptions(): IColumnEditorOptions {
UNCOV
1735
        return this._editorOptions;
×
1736
    }
1737

1738
    /**
1739
     * @hidden
1740
     * @internal
1741
     */
1742
    public get collapsible() {
1743
        return false;
×
1744
    }
1745
    public set collapsible(_value: boolean) { }
1746

1747
    /**
1748
     * @hidden
1749
     * @internal
1750
     */
1751
    public get expanded() {
1752
        return true;
×
1753
    }
1754
    public set expanded(_value: boolean) { }
1755

1756
    /**
1757
     * @hidden
1758
     */
1759
    public defaultWidth: string;
1760

1761
    /**
1762
     * @hidden
1763
     */
1764
    public widthSetByUser: boolean;
1765

1766
    /**
1767
     * @hidden
1768
     */
1769
    public hasNestedPath: boolean;
1770

1771
    /**
1772
     * @hidden
1773
     * @internal
1774
     */
1775
    public defaultTimeFormat = 'hh:mm:ss a';
3✔
1776

1777
    /**
1778
     * @hidden
1779
     * @internal
1780
     */
1781
    public defaultDateTimeFormat = 'dd/MM/yyyy HH:mm:ss a';
3✔
1782

1783

1784
    /**
1785
     * Returns the filteringExpressionsTree of the column.
1786
     * ```typescript
1787
     * let tree =  this.column.filteringExpressionsTree;
1788
     * ```
1789
     *
1790
     * @memberof IgxColumnComponent
1791
     */
1792
    public get filteringExpressionsTree(): FilteringExpressionsTree {
UNCOV
1793
        return ExpressionsTreeUtil.find(this.grid.filteringExpressionsTree, this.field) as FilteringExpressionsTree;
×
1794
    }
1795

1796
    /* alternateName: parentColumn */
1797
    /**
1798
     * Sets/gets the parent column.
1799
     * ```typescript
1800
     * let parentColumn = this.column.parent;
1801
     * ```
1802
     * ```typescript
1803
     * this.column.parent = higherLevelColumn;
1804
     * ```
1805
     */
1806
    public parent: ColumnType | null = null;
3✔
1807

1808
    /* blazorSuppress */
1809
    /**
1810
     * Sets/gets the children columns.
1811
     * ```typescript
1812
     * let columnChildren = this.column.children;
1813
     * ```
1814
     *
1815
     * @deprecated in version 18.1.0. Use the `childColumns` property instead.
1816
     */
1817
    public children: QueryList<IgxColumnComponent>;
1818
    /**
1819
     * @hidden
1820
     */
1821
    public destroy$ = new Subject<any>();
3✔
1822

1823
    /**
1824
     * @hidden
1825
     */
1826
    public widthConstrained = false;
3✔
1827

1828
    /**
1829
     * @hidden
1830
     */
1831
    protected _applySelectableClass = false;
3✔
1832

1833
    protected _vIndex = NaN;
3✔
1834
    protected _pinningPosition = null;
3✔
1835
    /**
1836
     * @hidden
1837
     */
1838
    protected _pinned = false;
3✔
1839
    /**
1840
     * @hidden
1841
     */
1842
    protected _bodyTemplate: TemplateRef<IgxCellTemplateContext>;
1843
    /**
1844
     * @hidden
1845
     */
1846
    protected _errorTemplate: TemplateRef<IgxCellTemplateContext>;
1847
    /**
1848
     * @hidden
1849
     */
1850
    protected _headerTemplate: TemplateRef<IgxColumnTemplateContext>;
1851
    /**
1852
     * @hidden
1853
     */
1854
    protected _summaryTemplate: TemplateRef<IgxSummaryTemplateContext>;
1855
    /**
1856
     * @hidden
1857
     */
1858
    protected _inlineEditorTemplate: TemplateRef<IgxCellTemplateContext>;
1859
    /**
1860
     * @hidden
1861
     */
1862
    protected _filterCellTemplate: TemplateRef<IgxColumnTemplateContext>;
1863
    /**
1864
     * @hidden
1865
     */
1866
    protected _summaries = null;
3✔
1867
    /**
1868
     * @hidden
1869
     */
1870
    private _disabledSummaries: string[] = [];
3✔
1871
    /**
1872
     * @hidden
1873
     */
1874
    protected _filters = null;
3✔
1875
    /**
1876
     * @hidden
1877
     */
1878
    protected _sortStrategy: ISortingStrategy = DefaultSortingStrategy.instance();
3✔
1879
    /**
1880
     * @hidden
1881
     */
1882
    protected _groupingComparer: (a: any, b: any, currRec?: any, groupRec?: any) => number;
1883

1884
    protected _mergingComparer: (prevRecord: any, record: any, field: string) => boolean;
1885
    /**
1886
     * @hidden
1887
     */
1888
    protected _hidden = false;
3✔
1889
    /**
1890
     * @hidden
1891
     */
1892
    protected _index: number;
1893
    /**
1894
     * @hidden
1895
     */
1896
    protected _disablePinning = false;
3✔
1897
    /**
1898
     * @hidden
1899
     */
1900
    protected _width: string;
1901
    /**
1902
     * @hidden
1903
     */
1904
    protected _defaultMinWidth = '';
3✔
1905
    /**
1906
     * @hidden
1907
     */
1908
    protected _maxWidth;
1909
    /**
1910
     * @hidden
1911
     */
1912
    protected _hasSummary = false;
3✔
1913
    /**
1914
     * @hidden
1915
     */
1916
    protected _editable: boolean;
1917
    /**
1918
     * @hidden
1919
     */
1920
    protected _groupable = false;
3✔
1921
    /**
1922
     * @hidden
1923
     */
1924
    protected _merge = false;
3✔
1925
    /**
1926
     *  @hidden
1927
     */
1928
    protected _visibleWhenCollapsed;
1929
    /**
1930
     * @hidden
1931
     */
1932
    protected _collapsible = false;
3✔
1933
    /**
1934
     * @hidden
1935
     */
1936
    protected _expanded = true;
3✔
1937
    /**
1938
     * @hidden
1939
     */
1940
    protected _selectable = true;
3✔
1941
    /**
1942
     * @hidden
1943
     */
1944
    protected get isPrimaryColumn(): boolean {
1945
        return this.field !== undefined && this.grid !== undefined && this.field === this.grid.primaryKey;
204✔
1946
    }
1947

1948
    private _field: string;
1949
    private _calcWidth = null;
3✔
1950
    private _columnPipeArgs: IColumnPipeArgs = { digitsInfo: DEFAULT_DIGITS_INFO };
3✔
1951
    private _editorOptions: IColumnEditorOptions = { };
3✔
1952

1953
    /**
1954
     * @hidden
1955
     * @internal
1956
     */
1957
    public resetCaches() {
1958
        this._vIndex = NaN;
21✔
1959
        if (this.grid) {
21✔
1960
            this.cacheCalcWidth();
21✔
1961
        }
1962
    }
1963

1964
    /**
1965
     * @hidden
1966
     */
1967
    public ngOnDestroy() {
1968
        this.destroy$.next(true);
3✔
1969
        this.destroy$.complete();
3✔
1970
    }
1971
    /**
1972
     * @hidden
1973
     */
1974
    public ngAfterContentInit(): void {
1975
        if (this.summaryTemplateDirective) {
3!
UNCOV
1976
            this._summaryTemplate = this.summaryTemplateDirective.template;
×
1977
        }
1978
        if (this.cellTemplate) {
3!
UNCOV
1979
            this._bodyTemplate = this.cellTemplate.template;
×
1980
        }
1981
        if (this.cellValidationErrorTemplate) {
3!
UNCOV
1982
            this._errorTemplate = this.cellValidationErrorTemplate.template;
×
1983
        }
1984
        if (this.headTemplate && this.headTemplate.length) {
3!
UNCOV
1985
            this._headerTemplate = this.headTemplate.toArray()[0].template;
×
1986
        }
1987
        if (this.editorTemplate) {
3!
UNCOV
1988
            this._inlineEditorTemplate = this.editorTemplate.template;
×
1989
        }
1990
        if (this.filterCellTemplateDirective) {
3!
1991
            this._filterCellTemplate = this.filterCellTemplateDirective.template;
×
1992
        }
1993
        if (!this._columnPipeArgs.format) {
3✔
1994
            this._columnPipeArgs.format = this.dataType === GridColumnDataType.Time ?
3!
1995
                DEFAULT_TIME_FORMAT : this.dataType === GridColumnDataType.DateTime ?
3!
1996
                    DEFAULT_DATE_TIME_FORMAT : DEFAULT_DATE_FORMAT;
1997
        }
1998
        if (!this.summaries) {
3!
UNCOV
1999
            switch (this.dataType) {
×
2000
                case GridColumnDataType.Number:
2001
                case GridColumnDataType.Currency:
2002
                case GridColumnDataType.Percent:
UNCOV
2003
                    this.summaries = IgxNumberSummaryOperand;
×
UNCOV
2004
                    break;
×
2005
                case GridColumnDataType.Date:
2006
                case GridColumnDataType.DateTime:
UNCOV
2007
                    this.summaries = IgxDateSummaryOperand;
×
UNCOV
2008
                    break;
×
2009
                case GridColumnDataType.Time:
UNCOV
2010
                    this.summaries = IgxTimeSummaryOperand;
×
UNCOV
2011
                    break;
×
2012

2013
                case GridColumnDataType.String:
2014
                case GridColumnDataType.Boolean:
2015
                default:
UNCOV
2016
                    this.summaries = IgxSummaryOperand;
×
UNCOV
2017
                    break;
×
2018
            }
2019
        }
2020
        if (!this.filters) {
3✔
2021
            switch (this.dataType) {
3!
2022
                case GridColumnDataType.Boolean:
UNCOV
2023
                    this.filters = IgxBooleanFilteringOperand.instance();
×
UNCOV
2024
                    break;
×
2025
                case GridColumnDataType.Number:
2026
                case GridColumnDataType.Currency:
2027
                case GridColumnDataType.Percent:
2028
                    this.filters = IgxNumberFilteringOperand.instance();
2✔
2029
                    break;
2✔
2030
                case GridColumnDataType.Date:
UNCOV
2031
                    this.filters = IgxDateFilteringOperand.instance();
×
UNCOV
2032
                    break;
×
2033
                case GridColumnDataType.Time:
UNCOV
2034
                    this.filters = IgxTimeFilteringOperand.instance();
×
UNCOV
2035
                    break;
×
2036
                case GridColumnDataType.DateTime:
UNCOV
2037
                    this.filters = IgxDateTimeFilteringOperand.instance();
×
UNCOV
2038
                    break;
×
2039
                case GridColumnDataType.Image:
UNCOV
2040
                    this.filterable = false;
×
UNCOV
2041
                    break;
×
2042
                case GridColumnDataType.String:
2043
                default:
2044
                    this.filters = IgxStringFilteringOperand.instance();
1✔
2045
                    break;
1✔
2046
            }
2047
        }
2048
    }
2049

2050
    /**
2051
     * @hidden
2052
     */
2053
    public getGridTemplate(isRow: boolean): string {
UNCOV
2054
        if (isRow) {
×
UNCOV
2055
            const rowsCount = this.grid.type !== 'pivot' ? this.grid.multiRowLayoutRowSize : this.children.length - 1;
×
UNCOV
2056
            return `repeat(${rowsCount},1fr)`;
×
2057
        } else {
UNCOV
2058
            return this.getColumnSizesString(this.children);
×
2059
        }
2060
    }
2061

2062
    /** @hidden @internal **/
2063
    public getInitialChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<MRLColumnSizeInfo> {
UNCOV
2064
        const columnSizes: MRLColumnSizeInfo[] = [];
×
2065
        // find the smallest col spans
UNCOV
2066
        children.forEach(col => {
×
UNCOV
2067
            if (!col.colStart) {
×
UNCOV
2068
                return;
×
2069
            }
UNCOV
2070
            const newWidthSet = col.widthSetByUser && columnSizes[col.colStart - 1] && !columnSizes[col.colStart - 1].widthSetByUser;
×
UNCOV
2071
            const newSpanSmaller = columnSizes[col.colStart - 1] && columnSizes[col.colStart - 1].colSpan > col.gridColumnSpan;
×
UNCOV
2072
            const bothWidthsSet = col.widthSetByUser && columnSizes[col.colStart - 1] && columnSizes[col.colStart - 1].widthSetByUser;
×
UNCOV
2073
            const bothWidthsNotSet = !col.widthSetByUser && columnSizes[col.colStart - 1] && !columnSizes[col.colStart - 1].widthSetByUser;
×
2074

UNCOV
2075
            if (columnSizes[col.colStart - 1] === undefined) {
×
2076
                // If nothing is defined yet take any column at first
2077
                // We use colEnd to know where the column actually ends, because not always it starts where we have it set in columnSizes.
UNCOV
2078
                columnSizes[col.colStart - 1] = {
×
2079
                    ref: col,
2080
                    width: col.width === 'fit-content' ? col.autoSize :
×
2081
                        col.widthSetByUser || this.grid.columnWidthSetByUser ? parseFloat(col.calcWidth) : null,
×
2082
                    colSpan: col.gridColumnSpan,
2083
                    colEnd: col.colStart + col.gridColumnSpan,
2084
                    widthSetByUser: col.widthSetByUser
2085
                };
UNCOV
2086
            } else if (newWidthSet || (newSpanSmaller && ((bothWidthsSet) || (bothWidthsNotSet)))) {
×
2087
                // If a column is set already it should either not have width defined or have width with bigger span than the new one.
2088

2089
                /**
2090
                 *  If replaced column has bigger span, we want to fill the remaining columns
2091
                 *  that the replacing column does not fill with the old one.
2092
                 */
UNCOV
2093
                if (bothWidthsSet && newSpanSmaller) {
×
2094
                    // Start from where the new column set would end and apply the old column to the rest depending on how much it spans.
2095
                    // We have not yet replaced it so we can use it directly from the columnSizes collection.
2096
                    // This is where colEnd is used because the colStart of the old column is not actually i + 1.
UNCOV
2097
                    for (let i = col.colStart - 1 + col.gridColumnSpan; i < columnSizes[col.colStart - 1].colEnd - 1; i++) {
×
UNCOV
2098
                        if (!columnSizes[i] || !columnSizes[i].widthSetByUser) {
×
UNCOV
2099
                            columnSizes[i] = columnSizes[col.colStart - 1];
×
2100
                        } else {
UNCOV
2101
                            break;
×
2102
                        }
2103
                    }
2104
                }
2105

2106
                // Replace the old column with the new one.
UNCOV
2107
                columnSizes[col.colStart - 1] = {
×
2108
                    ref: col,
2109
                    width: col.width === 'fit-content' ? col.autoSize :
×
2110
                        col.widthSetByUser || this.grid.columnWidthSetByUser ? parseFloat(col.calcWidth) : null,
×
2111
                    colSpan: col.gridColumnSpan,
2112
                    colEnd: col.colStart + col.gridColumnSpan,
2113
                    widthSetByUser: col.widthSetByUser
2114
                };
UNCOV
2115
            } else if (bothWidthsSet && columnSizes[col.colStart - 1].colSpan < col.gridColumnSpan) {
×
2116
                // If the column already in the columnSizes has smaller span, we still need to fill any empty places with the current col.
2117
                // Start from where the smaller column set would end and apply the bigger column to the rest depending on how much it spans.
2118
                // Since here we do not have it in columnSizes we set it as a new column keeping the same colSpan.
UNCOV
2119
                for (let i = col.colStart - 1 + columnSizes[col.colStart - 1].colSpan; i < col.colStart - 1 + col.gridColumnSpan; i++) {
×
UNCOV
2120
                    if (!columnSizes[i] || !columnSizes[i].widthSetByUser) {
×
UNCOV
2121
                        columnSizes[i] = {
×
2122
                            ref: col,
2123
                            width: col.width === 'fit-content' ? col.autoSize :
×
2124
                                col.widthSetByUser || this.grid.columnWidthSetByUser ? parseFloat(col.calcWidth) : null,
×
2125
                            colSpan: col.gridColumnSpan,
2126
                            colEnd: col.colStart + col.gridColumnSpan,
2127
                            widthSetByUser: col.widthSetByUser
2128
                        };
2129
                    } else {
UNCOV
2130
                        break;
×
2131
                    }
2132
                }
2133
            }
2134
        });
2135

2136
        // Flatten columnSizes so there are not columns with colSpan > 1
UNCOV
2137
        for (let i = 0; i < columnSizes.length; i++) {
×
UNCOV
2138
            if (columnSizes[i] && columnSizes[i].colSpan > 1) {
×
UNCOV
2139
                let j = 1;
×
2140

2141
                // Replace all empty places depending on how much the current column spans starting from next col.
UNCOV
2142
                for (; j < columnSizes[i].colSpan && i + j + 1 < columnSizes[i].colEnd; j++) {
×
UNCOV
2143
                    if (columnSizes[i + j] &&
×
2144
                        ((!columnSizes[i].width && columnSizes[i + j].width) ||
2145
                            (!columnSizes[i].width && !columnSizes[i + j].width && columnSizes[i + j].colSpan <= columnSizes[i].colSpan) ||
2146
                            (!!columnSizes[i + j].width && columnSizes[i + j].colSpan <= columnSizes[i].colSpan))) {
2147
                        // If we reach an already defined column that has width and the current doesn't have or
2148
                        // if the reached column has bigger colSpan we stop.
UNCOV
2149
                        break;
×
2150
                    } else {
UNCOV
2151
                        const width = columnSizes[i].widthSetByUser ?
×
2152
                            columnSizes[i].width / columnSizes[i].colSpan :
2153
                            columnSizes[i].width;
UNCOV
2154
                        columnSizes[i + j] = {
×
2155
                            ref: columnSizes[i].ref,
2156
                            width,
2157
                            colSpan: 1,
2158
                            colEnd: columnSizes[i].colEnd,
2159
                            widthSetByUser: columnSizes[i].widthSetByUser
2160
                        };
2161
                    }
2162
                }
2163

2164
                // Update the current column width so it is divided between all columns it spans and set it to 1.
UNCOV
2165
                columnSizes[i].width = columnSizes[i].widthSetByUser ?
×
2166
                    columnSizes[i].width / columnSizes[i].colSpan :
2167
                    columnSizes[i].width;
UNCOV
2168
                columnSizes[i].colSpan = 1;
×
2169

2170
                // Update the index based on how much we have replaced. Subtract 1 because we started from 1.
UNCOV
2171
                i += j - 1;
×
2172
            }
2173
        }
2174

UNCOV
2175
        return columnSizes;
×
2176
    }
2177

2178
    /** @hidden @internal **/
2179
    public getFilledChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<string> {
UNCOV
2180
        const columnSizes = this.getInitialChildColumnSizes(children);
×
2181

2182
        // fill the gaps if there are any
UNCOV
2183
        const result: string[] = [];
×
UNCOV
2184
        for (const size of columnSizes) {
×
UNCOV
2185
            if (size && !!size.width) {
×
UNCOV
2186
                result.push(size.width + 'px');
×
2187
            } else {
UNCOV
2188
                const currentWidth = parseFloat(this.grid.getPossibleColumnWidth());
×
UNCOV
2189
                result.push((this.getConstrainedSizePx(currentWidth)) + 'px');
×
2190
            }
2191
        }
UNCOV
2192
        return result;
×
2193
    }
2194

2195
    /** @hidden @internal **/
2196
    public getResizableColUnderEnd(): MRLResizeColumnInfo[] {
UNCOV
2197
        if (this.columnLayout || !this.columnLayoutChild || this.columnGroup) {
×
2198
            return [{ target: this, spanUsed: 1 }];
×
2199
        }
2200

UNCOV
2201
        const columnSized = this.getInitialChildColumnSizes(this.parent.children as QueryList<IgxColumnComponent>);
×
UNCOV
2202
        const targets: MRLResizeColumnInfo[] = [];
×
UNCOV
2203
        const colEnd = this.colEnd ? this.colEnd : this.colStart + 1;
×
2204

UNCOV
2205
        for (let i = 0; i < columnSized.length; i++) {
×
UNCOV
2206
            if (this.colStart <= i + 1 && i + 1 < colEnd) {
×
UNCOV
2207
                targets.push({ target: columnSized[i].ref, spanUsed: 1 });
×
2208
            }
2209
        }
2210

UNCOV
2211
        const targetsSquashed: MRLResizeColumnInfo[] = [];
×
UNCOV
2212
        for (const target of targets) {
×
UNCOV
2213
            if (targetsSquashed.length && targetsSquashed[targetsSquashed.length - 1].target.field === target.target.field) {
×
2214
                targetsSquashed[targetsSquashed.length - 1].spanUsed++;
×
2215
            } else {
UNCOV
2216
                targetsSquashed.push(target);
×
2217
            }
2218
        }
2219

UNCOV
2220
        return targetsSquashed;
×
2221
    }
2222

2223
    /**
2224
     * Pins the column in the specified position at the provided index in that pinned area.
2225
     * Defaults to index `0` if not provided, or to the initial index in the pinned area.
2226
     * Returns `true` if the column is successfully pinned. Returns `false` if the column cannot be pinned.
2227
     * Column cannot be pinned if:
2228
     * - Is already pinned
2229
     * - index argument is out of range
2230
     * ```typescript
2231
     * let success = this.column.pin();
2232
     * ```
2233
     *
2234
     * @memberof IgxColumnComponent
2235
     */
2236
    public pin(index?: number, pinningPosition?: ColumnPinningPosition): boolean {
2237
        // TODO: Probably should the return type of the old functions
2238
        // should be moved as a event parameter.
UNCOV
2239
        const grid = (this.grid as any);
×
UNCOV
2240
        if (this._pinned) {
×
UNCOV
2241
            return false;
×
2242
        }
2243

UNCOV
2244
        if (this.parent && !this.parent.pinned) {
×
UNCOV
2245
            return this.topLevelParent.pin(index, pinningPosition);
×
2246
        }
UNCOV
2247
        const targetPinPosition = pinningPosition !== null && pinningPosition !== undefined ?  pinningPosition : this.pinningPosition;
×
UNCOV
2248
        const pinningVisibleCollection = targetPinPosition === ColumnPinningPosition.Start ?
×
2249
        grid.pinnedStartColumns : grid.pinnedEndColumns;
UNCOV
2250
        const pinningCollection = targetPinPosition === ColumnPinningPosition.Start ?
×
2251
        grid._pinnedStartColumns : grid._pinnedEndColumns;
UNCOV
2252
        const hasIndex = index !== undefined && index !== null;
×
UNCOV
2253
        if (hasIndex && (index < 0 || index > pinningVisibleCollection.length)) {
×
UNCOV
2254
            return false;
×
2255
        }
2256

UNCOV
2257
        if (!this.parent && !this.pinnable) {
×
2258
            return false;
×
2259
        }
2260

UNCOV
2261
        const rootPinnedCols = pinningCollection.filter((c) => c.level === 0);
×
UNCOV
2262
        index = hasIndex ? index : rootPinnedCols.length;
×
UNCOV
2263
        const args: IPinColumnCancellableEventArgs = { column: this, insertAtIndex: index, isPinned: false, cancel: false };
×
UNCOV
2264
        this.grid.columnPin.emit(args);
×
2265

UNCOV
2266
        if (args.cancel) {
×
2267
            return;
×
2268
        }
2269

UNCOV
2270
        this.grid.crudService.endEdit(false);
×
2271

UNCOV
2272
        this._pinned = true;
×
UNCOV
2273
        if (pinningPosition !== null && pinningPosition !== undefined) {
×
2274
            // if user has set some position in the params, overwrite the column's position.
UNCOV
2275
            this._pinningPosition = pinningPosition;
×
2276
        }
2277

UNCOV
2278
        this.pinnedChange.emit(this._pinned);
×
2279
        // it is possible that index is the last position, so will need to find target column by [index-1]
UNCOV
2280
        const targetColumn = args.insertAtIndex === pinningCollection.length ?
×
2281
        pinningCollection[args.insertAtIndex - 1] : pinningCollection[args.insertAtIndex];
2282

UNCOV
2283
        if (pinningCollection.indexOf(this) === -1) {
×
UNCOV
2284
            if (!grid.hasColumnGroups) {
×
UNCOV
2285
                pinningCollection.splice(args.insertAtIndex, 0, this);
×
UNCOV
2286
                grid._pinnedColumns = grid._pinnedStartColumns.concat(grid._pinnedEndColumns);
×
2287
            } else {
2288
                // insert based only on root collection
UNCOV
2289
                if (this.level === 0) {
×
UNCOV
2290
                    rootPinnedCols.splice(args.insertAtIndex, 0, this);
×
2291
                }
UNCOV
2292
                let allPinned = [];
×
2293
                // FIX: this is duplicated on every step in the hierarchy....
2294
                // re-create hierarchy
UNCOV
2295
                rootPinnedCols.forEach(group => {
×
UNCOV
2296
                    allPinned.push(group);
×
UNCOV
2297
                    allPinned = allPinned.concat(group.allChildren);
×
2298
                });
UNCOV
2299
                grid._pinnedColumns = allPinned;
×
UNCOV
2300
                if (this.pinningPosition === ColumnPinningPosition.Start) {
×
UNCOV
2301
                    grid._pinnedStartColumns = allPinned;
×
2302
                } else {
UNCOV
2303
                    grid._pinnedEndColumns = allPinned;
×
2304
                }
2305
            }
2306

UNCOV
2307
            if (grid._unpinnedColumns.indexOf(this) !== -1) {
×
UNCOV
2308
                const childrenCount = this.allChildren.length;
×
UNCOV
2309
                grid._unpinnedColumns.splice(grid._unpinnedColumns.indexOf(this), 1 + childrenCount);
×
2310
            }
2311
        }
2312

UNCOV
2313
        if (hasIndex) {
×
UNCOV
2314
            index === pinningCollection.length - 1 ?
×
2315
                grid._moveColumns(this, targetColumn, DropPosition.AfterDropTarget) : grid._moveColumns(this, targetColumn, DropPosition.BeforeDropTarget);
2316
        }
2317

UNCOV
2318
        if (this.columnGroup) {
×
UNCOV
2319
            this.allChildren.forEach(child => child.pin(null, targetPinPosition));
×
UNCOV
2320
            grid.reinitPinStates();
×
2321
        }
2322

UNCOV
2323
        grid.resetCaches();
×
UNCOV
2324
        grid.notifyChanges();
×
UNCOV
2325
        if (this.columnLayoutChild) {
×
UNCOV
2326
            this.grid.columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
×
2327
        }
UNCOV
2328
        this.grid.filteringService.refreshExpressions();
×
UNCOV
2329
        const eventArgs: IPinColumnEventArgs = { column: this, insertAtIndex: index, isPinned: true };
×
UNCOV
2330
        this.grid.columnPinned.emit(eventArgs);
×
UNCOV
2331
        return true;
×
2332
    }
2333
    /**
2334
     * Unpins the column and place it at the provided index in the unpinned area.
2335
     * Defaults to index `0` if not provided, or to the initial index in the unpinned area.
2336
     * Returns `true` if the column is successfully unpinned. Returns `false` if the column cannot be unpinned.
2337
     * Column cannot be unpinned if:
2338
     * - Is already unpinned
2339
     * - index argument is out of range
2340
     * ```typescript
2341
     * let success = this.column.unpin();
2342
     * ```
2343
     *
2344
     * @memberof IgxColumnComponent
2345
     */
2346
    public unpin(index?: number): boolean {
UNCOV
2347
        const grid = (this.grid as any);
×
UNCOV
2348
        if (!this._pinned) {
×
UNCOV
2349
            return false;
×
2350
        }
2351

UNCOV
2352
        if (this.parent && this.parent.pinned) {
×
UNCOV
2353
            return this.topLevelParent.unpin(index);
×
2354
        }
UNCOV
2355
        const hasIndex = index !== undefined && index !== null;
×
UNCOV
2356
        if (hasIndex && (index < 0 || index > grid._unpinnedColumns.length)) {
×
UNCOV
2357
            return false;
×
2358
        }
2359

2360
        // estimate the exact index at which column will be inserted
2361
        // takes into account initial unpinned index of the column
UNCOV
2362
        if (!hasIndex) {
×
UNCOV
2363
            const indices = grid._unpinnedColumns.map(col => col.index);
×
UNCOV
2364
            indices.push(this.index);
×
UNCOV
2365
            indices.sort((a, b) => a - b);
×
UNCOV
2366
            index = indices.indexOf(this.index);
×
2367
        }
2368

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

UNCOV
2372
        if (args.cancel) {
×
2373
            return;
×
2374
        }
2375

UNCOV
2376
        this.grid.crudService.endEdit(false);
×
2377

UNCOV
2378
        this._pinned = false;
×
UNCOV
2379
        this.pinnedChange.emit(this._pinned);
×
2380

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

UNCOV
2385
        if (!hasIndex) {
×
UNCOV
2386
            grid._unpinnedColumns.splice(index, 0, this);
×
UNCOV
2387
            if (grid._pinnedColumns.indexOf(this) !== -1) {
×
UNCOV
2388
                grid._pinnedColumns.splice(grid._pinnedColumns.indexOf(this), 1);
×
2389
            }
UNCOV
2390
            if (this.pinningPosition === ColumnPinningPosition.Start && grid._pinnedStartColumns.indexOf(this) !== -1) {
×
UNCOV
2391
                grid._pinnedStartColumns.splice(grid._pinnedStartColumns.indexOf(this), 1);
×
2392
            }
UNCOV
2393
            if (this.pinningPosition === ColumnPinningPosition.End && grid._pinnedEndColumns.indexOf(this) !== -1) {
×
UNCOV
2394
                grid._pinnedEndColumns.splice(grid._pinnedEndColumns.indexOf(this), 1);
×
2395
            }
2396
        }
2397

UNCOV
2398
        if (hasIndex) {
×
UNCOV
2399
            grid.moveColumn(this, targetColumn);
×
2400
        }
2401

UNCOV
2402
        if (this.columnGroup) {
×
UNCOV
2403
            this.allChildren.forEach(child => child.unpin());
×
2404
        }
2405

UNCOV
2406
        grid.reinitPinStates();
×
UNCOV
2407
        grid.resetCaches();
×
2408

UNCOV
2409
        grid.notifyChanges();
×
UNCOV
2410
        if (this.columnLayoutChild) {
×
UNCOV
2411
            this.grid.columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
×
2412
        }
UNCOV
2413
        this.grid.filteringService.refreshExpressions();
×
2414

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

UNCOV
2417
        return true;
×
2418
    }
2419

2420
    /**
2421
     * Moves a column to the specified visible index.
2422
     * If passed index is invalid, or if column would receive a different visible index after moving, moving is not performed.
2423
     * If passed index would move the column to a different column group. moving is not performed.
2424
     *
2425
     * @example
2426
     * ```typescript
2427
     * column.move(index);
2428
     * ```
2429
     * @memberof IgxColumnComponent
2430
     */
2431
    public move(index: number) {
2432
        let target;
UNCOV
2433
        let columns = this.grid.columns.filter(c => c.visibleIndex > -1);
×
2434
        // grid last visible index
UNCOV
2435
        const li = columns.map(c => c.visibleIndex).reduce((a, b) => Math.max(a, b));
×
UNCOV
2436
        const parent = this.parent;
×
UNCOV
2437
        const isPreceding = this.visibleIndex < index;
×
2438

UNCOV
2439
        if (index === this.visibleIndex || index < 0 || index > li) {
×
UNCOV
2440
            return;
×
2441
        }
2442

UNCOV
2443
        if (parent) {
×
UNCOV
2444
            columns = columns.filter(c => c.level >= this.level && c !== this && c.parent !== this &&
×
2445
                c.topLevelParent === this.topLevelParent);
2446
        }
2447

2448
        // 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.
2449
        // 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.
2450

UNCOV
2451
        if (isPreceding) {
×
UNCOV
2452
            columns = columns.filter(c => c.visibleIndex > this.visibleIndex);
×
UNCOV
2453
            target = columns.find(c => c.level === this.level && c.visibleIndex + (c as any).calcChildren() - this.calcChildren() === index);
×
2454
        } else {
UNCOV
2455
            columns = columns.filter(c => c.visibleIndex < this.visibleIndex);
×
UNCOV
2456
            target = columns.find(c => c.level === this.level && c.visibleIndex === index);
×
2457
        }
2458

UNCOV
2459
        if (!target || (target.pinned && this.disablePinning)) {
×
UNCOV
2460
            return;
×
2461
        }
2462

UNCOV
2463
        const pos = isPreceding ? DropPosition.AfterDropTarget : DropPosition.BeforeDropTarget;
×
UNCOV
2464
        this.grid.moveColumn(this, target as IgxColumnComponent, pos);
×
2465
    }
2466

2467
    /**
2468
     * No children for the column, so will returns 1 or 0, if the column is hidden.
2469
     *
2470
     * @hidden
2471
     */
2472
    public calcChildren(): number {
UNCOV
2473
        const children = this.hidden ? 0 : 1;
×
UNCOV
2474
        return children;
×
2475
    }
2476

2477
    /**
2478
     * Toggles column vibisility and emits the respective event.
2479
     *
2480
     * @hidden
2481
     */
2482
    public toggleVisibility(value?: boolean) {
UNCOV
2483
        const newValue = value ?? !this.hidden;
×
UNCOV
2484
        const eventArgs: IColumnVisibilityChangingEventArgs = { column: this, newValue, cancel: false };
×
UNCOV
2485
        this.grid.columnVisibilityChanging.emit(eventArgs);
×
2486

UNCOV
2487
        if (eventArgs.cancel) {
×
2488
            return;
×
2489
        }
UNCOV
2490
        this.hidden = newValue;
×
UNCOV
2491
        this.grid.columnVisibilityChanged.emit({ column: this, newValue });
×
2492
    }
2493

2494
    /**
2495
     * Returns a reference to the top level parent column.
2496
     * ```typescript
2497
     * let topLevelParent =  this.column.topLevelParent;
2498
     * ```
2499
     */
2500
    public get topLevelParent(): ColumnType | undefined {
UNCOV
2501
        let parent = this.parent;
×
UNCOV
2502
        while (parent && parent.parent) {
×
UNCOV
2503
            parent = parent.parent;
×
2504
        }
UNCOV
2505
        return parent ?? undefined;
×
2506
    }
2507

2508
    /**
2509
     * @hidden @internal
2510
     */
2511
    public get headerCell(): IgxGridHeaderComponent {
UNCOV
2512
        return this.grid.headerCellList.find((header) => header.column === this);
×
2513
    }
2514

2515
    /**
2516
     * @hidden @internal
2517
     */
2518
    public get filterCell(): IgxGridFilteringCellComponent {
UNCOV
2519
        return this.grid.filterCellList.find((filterCell) => filterCell.column === this);
×
2520
    }
2521

2522
    /**
2523
     * @hidden @internal
2524
     */
2525
    public get headerGroup(): IgxGridHeaderGroupComponent {
UNCOV
2526
        return this.grid.headerGroupsList.find(group => group.column === this);
×
2527
    }
2528

2529
    /**
2530
     * Autosize the column to the longest currently visible cell value, including the header cell.
2531
     * ```typescript
2532
     * @ViewChild('grid') grid: IgxGridComponent;
2533
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2534
     * column.autosize();
2535
     * ```
2536
     *
2537
     * @memberof IgxColumnComponent
2538
     * @param byHeaderOnly Set if column should be autosized based only on the header content.
2539
     */
2540
    public autosize(byHeaderOnly = false) {
×
UNCOV
2541
        if (!this.columnGroup) {
×
UNCOV
2542
            this.width = this.getAutoSize(byHeaderOnly);
×
UNCOV
2543
            this.grid.reflow();
×
2544
        }
2545
    }
2546

2547
    /**
2548
     * @hidden
2549
     */
2550
    public getAutoSize(byHeader = false): string {
×
UNCOV
2551
        const size = !byHeader ? this.getLargestCellWidth() :
×
UNCOV
2552
            (Object.values(this.getHeaderCellWidths()).reduce((a, b) => a + b) + 'px');
×
UNCOV
2553
        const isPercentageWidth = this.width && typeof this.width === 'string' && this.width.indexOf('%') !== -1;
×
2554

2555
        let newWidth;
UNCOV
2556
        if (isPercentageWidth) {
×
UNCOV
2557
            const gridAvailableSize = this.grid.calcWidth;
×
UNCOV
2558
            const percentageSize = parseFloat(size) / gridAvailableSize * 100;
×
UNCOV
2559
            newWidth = percentageSize + '%';
×
2560
        } else {
UNCOV
2561
            newWidth = size;
×
2562
        }
2563

UNCOV
2564
        const maxWidth = isPercentageWidth ? this.maxWidthPercent : this.maxWidthPx;
×
UNCOV
2565
        const minWidth = isPercentageWidth ? this.minWidthPercent : this.minWidthPx;
×
UNCOV
2566
        if (this.maxWidth && (parseFloat(newWidth) > maxWidth)) {
×
UNCOV
2567
            newWidth = isPercentageWidth ? maxWidth + '%' : maxWidth + 'px';
×
UNCOV
2568
        } else if (parseFloat(newWidth) < minWidth) {
×
UNCOV
2569
            newWidth = isPercentageWidth ? minWidth + '%' : minWidth + 'px';
×
2570
        }
2571

UNCOV
2572
        return newWidth;
×
2573
    }
2574

2575
    /**
2576
     * @hidden
2577
     */
2578
    public getCalcWidth(): any {
2579
        if (this._calcWidth && !isNaN(this.calcPixelWidth)) {
3✔
2580
            return this._calcWidth;
3✔
2581
        }
UNCOV
2582
        this.cacheCalcWidth();
×
UNCOV
2583
        return this._calcWidth;
×
2584
    }
2585

2586

2587
    /**
2588
     * @hidden
2589
     * Returns the width and padding of a header cell.
2590
     */
2591
    public getHeaderCellWidths() {
UNCOV
2592
        return this.grid.getHeaderCellWidth(this.headerCell.nativeElement);
×
2593
    }
2594

2595
    /**
2596
     * @hidden
2597
     * Returns the size (in pixels) of the longest currently visible cell, including the header cell.
2598
     * ```typescript
2599
     * @ViewChild('grid') grid: IgxGridComponent;
2600
     *
2601
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2602
     * let size = column.getLargestCellWidth();
2603
     * ```
2604
     * @memberof IgxColumnComponent
2605
     */
2606
    public getLargestCellWidth(): string {
UNCOV
2607
        const range = this.grid.document.createRange();
×
UNCOV
2608
        const largest = new Map<number, number>();
×
2609

UNCOV
2610
        if (this._cells.length > 0) {
×
UNCOV
2611
            const cellsContentWidths = [];
×
UNCOV
2612
            this._cells.forEach((cell) => cellsContentWidths.push(cell.calculateSizeToFit(range)));
×
2613

UNCOV
2614
            const index = cellsContentWidths.indexOf(Math.max(...cellsContentWidths));
×
UNCOV
2615
            const cellStyle = this.grid.document.defaultView.getComputedStyle(this._cells[index].nativeElement);
×
UNCOV
2616
            const cellPadding = parseFloat(cellStyle.paddingLeft) + parseFloat(cellStyle.paddingRight) +
×
2617
                parseFloat(cellStyle.borderLeftWidth) + parseFloat(cellStyle.borderRightWidth);
2618

UNCOV
2619
            largest.set(Math.max(...cellsContentWidths), cellPadding);
×
2620
        }
2621

UNCOV
2622
        if (this.headerCell && this.autosizeHeader) {
×
UNCOV
2623
            const headerCellWidths = this.getHeaderCellWidths();
×
UNCOV
2624
            largest.set(headerCellWidths.width, headerCellWidths.padding);
×
2625
        }
2626

UNCOV
2627
        const largestCell = Math.max(...Array.from(largest.keys()));
×
UNCOV
2628
        const width = Math.ceil(largestCell + largest.get(largestCell));
×
2629

UNCOV
2630
        if (Number.isNaN(width)) {
×
2631
            return this.width;
×
2632
        } else {
UNCOV
2633
            return width + 'px';
×
2634
        }
2635
    }
2636

2637
    /**
2638
     * @hidden
2639
     */
2640
    public getCellWidth() {
2641
        const colWidth = this.width;
102✔
2642
        const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1;
102✔
2643

2644
        if (this.columnLayoutChild) {
102!
2645
            return '';
×
2646
        }
2647

2648
        if (colWidth && !isPercentageWidth) {
102!
2649

2650
            let cellWidth = colWidth;
102✔
2651
            if (typeof cellWidth !== 'string' || cellWidth.endsWith('px') === false) {
102!
UNCOV
2652
                cellWidth += 'px';
×
2653
            }
2654

2655
            return cellWidth;
102✔
2656
        } else {
UNCOV
2657
            return colWidth;
×
2658
        }
2659
    }
2660

2661
    /**
2662
     * @hidden
2663
     */
2664
    public populateVisibleIndexes() { }
2665

2666
    protected getColumnSizesString(children: QueryList<IgxColumnComponent>): string {
UNCOV
2667
        const res = this.getFilledChildColumnSizes(children);
×
UNCOV
2668
        return res.join(' ');
×
2669
    }
2670

2671
    /**
2672
     * @hidden
2673
     * @internal
2674
     */
2675
    public getConstrainedSizePx(newSize) {
2676
        if (this.maxWidth && newSize >= this.maxWidthPx) {
21!
UNCOV
2677
            this.widthConstrained = true;
×
UNCOV
2678
            return this.maxWidthPx;
×
2679
        } else if (this.minWidth && newSize <= this.userSetMinWidthPx) {
21!
UNCOV
2680
            this.widthConstrained = true;
×
UNCOV
2681
            return this.userSetMinWidthPx;
×
2682
        } else if (!this.minWidth && (!this.widthSetByUser || this.width === 'fit-content') && !this.grid.columnWidthSetByUser && (!newSize || newSize <= this.grid.minColumnWidth)) {
21!
2683
            return this.grid.minColumnWidth;
3✔
2684
        } else {
2685
            this.widthConstrained = false;
18✔
2686
            return newSize;
18✔
2687
        }
2688
    }
2689

2690
    /**
2691
     * @hidden
2692
     * @internal
2693
     */
2694
    protected cacheCalcWidth(): any {
2695
        const colWidth = this.width;
21✔
2696
        const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1;
21✔
2697
        const isAutoWidth = colWidth && typeof colWidth === 'string' && colWidth === 'fit-content';
21✔
2698
        if (isPercentageWidth && this.grid.isColumnWidthSum) {
21!
UNCOV
2699
            this._calcWidth = this.userSetMinWidthPx ? this.userSetMinWidthPx : this.grid.minColumnWidth;
×
2700
        } else if (isPercentageWidth) {
21!
UNCOV
2701
            const currentCalcWidth = parseFloat(colWidth) / 100 * this.grid.calcWidth;
×
UNCOV
2702
            this._calcWidth = this.grid.calcWidth ? this.getConstrainedSizePx(currentCalcWidth) : 0;
×
2703
        } else if (!colWidth || isAutoWidth && !this.autoSize) {
21!
2704
            // no width
2705
            const currentCalcWidth = this.defaultWidth || this.grid.getPossibleColumnWidth();
3✔
2706
            this._calcWidth = this.getConstrainedSizePx(parseFloat(currentCalcWidth));
3✔
2707
        } else {
2708
            let possibleColumnWidth = '';
18✔
2709
            if (!this.widthSetByUser && this.userSetMinWidthPx && this.userSetMinWidthPx < this.grid.minColumnWidth) {
18!
UNCOV
2710
                possibleColumnWidth = this.defaultWidth = this.grid.getPossibleColumnWidth();
×
2711
            } else {
2712
                possibleColumnWidth = this.width;
18✔
2713
            }
2714

2715
            const currentCalcWidth = parseFloat(possibleColumnWidth);
18✔
2716
            this._calcWidth = this.getConstrainedSizePx(currentCalcWidth);
18✔
2717
        }
2718
        this.calcPixelWidth = parseFloat(this._calcWidth);
21✔
2719
    }
2720

2721
    /**
2722
     * @hidden
2723
     * @internal
2724
     */
2725
    protected setExpandCollapseState() {
UNCOV
2726
        this.children.filter(col => (col.visibleWhenCollapsed !== undefined)).forEach(c => {
×
UNCOV
2727
            if (!this.collapsible) {
×
UNCOV
2728
                c.hidden = this.hidden; return;
×
2729
            }
UNCOV
2730
            c.hidden = this._expanded ? c.visibleWhenCollapsed : !c.visibleWhenCollapsed;
×
2731
        });
2732
    }
2733
    /**
2734
     * @hidden
2735
     * @internal
2736
     */
2737
    protected checkCollapsibleState() {
UNCOV
2738
        if (!this.children) {
×
2739
            return false;
×
2740
        }
UNCOV
2741
        const cols = this.children.map(child => child.visibleWhenCollapsed);
×
UNCOV
2742
        return (cols.some(c => c === true) && cols.some(c => c === false));
×
2743
    }
2744

2745
    /**
2746
     * @hidden
2747
     */
2748
    public get pinnable() {
UNCOV
2749
        return (this.grid as any)._init || !this.pinned;
×
2750
    }
2751

2752
    /**
2753
     * @hidden
2754
     */
2755
    public get applySelectableClass(): boolean {
UNCOV
2756
        return this._applySelectableClass;
×
2757
    }
2758

2759
    /**
2760
     * @hidden
2761
     */
2762
    public set applySelectableClass(value: boolean) {
UNCOV
2763
        if (this.selectable) {
×
UNCOV
2764
            this._applySelectableClass = value;
×
2765
        }
2766
    }
2767
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc