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

IgniteUI / igniteui-angular / 5472939379

pending completion
5472939379

push

github

web-flow
Merge pull request #13191 from IgniteUI/rkaraivanov/fix-13178-14

fix(grid): Non-destructive datetime sort operation

12071 of 14074 branches covered (85.77%)

2 of 2 new or added lines in 1 file covered. (100.0%)

25434 of 27588 relevant lines covered (92.19%)

32090.35 hits per line

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

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

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

64
/**
65
 * **Ignite UI for Angular Column** -
66
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid#columns-configuration)
67
 *
68
 * The Ignite UI Column is used within an `igx-grid` element to define what data the column will show. Features such as sorting,
69
 * filtering & editing are enabled at the column level.  You can also provide a template containing custom content inside
70
 * the column using `ng-template` which will be used for all cells within the column.
71
 */
72
@Component({
73
    changeDetection: ChangeDetectionStrategy.OnPush,
74
    selector: 'igx-column',
75
    template: ``
76
})
77
export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnType {
2✔
78
    /**
79
     * Sets/gets the `field` value.
80
     * ```typescript
81
     * let columnField = this.column.field;
82
     * ```
83
     * ```html
84
     * <igx-column [field] = "'ID'"></igx-column>
85
     * ```
86
     *
87
     * @memberof IgxColumnComponent
88
     */
89
    @Input()
90
    public set field(value: string) {
91
        this._field = value;
25,540✔
92
        this.hasNestedPath = value?.includes('.');
25,540✔
93
    }
94
    public get field(): string {
95
        return this._field;
18,618,421✔
96
    }
97

98

99
    /**
100
     * @hidden @internal
101
     */
102
    public validators: Validator[] = [];
26,583✔
103

104
    /**
105
     * Sets/gets the `header` value.
106
     * ```typescript
107
     * let columnHeader = this.column.header;
108
     * ```
109
     * ```html
110
     * <igx-column [header] = "'ID'"></igx-column>
111
     * ```
112
     *
113
     * @memberof IgxColumnComponent
114
     */
115
    @notifyChanges()
116
    @WatchColumnChanges()
117
    @Input()
118
    public header = '';
26,583✔
119
    /**
120
     * Sets/gets the `title` value.
121
     * ```typescript
122
     * let title = this.column.title;
123
     * ```
124
     * ```html
125
     * <igx-column [title] = "'Some column tooltip'"></igx-column>
126
     * ```
127
     *
128
     * @memberof IgxColumnComponent
129
     */
130
    @notifyChanges()
131
    @WatchColumnChanges()
132
    @Input()
133
    public title = '';
26,583✔
134
    /**
135
     * Sets/gets whether the column is sortable.
136
     * Default value is `false`.
137
     * ```typescript
138
     * let isSortable = this.column.sortable;
139
     * ```
140
     * ```html
141
     * <igx-column [sortable] = "true"></igx-column>
142
     * ```
143
     *
144
     * @memberof IgxColumnComponent
145
     */
146
    @WatchColumnChanges()
147
    @Input()
148
    public sortable = false;
26,583✔
149
    /**
150
     * Returns if the column is selectable.
151
     * ```typescript
152
     * let columnSelectable = this.column.selectable;
153
     * ```
154
     *
155
     * @memberof IgxColumnComponent
156
     */
157
    @WatchColumnChanges()
158
    @Input()
159
    public get selectable(): boolean {
2✔
160
        return this._selectable;
100,926✔
161
    }
162

163
    /**
164
     * Sets if the column is selectable.
165
     * Default value is `true`.
166
     * ```html
167
     * <igx-column [selectable] = "false"></igx-column>
168
     * ```
169
     *
170
     * @memberof IgxColumnComponent
171
     */
172
    public set selectable(value: boolean) {
173
        this._selectable = value;
1,457✔
174
    }
175

176
    /**
177
     * Sets/gets whether the column is groupable.
178
     * Default value is `false`.
179
     * ```typescript
180
     * let isGroupable = this.column.groupable;
181
     * ```
182
     * ```html
183
     * <igx-column [groupable] = "true"></igx-column>
184
     * ```
185
     *
186
     * @memberof IgxColumnComponent
187
     */
188
    @notifyChanges(true)
189
    @WatchColumnChanges()
190
    @Input()
191
    public get groupable(): boolean {
2✔
192
        return this._groupable;
138,307✔
193
    }
194
    public set groupable(value: boolean) {
195
        this._groupable = value;
3,193✔
196
        this.grid.groupablePipeTrigger++;
3,193✔
197
    }
198
    /**
199
     * Gets whether the column is editable.
200
     * Default value is `false`.
201
     * ```typescript
202
     * let isEditable = this.column.editable;
203
     * ```
204
     *
205
     * @memberof IgxColumnComponent
206
     */
207
    @WatchColumnChanges()
208
    @Input()
209
    public get editable(): boolean {
2✔
210
        // Updating the primary key when grid has transactions (incl. row edit)
211
        // should not be allowed, as that can corrupt transaction state.
212
        const rowEditable = this.grid && this.grid.rowEditable;
1,891,418✔
213
        const hasTransactions = this.grid && this.grid.transactions.enabled;
1,891,418✔
214

215
        if (this.isPrimaryColumn && (rowEditable || hasTransactions)) {
1,891,418✔
216
            return false;
79,029✔
217
        }
218

219
        if (this._editable !== undefined) {
1,812,389✔
220
            return this._editable;
276,554✔
221
        } else {
222
            return rowEditable;
1,535,835✔
223
        }
224
    }
225
    /**
226
     * Sets whether the column is editable.
227
     * ```typescript
228
     * this.column.editable = true;
229
     * ```
230
     * ```html
231
     * <igx-column [editable] = "true"></igx-column>
232
     * ```
233
     *
234
     * @memberof IgxColumnComponent
235
     */
236
    public set editable(editable: boolean) {
237
        this._editable = editable;
4,181✔
238
    }
239
    /**
240
     * Sets/gets whether the column is filterable.
241
     * Default value is `true`.
242
     * ```typescript
243
     * let isFilterable = this.column.filterable;
244
     * ```
245
     * ```html
246
     * <igx-column [filterable] = "false"></igx-column>
247
     * ```
248
     *
249
     * @memberof IgxColumnComponent
250
     */
251
    @notifyChanges()
252
    @WatchColumnChanges()
253
    @Input()
254
    public filterable = true;
26,583✔
255
    /**
256
     * Sets/gets whether the column is resizable.
257
     * Default value is `false`.
258
     * ```typescript
259
     * let isResizable = this.column.resizable;
260
     * ```
261
     * ```html
262
     * <igx-column [resizable] = "true"></igx-column>
263
     * ```
264
     *
265
     * @memberof IgxColumnComponent
266
     */
267
    @WatchColumnChanges()
268
    @Input()
269
    public resizable = false;
26,583✔
270

271
    /**
272
     * Sets/gets whether the column header is included in autosize logic.
273
     * Useful when template for a column header is sized based on parent, for example a default `div`.
274
     * Default value is `false`.
275
     * ```typescript
276
     * let isResizable = this.column.resizable;
277
     * ```
278
     * ```html
279
     * <igx-column [resizable] = "true"></igx-column>
280
     * ```
281
     *
282
     * @memberof IgxColumnComponent
283
     */
284
    @WatchColumnChanges()
285
    @Input()
286
    public autosizeHeader = true;
26,583✔
287

288
    /**
289
     * Gets a value indicating whether the summary for the column is enabled.
290
     * ```typescript
291
     * let hasSummary = this.column.hasSummary;
292
     * ```
293
     *
294
     * @memberof IgxColumnComponent
295
     */
296
    @notifyChanges(true)
297
    @WatchColumnChanges()
298
    @Input()
299
    public get hasSummary() {
2✔
300
        return this._hasSummary;
1,868,720✔
301
    }
302
    /**
303
     * Sets a value indicating whether the summary for the column is enabled.
304
     * Default value is `false`.
305
     * ```html
306
     * <igx-column [hasSummary] = "true"></igx-column>
307
     * ```
308
     *
309
     * @memberof IgxColumnComponent
310
     */
311
    public set hasSummary(value) {
312
        this._hasSummary = value;
3,101✔
313

314
        if (this.grid) {
3,101✔
315
            this.grid.summaryService.resetSummaryHeight();
3,101✔
316
        }
317
    }
318
    /**
319
     * Gets whether the column is hidden.
320
     * ```typescript
321
     * let isHidden = this.column.hidden;
322
     * ```
323
     *
324
     * @memberof IgxColumnComponent
325
     */
326
    @notifyChanges(true)
327
    @WatchColumnChanges()
328
    @Input()
329
    public get hidden(): boolean {
2✔
330
        return this._hidden;
1,128,995✔
331
    }
332
    /**
333
     * Sets the column hidden property.
334
     * Default value is `false`.
335
     * ```html
336
     * <igx-column [hidden] = "true"></igx-column>
337
     * ```
338
     *
339
     * Two-way data binding.
340
     * ```html
341
     * <igx-column [(hidden)] = "model.isHidden"></igx-column>
342
     * ```
343
     *
344
     * @memberof IgxColumnComponent
345
     */
346
    public set hidden(value: boolean) {
347
        if (this._hidden !== value) {
5,416✔
348
            this._hidden = value;
1,198✔
349
            this.hiddenChange.emit(this._hidden);
1,198✔
350
            if (this.columnLayoutChild && this.parent.hidden !== value) {
1,198✔
351
                this.parent.hidden = value;
5✔
352
                return;
5✔
353
            }
354
            if (this.grid) {
1,193✔
355
                this.grid.crudService.endEdit(false);
1,193✔
356
                this.grid.summaryService.resetSummaryHeight();
1,193✔
357
                this.grid.filteringService.refreshExpressions();
1,193✔
358
                this.grid.filteringService.hideFilteringRowOnColumnVisibilityChange(this);
1,193✔
359
                this.grid.notifyChanges();
1,193✔
360
            }
361
        }
362
    }
363

364
    /**
365
     * Returns if the column is selected.
366
     * ```typescript
367
     * let isSelected = this.column.selected;
368
     * ```
369
     *
370
     * @memberof IgxColumnComponent
371
     */
372
    public get selected(): boolean {
373
        return this.grid.selectionService.isColumnSelected(this.field);
1,341,063✔
374
    }
375

376
    /**
377
     * Select/deselect a column.
378
     * Default value is `false`.
379
     * ```typescript
380
     * this.column.selected = true;
381
     * ```
382
     *
383
     * @memberof IgxColumnComponent
384
     */
385
    public set selected(value: boolean) {
386
        if (this.selectable && value !== this.selected) {
46✔
387
            if (value) {
42✔
388
                this.grid.selectionService.selectColumnsWithNoEvent([this.field]);
37✔
389
            } else {
390
                this.grid.selectionService.deselectColumnsWithNoEvent([this.field]);
5✔
391
            }
392
            this.grid.notifyChanges();
42✔
393
        }
394
    }
395

396
    /**
397
     * @hidden
398
     */
399
    @Output()
400
    public hiddenChange = new EventEmitter<boolean>();
26,583✔
401

402
    /** @hidden */
403
    @Output()
404
    public expandedChange = new EventEmitter<boolean>();
26,583✔
405

406
    /** @hidden */
407
    @Output()
408
    public collapsibleChange = new EventEmitter<boolean>();
26,583✔
409
    /** @hidden */
410
    @Output()
411
    public visibleWhenCollapsedChange = new EventEmitter<boolean>();
26,583✔
412

413
    /** @hidden */
414
    @Output()
415
    public columnChange = new EventEmitter<void>();
26,583✔
416

417
    /**
418
     * Gets whether the hiding is disabled.
419
     * ```typescript
420
     * let isHidingDisabled =  this.column.disableHiding;
421
     * ```
422
     *
423
     * @memberof IgxColumnComponent
424
     */
425
    @notifyChanges()
426
    @WatchColumnChanges()
427
    @Input()
428
    public disableHiding = false;
26,583✔
429
    /**
430
     * Gets whether the pinning is disabled.
431
     * ```typescript
432
     * let isPinningDisabled =  this.column.disablePinning;
433
     * ```
434
     *
435
     * @memberof IgxColumnComponent
436
     */
437
    @notifyChanges()
438
    @WatchColumnChanges()
439
    @Input()
440
    public disablePinning = false;
26,583✔
441
    /**
442
     * @deprecated in version 13.1.0. Use `IgxGridComponent.moving` instead.
443
     *
444
     * Sets/gets whether the column is movable.
445
     * Default value is `false`.
446
     *
447
     * ```typescript
448
     * let isMovable = this.column.movable;
449
     * ```
450
     * ```html
451
     * <igx-column [movable] = "true"></igx-column>
452
     * ```
453
     *
454
     * @memberof IgxColumnComponent
455
     */
456
    @Input()
457
    public movable = false;
26,583✔
458
    /**
459
     * Gets the `width` of the column.
460
     * ```typescript
461
     * let columnWidth = this.column.width;
462
     * ```
463
     *
464
     * @memberof IgxColumnComponent
465
     */
466
    @notifyChanges(true)
467
    @WatchColumnChanges()
468
    @Input()
469
    public get width(): string {
2✔
470
        const isAutoWidth = this._width && typeof this._width === 'string' && this._width === 'auto';
4,993,548✔
471
        if (isAutoWidth) {
4,993,548✔
472
            if (!this.autoSize) {
12,285✔
473
                return 'fit-content';
4,529✔
474
            } else {
475
                return this.autoSize + 'px';
7,756✔
476
            }
477

478
        }
479
        return this.widthSetByUser ? this._width : this.defaultWidth;
4,981,263✔
480
    }
481

482
    public autoSize: number;
483

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

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

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

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

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

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

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

629
    /**
630
     * Sets conditional style properties on the column cells.
631
     * Similar to `ngStyle` it accepts an object literal where the keys are
632
     * the style properties and the value is the expression to be evaluated.
633
     * As with `cellClasses` it accepts a callback function.
634
     * ```typescript
635
     * styles = {
636
     *  background: 'royalblue',
637
     *  color: (rowData, columnKey, cellValue, rowIndex) => value.startsWith('Important') ? 'red': 'inherit'
638
     * }
639
     * ```
640
     * ```html
641
     * <igx-column [cellStyles]="styles"></igx-column>
642
     * ```
643
     *
644
     * @memberof IgxColumnComponent
645
     */
646
    @notifyChanges()
647
    @WatchColumnChanges()
648
    @Input()
649
    public cellStyles = null;
26,583✔
650
    /**
651
     * Applies display format to cell values in the column. Does not modify the underlying data.
652
     *
653
     * @remark
654
     * Note: As the formatter is used in places like the Excel style filtering dialog, in certain
655
     * scenarios (remote filtering for example), the row data argument can be `undefined`.
656
     *
657
     *
658
     * In this example, we check to see if the column name is Salary, and then provide a method as the column formatter
659
     * to format the value into a currency string.
660
     *
661
     * @example
662
     * ```typescript
663
     * columnInit(column: IgxColumnComponent) {
664
     *   if (column.field == "Salary") {
665
     *     column.formatter = (salary => this.format(salary));
666
     *   }
667
     * }
668
     *
669
     * format(value: number) : string {
670
     *   return formatCurrency(value, "en-us", "$");
671
     * }
672
     * ```
673
     *
674
     * @example
675
     * ```typescript
676
     * const column = this.grid.getColumnByName('Address');
677
     * const addressFormatter = (address: string, rowData: any) => data.privacyEnabled ? 'unknown' : address;
678
     * column.formatter = addressFormatter;
679
     * ```
680
     *
681
     * @memberof IgxColumnComponent
682
     */
683
    @notifyChanges()
684
    @WatchColumnChanges()
685
    @Input()
686
    public formatter: (value: any, rowData?: any) => any;
2✔
687

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

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

779
    /** @hidden */
780
    @Input()
781
    public collapsibleIndicatorTemplate: TemplateRef<any>;
782

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

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

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

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

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

853
    /**
854
     * @hidden
855
     */
856
    @Output()
857
    public widthChange = new EventEmitter<string>();
26,583✔
858

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

906
    public calcPixelWidth: number;
907

908
    /**
909
     * @hidden
910
     */
911
    public get maxWidthPx() {
912
        const gridAvailableSize = this.grid.calcWidth;
67,385✔
913
        const isPercentageWidth = this.maxWidth && typeof this.maxWidth === 'string' && this.maxWidth.indexOf('%') !== -1;
67,385✔
914
        return isPercentageWidth ? parseFloat(this.maxWidth) / 100 * gridAvailableSize : parseFloat(this.maxWidth);
67,385✔
915
    }
916

917
    /**
918
     * @hidden
919
     */
920
    public get maxWidthPercent() {
921
        const gridAvailableSize = this.grid.calcWidth;
17✔
922
        const isPercentageWidth = this.maxWidth && typeof this.maxWidth === 'string' && this.maxWidth.indexOf('%') !== -1;
17✔
923
        return isPercentageWidth ? parseFloat(this.maxWidth) : parseFloat(this.maxWidth) / gridAvailableSize * 100;
17✔
924
    }
925

926
    /**
927
     * @hidden
928
     */
929
    public get minWidthPx() {
930
        const gridAvailableSize = this.grid.calcWidth;
67,454✔
931
        const isPercentageWidth = this.minWidth && typeof this.minWidth === 'string' && this.minWidth.indexOf('%') !== -1;
67,454✔
932
        return isPercentageWidth ? parseFloat(this.minWidth) / 100 * gridAvailableSize : parseFloat(this.minWidth);
67,454✔
933
    }
934

935
    /**
936
     * @hidden
937
     */
938
    public get minWidthPercent() {
939
        const gridAvailableSize = this.grid.calcWidth;
17✔
940
        const isPercentageWidth = this.minWidth && typeof this.minWidth === 'string' && this.minWidth.indexOf('%') !== -1;
17✔
941
        return isPercentageWidth ? parseFloat(this.minWidth) : parseFloat(this.minWidth) / gridAvailableSize * 100;
17✔
942
    }
943

944

945
    /**
946
     * Sets/gets the minimum `width` of the column.
947
     * Default value is `88`;
948
     * ```typescript
949
     * let columnMinWidth = this.column.minWidth;
950
     * ```
951
     * ```html
952
     * <igx-column [minWidth] = "'100px'"></igx-column>
953
     * ```
954
     *
955
     * @memberof IgxColumnComponent
956
     */
957
    @notifyChanges()
958
    @WatchColumnChanges()
959
    @Input()
960
    public set minWidth(value: string) {
2✔
961
        const minVal = parseFloat(value);
1,341✔
962
        if (Number.isNaN(minVal)) {
1,341✔
963
            return;
34✔
964
        }
965
        this._defaultMinWidth = value;
1,307✔
966

967
    }
968
    public get minWidth(): string {
969
        return !this._defaultMinWidth ? this.defaultMinWidth : this._defaultMinWidth;
273,751✔
970
    }
971

972
    /**
973
     * Gets the column index.
974
     * ```typescript
975
     * let columnIndex = this.column.index;
976
     * ```
977
     *
978
     * @memberof IgxColumnComponent
979
     */
980
    public get index(): number {
981
        return (this.grid as any)._columns.indexOf(this);
202,524✔
982
    }
983

984
    /**
985
     * Gets whether the column is `pinned`.
986
     * ```typescript
987
     * let isPinned = this.column.pinned;
988
     * ```
989
     *
990
     * @memberof IgxColumnComponent
991
     */
992
    @WatchColumnChanges()
993
    @Input()
994
    public get pinned(): boolean {
2✔
995
        return this._pinned;
1,875,547✔
996
    }
997
    /**
998
     * Sets whether the column is pinned.
999
     * Default value is `false`.
1000
     * ```html
1001
     * <igx-column [pinned] = "true"></igx-column>
1002
     * ```
1003
     *
1004
     * Two-way data binding.
1005
     * ```html
1006
     * <igx-column [(pinned)] = "model.columns[0].isPinned"></igx-column>
1007
     * ```
1008
     *
1009
     * @memberof IgxColumnComponent
1010
     */
1011
    public set pinned(value: boolean) {
1012
        if (this._pinned !== value) {
2,421✔
1013
            const isAutoWidth = this.width && typeof this.width === 'string' && this.width === 'fit-content';
838✔
1014
            if (this.grid && this.width && (isAutoWidth || !isNaN(parseInt(this.width, 10)))) {
838✔
1015
                if (value) {
608✔
1016
                    this.pin();
427✔
1017
                } else {
1018
                    this.unpin();
181✔
1019
                }
1020
                return;
608✔
1021
            }
1022
            /* No grid/width available at initialization. `initPinning` in the grid
1023
               will re-init the group (if present)
1024
            */
1025
            this._pinned = value;
230✔
1026
            this.pinnedChange.emit(this._pinned);
230✔
1027
        }
1028
    }
1029

1030
    /**
1031
     * Gets the column `summaries`.
1032
     * ```typescript
1033
     * let columnSummaries = this.column.summaries;
1034
     * ```
1035
     *
1036
     * @memberof IgxColumnComponent
1037
     */
1038
    @notifyChanges(true)
1039
    @WatchColumnChanges()
1040
    @Input()
1041
    public get summaries(): any {
2✔
1042
        return this._summaries;
54,976✔
1043
    }
1044
    /**
1045
     * Sets the column `summaries`.
1046
     * ```typescript
1047
     * this.column.summaries = IgxNumberSummaryOperand;
1048
     * ```
1049
     *
1050
     * @memberof IgxColumnComponent
1051
     */
1052
    public set summaries(classRef: any) {
1053
        if (isConstructor(classRef)) {
22,577✔
1054
            this._summaries = new classRef();
22,577✔
1055
        }
1056

1057
        if (this.grid) {
22,577✔
1058
            this.grid.summaryService.removeSummariesCachePerColumn(this.field);
22,577✔
1059
            this.grid.summaryPipeTrigger++;
22,577✔
1060
            this.grid.summaryService.resetSummaryHeight();
22,577✔
1061
        }
1062
    }
1063
    /**
1064
     * Gets the column `filters`.
1065
     * ```typescript
1066
     * let columnFilters = this.column.filters'
1067
     * ```
1068
     *
1069
     * @memberof IgxColumnComponent
1070
     */
1071
    @Input()
1072
    public get filters(): IgxFilteringOperand {
1073
        return this._filters;
93,037✔
1074
    }
1075
    /**
1076
     * Sets the column `filters`.
1077
     * ```typescript
1078
     * this.column.filters = IgxBooleanFilteringOperand.instance().
1079
     * ```
1080
     *
1081
     * @memberof IgxColumnComponent
1082
     */
1083
    public set filters(instance: IgxFilteringOperand) {
1084
        this._filters = instance;
22,571✔
1085
    }
1086
    /**
1087
     * Gets the column `sortStrategy`.
1088
     * ```typescript
1089
     * let sortStrategy = this.column.sortStrategy
1090
     * ```
1091
     *
1092
     * @memberof IgxColumnComponent
1093
     */
1094
    @Input()
1095
    public get sortStrategy(): ISortingStrategy {
1096
        return this._sortStrategy;
2,596✔
1097
    }
1098
    /**
1099
     * Sets the column `sortStrategy`.
1100
     * ```typescript
1101
     * this.column.sortStrategy = new CustomSortingStrategy().
1102
     * class CustomSortingStrategy extends SortingStrategy {...}
1103
     * ```
1104
     *
1105
     * @memberof IgxColumnComponent
1106
     */
1107
    public set sortStrategy(classRef: ISortingStrategy) {
1108
        this._sortStrategy = classRef;
1,286✔
1109
    }
1110
    /**
1111
     * Gets the function that compares values for grouping.
1112
     * ```typescript
1113
     * let groupingComparer = this.column.groupingComparer'
1114
     * ```
1115
     *
1116
     * @memberof IgxColumnComponent
1117
     */
1118
    @Input()
1119
    public get groupingComparer(): (a: any, b: any, currRec?: any, groupRec?: any) => number {
1120
        return this._groupingComparer;
3,003✔
1121
    }
1122
    /**
1123
     * Sets a custom function to compare values for grouping.
1124
     * Subsequent values in the sorted data that the function returns 0 for are grouped.
1125
     * ```typescript
1126
     * this.column.groupingComparer = (a: any, b: any, currRec?: any, groupRec?: any) => { return a === b ? 0 : -1; }
1127
     * ```
1128
     *
1129
     * @memberof IgxColumnComponent
1130
     */
1131
    public set groupingComparer(funcRef: (a: any, b: any, currRec?: any, groupRec?: any) => number) {
1132
        this._groupingComparer = funcRef;
1,288✔
1133
    }
1134
    /**
1135
     * Gets the default minimum `width` of the column.
1136
     * ```typescript
1137
     * let defaultMinWidth =  this.column.defaultMinWidth;
1138
     * ```
1139
     *
1140
     * @memberof IgxColumnComponent
1141
     */
1142
    public get defaultMinWidth(): string {
1143
        if (!this.grid) {
254,251!
1144
            return '80';
×
1145
        }
1146
        switch (this.grid.displayDensity) {
254,251✔
1147
            case DisplayDensity.cosy:
1148
                return '64';
589✔
1149
            case DisplayDensity.compact:
1150
                return '56';
1,129✔
1151
            default:
1152
                return '80';
252,533✔
1153
        }
1154
    }
1155
    /**
1156
     * Returns a reference to the `summaryTemplate`.
1157
     * ```typescript
1158
     * let summaryTemplate = this.column.summaryTemplate;
1159
     * ```
1160
     *
1161
     * @memberof IgxColumnComponent
1162
     */
1163
    @notifyChanges()
1164
    @WatchColumnChanges()
1165
    @Input()
1166
    public get summaryTemplate(): TemplateRef<any> {
2✔
1167
        return this._summaryTemplate;
63,760✔
1168
    }
1169
    /**
1170
     * Sets the summary template.
1171
     * ```html
1172
     * <ng-template #summaryTemplate igxSummary let-summaryResults>
1173
     *    <p>{{ summaryResults[0].label }}: {{ summaryResults[0].summaryResult }}</p>
1174
     *    <p>{{ summaryResults[1].label }}: {{ summaryResults[1].summaryResult }}</p>
1175
     * </ng-template>
1176
     * ```
1177
     * ```typescript
1178
     * @ViewChild("'summaryTemplate'", {read: TemplateRef })
1179
     * public summaryTemplate: TemplateRef<any>;
1180
     * this.column.summaryTemplate = this.summaryTemplate;
1181
     * ```
1182
     *
1183
     * @memberof IgxColumnComponent
1184
     */
1185
    public set summaryTemplate(template: TemplateRef<any>) {
1186
        this._summaryTemplate = template;
1,291✔
1187
    }
1188

1189
    /**
1190
     * Returns a reference to the `bodyTemplate`.
1191
     * ```typescript
1192
     * let bodyTemplate = this.column.bodyTemplate;
1193
     * ```
1194
     *
1195
     * @memberof IgxColumnComponent
1196
     */
1197
    @notifyChanges()
1198
    @WatchColumnChanges()
1199
    @Input('cellTemplate')
1200
    public get bodyTemplate(): TemplateRef<any> {
2✔
1201
        return this._bodyTemplate;
944,983✔
1202
    }
1203
    /**
1204
     * Sets the body template.
1205
     * ```html
1206
     * <ng-template #bodyTemplate igxCell let-val>
1207
     *    <div style = "background-color: yellowgreen" (click) = "changeColor(val)">
1208
     *       <span> {{val}} </span>
1209
     *    </div>
1210
     * </ng-template>
1211
     * ```
1212
     * ```typescript
1213
     * @ViewChild("'bodyTemplate'", {read: TemplateRef })
1214
     * public bodyTemplate: TemplateRef<any>;
1215
     * this.column.bodyTemplate = this.bodyTemplate;
1216
     * ```
1217
     *
1218
     * @memberof IgxColumnComponent
1219
     */
1220
    public set bodyTemplate(template: TemplateRef<any>) {
1221
        this._bodyTemplate = template;
1,254✔
1222
    }
1223
    /**
1224
     * Returns a reference to the header template.
1225
     * ```typescript
1226
     * let headerTemplate = this.column.headerTemplate;
1227
     * ```
1228
     *
1229
     * @memberof IgxColumnComponent
1230
     */
1231
    @notifyChanges()
1232
    @WatchColumnChanges()
1233
    @Input()
1234
    public get headerTemplate(): TemplateRef<any> {
2✔
1235
        return this._headerTemplate;
209,339✔
1236
    }
1237
    /**
1238
     * Sets the header template.
1239
     * Note that the column header height is fixed and any content bigger than it will be cut off.
1240
     * ```html
1241
     * <ng-template #headerTemplate>
1242
     *   <div style = "background-color:black" (click) = "changeColor(val)">
1243
     *       <span style="color:red" >{{column.field}}</span>
1244
     *   </div>
1245
     * </ng-template>
1246
     * ```
1247
     * ```typescript
1248
     * @ViewChild("'headerTemplate'", {read: TemplateRef })
1249
     * public headerTemplate: TemplateRef<any>;
1250
     * this.column.headerTemplate = this.headerTemplate;
1251
     * ```
1252
     *
1253
     * @memberof IgxColumnComponent
1254
     */
1255
    public set headerTemplate(template: TemplateRef<any>) {
1256
        this._headerTemplate = template;
3,342✔
1257
    }
1258
    /**
1259
     * Returns a reference to the inline editor template.
1260
     * ```typescript
1261
     * let inlineEditorTemplate = this.column.inlineEditorTemplate;
1262
     * ```
1263
     *
1264
     * @memberof IgxColumnComponent
1265
     */
1266
    @notifyChanges()
1267
    @WatchColumnChanges()
1268
    @Input('cellEditorTemplate')
1269
    public get inlineEditorTemplate(): TemplateRef<any> {
2✔
1270
        return this._inlineEditorTemplate;
4,989✔
1271
    }
1272
    /**
1273
     * Sets the inline editor template.
1274
     * ```html
1275
     * <ng-template #inlineEditorTemplate igxCellEditor let-cell="cell">
1276
     *     <input type="string" [(ngModel)]="cell.value"/>
1277
     * </ng-template>
1278
     * ```
1279
     * ```typescript
1280
     * @ViewChild("'inlineEditorTemplate'", {read: TemplateRef })
1281
     * public inlineEditorTemplate: TemplateRef<any>;
1282
     * this.column.inlineEditorTemplate = this.inlineEditorTemplate;
1283
     * ```
1284
     *
1285
     * @memberof IgxColumnComponent
1286
     */
1287
    public set inlineEditorTemplate(template: TemplateRef<any>) {
1288
        this._inlineEditorTemplate = template;
1,254✔
1289
    }
1290

1291
    /**
1292
     * Returns a reference to the validation error template.
1293
     * ```typescript
1294
     * let errorTemplate = this.column.errorTemplate;
1295
     * ```
1296
     */
1297
    @notifyChanges()
1298
    @WatchColumnChanges()
1299
    @Input('errorTemplate')
1300
    public get errorTemplate(): TemplateRef<any> {
2✔
1301
        return this._errorTemplate;
906,906✔
1302
    }
1303
    /**
1304
     * Sets the error template.
1305
     * ```html
1306
     * <ng-template igxCellValidationError let-cell="cell" #errorTemplate >
1307
     *     <div *ngIf="cell.validation.errors?.['forbiddenName']">
1308
     *      This name is forbidden.
1309
     *     </div>
1310
     * </ng-template>
1311
     * ```
1312
     * ```typescript
1313
     * @ViewChild("'errorTemplate'", {read: TemplateRef })
1314
     * public errorTemplate: TemplateRef<any>;
1315
     * this.column.errorTemplate = this.errorTemplate;
1316
     * ```
1317
     */
1318
    public set errorTemplate(template: TemplateRef<any>) {
1319
        this._errorTemplate = template;
1,285✔
1320
    }
1321

1322
    /**
1323
     * Returns a reference to the `filterCellTemplate`.
1324
     * ```typescript
1325
     * let filterCellTemplate = this.column.filterCellTemplate;
1326
     * ```
1327
     *
1328
     * @memberof IgxColumnComponent
1329
     */
1330
    @notifyChanges()
1331
    @WatchColumnChanges()
1332
    @Input('filterCellTemplate')
1333
    public get filterCellTemplate(): TemplateRef<any> {
2✔
1334
        return this._filterCellTemplate;
32,654✔
1335
    }
1336
    /**
1337
     * Sets the quick filter template.
1338
     * ```html
1339
     * <ng-template #filterCellTemplate IgxFilterCellTemplate let-column="column">
1340
     *    <input (input)="onInput()">
1341
     * </ng-template>
1342
     * ```
1343
     * ```typescript
1344
     * @ViewChild("'filterCellTemplate'", {read: TemplateRef })
1345
     * public filterCellTemplate: TemplateRef<any>;
1346
     * this.column.filterCellTemplate = this.filterCellTemplate;
1347
     * ```
1348
     *
1349
     * @memberof IgxColumnComponent
1350
     */
1351
    public set filterCellTemplate(template: TemplateRef<any>) {
1352
        this._filterCellTemplate = template;
1,293✔
1353
    }
1354

1355
    /**
1356
     * Gets the cells of the column.
1357
     * ```typescript
1358
     * let columnCells = this.column.cells;
1359
     * ```
1360
     *
1361
     */
1362
    public get cells(): CellType[] {
1363
        return this.grid.dataView
23✔
1364
            .map((rec, index) => {
1365
                if (!this.grid.isGroupByRecord(rec) && !this.grid.isSummaryRow(rec)) {
1,273✔
1366
                    this.grid.pagingMode === 1 && this.grid.paginator.page !== 0 ? index = index + this.grid.paginator.perPage * this.grid.paginator.page : index = this.grid.dataRowList.first.index + index;
1,273!
1367
                    const cell = new IgxGridCell(this.grid as any, index, this.field);
1,273✔
1368
                    return cell;
1,273✔
1369
                }
1370
            }).filter(cell => cell);
1,273✔
1371
    }
1372

1373

1374
    /**
1375
     * @hidden @internal
1376
     */
1377
    public get _cells(): CellType[] {
1378
        return this.grid.rowList.filter((row) => row instanceof IgxRowDirective)
2,565✔
1379
            .map((row) => {
1380
                if (row._cells) {
2,565✔
1381
                    return row._cells.filter((cell) => cell.columnIndex === this.index);
14,227✔
1382
                }
1383
            }).reduce((a, b) => a.concat(b), []);
2,565✔
1384
    }
1385

1386
    /**
1387
     * Gets the column visible index.
1388
     * If the column is not visible, returns `-1`.
1389
     * ```typescript
1390
     * let visibleColumnIndex =  this.column.visibleIndex;
1391
     * ```
1392
     *
1393
     * @memberof IgxColumnComponent
1394
     */
1395
    public get visibleIndex(): number {
1396
        if (!isNaN(this._vIndex)) {
4,276,170✔
1397
            return this._vIndex;
3,891,360✔
1398
        }
1399
        const unpinnedColumns = this.grid.unpinnedColumns.filter(c => !c.columnGroup);
3,873,864✔
1400
        const pinnedColumns = this.grid.pinnedColumns.filter(c => !c.columnGroup);
384,810✔
1401
        let col = this;
384,810✔
1402
        let vIndex = -1;
384,810✔
1403

1404
        if (this.columnGroup) {
384,810✔
1405
            col = this.allChildren.filter(c => !c.columnGroup && !c.hidden)[0] as any;
23,705✔
1406
        }
1407
        if (this.columnLayoutChild) {
384,810✔
1408
            return this.parent.childrenVisibleIndexes.find(x => x.column === this).index;
696,181✔
1409
        }
1410

1411
        if (!this.pinned) {
117,342✔
1412
            const indexInCollection = unpinnedColumns.indexOf(col);
114,935✔
1413
            vIndex = indexInCollection === -1 ?
114,935✔
1414
                -1 :
1415
                (this.grid.isPinningToStart ?
114,575✔
1416
                    pinnedColumns.length + indexInCollection :
1417
                    indexInCollection);
1418
        } else {
1419
            const indexInCollection = pinnedColumns.indexOf(col);
2,407✔
1420
            vIndex = this.grid.isPinningToStart ?
2,407✔
1421
                indexInCollection :
1422
                unpinnedColumns.length + indexInCollection;
1423
        }
1424
        this._vIndex = vIndex;
117,342✔
1425
        return vIndex;
117,342✔
1426
    }
1427
    /**
1428
     * Returns a boolean indicating if the column is a `ColumnGroup`.
1429
     * ```typescript
1430
     * let columnGroup =  this.column.columnGroup;
1431
     * ```
1432
     *
1433
     * @memberof IgxColumnComponent
1434
     */
1435
    public get columnGroup() {
1436
        return false;
7,223,789✔
1437
    }
1438
    /**
1439
     * Returns a boolean indicating if the column is a `ColumnLayout` for multi-row layout.
1440
     * ```typescript
1441
     * let columnGroup =  this.column.columnGroup;
1442
     * ```
1443
     *
1444
     * @memberof IgxColumnComponent
1445
     */
1446
    public get columnLayout() {
1447
        return false;
16,960,295✔
1448
    }
1449

1450
    /**
1451
     * Returns a boolean indicating if the column is a child of a `ColumnLayout` for multi-row layout.
1452
     * ```typescript
1453
     * let columnLayoutChild =  this.column.columnLayoutChild;
1454
     * ```
1455
     *
1456
     * @memberof IgxColumnComponent
1457
     */
1458
    public get columnLayoutChild() {
1459
        return this.parent && this.parent.columnLayout;
10,248,604✔
1460
    }
1461

1462
    /**
1463
     * Returns the children columns collection.
1464
     * Returns an empty array if the column does not contain children columns.
1465
     * ```typescript
1466
     * let childrenColumns =  this.column.allChildren;
1467
     * ```
1468
     *
1469
     * @memberof IgxColumnComponent
1470
     */
1471
    public get allChildren(): IgxColumnComponent[] {
1472
        return [];
355✔
1473
    }
1474
    /**
1475
     * Returns the level of the column in a column group.
1476
     * Returns `0` if the column doesn't have a `parent`.
1477
     * ```typescript
1478
     * let columnLevel =  this.column.level;
1479
     * ```
1480
     *
1481
     * @memberof IgxColumnComponent
1482
     */
1483
    public get level() {
1484
        let ptr = this.parent;
572,017✔
1485
        let lvl = 0;
572,017✔
1486

1487
        while (ptr) {
572,017✔
1488
            lvl++;
180,863✔
1489
            ptr = ptr.parent;
180,863✔
1490
        }
1491
        return lvl;
572,017✔
1492
    }
1493

1494
    public get isLastPinned(): boolean {
1495
        return this.grid.isPinningToStart &&
843,887✔
1496
            this.grid.pinnedColumns[this.grid.pinnedColumns.length - 1] === this;
1497
    }
1498

1499
    public get isFirstPinned(): boolean {
1500
        const pinnedCols = this.grid.pinnedColumns.filter(x => !x.columnGroup);
775,834✔
1501
        return !this.grid.isPinningToStart && pinnedCols[0] === this;
775,834✔
1502
    }
1503

1504
    public get rightPinnedOffset(): string {
1505
        return this.pinned && !this.grid.isPinningToStart ?
545,462✔
1506
            - this.grid.pinnedWidth - this.grid.headerFeaturesWidth + 'px' :
1507
            null;
1508
    }
1509

1510
    public get gridRowSpan(): number {
1511
        return this.rowEnd && this.rowStart ? this.rowEnd - this.rowStart : 1;
1,270✔
1512
    }
1513
    public get gridColumnSpan(): number {
1514
        return this.colEnd && this.colStart ? this.colEnd - this.colStart : 1;
593,811✔
1515
    }
1516

1517
    /**
1518
     * Indicates whether the column will be visible when its parent is collapsed.
1519
     * ```html
1520
     * <igx-column-group>
1521
     *   <igx-column [visibleWhenCollapsed]="true"></igx-column>
1522
     * </igx-column-group>
1523
     * ```
1524
     *
1525
     * @memberof IgxColumnComponent
1526
     */
1527
    @notifyChanges(true)
1528
    @Input()
1529
    public set visibleWhenCollapsed(value: boolean) {
2✔
1530
        this._visibleWhenCollapsed = value;
1,596✔
1531
        this.visibleWhenCollapsedChange.emit(this._visibleWhenCollapsed);
1,596✔
1532
        if (this.parent) {
1,596✔
1533
            this.parent.setExpandCollapseState();
19✔
1534
        }
1535
    }
1536

1537
    public get visibleWhenCollapsed(): boolean {
1538
        return this._visibleWhenCollapsed;
8,024✔
1539
    }
1540

1541
    /**
1542
     * @remarks
1543
     * Pass optional parameters for DatePipe and/or DecimalPipe to format the display value for date and numeric columns.
1544
     * Accepts an `IColumnPipeArgs` object with any of the `format`, `timezone` and `digitsInfo` properties.
1545
     * For more details see https://angular.io/api/common/DatePipe and https://angular.io/api/common/DecimalPipe
1546
     * @example
1547
     * ```typescript
1548
     * const pipeArgs: IColumnPipeArgs = {
1549
     *      format: 'longDate',
1550
     *      timezone: 'UTC',
1551
     *      digitsInfo: '1.1-2'
1552
     * }
1553
     * ```
1554
     * ```html
1555
     * <igx-column dataType="date" [pipeArgs]="pipeArgs"></igx-column>
1556
     * <igx-column dataType="number" [pipeArgs]="pipeArgs"></igx-column>
1557
     * ```
1558
     * @memberof IgxColumnComponent
1559
     */
1560
    @notifyChanges()
1561
    @WatchColumnChanges()
1562
    @Input()
1563
    public set pipeArgs(value: IColumnPipeArgs) {
2✔
1564
        this._columnPipeArgs = Object.assign(this._columnPipeArgs, value);
1,308✔
1565
        this.grid.summaryService.clearSummaryCache();
1,308✔
1566
        this.grid.pipeTrigger++;
1,308✔
1567
    }
1568
    public get pipeArgs(): IColumnPipeArgs {
1569
        return this._columnPipeArgs;
1,360,155✔
1570
    }
1571

1572
    /**
1573
     * @hidden
1574
     * @internal
1575
     */
1576
    public get collapsible() {
1577
        return false;
×
1578
    }
1579
    public set collapsible(_value: boolean) { }
1580

1581
    /**
1582
     * @hidden
1583
     * @internal
1584
     */
1585
    public get expanded() {
1586
        return true;
×
1587
    }
1588
    public set expanded(_value: boolean) { }
1589

1590
    /**
1591
     * hidden
1592
     */
1593
    public defaultWidth: string;
1594

1595
    /**
1596
     * hidden
1597
     */
1598
    public widthSetByUser: boolean;
1599

1600
    /**
1601
     * @hidden
1602
     */
1603
    public hasNestedPath: boolean;
1604

1605
    /**
1606
     * @hidden
1607
     * @internal
1608
     */
1609
    public defaultTimeFormat = 'hh:mm:ss tt';
26,583✔
1610

1611
    /**
1612
     * @hidden
1613
     * @internal
1614
     */
1615
    public defaultDateTimeFormat = 'dd/MM/yyyy HH:mm:ss tt';
26,583✔
1616

1617

1618
    /**
1619
     * Returns the filteringExpressionsTree of the column.
1620
     * ```typescript
1621
     * let tree =  this.column.filteringExpressionsTree;
1622
     * ```
1623
     *
1624
     * @memberof IgxColumnComponent
1625
     */
1626
    public get filteringExpressionsTree(): FilteringExpressionsTree {
1627
        return this.grid.filteringExpressionsTree.find(this.field) as FilteringExpressionsTree;
53,053✔
1628
    }
1629
    /**
1630
     * Sets/gets the parent column.
1631
     * ```typescript
1632
     * let parentColumn = this.column.parent;
1633
     * ```
1634
     * ```typescript
1635
     * this.column.parent = higherLevelColumn;
1636
     * ```
1637
     *
1638
     * @memberof IgxColumnComponent
1639
     */
1640
    public parent = null;
26,583✔
1641
    /**
1642
     * Sets/gets the children columns.
1643
     * ```typescript
1644
     * let columnChildren = this.column.children;
1645
     * ```
1646
     * ```typescript
1647
     * this.column.children = childrenColumns;
1648
     * ```
1649
     *
1650
     * @memberof IgxColumnComponent
1651
     */
1652
    public children: QueryList<IgxColumnComponent>;
1653
    /**
1654
     * @hidden
1655
     */
1656
    public destroy$ = new Subject<any>();
26,583✔
1657

1658
    /**
1659
     * @hidden
1660
     */
1661
    protected _applySelectableClass = false;
26,583✔
1662

1663
    protected _vIndex = NaN;
26,583✔
1664
    /**
1665
     * @hidden
1666
     */
1667
    protected _pinned = false;
26,583✔
1668
    /**
1669
     * @hidden
1670
     */
1671
    protected _bodyTemplate: TemplateRef<any>;
1672
    /**
1673
     * @hidden
1674
     */
1675
    protected _errorTemplate: TemplateRef<any>;
1676
    /**
1677
     * @hidden
1678
     */
1679
    protected _headerTemplate: TemplateRef<any>;
1680
    /**
1681
     * @hidden
1682
     */
1683
    protected _summaryTemplate: TemplateRef<any>;
1684
    /**
1685
     * @hidden
1686
     */
1687
    protected _inlineEditorTemplate: TemplateRef<any>;
1688
    /**
1689
     * @hidden
1690
     */
1691
    protected _filterCellTemplate: TemplateRef<any>;
1692
    /**
1693
     * @hidden
1694
     */
1695
    protected _summaries = null;
26,583✔
1696
    /**
1697
     * @hidden
1698
     */
1699
    protected _filters = null;
26,583✔
1700
    /**
1701
     * @hidden
1702
     */
1703
    protected _sortStrategy: ISortingStrategy = DefaultSortingStrategy.instance();
26,583✔
1704
    /**
1705
     * @hidden
1706
     */
1707
    protected _groupingComparer: (a: any, b: any, currRec?: any, groupRec?: any) => number;
1708
    /**
1709
     * @hidden
1710
     */
1711
    protected _hidden = false;
26,583✔
1712
    /**
1713
     * @hidden
1714
     */
1715
    protected _index: number;
1716
    /**
1717
     * @hidden
1718
     */
1719
    protected _disablePinning = false;
26,583✔
1720
    /**
1721
     * @hidden
1722
     */
1723
    protected _width: string;
1724
    /**
1725
     * @hidden
1726
     */
1727
    protected _defaultMinWidth = '';
26,583✔
1728
    /**
1729
     * @hidden
1730
     */
1731
    protected _hasSummary = false;
26,583✔
1732
    /**
1733
     * @hidden
1734
     */
1735
    protected _editable: boolean;
1736
    /**
1737
     * @hidden
1738
     */
1739
    protected _groupable = false;
26,583✔
1740
    /**
1741
     *  @hidden
1742
     */
1743
    protected _visibleWhenCollapsed;
1744
    /**
1745
     * @hidden
1746
     */
1747
    protected _collapsible = false;
26,583✔
1748
    /**
1749
     * @hidden
1750
     */
1751
    protected _expanded = true;
26,583✔
1752
    /**
1753
     * @hidden
1754
     */
1755
    protected _selectable = true;
26,583✔
1756
    /**
1757
     * @hidden
1758
     */
1759
    protected get isPrimaryColumn(): boolean {
1760
        return this.field !== undefined && this.grid !== undefined && this.field === this.grid.primaryKey;
1,891,418✔
1761
    }
1762

1763
    private _field: string;
1764
    private _calcWidth = null;
26,583✔
1765
    private _columnPipeArgs: IColumnPipeArgs = { digitsInfo: DEFAULT_DIGITS_INFO };
26,583✔
1766

1767
    constructor(
1768
        @Inject(IGX_GRID_BASE) public grid: GridType,
26,583✔
1769
        @Optional() @Self() @Inject(NG_VALIDATORS) private _validators: Validator[],
26,583✔
1770
        public cdr: ChangeDetectorRef,
26,583✔
1771
        protected platform: PlatformUtil,
26,583✔
1772
    ) {
1773
        this.validators  = _validators;
26,583✔
1774
     }
1775

1776
    /**
1777
     * @hidden
1778
     * @internal
1779
     */
1780
    public resetCaches() {
1781
        this._vIndex = NaN;
273,668✔
1782
        if (this.grid) {
273,668✔
1783
            this.cacheCalcWidth();
273,668✔
1784
        }
1785
    }
1786

1787
    /**
1788
     * @hidden
1789
     */
1790
    public ngOnDestroy() {
1791
        this.destroy$.next(true);
19,692✔
1792
        this.destroy$.complete();
19,692✔
1793
    }
1794
    /**
1795
     * @hidden
1796
     */
1797
    public ngAfterContentInit(): void {
1798
        if (this.summaryTemplateDirective) {
22,565✔
1799
            this._summaryTemplate = this.summaryTemplateDirective.template;
6✔
1800
        }
1801
        if (this.cellTemplate) {
22,565✔
1802
            this._bodyTemplate = this.cellTemplate.template;
49✔
1803
        }
1804
        if (this.cellValidationErrorTemplate) {
22,565✔
1805
            this._errorTemplate = this.cellValidationErrorTemplate.template;
16✔
1806
        }
1807
        if (this.headTemplate && this.headTemplate.length) {
22,565✔
1808
            this._headerTemplate = this.headTemplate.toArray()[0].template;
79✔
1809
        }
1810
        if (this.editorTemplate) {
22,565✔
1811
            this._inlineEditorTemplate = this.editorTemplate.template;
4✔
1812
        }
1813
        if (this.filterCellTemplateDirective) {
22,565!
1814
            this._filterCellTemplate = this.filterCellTemplateDirective.template;
×
1815
        }
1816
        if (!this._columnPipeArgs.format) {
22,565✔
1817
            this._columnPipeArgs.format = this.dataType === GridColumnDataType.Time ?
21,322✔
1818
                DEFAULT_TIME_FORMAT : this.dataType === GridColumnDataType.DateTime ?
21,107✔
1819
                    DEFAULT_DATE_TIME_FORMAT : DEFAULT_DATE_FORMAT;
1820
        }
1821
        if (!this.summaries) {
22,565✔
1822
            switch (this.dataType) {
21,285✔
1823
                case GridColumnDataType.String:
1824
                case GridColumnDataType.Boolean:
1825
                    this.summaries = IgxSummaryOperand;
12,869✔
1826
                    break;
12,869✔
1827
                case GridColumnDataType.Number:
1828
                case GridColumnDataType.Currency:
1829
                case GridColumnDataType.Percent:
1830
                    this.summaries = IgxNumberSummaryOperand;
6,484✔
1831
                    break;
6,484✔
1832
                case GridColumnDataType.Date:
1833
                case GridColumnDataType.DateTime:
1834
                    this.summaries = IgxDateSummaryOperand;
1,705✔
1835
                    break;
1,705✔
1836
                case GridColumnDataType.Time:
1837
                    this.summaries = IgxTimeSummaryOperand;
215✔
1838
                    break;
215✔
1839
                default:
1840
                    this.summaries = IgxSummaryOperand;
12✔
1841
                    break;
12✔
1842
            }
1843
        }
1844
        if (!this.filters) {
22,565✔
1845
            switch (this.dataType) {
21,014✔
1846
                case GridColumnDataType.Boolean:
1847
                    this.filters = IgxBooleanFilteringOperand.instance();
1,146✔
1848
                    break;
1,146✔
1849
                case GridColumnDataType.Number:
1850
                case GridColumnDataType.Currency:
1851
                case GridColumnDataType.Percent:
1852
                    this.filters = IgxNumberFilteringOperand.instance();
6,516✔
1853
                    break;
6,516✔
1854
                case GridColumnDataType.Date:
1855
                    this.filters = IgxDateFilteringOperand.instance();
1,468✔
1856
                    break;
1,468✔
1857
                case GridColumnDataType.Time:
1858
                    this.filters = IgxTimeFilteringOperand.instance();
215✔
1859
                    break;
215✔
1860
                case GridColumnDataType.DateTime:
1861
                    this.filters = IgxDateTimeFilteringOperand.instance();
242✔
1862
                    break;
242✔
1863
                case GridColumnDataType.String:
1864
                default:
1865
                    this.filters = IgxStringFilteringOperand.instance();
11,427✔
1866
                    break;
11,427✔
1867
            }
1868
        }
1869
    }
1870

1871
    /**
1872
     * @hidden
1873
     */
1874
    public getGridTemplate(isRow: boolean): string {
1875
        if (isRow) {
21,192✔
1876
            const rowsCount = !this.grid.isPivot ? this.grid.multiRowLayoutRowSize : this.children.length - 1;
10,597!
1877
            return `repeat(${rowsCount},1fr)`;
10,597✔
1878
        } else {
1879
            return this.getColumnSizesString(this.children);
10,595✔
1880
        }
1881
    }
1882

1883
    public getInitialChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<MRLColumnSizeInfo> {
1884
        const columnSizes: MRLColumnSizeInfo[] = [];
134,535✔
1885
        // find the smallest col spans
1886
        children.forEach(col => {
134,535✔
1887
            if (!col.colStart) {
418,076✔
1888
                return;
111,181✔
1889
            }
1890
            const newWidthSet = col.widthSetByUser && columnSizes[col.colStart - 1] && !columnSizes[col.colStart - 1].widthSetByUser;
306,895✔
1891
            const newSpanSmaller = columnSizes[col.colStart - 1] && columnSizes[col.colStart - 1].colSpan > col.gridColumnSpan;
306,895✔
1892
            const bothWidthsSet = col.widthSetByUser && columnSizes[col.colStart - 1] && columnSizes[col.colStart - 1].widthSetByUser;
306,895✔
1893
            const bothWidthsNotSet = !col.widthSetByUser && columnSizes[col.colStart - 1] && !columnSizes[col.colStart - 1].widthSetByUser;
306,895✔
1894

1895
            if (columnSizes[col.colStart - 1] === undefined) {
306,895✔
1896
                // If nothing is defined yet take any column at first
1897
                // We use colEnd to know where the column actually ends, because not always it starts where we have it set in columnSizes.
1898
                columnSizes[col.colStart - 1] = {
188,333✔
1899
                    ref: col,
1900
                    width: col.width === 'fit-content' ? col.autoSize :
188,333!
1901
                        col.widthSetByUser || this.grid.columnWidthSetByUser ? parseInt(col.calcWidth, 10) : null,
549,495✔
1902
                    colSpan: col.gridColumnSpan,
1903
                    colEnd: col.colStart + col.gridColumnSpan,
1904
                    widthSetByUser: col.widthSetByUser
1905
                };
1906
            } else if (newWidthSet || (newSpanSmaller && ((bothWidthsSet) || (bothWidthsNotSet)))) {
118,562✔
1907
                // If a column is set already it should either not have width defined or have width with bigger span than the new one.
1908

1909
                /**
1910
                 *  If replaced column has bigger span, we want to fill the remaining columns
1911
                 *  that the replacing column does not fill with the old one.
1912
                 */
1913
                if (bothWidthsSet && newSpanSmaller) {
43,020✔
1914
                    // Start from where the new column set would end and apply the old column to the rest depending on how much it spans.
1915
                    // We have not yet replaced it so we can use it directly from the columnSizes collection.
1916
                    // This is where colEnd is used because the colStart of the old column is not actually i + 1.
1917
                    for (let i = col.colStart - 1 + col.gridColumnSpan; i < columnSizes[col.colStart - 1].colEnd - 1; i++) {
4,167✔
1918
                        if (!columnSizes[i] || !columnSizes[i].widthSetByUser) {
2,413✔
1919
                            columnSizes[i] = columnSizes[col.colStart - 1];
2,360✔
1920
                        } else {
1921
                            break;
53✔
1922
                        }
1923
                    }
1924
                }
1925

1926
                // Replace the old column with the new one.
1927
                columnSizes[col.colStart - 1] = {
43,020✔
1928
                    ref: col,
1929
                    width: col.width === 'fit-content' ? col.autoSize :
43,020!
1930
                        col.widthSetByUser || this.grid.columnWidthSetByUser ? parseInt(col.calcWidth, 10) : null,
121,459✔
1931
                    colSpan: col.gridColumnSpan,
1932
                    colEnd: col.colStart + col.gridColumnSpan,
1933
                    widthSetByUser: col.widthSetByUser
1934
                };
1935
            } else if (bothWidthsSet && columnSizes[col.colStart - 1].colSpan < col.gridColumnSpan) {
75,542✔
1936
                // If the column already in the columnSizes has smaller span, we still need to fill any empty places with the current col.
1937
                // Start from where the smaller column set would end and apply the bigger column to the rest depending on how much it spans.
1938
                // Since here we do not have it in columnSizes we set it as a new column keeping the same colSpan.
1939
                for (let i = col.colStart - 1 + columnSizes[col.colStart - 1].colSpan; i < col.colStart - 1 + col.gridColumnSpan; i++) {
3,121✔
1940
                    if (!columnSizes[i] || !columnSizes[i].widthSetByUser) {
3,121✔
1941
                        columnSizes[i] = {
37✔
1942
                            ref: col,
1943
                            width: col.width === 'fit-content' ? col.autoSize :
37!
1944
                                col.widthSetByUser || this.grid.columnWidthSetByUser ? parseInt(col.calcWidth, 10) : null,
74!
1945
                            colSpan: col.gridColumnSpan,
1946
                            colEnd: col.colStart + col.gridColumnSpan,
1947
                            widthSetByUser: col.widthSetByUser
1948
                        };
1949
                    } else {
1950
                        break;
3,084✔
1951
                    }
1952
                }
1953
            }
1954
        });
1955

1956
        // Flatten columnSizes so there are not columns with colSpan > 1
1957
        for (let i = 0; i < columnSizes.length; i++) {
134,535✔
1958
            if (columnSizes[i] && columnSizes[i].colSpan > 1) {
186,175✔
1959
                let j = 1;
10,350✔
1960

1961
                // Replace all empty places depending on how much the current column spans starting from next col.
1962
                for (; j < columnSizes[i].colSpan && i + j + 1 < columnSizes[i].colEnd; j++) {
10,350✔
1963
                    if (columnSizes[i + j] &&
9,980✔
1964
                        ((!columnSizes[i].width && columnSizes[i + j].width) ||
1965
                            (!columnSizes[i].width && !columnSizes[i + j].width && columnSizes[i + j].colSpan <= columnSizes[i].colSpan) ||
1966
                            (!!columnSizes[i + j].width && columnSizes[i + j].colSpan <= columnSizes[i].colSpan))) {
1967
                        // If we reach an already defined column that has width and the current doesn't have or
1968
                        // if the reached column has bigger colSpan we stop.
1969
                        break;
4,240✔
1970
                    } else {
1971
                        const width = columnSizes[i].widthSetByUser ?
5,740✔
1972
                            columnSizes[i].width / columnSizes[i].colSpan :
1973
                            columnSizes[i].width;
1974
                        columnSizes[i + j] = {
5,740✔
1975
                            ref: columnSizes[i].ref,
1976
                            width,
1977
                            colSpan: 1,
1978
                            colEnd: columnSizes[i].colEnd,
1979
                            widthSetByUser: columnSizes[i].widthSetByUser
1980
                        };
1981
                    }
1982
                }
1983

1984
                // Update the current column width so it is divided between all columns it spans and set it to 1.
1985
                columnSizes[i].width = columnSizes[i].widthSetByUser ?
10,350✔
1986
                    columnSizes[i].width / columnSizes[i].colSpan :
1987
                    columnSizes[i].width;
1988
                columnSizes[i].colSpan = 1;
10,350✔
1989

1990
                // Update the index based on how much we have replaced. Subtract 1 because we started from 1.
1991
                i += j - 1;
10,350✔
1992
            }
1993
        }
1994

1995
        return columnSizes;
134,535✔
1996
    }
1997

1998
    public getFilledChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<string> {
1999
        const columnSizes = this.getInitialChildColumnSizes(children);
22,263✔
2000

2001
        // fill the gaps if there are any
2002
        const result: string[] = [];
22,263✔
2003
        for (const size of columnSizes) {
22,263✔
2004
            if (size && !!size.width) {
49,785✔
2005
                result.push(size.width + 'px');
20,196✔
2006
            } else {
2007
                result.push(parseInt(this.grid.getPossibleColumnWidth(), 10) + 'px');
29,589✔
2008
            }
2009
        }
2010
        return result;
22,263✔
2011
    }
2012

2013
    public getResizableColUnderEnd(): MRLResizeColumnInfo[] {
2014
        if (this.columnLayout || !this.columnLayoutChild || this.columnGroup) {
6!
2015
            return [{ target: this, spanUsed: 1 }];
×
2016
        }
2017

2018
        const columnSized = this.getInitialChildColumnSizes(this.parent.children);
6✔
2019
        const targets: MRLResizeColumnInfo[] = [];
6✔
2020
        const colEnd = this.colEnd ? this.colEnd : this.colStart + 1;
6!
2021

2022
        for (let i = 0; i < columnSized.length; i++) {
6✔
2023
            if (this.colStart <= i + 1 && i + 1 < colEnd) {
36✔
2024
                targets.push({ target: columnSized[i].ref, spanUsed: 1 });
10✔
2025
            }
2026
        }
2027

2028
        const targetsSquashed: MRLResizeColumnInfo[] = [];
6✔
2029
        for (const target of targets) {
6✔
2030
            if (targetsSquashed.length && targetsSquashed[targetsSquashed.length - 1].target.field === target.target.field) {
10!
2031
                targetsSquashed[targetsSquashed.length - 1].spanUsed++;
×
2032
            } else {
2033
                targetsSquashed.push(target);
10✔
2034
            }
2035
        }
2036

2037
        return targetsSquashed;
6✔
2038
    }
2039

2040
    /**
2041
     * Pins the column at the provided index in the pinned area.
2042
     * Defaults to index `0` if not provided, or to the initial index in the pinned area.
2043
     * Returns `true` if the column is successfully pinned. Returns `false` if the column cannot be pinned.
2044
     * Column cannot be pinned if:
2045
     * - Is already pinned
2046
     * - index argument is out of range
2047
     * - The pinned area exceeds 80% of the grid width
2048
     * ```typescript
2049
     * let success = this.column.pin();
2050
     * ```
2051
     *
2052
     * @memberof IgxColumnComponent
2053
     */
2054
    public pin(index?: number): boolean {
2055
        // TODO: Probably should the return type of the old functions
2056
        // should be moved as a event parameter.
2057
        const grid = (this.grid as any);
677✔
2058
        if (this._pinned) {
677✔
2059
            return false;
55✔
2060
        }
2061

2062
        if (this.parent && !this.parent.pinned) {
622✔
2063
            return this.topLevelParent.pin(index);
9✔
2064
        }
2065

2066
        const hasIndex = index !== undefined;
613✔
2067
        if (hasIndex && (index < 0 || index > grid.pinnedColumns.length)) {
613✔
2068
            return false;
2✔
2069
        }
2070

2071
        if (!this.parent && !this.pinnable) {
611!
2072
            return false;
×
2073
        }
2074

2075
        const rootPinnedCols = grid._pinnedColumns.filter((c) => c.level === 0);
1,498✔
2076
        index = hasIndex ? index : rootPinnedCols.length;
611✔
2077
        const args: IPinColumnCancellableEventArgs = { column: this, insertAtIndex: index, isPinned: false, cancel: false };
611✔
2078
        this.grid.columnPin.emit(args);
611✔
2079

2080
        if (args.cancel) {
611!
2081
            return;
×
2082
        }
2083

2084
        this.grid.crudService.endEdit(false);
611✔
2085

2086
        this._pinned = true;
611✔
2087
        this.pinnedChange.emit(this._pinned);
611✔
2088
        // it is possible that index is the last position, so will need to find target column by [index-1]
2089
        const targetColumn = args.insertAtIndex === grid._pinnedColumns.length ?
611✔
2090
            grid._pinnedColumns[args.insertAtIndex - 1] : grid._pinnedColumns[args.insertAtIndex];
2091

2092
        if (grid._pinnedColumns.indexOf(this) === -1) {
611✔
2093
            if (!grid.hasColumnGroups) {
419✔
2094
                grid._pinnedColumns.splice(args.insertAtIndex, 0, this);
346✔
2095
            } else {
2096
                // insert based only on root collection
2097
                rootPinnedCols.splice(args.insertAtIndex, 0, this);
73✔
2098
                let allPinned = [];
73✔
2099
                // re-create hierarchy
2100
                rootPinnedCols.forEach(group => {
73✔
2101
                    allPinned.push(group);
137✔
2102
                    allPinned = allPinned.concat(group.allChildren);
137✔
2103
                });
2104
                grid._pinnedColumns = allPinned;
73✔
2105
            }
2106

2107
            if (grid._unpinnedColumns.indexOf(this) !== -1) {
419✔
2108
                const childrenCount = this.allChildren.length;
204✔
2109
                grid._unpinnedColumns.splice(grid._unpinnedColumns.indexOf(this), 1 + childrenCount);
204✔
2110
            }
2111
        }
2112

2113
        if (hasIndex) {
611✔
2114
            grid._moveColumns(this, targetColumn);
9✔
2115
        }
2116

2117
        if (this.columnGroup) {
611✔
2118
            this.allChildren.forEach(child => child.pin());
181✔
2119
            grid.reinitPinStates();
57✔
2120
        }
2121

2122
        grid.resetCaches();
611✔
2123
        grid.notifyChanges();
611✔
2124
        if (this.columnLayoutChild) {
611✔
2125
            this.grid.columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
1,369✔
2126
        }
2127
        this.grid.filteringService.refreshExpressions();
611✔
2128
        const eventArgs: IPinColumnEventArgs = { column: this, insertAtIndex: index, isPinned: true };
611✔
2129
        this.grid.columnPinned.emit(eventArgs);
611✔
2130
        return true;
611✔
2131
    }
2132
    /**
2133
     * Unpins the column and place it at the provided index in the unpinned area.
2134
     * Defaults to index `0` if not provided, or to the initial index in the unpinned area.
2135
     * Returns `true` if the column is successfully unpinned. Returns `false` if the column cannot be unpinned.
2136
     * Column cannot be unpinned if:
2137
     * - Is already unpinned
2138
     * - index argument is out of range
2139
     * ```typescript
2140
     * let success = this.column.unpin();
2141
     * ```
2142
     *
2143
     * @memberof IgxColumnComponent
2144
     */
2145
    public unpin(index?: number): boolean {
2146
        const grid = (this.grid as any);
319✔
2147
        if (!this._pinned) {
319✔
2148
            return false;
186✔
2149
        }
2150

2151
        if (this.parent && this.parent.pinned) {
133✔
2152
            return this.topLevelParent.unpin(index);
7✔
2153
        }
2154
        const hasIndex = index !== undefined;
126✔
2155
        if (hasIndex && (index < 0 || index > grid._unpinnedColumns.length)) {
126✔
2156
            return false;
2✔
2157
        }
2158

2159
        // estimate the exact index at which column will be inserted
2160
        // takes into account initial unpinned index of the column
2161
        if (!hasIndex) {
124✔
2162
            const indices = grid.unpinnedColumns.map(col => col.index);
1,055✔
2163
            indices.push(this.index);
120✔
2164
            indices.sort((a, b) => a - b);
1,367✔
2165
            index = indices.indexOf(this.index);
120✔
2166
        }
2167

2168
        const args: IPinColumnCancellableEventArgs = { column: this, insertAtIndex: index, isPinned: true, cancel: false };
124✔
2169
        this.grid.columnPin.emit(args);
124✔
2170

2171
        if (args.cancel) {
124!
2172
            return;
×
2173
        }
2174

2175
        this.grid.crudService.endEdit(false);
124✔
2176

2177
        this._pinned = false;
124✔
2178
        this.pinnedChange.emit(this._pinned);
124✔
2179

2180
        // it is possible that index is the last position, so will need to find target column by [index-1]
2181
        const targetColumn = args.insertAtIndex === grid._unpinnedColumns.length ?
124✔
2182
            grid._unpinnedColumns[args.insertAtIndex - 1] : grid._unpinnedColumns[args.insertAtIndex];
2183

2184
        if (!hasIndex) {
124✔
2185
            grid._unpinnedColumns.splice(index, 0, this);
120✔
2186
            if (grid._pinnedColumns.indexOf(this) !== -1) {
120✔
2187
                grid._pinnedColumns.splice(grid._pinnedColumns.indexOf(this), 1);
120✔
2188
            }
2189
        }
2190

2191
        if (hasIndex) {
124✔
2192
            grid.moveColumn(this, targetColumn);
4✔
2193
        }
2194

2195
        if (this.columnGroup) {
124✔
2196
            this.allChildren.forEach(child => child.unpin());
94✔
2197
        }
2198

2199
        grid.reinitPinStates();
124✔
2200
        grid.resetCaches();
124✔
2201

2202
        grid.notifyChanges();
124✔
2203
        if (this.columnLayoutChild) {
124✔
2204
            this.grid.columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
236✔
2205
        }
2206
        this.grid.filteringService.refreshExpressions();
124✔
2207

2208
        this.grid.columnPinned.emit({ column: this, insertAtIndex: index, isPinned: false });
124✔
2209

2210
        return true;
124✔
2211
    }
2212

2213
    /**
2214
     * Moves a column to the specified visible index.
2215
     * If passed index is invalid, or if column would receive a different visible index after moving, moving is not performed.
2216
     * If passed index would move the column to a different column group. moving is not performed.
2217
     *
2218
     * @example
2219
     * ```typescript
2220
     * column.move(index);
2221
     * ```
2222
     * @memberof IgxColumnComponent
2223
     */
2224
    public move(index: number) {
2225
        let target;
2226
        let columns = this.grid.columns.filter(c => c.visibleIndex > -1);
349✔
2227
        // grid last visible index
2228
        const li = columns.map(c => c.visibleIndex).reduce((a, b) => Math.max(a, b));
343✔
2229
        const parent = this.parent;
36✔
2230
        const isPreceding = this.visibleIndex < index;
36✔
2231

2232
        if (index === this.visibleIndex || index < 0 || index > li) {
36✔
2233
            return;
3✔
2234
        }
2235

2236
        if (parent) {
33✔
2237
            columns = columns.filter(c => c.level >= this.level && c !== this && c.parent !== this &&
84✔
2238
                c.topLevelParent === this.topLevelParent);
2239
        }
2240
        /* eslint-disable max-len */
2241
        // 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.
2242
        // 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.
2243
        /* eslint-enable max-len */
2244
        if (isPreceding) {
33✔
2245
            columns = columns.filter(c => c.visibleIndex > this.visibleIndex);
200✔
2246
            target = columns.find(c => c.level === this.level && c.visibleIndex + (c as any).calcChildren() - this.calcChildren() === index);
80✔
2247
        } else {
2248
            columns = columns.filter(c => c.visibleIndex < this.visibleIndex);
65✔
2249
            target = columns.find(c => c.level === this.level && c.visibleIndex === index);
15✔
2250
        }
2251

2252
        if (!target || (target.pinned && this.disablePinning)) {
33✔
2253
            return;
9✔
2254
        }
2255

2256
        const pos = isPreceding ? DropPosition.AfterDropTarget : DropPosition.BeforeDropTarget;
24✔
2257
        this.grid.moveColumn(this, target as IgxColumnComponent, pos);
24✔
2258
    }
2259

2260
    /**
2261
     * No children for the column, so will returns 1 or 0, if the column is hidden.
2262
     *
2263
     * @hidden
2264
     */
2265
    public calcChildren(): number {
2266
        const children = this.hidden ? 0 : 1;
70✔
2267
        return children;
70✔
2268
    }
2269

2270
    /**
2271
     * Toggles column vibisility and emits the respective event.
2272
     *
2273
     * @hidden
2274
     */
2275
    public toggleVisibility(value?: boolean) {
2276
        const newValue = value ?? !this.hidden;
65✔
2277
        const eventArgs: IColumnVisibilityChangingEventArgs = { column: this, newValue, cancel: false };
65✔
2278
        this.grid.columnVisibilityChanging.emit(eventArgs);
65✔
2279

2280
        if (eventArgs.cancel) {
65!
2281
            return;
×
2282
        }
2283
        this.hidden = newValue;
65✔
2284
        this.grid.columnVisibilityChanged.emit({ column: this, newValue });
65✔
2285
    }
2286

2287
    /**
2288
     * Returns a reference to the top level parent column.
2289
     * ```typescript
2290
     * let topLevelParent =  this.column.topLevelParent;
2291
     * ```
2292
     *
2293
     * @memberof IgxColumnComponent
2294
     */
2295
    public get topLevelParent() {
2296
        let parent = this.parent;
483✔
2297
        while (parent && parent.parent) {
483✔
2298
            parent = parent.parent;
34✔
2299
        }
2300
        return parent;
483✔
2301
    }
2302

2303
    /**
2304
     * Returns a reference to the header of the column.
2305
     * ```typescript
2306
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2307
     * let headerCell = column.headerCell;
2308
     * ```
2309
     *
2310
     * @memberof IgxColumnComponent
2311
     */
2312
    public get headerCell(): IgxGridHeaderComponent {
2313
        return this.grid.headerCellList.find((header) => header.column === this);
1,345✔
2314
    }
2315

2316
    /**
2317
     * Returns a reference to the filter cell of the column.
2318
     * ```typescript
2319
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2320
     * let filterell = column.filterell;
2321
     * ```
2322
     *
2323
     * @memberof IgxColumnComponent
2324
     */
2325
    public get filterCell(): IgxGridFilteringCellComponent {
2326
        return this.grid.filterCellList.find((filterCell) => filterCell.column === this);
7,082✔
2327
    }
2328

2329
    /**
2330
     * Returns a reference to the header group of the column.
2331
     *
2332
     * @memberof IgxColumnComponent
2333
     */
2334
    public get headerGroup(): IgxGridHeaderGroupComponent {
2335
        return this.grid.headerGroupsList.find(group => group.column === this);
11,574✔
2336
    }
2337

2338
    /**
2339
     * Autosize the column to the longest currently visible cell value, including the header cell.
2340
     * ```typescript
2341
     * @ViewChild('grid') grid: IgxGridComponent;
2342
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2343
     * column.autosize();
2344
     * ```
2345
     *
2346
     * @memberof IgxColumnComponent
2347
     * @param byHeaderOnly Set if column should be autosized based only on the header content.
2348
     */
2349
    public autosize(byHeaderOnly = false) {
17✔
2350
        if (!this.columnGroup) {
18✔
2351
            this.width = this.getAutoSize(byHeaderOnly);
18✔
2352
            this.grid.reflow();
18✔
2353
        }
2354
    }
2355

2356
    /**
2357
     * @hidden
2358
     */
2359
    public getAutoSize(byHeader = false): string {
8✔
2360
        const size = !byHeader ? this.getLargestCellWidth() :
26✔
2361
            (Object.values(this.getHeaderCellWidths()).reduce((a, b) => a + b) + 'px');
1✔
2362
        const isPercentageWidth = this.width && typeof this.width === 'string' && this.width.indexOf('%') !== -1;
26✔
2363

2364
        let newWidth;
2365
        if (isPercentageWidth) {
26✔
2366
            const gridAvailableSize = this.grid.calcWidth;
2✔
2367
            const percentageSize = parseFloat(size) / gridAvailableSize * 100;
2✔
2368
            newWidth = percentageSize + '%';
2✔
2369
        } else {
2370
            newWidth = size;
24✔
2371
        }
2372

2373
        const maxWidth = isPercentageWidth ? this.maxWidthPercent : this.maxWidthPx;
26✔
2374
        const minWidth = isPercentageWidth ? this.minWidthPercent : this.minWidthPx;
26✔
2375
        if (this.maxWidth && (parseFloat(newWidth) > maxWidth)) {
26✔
2376
            newWidth = isPercentageWidth ? maxWidth + '%' : maxWidth + 'px';
1!
2377
        } else if (parseFloat(newWidth) < minWidth) {
25✔
2378
            newWidth = isPercentageWidth ? minWidth + '%' : minWidth + 'px';
1!
2379
        }
2380

2381
        return newWidth;
26✔
2382
    }
2383

2384
    /**
2385
     * @hidden
2386
     */
2387
    public getCalcWidth(): any {
2388
        if (this._calcWidth && !isNaN(this.calcPixelWidth)) {
315,449✔
2389
            return this._calcWidth;
304,277✔
2390
        }
2391
        this.cacheCalcWidth();
11,172✔
2392
        return this._calcWidth;
11,172✔
2393
    }
2394

2395

2396
    /**
2397
     * @hidden
2398
     * Returns the width and padding of a header cell.
2399
     */
2400
    public getHeaderCellWidths() {
2401
        return this.grid.getHeaderCellWidth(this.headerCell.nativeElement);
25✔
2402
    }
2403

2404
    /**
2405
     * @hidden
2406
     * Returns the size (in pixels) of the longest currently visible cell, including the header cell.
2407
     * ```typescript
2408
     * @ViewChild('grid') grid: IgxGridComponent;
2409
     *
2410
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2411
     * let size = column.getLargestCellWidth();
2412
     * ```
2413
     * @memberof IgxColumnComponent
2414
     */
2415
    public getLargestCellWidth(): string {
2416
        const range = this.grid.document.createRange();
25✔
2417
        const largest = new Map<number, number>();
25✔
2418

2419
        if (this._cells.length > 0) {
25✔
2420
            const cellsContentWidths = [];
25✔
2421
            this._cells.forEach((cell) => cellsContentWidths.push(cell.calculateSizeToFit(range)));
270✔
2422

2423
            const index = cellsContentWidths.indexOf(Math.max(...cellsContentWidths));
25✔
2424
            const cellStyle = this.grid.document.defaultView.getComputedStyle(this._cells[index].nativeElement);
25✔
2425
            const cellPadding = parseFloat(cellStyle.paddingLeft) + parseFloat(cellStyle.paddingRight) +
25✔
2426
                parseFloat(cellStyle.borderLeftWidth) + parseFloat(cellStyle.borderRightWidth);
2427

2428
            largest.set(Math.max(...cellsContentWidths), cellPadding);
25✔
2429
        }
2430

2431
        if (this.headerCell && this.autosizeHeader) {
25✔
2432
            const headerCellWidths = this.getHeaderCellWidths();
24✔
2433
            largest.set(headerCellWidths.width, headerCellWidths.padding);
24✔
2434
        }
2435

2436
        const largestCell = Math.max(...Array.from(largest.keys()));
25✔
2437
        const width = Math.ceil(largestCell + largest.get(largestCell));
25✔
2438

2439
        if (Number.isNaN(width)) {
25!
2440
            return this.width;
×
2441
        } else {
2442
            return width + 'px';
25✔
2443
        }
2444
    }
2445

2446
    /**
2447
     * @hidden
2448
     */
2449
    public getCellWidth() {
2450
        const colWidth = this.width;
1,091,399✔
2451
        const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1;
1,091,399✔
2452

2453
        if (this.columnLayoutChild) {
1,091,399!
2454
            return '';
×
2455
        }
2456

2457
        if (colWidth && !isPercentageWidth) {
1,091,399✔
2458

2459
            let cellWidth = colWidth;
1,082,503✔
2460
            if (typeof cellWidth !== 'string' || cellWidth.endsWith('px') === false) {
1,082,503✔
2461
                cellWidth += 'px';
867✔
2462
            }
2463

2464
            return cellWidth;
1,082,503✔
2465
        } else {
2466
            return colWidth;
8,896✔
2467
        }
2468
    }
2469

2470
    /**
2471
     * @hidden
2472
     */
2473
    public populateVisibleIndexes() { }
2474

2475
    protected getColumnSizesString(children: QueryList<IgxColumnComponent>): string {
2476
        const res = this.getFilledChildColumnSizes(children);
10,595✔
2477
        return res.join(' ');
10,595✔
2478
    }
2479

2480
    /**
2481
     * @hidden
2482
     * @internal
2483
     */
2484
    protected cacheCalcWidth(): any {
2485
        const colWidth = this.width;
295,851✔
2486
        const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1;
295,851✔
2487
        const isAutoWidth = colWidth && typeof colWidth === 'string' && colWidth === 'fit-content';
295,851✔
2488
        if (isPercentageWidth) {
295,851✔
2489
            this._calcWidth = parseFloat(colWidth) / 100 * this.grid.calcWidth;
805✔
2490
        } else if (!colWidth || isAutoWidth && !this.autoSize) {
295,046✔
2491
            // no width
2492
            this._calcWidth = this.defaultWidth || this.grid.getPossibleColumnWidth();
28,875✔
2493
        } else {
2494
            this._calcWidth = this.width;
266,171✔
2495
        }
2496
        this.calcPixelWidth = parseFloat(this._calcWidth);
295,851✔
2497
    }
2498

2499
    /**
2500
     * @hidden
2501
     * @internal
2502
     */
2503
    protected setExpandCollapseState() {
2504
        this.children.filter(col => (col.visibleWhenCollapsed !== undefined)).forEach(c => {
245✔
2505
            if (!this.collapsible) {
226✔
2506
                c.hidden = this.hidden; return;
1✔
2507
            }
2508
            c.hidden = this._expanded ? c.visibleWhenCollapsed : !c.visibleWhenCollapsed;
225✔
2509
        });
2510
    }
2511
    /**
2512
     * @hidden
2513
     * @internal
2514
     */
2515
    protected checkCollapsibleState() {
2516
        if (!this.children) {
2,069!
2517
            return false;
×
2518
        }
2519
        const cols = this.children.map(child => child.visibleWhenCollapsed);
5,014✔
2520
        return (cols.some(c => c === true) && cols.some(c => c === false));
3,135✔
2521
    }
2522

2523
    /**
2524
     * @hidden
2525
     */
2526
    public get pinnable() {
2527
        return (this.grid as any)._init || !this.pinned;
386✔
2528
    }
2529

2530
    /**
2531
     * @hidden
2532
     */
2533
    public get applySelectableClass(): boolean {
2534
        return this._applySelectableClass;
8,151✔
2535
    }
2536

2537
    /**
2538
     * @hidden
2539
     */
2540
    public set applySelectableClass(value: boolean) {
2541
        if (this.selectable) {
369✔
2542
            this._applySelectableClass = value;
367✔
2543
        }
2544
    }
2545
}
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