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

IgniteUI / igniteui-angular / 9516304207

14 Jun 2024 12:42PM UTC coverage: 92.244% (+0.01%) from 92.234%
9516304207

push

github

web-flow
Merge pull request #14391 from IgniteUI/dpetev/grid-last-search-info-getter-16.1

refactor(grid): mark lastSearchInfo readonly/getter-only

15438 of 18149 branches covered (85.06%)

3 of 6 new or added lines in 3 files covered. (50.0%)

36 existing lines in 3 files now uncovered.

27023 of 29295 relevant lines covered (92.24%)

29726.48 hits per line

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

97.35
/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';
2✔
19
import { notifyChanges } from '../watch-changes';
2✔
20
import { WatchColumnChanges } from '../watch-changes';
2✔
21
import { GridColumnDataType } from '../../data-operations/data-util';
2✔
22
import {
23
    IgxFilteringOperand,
24
    IgxBooleanFilteringOperand,
25
    IgxNumberFilteringOperand,
26
    IgxDateFilteringOperand,
27
    IgxStringFilteringOperand,
28
    IgxDateTimeFilteringOperand,
29
    IgxTimeFilteringOperand
30
} from '../../data-operations/filtering-condition';
2✔
31
import { ISortingStrategy, DefaultSortingStrategy } from '../../data-operations/sorting-strategy';
32
import { DisplayDensity } from '../../core/density';
26,062✔
33
import { IgxRowDirective } from '../row.directive';
26,062✔
34
import { FilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
35
import { CellType, ColumnType, GridType, IgxCellTemplateContext, IgxColumnTemplateContext, IgxSummaryTemplateContext, IGX_GRID_BASE } from '../common/grid.interface';
36
import { IgxGridHeaderComponent } from '../headers/grid-header.component';
15,923,446✔
37
import { IgxGridFilteringCellComponent } from '../filtering/base/grid-filtering-cell.component';
38
import { IgxGridHeaderGroupComponent } from '../headers/grid-header-group.component';
39
import {
101,176✔
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';
1,504✔
52
import { MRLResizeColumnInfo, MRLColumnSizeInfo, IColumnPipeArgs } from './interfaces';
53
import { DropPosition } from '../moving/moving.service';
54
import { IColumnVisibilityChangingEventArgs, IPinColumnCancellableEventArgs, IPinColumnEventArgs } from '../common/events';
136,698✔
55
import { isConstructor, PlatformUtil } from '../../core/utils';
56
import { IgxGridCell } from '../grid-public-cell';
57
import { NG_VALIDATORS, Validator } from '@angular/forms';
3,284✔
58

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

1,820,420✔
64
/**
1,820,420✔
65
 * **Ignite UI for Angular Column** -
1,820,420✔
66
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid#columns-configuration)
75,053✔
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,
1,745,367✔
69
 * filtering & editing are enabled at the column level.  You can also provide a template containing custom content inside
254,720✔
70
 * the column using `ng-template` which will be used for all cells within the column.
71
 */
72
@Component({
1,490,647✔
73
    changeDetection: ChangeDetectionStrategy.OnPush,
74
    selector: 'igx-column',
75
    template: ``,
76
    standalone: true
77
})
78
export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnType {
79
    /**
80
     * Sets/gets the `field` value.
81
     * ```typescript
82
     * let columnField = this.column.field;
83
     * ```
84
     * ```html
85
     * <igx-column [field] = "'ID'"></igx-column>
86
     * ```
87
     *
4,280✔
88
     * @memberof IgxColumnComponent
89
     */
90
    @Input()
1,870,476✔
91
    public set field(value: string) {
92
        this._field = value;
93
        this.hasNestedPath = value?.includes('.');
94
    }
95
    public get field(): string {
96
        return this._field;
97
    }
98

99

100
    /**
101
     * @hidden @internal
102
     */
3,194✔
103
    public validators: Validator[] = [];
3,194!
104

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

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

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

216
        if (this.isPrimaryColumn && (rowEditable || hasTransactions)) {
217
            return false;
218
        }
219

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

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

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

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

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

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

397
    /**
249,907✔
398
     * @hidden
399
     */
717✔
400
    @Output()
401
    public hiddenChange = new EventEmitter<boolean>();
1,089✔
402

403
    /** @hidden */
248,101✔
404
    @Output()
405
    public expandedChange = new EventEmitter<boolean>();
406

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

414
    /** @hidden */
415
    @Output()
416
    public columnChange = new EventEmitter<void>();
417

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

5,105✔
477
        }
478
        return this.widthSetByUser ? this._width : this.defaultWidth;
479
    }
480

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

515
    /** @hidden @internal **/
1,323✔
516
    public autoSize: number;
517

518
    /**
32,215✔
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;
532

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

1,273!
549
    /**
1,273!
550
     * Sets conditional style properties on the column header.
1,273✔
551
     * Similar to `ngStyle` it accepts an object literal where the keys are
1,273✔
552
     * the style properties and the value is the expression to be evaluated.
553
     * ```typescript
1,273✔
554
     * styles = {
555
     *  background: 'royalblue',
556
     *  color: (column) => column.pinned ? 'red': 'inherit'
557
     * }
558
     * ```
559
     * ```html
2,788✔
560
     * <igx-column [headerStyles]="styles"></igx-column>
561
     * ```
2,788!
562
     *
16,686✔
563
     * @memberof IgxColumnComponent
564
     */
2,788✔
565
    @notifyChanges()
566
    @WatchColumnChanges()
567
    @Input()
568
    public headerStyles = null;
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>
4,143,095✔
577
     * ```
3,770,297✔
578
     *
579
     * @memberof IgxColumnComponent
3,807,946✔
580
     */
372,798✔
581
    @notifyChanges()
582
    @WatchColumnChanges()
372,798✔
583
    @Input()
372,798✔
584
    public headerGroupClasses = '';
372,798✔
585

22,917✔
586
    /**
587
     * Sets conditional style properties on the column header group wrapper.
372,798✔
588
     * Similar to `ngStyle` it accepts an object literal where the keys are
698,118✔
589
     * the style properties and the value is the expression to be evaluated.
590
     * ```typescript
103,778✔
591
     * styles = {
101,575✔
592
     *  background: 'royalblue',
101,575✔
593
     *  color: (column) => column.pinned ? 'red': 'inherit'
594
     * }
101,131✔
595
     * ```
596
     * ```html
597
     * <igx-column [headerGroupStyles]="styles"></igx-column>
598
     * ```
599
     *
2,203✔
600
     * @memberof IgxColumnComponent
2,203✔
601
     */
602
    @notifyChanges()
603
    @WatchColumnChanges()
604
    @Input()
103,778✔
605
    public headerGroupStyles = null;
103,778✔
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
     * ```
6,983,402✔
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;
16,719,587✔
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
     * }
12,800,348✔
639
     * ```
640
     * ```html
641
     * <igx-column [cellStyles]="styles"></igx-column>
642
     * ```
304✔
643
     *
644
     * @memberof IgxColumnComponent
645
     */
646
    @notifyChanges()
647
    @WatchColumnChanges()
648
    @Input()
649
    public cellStyles = null;
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
536,214✔
655
     * scenarios (remote filtering for example), the row data argument can be `undefined`.
536,214✔
656
     *
536,214✔
657
     *
176,641✔
658
     * In this example, we check to see if the column name is Salary, and then provide a method as the column formatter
176,641✔
659
     * to format the value into a currency string.
660
     *
536,214✔
661
     * @example
662
     * ```typescript
663
     * columnInit(column: IgxColumnComponent) {
664
     *   if (column.field == "Salary") {
821,012✔
665
     *     column.formatter = (salary => this.format(salary));
666
     *   }
667
     * }
668
     *
669
     * format(value: number) : string {
754,048✔
670
     *   return formatCurrency(value, "en-us", "$");
754,048✔
671
     * }
672
     * ```
673
     *
674
     * @example
526,660✔
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
     * ```
1,277✔
680
     *
681
     * @memberof IgxColumnComponent
682
     */
577,987✔
683
    @notifyChanges()
684
    @WatchColumnChanges()
685
    @Input()
1,653✔
686
    public formatter: (value: any, rowData?: any) => any;
1,653✔
687

1,653✔
688
    /**
21✔
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.
8,563✔
693
     *
694
     * ```typescript
695
     * columnInit(column: IgxColumnComponent) {
1,346✔
696
     *   if (column.field == "OrderDate") {
1,346✔
697
     *     column.summaryFormatter = this.summaryFormat;
1,346✔
698
     *   }
699
     * }
700
     *
1,328,630✔
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
     *   }
UNCOV
707
     *   return result;
×
708
     * }
709
     * ```
710
     *
711
     * @memberof IgxColumnComponent
712
     */
713
    @notifyChanges()
714
    @WatchColumnChanges()
UNCOV
715
    @Input()
×
716
    public summaryFormatter: (summary: IgxSummaryResult, summaryOperand: IgxSummaryOperand) => any;
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
     *
51,783✔
728
     * @memberof IgxColumnComponent
729
     */
730
    @WatchColumnChanges()
731
    @Input()
732
    public filteringIgnoreCase = true;
733
    /**
1,820,420✔
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
     * ```
27,122✔
739
     * ```html
27,122✔
740
     * <igx-column [sortingIgnoreCase] = "false"></igx-column>
27,122✔
741
     * ```
27,122✔
742
     *
743
     * @memberof IgxColumnComponent
744
     */
745
    @WatchColumnChanges()
27,122✔
746
    @Input()
27,122✔
747
    public sortingIgnoreCase = true;
27,122✔
748
    /**
27,122✔
749
     * Sets/gets whether the column is `searchable`.
27,122✔
750
     * Default value is `true`.
27,122✔
751
     * ```typescript
27,122✔
752
     * let isSearchable =  this.column.searchable';
27,122✔
753
     * ```
27,122✔
754
     * ```html
27,122✔
755
     *  <igx-column [searchable] = "false"></igx-column>
27,122✔
756
     * ```
27,122✔
757
     *
27,122✔
758
     * @memberof IgxColumnComponent
27,122✔
759
     */
27,122✔
760
    @notifyChanges()
27,122✔
761
    @WatchColumnChanges()
27,122✔
762
    @Input()
27,122✔
763
    public searchable = true;
27,122✔
764
    /**
27,122✔
765
     * Sets/gets the data type of the column values.
27,122✔
766
     * Default value is `string`.
27,122✔
767
     * ```typescript
27,122✔
768
     * let columnDataType = this.column.dataType;
27,122✔
769
     * ```
27,122✔
770
     * ```html
27,122✔
771
     * <igx-column [dataType] = "'number'"></igx-column>
772
     * ```
773
     *
774
     * @memberof IgxColumnComponent
775
     */
27,122✔
776
    @Input()
777
    public dataType: GridColumnDataType = GridColumnDataType.String;
778

779
    /** @hidden */
780
    @Input()
27,122✔
781
    public collapsibleIndicatorTemplate: TemplateRef<IgxColumnTemplateContext>;
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
27,122✔
793
     */
794
    @Input()
795
    public rowEnd: number;
796

27,122✔
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
27,122✔
801
     * <igx-column-layout>
27,122✔
802
     *   <igx-column [colEnd]="3" [rowStart]="1" [colStart]="1"></igx-column>
803
     * </igx-column-layout>
804
     * ```
805
     *
27,122✔
806
     * @memberof IgxColumnComponent
807
     */
808
    @Input()
809
    public colEnd: number;
27,122✔
810

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

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

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

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

859
    /**
268,310✔
860
     * @hidden
268,310!
861
     */
268,310✔
862
    @Output()
863
    public pinnedChange = new EventEmitter<boolean>();
864
    /**
865
     * @hidden
866
     */
867
    @ContentChild(IgxFilterCellTemplateDirective, { read: IgxFilterCellTemplateDirective })
868
    public filterCellTemplateDirective: IgxFilterCellTemplateDirective;
15,763✔
869
    /**
15,763✔
870
     * @hidden
871
     */
872
    @ContentChild(IgxSummaryTemplateDirective, { read: IgxSummaryTemplateDirective })
873
    protected summaryTemplateDirective: IgxSummaryTemplateDirective;
874
    /**
875
     * @hidden
22,929✔
876
     * @see {@link bodyTemplate}
6✔
877
     */
878
    @ContentChild(IgxCellTemplateDirective, { read: IgxCellTemplateDirective })
22,929✔
879
    protected cellTemplate: IgxCellTemplateDirective;
43✔
880
    /**
881
     * @hidden
22,929✔
882
     */
16✔
883
    @ContentChild(IgxCellValidationErrorDirective, { read: IgxCellValidationErrorDirective })
884
    protected cellValidationErrorTemplate: IgxCellValidationErrorDirective;
22,929✔
885
    /**
78✔
886
     * @hidden
887
     */
22,929✔
888
    @ContentChildren(IgxCellHeaderTemplateDirective, { read: IgxCellHeaderTemplateDirective, descendants: false })
4✔
889
    protected headTemplate: QueryList<IgxCellHeaderTemplateDirective>;
890
    /**
22,929!
UNCOV
891
     * @hidden
×
892
     */
893
    @ContentChild(IgxCellEditorTemplateDirective, { read: IgxCellEditorTemplateDirective })
22,929✔
894
    protected editorTemplate: IgxCellEditorTemplateDirective;
21,729✔
895
    /**
21,510✔
896
     * @hidden
897
     */
898
    @ContentChild(IgxCollapsibleIndicatorTemplateDirective, { read: IgxCollapsibleIndicatorTemplateDirective, static: false })
22,929✔
899
    protected collapseIndicatorTemplate: IgxCollapsibleIndicatorTemplateDirective;
21,693✔
900
    /**
901
     * @hidden
902
     */
903
    public get calcWidth(): any {
6,565✔
904
        return this.getCalcWidth();
6,565✔
905
    }
906

907
    /** @hidden @internal **/
1,749✔
908
    public calcPixelWidth: number;
1,749✔
909

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

919
    /**
22,929✔
920
     * @hidden
21,414✔
921
     */
922
    public get maxWidthPercent() {
1,169✔
923
        const gridAvailableSize = this.grid.calcWidth;
1,169✔
924
        const isPercentageWidth = this.maxWidth && typeof this.maxWidth === 'string' && this.maxWidth.indexOf('%') !== -1;
925
        return isPercentageWidth ? parseFloat(this.maxWidth) : parseFloat(this.maxWidth) / gridAvailableSize * 100;
926
    }
927

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

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

946

947
    /**
948
     * Sets/gets the minimum `width` of the column.
949
     * Default value is `88`;
950
     * ```typescript
951
     * let columnMinWidth = this.column.minWidth;
952
     * ```
21,450✔
953
     * ```html
10,726!
954
     * <igx-column [minWidth] = "'100px'"></igx-column>
10,726✔
955
     * ```
956
     *
957
     * @memberof IgxColumnComponent
10,724✔
958
     */
959
    @notifyChanges()
960
    @WatchColumnChanges()
961
    @Input()
962
    public set minWidth(value: string) {
133,083✔
963
        const minVal = parseFloat(value);
964
        if (Number.isNaN(minVal)) {
133,083✔
965
            return;
410,637✔
966
        }
112,187✔
967
        this._defaultMinWidth = value;
968

298,450✔
969
    }
298,450✔
970
    public get minWidth(): string {
298,450✔
971
        return !this._defaultMinWidth ? this.defaultMinWidth : this._defaultMinWidth;
298,450✔
972
    }
298,450✔
973

974
    /** @hidden @internal **/
975
    public get resolvedWidth(): string {
182,692✔
976
        if (this.columnLayoutChild) {
977
            return '';
182,692!
978
        }
532,925✔
979
        const isAutoWidth = this._width && typeof this._width === 'string' && this._width === 'auto';
980
        return isAutoWidth ? this.width : this.calcPixelWidth + 'px';
981
    }
982

983
    /**
984
     * Gets the column index.
115,758✔
985
     * ```typescript
986
     * let columnIndex = this.column.index;
987
     * ```
988
     *
989
     * @memberof IgxColumnComponent
990
     */
42,261✔
991
    public get index(): number {
992
        return (this.grid as any)._columns.indexOf(this);
993
    }
994

4,187✔
995
    /**
2,425✔
996
     * Gets whether the column is `pinned`.
2,372✔
997
     * ```typescript
998
     * let isPinned = this.column.pinned;
999
     * ```
53✔
1000
     *
1001
     * @memberof IgxColumnComponent
1002
     */
1003
    @WatchColumnChanges()
1004
    @Input()
42,261✔
1005
    public get pinned(): boolean {
1006
        return this._pinned;
42,261!
1007
    }
119,273✔
1008
    /**
1009
     * Sets whether the column is pinned.
1010
     * Default value is `false`.
1011
     * ```html
1012
     * <igx-column [pinned] = "true"></igx-column>
1013
     * ```
73,497✔
1014
     *
1015
     * Two-way data binding.
1016
     * ```html
1017
     * <igx-column [(pinned)] = "model.columns[0].isPinned"></igx-column>
2,994✔
1018
     * ```
2,994✔
1019
     *
37✔
1020
     * @memberof IgxColumnComponent
1021
     */
37!
1022
    public set pinned(value: boolean) {
74!
1023
        if (this._pinned !== value) {
1024
            const isAutoWidth = this.width && typeof this.width === 'string' && this.width === 'fit-content';
1025
            if (this.grid && this.width && (isAutoWidth || !isNaN(parseInt(this.width, 10)))) {
1026
                if (value) {
1027
                    this.pin();
1028
                } else {
1029
                    this.unpin();
2,957✔
1030
                }
1031
                return;
1032
            }
1033
            /* No grid/width available at initialization. `initPinning` in the grid
1034
               will re-init the group (if present)
1035
            */
133,083✔
1036
            this._pinned = value;
180,743✔
1037
            this.pinnedChange.emit(this._pinned);
10,165✔
1038
        }
1039
    }
10,165✔
1040

9,803✔
1041
    /**
1042
     * Gets the column `summaries`.
1043
     * ```typescript
1044
     * let columnSummaries = this.column.summaries;
1045
     * ```
1046
     *
4,260✔
1047
     * @memberof IgxColumnComponent
1048
     */
1049
    @notifyChanges(true)
5,543✔
1050
    @WatchColumnChanges()
1051
    @Input()
1052
    public get summaries(): any {
5,543✔
1053
        return this._summaries;
1054
    }
1055
    /**
1056
     * Sets the column `summaries`.
1057
     * ```typescript
1058
     * this.column.summaries = IgxNumberSummaryOperand;
1059
     * ```
1060
     *
1061
     * @memberof IgxColumnComponent
1062
     */
10,165✔
1063
    public set summaries(classRef: any) {
1064
        if (isConstructor(classRef)) {
1065
            this._summaries = new classRef();
10,165✔
1066
        }
1067

10,165✔
1068
        if (this.grid) {
1069
            this.grid.summaryService.removeSummariesCachePerColumn(this.field);
1070
            this.grid.summaryPipeTrigger++;
133,083✔
1071
            this.grid.summaryService.resetSummaryHeight();
1072
        }
1073
    }
1074
    /**
22,078✔
1075
     * Gets the column `filters`.
1076
     * ```typescript
22,078✔
1077
     * let columnFilters = this.column.filters'
22,078✔
1078
     * ```
48,857✔
1079
     *
20,370✔
1080
     * @memberof IgxColumnComponent
1081
     */
1082
    @Input()
28,487✔
1083
    public get filters(): IgxFilteringOperand {
1084
        return this._filters;
1085
    }
22,078✔
1086
    /**
1087
     * Sets the column `filters`.
1088
     * ```typescript
1089
     * this.column.filters = IgxBooleanFilteringOperand.instance().
6!
UNCOV
1090
     * ```
×
1091
     *
1092
     * @memberof IgxColumnComponent
6✔
1093
     */
6✔
1094
    public set filters(instance: IgxFilteringOperand) {
6!
1095
        this._filters = instance;
6✔
1096
    }
36✔
1097
    /**
10✔
1098
     * Gets the column `sortStrategy`.
1099
     * ```typescript
1100
     * let sortStrategy = this.column.sortStrategy
6✔
1101
     * ```
6✔
1102
     *
10!
UNCOV
1103
     * @memberof IgxColumnComponent
×
1104
     */
1105
    @Input()
1106
    public get sortStrategy(): ISortingStrategy {
10✔
1107
        return this._sortStrategy;
1108
    }
1109
    /**
6✔
1110
     * Sets the column `sortStrategy`.
1111
     * ```typescript
1112
     * this.column.sortStrategy = new CustomSortingStrategy().
1113
     * class CustomSortingStrategy extends SortingStrategy {...}
1114
     * ```
1115
     *
1116
     * @memberof IgxColumnComponent
1117
     */
1118
    public set sortStrategy(classRef: ISortingStrategy) {
1119
        this._sortStrategy = classRef;
1120
    }
1121
    /**
1122
     * Gets the function that compares values for grouping.
1123
     * ```typescript
1124
     * let groupingComparer = this.column.groupingComparer'
1125
     * ```
1126
     *
1127
     * @memberof IgxColumnComponent
1128
     */
593✔
1129
    @Input()
593✔
1130
    public get groupingComparer(): (a: any, b: any, currRec?: any, groupRec?: any) => number {
56✔
1131
        return this._groupingComparer;
1132
    }
537✔
1133
    /**
10✔
1134
     * Sets a custom function to compare values for grouping.
1135
     * Subsequent values in the sorted data that the function returns 0 for are grouped.
527✔
1136
     * ```typescript
527✔
1137
     * this.column.groupingComparer = (a: any, b: any, currRec?: any, groupRec?: any) => { return a === b ? 0 : -1; }
2✔
1138
     * ```
1139
     *
525!
UNCOV
1140
     * @memberof IgxColumnComponent
×
1141
     */
1142
    public set groupingComparer(funcRef: (a: any, b: any, currRec?: any, groupRec?: any) => number) {
1,081✔
1143
        this._groupingComparer = funcRef;
525✔
1144
    }
525✔
1145
    /**
525✔
1146
     * Gets the default minimum `width` of the column.
525!
UNCOV
1147
     * ```typescript
×
1148
     * let defaultMinWidth =  this.column.defaultMinWidth;
1149
     * ```
525✔
1150
     *
525✔
1151
     * @memberof IgxColumnComponent
525✔
1152
     */
1153
    public get defaultMinWidth(): string {
525✔
1154
        if (!this.grid) {
1155
            return '80';
525✔
1156
        }
400✔
1157
        switch (this.grid.displayDensity) {
352✔
1158
            case DisplayDensity.cosy:
1159
                return '64';
1160
            case DisplayDensity.compact:
1161
                return '56';
48✔
1162
            default:
48✔
1163
                return '80';
1164
        }
48✔
1165
    }
85✔
1166
    /**
85✔
1167
     * Returns a reference to the `summaryTemplate`.
1168
     * ```typescript
48✔
1169
     * let summaryTemplate = this.column.summaryTemplate;
1170
     * ```
400✔
1171
     *
180✔
1172
     * @memberof IgxColumnComponent
180✔
1173
     */
1174
    @notifyChanges()
1175
    @WatchColumnChanges()
525✔
1176
    @Input()
10✔
1177
    public get summaryTemplate(): TemplateRef<IgxSummaryTemplateContext> {
1178
        return this._summaryTemplate;
1179
    }
525✔
1180
    /**
186✔
1181
     * Sets the summary template.
59✔
1182
     * ```html
1183
     * <ng-template #summaryTemplate igxSummary let-summaryResults>
525✔
1184
     *    <p>{{ summaryResults[0].label }}: {{ summaryResults[0].summaryResult }}</p>
525✔
1185
     *    <p>{{ summaryResults[1].label }}: {{ summaryResults[1].summaryResult }}</p>
525✔
1186
     * </ng-template>
400✔
1187
     * ```
1188
     * ```typescript
525✔
1189
     * @ViewChild("'summaryTemplate'", {read: TemplateRef })
525✔
1190
     * public summaryTemplate: TemplateRef<any>;
525✔
1191
     * this.column.summaryTemplate = this.summaryTemplate;
525✔
1192
     * ```
1193
     *
1194
     * @memberof IgxColumnComponent
1195
     */
1196
    public set summaryTemplate(template: TemplateRef<IgxSummaryTemplateContext>) {
1197
        this._summaryTemplate = template;
1198
    }
1199

1200
    /**
1201
     * Returns a reference to the `bodyTemplate`.
1202
     * ```typescript
1203
     * let bodyTemplate = this.column.bodyTemplate;
1204
     * ```
1205
     *
1206
     * @memberof IgxColumnComponent
1207
     */
331✔
1208
    @notifyChanges()
331✔
1209
    @WatchColumnChanges()
198✔
1210
    @Input('cellTemplate')
1211
    public get bodyTemplate(): TemplateRef<IgxCellTemplateContext> {
133✔
1212
        return this._bodyTemplate;
7✔
1213
    }
1214
    /**
126✔
1215
     * Sets the body template.
126✔
1216
     * ```html
2✔
1217
     * <ng-template #bodyTemplate igxCell let-val>
1218
     *    <div style = "background-color: yellowgreen" (click) = "changeColor(val)">
1219
     *       <span> {{val}} </span>
1220
     *    </div>
124✔
1221
     * </ng-template>
1,055✔
1222
     * ```
120✔
1223
     * ```typescript
1,367✔
1224
     * @ViewChild("'bodyTemplate'", {read: TemplateRef })
120✔
1225
     * public bodyTemplate: TemplateRef<any>;
1226
     * this.column.bodyTemplate = this.bodyTemplate;
124✔
1227
     * ```
124✔
1228
     *
124!
UNCOV
1229
     * @memberof IgxColumnComponent
×
1230
     */
1231
    public set bodyTemplate(template: TemplateRef<IgxCellTemplateContext>) {
124✔
1232
        this._bodyTemplate = template;
124✔
1233
    }
124✔
1234
    /**
1235
     * Returns a reference to the header template.
124✔
1236
     * ```typescript
1237
     * let headerTemplate = this.column.headerTemplate;
124✔
1238
     * ```
120✔
1239
     *
120!
1240
     * @memberof IgxColumnComponent
120✔
1241
     */
1242
    @notifyChanges()
1243
    @WatchColumnChanges()
124✔
1244
    @Input()
4✔
1245
    public get headerTemplate(): TemplateRef<IgxColumnTemplateContext> {
1246
        return this._headerTemplate;
124✔
1247
    }
94✔
1248
    /**
1249
     * Sets the header template.
124✔
1250
     * Note that the column header height is fixed and any content bigger than it will be cut off.
124✔
1251
     * ```html
124✔
1252
     * <ng-template #headerTemplate>
124✔
1253
     *   <div style = "background-color:black" (click) = "changeColor(val)">
236✔
1254
     *       <span style="color:red" >{{column.field}}</span>
1255
     *   </div>
124✔
1256
     * </ng-template>
124✔
1257
     * ```
124✔
1258
     * ```typescript
1259
     * @ViewChild("'headerTemplate'", {read: TemplateRef })
1260
     * public headerTemplate: TemplateRef<any>;
1261
     * this.column.headerTemplate = this.headerTemplate;
1262
     * ```
1263
     *
1264
     * @memberof IgxColumnComponent
1265
     */
1266
    public set headerTemplate(template: TemplateRef<IgxColumnTemplateContext>) {
1267
        this._headerTemplate = template;
1268
    }
1269
    /**
1270
     * Returns a reference to the inline editor template.
1271
     * ```typescript
1272
     * let inlineEditorTemplate = this.column.inlineEditorTemplate;
349✔
1273
     * ```
1274
     *
343✔
1275
     * @memberof IgxColumnComponent
36✔
1276
     */
36✔
1277
    @notifyChanges()
36✔
1278
    @WatchColumnChanges()
3✔
1279
    @Input('cellEditorTemplate')
1280
    public get inlineEditorTemplate(): TemplateRef<IgxCellTemplateContext> {
33✔
1281
        return this._inlineEditorTemplate;
84✔
1282
    }
1283
    /**
1284
     * Sets the inline editor template.
1285
     * ```html
1286
     * <ng-template #inlineEditorTemplate igxCellEditor let-cell="cell">
1287
     *     <input type="string" [(ngModel)]="cell.value"/>
1288
     * </ng-template>
33✔
1289
     * ```
200✔
1290
     * ```typescript
80✔
1291
     * @ViewChild("'inlineEditorTemplate'", {read: TemplateRef })
1292
     * public inlineEditorTemplate: TemplateRef<any>;
1293
     * this.column.inlineEditorTemplate = this.inlineEditorTemplate;
65✔
1294
     * ```
15✔
1295
     *
1296
     * @memberof IgxColumnComponent
33✔
1297
     */
9✔
1298
    public set inlineEditorTemplate(template: TemplateRef<IgxCellTemplateContext>) {
1299
        this._inlineEditorTemplate = template;
24✔
1300
    }
24✔
1301

1302
    /**
1303
     * Returns a reference to the validation error template.
1304
     * ```typescript
1305
     * let errorTemplate = this.column.errorTemplate;
1306
     * ```
1307
     */
1308
    @notifyChanges()
70✔
1309
    @WatchColumnChanges()
70✔
1310
    @Input('errorTemplate')
1311
    public get errorTemplate(): TemplateRef<IgxCellTemplateContext> {
1312
        return this._errorTemplate;
1313
    }
1314
    /**
1315
     * Sets the error template.
1316
     * ```html
1317
     * <ng-template igxCellValidationError let-cell="cell" #errorTemplate >
65✔
1318
     *     <div *ngIf="cell.validation.errors?.['forbiddenName']">
65✔
1319
     *      This name is forbidden.
65✔
1320
     *     </div>
65!
UNCOV
1321
     * </ng-template>
×
1322
     * ```
1323
     * ```typescript
65✔
1324
     * @ViewChild("'errorTemplate'", {read: TemplateRef })
65✔
1325
     * public errorTemplate: TemplateRef<any>;
1326
     * this.column.errorTemplate = this.errorTemplate;
1327
     * ```
1328
     */
1329
    public set errorTemplate(template: TemplateRef<IgxCellTemplateContext>) {
1330
        this._errorTemplate = template;
1331
    }
1332

1333
    /**
1334
     * Returns a reference to the `filterCellTemplate`.
1335
     * ```typescript
486✔
1336
     * let filterCellTemplate = this.column.filterCellTemplate;
486✔
1337
     * ```
34✔
1338
     *
1339
     * @memberof IgxColumnComponent
486✔
1340
     */
1341
    @notifyChanges()
1342
    @WatchColumnChanges()
1343
    @Input('filterCellTemplate')
1344
    public get filterCellTemplate(): TemplateRef<IgxColumnTemplateContext> {
1345
        return this._filterCellTemplate;
1346
    }
1347
    /**
1348
     * Sets the quick filter template.
1349
     * ```html
1350
     * <ng-template #filterCellTemplate IgxFilterCellTemplate let-column="column">
1351
     *    <input (input)="onInput()">
1,551✔
1352
     * </ng-template>
1353
     * ```
1354
     * ```typescript
1355
     * @ViewChild("'filterCellTemplate'", {read: TemplateRef })
1356
     * public filterCellTemplate: TemplateRef<any>;
1357
     * this.column.filterCellTemplate = this.filterCellTemplate;
1358
     * ```
1359
     *
1360
     * @memberof IgxColumnComponent
1361
     */
1362
    public set filterCellTemplate(template: TemplateRef<IgxColumnTemplateContext>) {
1363
        this._filterCellTemplate = template;
6,998✔
1364
    }
1365

1366
    /**
1367
     * Gets the cells of the column.
1368
     * ```typescript
1369
     * let columnCells = this.column.cells;
1370
     * ```
1371
     *
9,890✔
1372
     */
1373
    public get cells(): CellType[] {
1374
        return this.grid.dataView
1375
            .map((rec, index) => {
1376
                if (!this.grid.isGroupByRecord(rec) && !this.grid.isSummaryRow(rec)) {
1377
                    this.grid.pagingMode === 1 && this.grid.page !== 0 ? index = index + this.grid.perPage * this.grid.page : index = this.grid.dataRowList.first.index + index;
1378
                    const cell = new IgxGridCell(this.grid as any, index, this);
1379
                    return cell;
1380
                }
1381
            }).filter(cell => cell);
1382
    }
1383

1384

17✔
1385
    /**
18!
1386
     * @hidden @internal
18✔
1387
     */
18✔
1388
    public get _cells(): CellType[] {
1389
        return this.grid.rowList.filter((row) => row instanceof IgxRowDirective)
1390
            .map((row) => {
1391
                if (row._cells) {
1392
                    return row._cells.filter((cell) => cell.columnIndex === this.index);
1393
                }
8✔
1394
            }).reduce((a, b) => a.concat(b), []);
26✔
1395
    }
1✔
1396

26✔
1397
    /**
1398
     * Gets the column visible index.
26✔
1399
     * If the column is not visible, returns `-1`.
2✔
1400
     * ```typescript
2✔
1401
     * let visibleColumnIndex =  this.column.visibleIndex;
2✔
1402
     * ```
1403
     *
1404
     * @memberof IgxColumnComponent
24✔
1405
     */
1406
    public get visibleIndex(): number {
26✔
1407
        if (!isNaN(this._vIndex)) {
26✔
1408
            return this._vIndex;
26✔
1409
        }
1!
1410
        const unpinnedColumns = this.grid.unpinnedColumns.filter(c => !c.columnGroup);
1411
        const pinnedColumns = this.grid.pinnedColumns.filter(c => !c.columnGroup);
25✔
1412
        // eslint-disable-next-line @typescript-eslint/no-this-alias
1!
1413
        let col = this;
1414
        let vIndex = -1;
26✔
1415

1416
        if (this.columnGroup) {
1417
            col = this.allChildren.filter(c => !c.columnGroup && !c.hidden)[0] as any;
1418
        }
1419
        if (this.columnLayoutChild) {
1420
            return this.parent.childrenVisibleIndexes.find(x => x.column === this).index;
297,285✔
1421
        }
288,072✔
1422

1423
        if (!this.pinned) {
9,213✔
1424
            const indexInCollection = unpinnedColumns.indexOf(col);
9,213✔
1425
            vIndex = indexInCollection === -1 ?
1426
                -1 :
1427
                (this.grid.isPinningToStart ?
1428
                    pinnedColumns.length + indexInCollection :
1429
                    indexInCollection);
1430
        } else {
1431
            const indexInCollection = pinnedColumns.indexOf(col);
25✔
1432
            vIndex = this.grid.isPinningToStart ?
1433
                indexInCollection :
1434
                unpinnedColumns.length + indexInCollection;
1435
        }
1436
        this._vIndex = vIndex;
1437
        return vIndex;
1438
    }
1439

1440
    /**
1441
     * Returns a boolean indicating if the column is a `ColumnGroup`.
1442
     * ```typescript
1443
     * let columnGroup =  this.column.columnGroup;
1444
     * ```
1445
     *
25✔
1446
     * @memberof IgxColumnComponent
25✔
1447
     */
25!
1448
    public get columnGroup() {
25✔
1449
        return false;
270✔
1450
    }
25✔
1451
    /**
25✔
1452
     * Returns a boolean indicating if the column is a `ColumnLayout` for multi-row layout.
25✔
1453
     * ```typescript
1454
     * let columnGroup =  this.column.columnGroup;
25✔
1455
     * ```
1456
     *
25✔
1457
     * @memberof IgxColumnComponent
24✔
1458
     */
24✔
1459
    public get columnLayout() {
1460
        return false;
25✔
1461
    }
25✔
1462

25!
UNCOV
1463
    /**
×
1464
     * Returns a boolean indicating if the column is a child of a `ColumnLayout` for multi-row layout.
1465
     * ```typescript
1466
     * let columnLayoutChild =  this.column.columnLayoutChild;
25✔
1467
     * ```
1468
     *
1469
     * @memberof IgxColumnComponent
1470
     */
1471
    public get columnLayoutChild() {
1472
        return this.parent && this.parent.columnLayout;
1473
    }
875,878✔
1474

875,878✔
1475
    /** @hidden @internal **/
875,878!
UNCOV
1476
    public get allChildren(): IgxColumnComponent[] {
×
1477
        return [];
1478
    }
875,878✔
1479
    /**
868,019✔
1480
     * Returns the level of the column in a column group.
868,019✔
1481
     * Returns `0` if the column doesn't have a `parent`.
1,877✔
1482
     * ```typescript
1483
     * let columnLevel =  this.column.level;
868,019✔
1484
     * ```
1485
     *
1486
     * @memberof IgxColumnComponent
7,859✔
1487
     */
1488
    public get level() {
1489
        let ptr = this.parent;
1490
        let lvl = 0;
1491

1492
        while (ptr) {
1493
            lvl++;
1494
            ptr = ptr.parent;
10,724✔
1495
        }
10,724✔
1496
        return lvl;
1497
    }
1498

1499
    /** @hidden @internal **/
1500
    public get isLastPinned(): boolean {
1501
        return this.grid.isPinningToStart &&
1502
            this.grid.pinnedColumns[this.grid.pinnedColumns.length - 1] === this;
289,662✔
1503
    }
289,662✔
1504

289,662✔
1505
    /** @hidden @internal **/
289,662✔
1506
    public get isFirstPinned(): boolean {
846✔
1507
        const pinnedCols = this.grid.pinnedColumns.filter(x => !x.columnGroup);
1508
        return !this.grid.isPinningToStart && pinnedCols[0] === this;
288,816✔
1509
    }
1510

28,134✔
1511
    /** @hidden @internal **/
1512
    public get rightPinnedOffset(): string {
1513
        return this.pinned && !this.grid.isPinningToStart ?
260,682✔
1514
            - this.grid.pinnedWidth - this.grid.headerFeaturesWidth + 'px' :
1515
            null;
289,662✔
1516
    }
1517

1518
    public get gridRowSpan(): number {
1519
        return this.rowEnd && this.rowStart ? this.rowEnd - this.rowStart : 1;
1520
    }
1521
    public get gridColumnSpan(): number {
1522
        return this.colEnd && this.colStart ? this.colEnd - this.colStart : 1;
273✔
1523
    }
254✔
1524

1✔
1525
    /**
1✔
1526
     * Indicates whether the column will be visible when its parent is collapsed.
1527
     * ```html
253✔
1528
     * <igx-column-group>
1529
     *   <igx-column [visibleWhenCollapsed]="true"></igx-column>
1530
     * </igx-column-group>
1531
     * ```
1532
     *
1533
     * @memberof IgxColumnComponent
1534
     */
1535
    @notifyChanges(true)
2,197!
UNCOV
1536
    @Input()
×
1537
    public set visibleWhenCollapsed(value: boolean) {
1538
        this._visibleWhenCollapsed = value;
5,415✔
1539
        this.visibleWhenCollapsedChange.emit(this._visibleWhenCollapsed);
3,371✔
1540
        if (this.parent) {
1541
            this.parent?.setExpandCollapseState?.();
1542
        }
1543
    }
1544

1545
    public get visibleWhenCollapsed(): boolean {
382✔
1546
        return this._visibleWhenCollapsed;
1547
    }
1548

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

1580
    /**
1581
     * @hidden
1582
     * @internal
1583
     */
1584
    public get collapsible() {
1585
        return false;
1586
    }
1587
    public set collapsible(_value: boolean) { }
1588

1589
    /**
1590
     * @hidden
1591
     * @internal
1592
     */
1593
    public get expanded() {
1594
        return true;
1595
    }
1596
    public set expanded(_value: boolean) { }
1597

1598
    /**
1599
     * @hidden
1600
     */
1601
    public defaultWidth: string;
1602

1603
    /**
1604
     * @hidden
1605
     */
1606
    public widthSetByUser: boolean;
1607

1608
    /**
1609
     * @hidden
1610
     */
1611
    public hasNestedPath: boolean;
1612

1613
    /**
1614
     * @hidden
1615
     * @internal
1616
     */
1617
    public defaultTimeFormat = 'hh:mm:ss tt';
1618

1619
    /**
1620
     * @hidden
1621
     * @internal
1622
     */
1623
    public defaultDateTimeFormat = 'dd/MM/yyyy HH:mm:ss tt';
1624

1625

1626
    /**
1627
     * Returns the filteringExpressionsTree of the column.
1628
     * ```typescript
1629
     * let tree =  this.column.filteringExpressionsTree;
1630
     * ```
1631
     *
1632
     * @memberof IgxColumnComponent
1633
     */
2✔
1634
    public get filteringExpressionsTree(): FilteringExpressionsTree {
1635
        return this.grid.filteringExpressionsTree.find(this.field) as FilteringExpressionsTree;
1636
    }
1637
    /**
2✔
1638
     * Sets/gets the parent column.
1639
     * ```typescript
1640
     * let parentColumn = this.column.parent;
1641
     * ```
2✔
1642
     * ```typescript
1643
     * this.column.parent = higherLevelColumn;
1644
     * ```
2✔
1645
     *
1646
     * @memberof IgxColumnComponent
1647
     */
2✔
1648
    public parent = null;
1649
    /**
1650
     * Sets/gets the children columns.
1651
     * ```typescript
2✔
1652
     * let columnChildren = this.column.children;
1653
     * ```
1654
     * ```typescript
2✔
1655
     * this.column.children = childrenColumns;
1656
     * ```
1657
     *
1658
     * @memberof IgxColumnComponent
2✔
1659
     */
1660
    public children: QueryList<IgxColumnComponent>;
1661
    /**
2✔
1662
     * @hidden
1663
     */
1664
    public destroy$ = new Subject<any>();
2✔
1665

1666
    /**
1667
     * @hidden
1668
     */
2✔
1669
    protected _applySelectableClass = false;
1670

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

1771
    private _field: string;
2✔
1772
    private _calcWidth = null;
1773
    private _columnPipeArgs: IColumnPipeArgs = { digitsInfo: DEFAULT_DIGITS_INFO };
1774

1775
    constructor(
1776
        @Inject(IGX_GRID_BASE) public grid: GridType,
1777
        @Optional() @Self() @Inject(NG_VALIDATORS) private _validators: Validator[],
1778
        /** @hidden @internal **/
1779
        public cdr: ChangeDetectorRef,
1780
        protected platform: PlatformUtil,
1781
    ) {
1782
        this.validators  = _validators;
1783
     }
1784

1785
    /**
1786
     * @hidden
1787
     * @internal
1788
     */
1789
    public resetCaches() {
1790
        this._vIndex = NaN;
1791
        if (this.grid) {
1792
            this.cacheCalcWidth();
1793
        }
1794
    }
1795

1796
    /**
1797
     * @hidden
1798
     */
1799
    public ngOnDestroy() {
1800
        this.destroy$.next(true);
1801
        this.destroy$.complete();
1802
    }
1803
    /**
1804
     * @hidden
1805
     */
1806
    public ngAfterContentInit(): void {
1807
        if (this.summaryTemplateDirective) {
1808
            this._summaryTemplate = this.summaryTemplateDirective.template;
1809
        }
1810
        if (this.cellTemplate) {
1811
            this._bodyTemplate = this.cellTemplate.template;
1812
        }
1813
        if (this.cellValidationErrorTemplate) {
1814
            this._errorTemplate = this.cellValidationErrorTemplate.template;
1815
        }
1816
        if (this.headTemplate && this.headTemplate.length) {
1817
            this._headerTemplate = this.headTemplate.toArray()[0].template;
1818
        }
1819
        if (this.editorTemplate) {
1820
            this._inlineEditorTemplate = this.editorTemplate.template;
1821
        }
1822
        if (this.filterCellTemplateDirective) {
1823
            this._filterCellTemplate = this.filterCellTemplateDirective.template;
1824
        }
1825
        if (!this._columnPipeArgs.format) {
1826
            this._columnPipeArgs.format = this.dataType === GridColumnDataType.Time ?
1827
                DEFAULT_TIME_FORMAT : this.dataType === GridColumnDataType.DateTime ?
1828
                    DEFAULT_DATE_TIME_FORMAT : DEFAULT_DATE_FORMAT;
1829
        }
1830
        if (!this.summaries) {
1831
            switch (this.dataType) {
1832
                case GridColumnDataType.Number:
1833
                case GridColumnDataType.Currency:
1834
                case GridColumnDataType.Percent:
1835
                    this.summaries = IgxNumberSummaryOperand;
1836
                    break;
1837
                case GridColumnDataType.Date:
1838
                case GridColumnDataType.DateTime:
1839
                    this.summaries = IgxDateSummaryOperand;
1840
                    break;
1841
                case GridColumnDataType.Time:
1842
                    this.summaries = IgxTimeSummaryOperand;
1843
                    break;
1844

1845
                case GridColumnDataType.String:
1846
                case GridColumnDataType.Boolean:
1847
                default:
1848
                    this.summaries = IgxSummaryOperand;
1849
                    break;
1850
            }
1851
        }
1852
        if (!this.filters) {
1853
            switch (this.dataType) {
1854
                case GridColumnDataType.Boolean:
1855
                    this.filters = IgxBooleanFilteringOperand.instance();
1856
                    break;
1857
                case GridColumnDataType.Number:
1858
                case GridColumnDataType.Currency:
1859
                case GridColumnDataType.Percent:
1860
                    this.filters = IgxNumberFilteringOperand.instance();
1861
                    break;
1862
                case GridColumnDataType.Date:
1863
                    this.filters = IgxDateFilteringOperand.instance();
1864
                    break;
1865
                case GridColumnDataType.Time:
1866
                    this.filters = IgxTimeFilteringOperand.instance();
1867
                    break;
1868
                case GridColumnDataType.DateTime:
1869
                    this.filters = IgxDateTimeFilteringOperand.instance();
1870
                    break;
1871
                case GridColumnDataType.Image:
1872
                    this.filterable = false;
1873
                    break;
1874
                case GridColumnDataType.String:
1875
                default:
1876
                    this.filters = IgxStringFilteringOperand.instance();
1877
                    break;
1878
            }
1879
        }
1880
    }
1881

1882
    /**
1883
     * @hidden
1884
     */
1885
    public getGridTemplate(isRow: boolean): string {
1886
        if (isRow) {
1887
            const rowsCount = !this.grid.isPivot ? this.grid.multiRowLayoutRowSize : this.children.length - 1;
1888
            return `repeat(${rowsCount},1fr)`;
1889
        } else {
1890
            return this.getColumnSizesString(this.children);
1891
        }
1892
    }
1893

1894
    /** @hidden @internal **/
1895
    public getInitialChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<MRLColumnSizeInfo> {
1896
        const columnSizes: MRLColumnSizeInfo[] = [];
1897
        // find the smallest col spans
1898
        children.forEach(col => {
1899
            if (!col.colStart) {
1900
                return;
1901
            }
1902
            const newWidthSet = col.widthSetByUser && columnSizes[col.colStart - 1] && !columnSizes[col.colStart - 1].widthSetByUser;
1903
            const newSpanSmaller = columnSizes[col.colStart - 1] && columnSizes[col.colStart - 1].colSpan > col.gridColumnSpan;
1904
            const bothWidthsSet = col.widthSetByUser && columnSizes[col.colStart - 1] && columnSizes[col.colStart - 1].widthSetByUser;
1905
            const bothWidthsNotSet = !col.widthSetByUser && columnSizes[col.colStart - 1] && !columnSizes[col.colStart - 1].widthSetByUser;
1906

1907
            if (columnSizes[col.colStart - 1] === undefined) {
1908
                // If nothing is defined yet take any column at first
1909
                // We use colEnd to know where the column actually ends, because not always it starts where we have it set in columnSizes.
1910
                columnSizes[col.colStart - 1] = {
1911
                    ref: col,
1912
                    width: col.width === 'fit-content' ? col.autoSize :
1913
                        col.widthSetByUser || this.grid.columnWidthSetByUser ? parseInt(col.calcWidth, 10) : null,
1914
                    colSpan: col.gridColumnSpan,
1915
                    colEnd: col.colStart + col.gridColumnSpan,
1916
                    widthSetByUser: col.widthSetByUser
1917
                };
1918
            } else if (newWidthSet || (newSpanSmaller && ((bothWidthsSet) || (bothWidthsNotSet)))) {
1919
                // If a column is set already it should either not have width defined or have width with bigger span than the new one.
1920

1921
                /**
1922
                 *  If replaced column has bigger span, we want to fill the remaining columns
1923
                 *  that the replacing column does not fill with the old one.
1924
                 */
1925
                if (bothWidthsSet && newSpanSmaller) {
1926
                    // Start from where the new column set would end and apply the old column to the rest depending on how much it spans.
1927
                    // We have not yet replaced it so we can use it directly from the columnSizes collection.
1928
                    // This is where colEnd is used because the colStart of the old column is not actually i + 1.
1929
                    for (let i = col.colStart - 1 + col.gridColumnSpan; i < columnSizes[col.colStart - 1].colEnd - 1; i++) {
1930
                        if (!columnSizes[i] || !columnSizes[i].widthSetByUser) {
1931
                            columnSizes[i] = columnSizes[col.colStart - 1];
1932
                        } else {
1933
                            break;
1934
                        }
1935
                    }
1936
                }
1937

1938
                // Replace the old column with the new one.
1939
                columnSizes[col.colStart - 1] = {
1940
                    ref: col,
1941
                    width: col.width === 'fit-content' ? col.autoSize :
1942
                        col.widthSetByUser || this.grid.columnWidthSetByUser ? parseInt(col.calcWidth, 10) : null,
1943
                    colSpan: col.gridColumnSpan,
1944
                    colEnd: col.colStart + col.gridColumnSpan,
1945
                    widthSetByUser: col.widthSetByUser
1946
                };
1947
            } else if (bothWidthsSet && columnSizes[col.colStart - 1].colSpan < col.gridColumnSpan) {
1948
                // If the column already in the columnSizes has smaller span, we still need to fill any empty places with the current col.
1949
                // Start from where the smaller column set would end and apply the bigger column to the rest depending on how much it spans.
1950
                // Since here we do not have it in columnSizes we set it as a new column keeping the same colSpan.
1951
                for (let i = col.colStart - 1 + columnSizes[col.colStart - 1].colSpan; i < col.colStart - 1 + col.gridColumnSpan; i++) {
1952
                    if (!columnSizes[i] || !columnSizes[i].widthSetByUser) {
1953
                        columnSizes[i] = {
1954
                            ref: col,
1955
                            width: col.width === 'fit-content' ? col.autoSize :
1956
                                col.widthSetByUser || this.grid.columnWidthSetByUser ? parseInt(col.calcWidth, 10) : null,
1957
                            colSpan: col.gridColumnSpan,
1958
                            colEnd: col.colStart + col.gridColumnSpan,
1959
                            widthSetByUser: col.widthSetByUser
1960
                        };
1961
                    } else {
1962
                        break;
1963
                    }
1964
                }
1965
            }
1966
        });
1967

1968
        // Flatten columnSizes so there are not columns with colSpan > 1
1969
        for (let i = 0; i < columnSizes.length; i++) {
1970
            if (columnSizes[i] && columnSizes[i].colSpan > 1) {
1971
                let j = 1;
1972

1973
                // Replace all empty places depending on how much the current column spans starting from next col.
1974
                for (; j < columnSizes[i].colSpan && i + j + 1 < columnSizes[i].colEnd; j++) {
1975
                    if (columnSizes[i + j] &&
1976
                        ((!columnSizes[i].width && columnSizes[i + j].width) ||
1977
                            (!columnSizes[i].width && !columnSizes[i + j].width && columnSizes[i + j].colSpan <= columnSizes[i].colSpan) ||
1978
                            (!!columnSizes[i + j].width && columnSizes[i + j].colSpan <= columnSizes[i].colSpan))) {
1979
                        // If we reach an already defined column that has width and the current doesn't have or
1980
                        // if the reached column has bigger colSpan we stop.
1981
                        break;
1982
                    } else {
1983
                        const width = columnSizes[i].widthSetByUser ?
1984
                            columnSizes[i].width / columnSizes[i].colSpan :
1985
                            columnSizes[i].width;
1986
                        columnSizes[i + j] = {
1987
                            ref: columnSizes[i].ref,
1988
                            width,
1989
                            colSpan: 1,
1990
                            colEnd: columnSizes[i].colEnd,
1991
                            widthSetByUser: columnSizes[i].widthSetByUser
1992
                        };
1993
                    }
1994
                }
1995

1996
                // Update the current column width so it is divided between all columns it spans and set it to 1.
1997
                columnSizes[i].width = columnSizes[i].widthSetByUser ?
1998
                    columnSizes[i].width / columnSizes[i].colSpan :
1999
                    columnSizes[i].width;
2000
                columnSizes[i].colSpan = 1;
2001

2002
                // Update the index based on how much we have replaced. Subtract 1 because we started from 1.
2003
                i += j - 1;
2004
            }
2005
        }
2006

2007
        return columnSizes;
2008
    }
2009

2010
    /** @hidden @internal **/
2011
    public getFilledChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<string> {
2012
        const columnSizes = this.getInitialChildColumnSizes(children);
2013

2014
        // fill the gaps if there are any
2015
        const result: string[] = [];
2016
        for (const size of columnSizes) {
2017
            if (size && !!size.width) {
2018
                result.push(size.width + 'px');
2019
            } else {
2020
                result.push(parseInt(this.grid.getPossibleColumnWidth(), 10) + 'px');
2021
            }
2022
        }
2023
        return result;
2024
    }
2025

2026
    /** @hidden @internal **/
2027
    public getResizableColUnderEnd(): MRLResizeColumnInfo[] {
2028
        if (this.columnLayout || !this.columnLayoutChild || this.columnGroup) {
2029
            return [{ target: this, spanUsed: 1 }];
2030
        }
2031

2032
        const columnSized = this.getInitialChildColumnSizes(this.parent.children);
2033
        const targets: MRLResizeColumnInfo[] = [];
2034
        const colEnd = this.colEnd ? this.colEnd : this.colStart + 1;
2035

2036
        for (let i = 0; i < columnSized.length; i++) {
2037
            if (this.colStart <= i + 1 && i + 1 < colEnd) {
2038
                targets.push({ target: columnSized[i].ref, spanUsed: 1 });
2039
            }
2040
        }
2041

2042
        const targetsSquashed: MRLResizeColumnInfo[] = [];
2043
        for (const target of targets) {
2044
            if (targetsSquashed.length && targetsSquashed[targetsSquashed.length - 1].target.field === target.target.field) {
2045
                targetsSquashed[targetsSquashed.length - 1].spanUsed++;
2046
            } else {
2047
                targetsSquashed.push(target);
2048
            }
2049
        }
2050

2051
        return targetsSquashed;
2052
    }
2053

2054
    /**
2055
     * Pins the column at the provided index in the pinned area.
2056
     * Defaults to index `0` if not provided, or to the initial index in the pinned area.
2057
     * Returns `true` if the column is successfully pinned. Returns `false` if the column cannot be pinned.
2058
     * Column cannot be pinned if:
2059
     * - Is already pinned
2060
     * - index argument is out of range
2061
     * - The pinned area exceeds 80% of the grid width
2062
     * ```typescript
2063
     * let success = this.column.pin();
2064
     * ```
2065
     *
2066
     * @memberof IgxColumnComponent
2067
     */
2068
    public pin(index?: number): boolean {
2069
        // TODO: Probably should the return type of the old functions
2070
        // should be moved as a event parameter.
2071
        const grid = (this.grid as any);
2072
        if (this._pinned) {
2073
            return false;
2074
        }
2075

2076
        if (this.parent && !this.parent.pinned) {
2077
            return this.topLevelParent.pin(index);
2078
        }
2079

2080
        const hasIndex = index !== undefined;
2081
        if (hasIndex && (index < 0 || index > grid.pinnedColumns.length)) {
2082
            return false;
2083
        }
2084

2085
        if (!this.parent && !this.pinnable) {
2086
            return false;
2087
        }
2088

2089
        const rootPinnedCols = grid._pinnedColumns.filter((c) => c.level === 0);
2090
        index = hasIndex ? index : rootPinnedCols.length;
2091
        const args: IPinColumnCancellableEventArgs = { column: this, insertAtIndex: index, isPinned: false, cancel: false };
2092
        this.grid.columnPin.emit(args);
2093

2094
        if (args.cancel) {
2095
            return;
2096
        }
2097

2098
        this.grid.crudService.endEdit(false);
2099

2100
        this._pinned = true;
2101
        this.pinnedChange.emit(this._pinned);
2102
        // it is possible that index is the last position, so will need to find target column by [index-1]
2103
        const targetColumn = args.insertAtIndex === grid._pinnedColumns.length ?
2104
            grid._pinnedColumns[args.insertAtIndex - 1] : grid._pinnedColumns[args.insertAtIndex];
2105

2106
        if (grid._pinnedColumns.indexOf(this) === -1) {
2107
            if (!grid.hasColumnGroups) {
2108
                grid._pinnedColumns.splice(args.insertAtIndex, 0, this);
2109
            } else {
2110
                // insert based only on root collection
2111
                rootPinnedCols.splice(args.insertAtIndex, 0, this);
2112
                let allPinned = [];
2113
                // re-create hierarchy
2114
                rootPinnedCols.forEach(group => {
2115
                    allPinned.push(group);
2116
                    allPinned = allPinned.concat(group.allChildren);
2117
                });
2118
                grid._pinnedColumns = allPinned;
2119
            }
2120

2121
            if (grid._unpinnedColumns.indexOf(this) !== -1) {
2122
                const childrenCount = this.allChildren.length;
2123
                grid._unpinnedColumns.splice(grid._unpinnedColumns.indexOf(this), 1 + childrenCount);
2124
            }
2125
        }
2126

2127
        if (hasIndex) {
2128
            index === grid._pinnedColumns.length - 1 ? 
2129
            grid._moveColumns(this, targetColumn, DropPosition.AfterDropTarget) : grid._moveColumns(this, targetColumn, DropPosition.BeforeDropTarget);
2130
        }
2131

2132
        if (this.columnGroup) {
2133
            this.allChildren.forEach(child => child.pin());
2134
            grid.reinitPinStates();
2135
        }
2136

2137
        grid.resetCaches();
2138
        grid.notifyChanges();
2139
        if (this.columnLayoutChild) {
2140
            this.grid.columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
2141
        }
2142
        this.grid.filteringService.refreshExpressions();
2143
        const eventArgs: IPinColumnEventArgs = { column: this, insertAtIndex: index, isPinned: true };
2144
        this.grid.columnPinned.emit(eventArgs);
2145
        return true;
2146
    }
2147
    /**
2148
     * Unpins the column and place it at the provided index in the unpinned area.
2149
     * Defaults to index `0` if not provided, or to the initial index in the unpinned area.
2150
     * Returns `true` if the column is successfully unpinned. Returns `false` if the column cannot be unpinned.
2151
     * Column cannot be unpinned if:
2152
     * - Is already unpinned
2153
     * - index argument is out of range
2154
     * ```typescript
2155
     * let success = this.column.unpin();
2156
     * ```
2157
     *
2158
     * @memberof IgxColumnComponent
2159
     */
2160
    public unpin(index?: number): boolean {
2161
        const grid = (this.grid as any);
2162
        if (!this._pinned) {
2163
            return false;
2164
        }
2165

2166
        if (this.parent && this.parent.pinned) {
2167
            return this.topLevelParent.unpin(index);
2168
        }
2169
        const hasIndex = index !== undefined;
2170
        if (hasIndex && (index < 0 || index > grid._unpinnedColumns.length)) {
2171
            return false;
2172
        }
2173

2174
        // estimate the exact index at which column will be inserted
2175
        // takes into account initial unpinned index of the column
2176
        if (!hasIndex) {
2177
            const indices = grid.unpinnedColumns.map(col => col.index);
2178
            indices.push(this.index);
2179
            indices.sort((a, b) => a - b);
2180
            index = indices.indexOf(this.index);
2181
        }
2182

2183
        const args: IPinColumnCancellableEventArgs = { column: this, insertAtIndex: index, isPinned: true, cancel: false };
2184
        this.grid.columnPin.emit(args);
2185

2186
        if (args.cancel) {
2187
            return;
2188
        }
2189

2190
        this.grid.crudService.endEdit(false);
2191

2192
        this._pinned = false;
2193
        this.pinnedChange.emit(this._pinned);
2194

2195
        // it is possible that index is the last position, so will need to find target column by [index-1]
2196
        const targetColumn = args.insertAtIndex === grid._unpinnedColumns.length ?
2197
            grid._unpinnedColumns[args.insertAtIndex - 1] : grid._unpinnedColumns[args.insertAtIndex];
2198

2199
        if (!hasIndex) {
2200
            grid._unpinnedColumns.splice(index, 0, this);
2201
            if (grid._pinnedColumns.indexOf(this) !== -1) {
2202
                grid._pinnedColumns.splice(grid._pinnedColumns.indexOf(this), 1);
2203
            }
2204
        }
2205

2206
        if (hasIndex) {
2207
            grid.moveColumn(this, targetColumn);
2208
        }
2209

2210
        if (this.columnGroup) {
2211
            this.allChildren.forEach(child => child.unpin());
2212
        }
2213

2214
        grid.reinitPinStates();
2215
        grid.resetCaches();
2216

2217
        grid.notifyChanges();
2218
        if (this.columnLayoutChild) {
2219
            this.grid.columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
2220
        }
2221
        this.grid.filteringService.refreshExpressions();
2222

2223
        this.grid.columnPinned.emit({ column: this, insertAtIndex: index, isPinned: false });
2224

2225
        return true;
2226
    }
2227

2228
    /**
2229
     * Moves a column to the specified visible index.
2230
     * If passed index is invalid, or if column would receive a different visible index after moving, moving is not performed.
2231
     * If passed index would move the column to a different column group. moving is not performed.
2232
     *
2233
     * @example
2234
     * ```typescript
2235
     * column.move(index);
2236
     * ```
2237
     * @memberof IgxColumnComponent
2238
     */
2239
    public move(index: number) {
2240
        let target;
2241
        let columns = this.grid.columns.filter(c => c.visibleIndex > -1);
2242
        // grid last visible index
2243
        const li = columns.map(c => c.visibleIndex).reduce((a, b) => Math.max(a, b));
2244
        const parent = this.parent;
2245
        const isPreceding = this.visibleIndex < index;
2246

2247
        if (index === this.visibleIndex || index < 0 || index > li) {
2248
            return;
2249
        }
2250

2251
        if (parent) {
2252
            columns = columns.filter(c => c.level >= this.level && c !== this && c.parent !== this &&
2253
                c.topLevelParent === this.topLevelParent);
2254
        }
2255
        /* eslint-disable max-len */
2256
        // 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.
2257
        // 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.
2258
        /* eslint-enable max-len */
2259
        if (isPreceding) {
2260
            columns = columns.filter(c => c.visibleIndex > this.visibleIndex);
2261
            target = columns.find(c => c.level === this.level && c.visibleIndex + (c as any).calcChildren() - this.calcChildren() === index);
2262
        } else {
2263
            columns = columns.filter(c => c.visibleIndex < this.visibleIndex);
2264
            target = columns.find(c => c.level === this.level && c.visibleIndex === index);
2265
        }
2266

2267
        if (!target || (target.pinned && this.disablePinning)) {
2268
            return;
2269
        }
2270

2271
        const pos = isPreceding ? DropPosition.AfterDropTarget : DropPosition.BeforeDropTarget;
2272
        this.grid.moveColumn(this, target as IgxColumnComponent, pos);
2273
    }
2274

2275
    /**
2276
     * No children for the column, so will returns 1 or 0, if the column is hidden.
2277
     *
2278
     * @hidden
2279
     */
2280
    public calcChildren(): number {
2281
        const children = this.hidden ? 0 : 1;
2282
        return children;
2283
    }
2284

2285
    /**
2286
     * Toggles column vibisility and emits the respective event.
2287
     *
2288
     * @hidden
2289
     */
2290
    public toggleVisibility(value?: boolean) {
2291
        const newValue = value ?? !this.hidden;
2292
        const eventArgs: IColumnVisibilityChangingEventArgs = { column: this, newValue, cancel: false };
2293
        this.grid.columnVisibilityChanging.emit(eventArgs);
2294

2295
        if (eventArgs.cancel) {
2296
            return;
2297
        }
2298
        this.hidden = newValue;
2299
        this.grid.columnVisibilityChanged.emit({ column: this, newValue });
2300
    }
2301

2302
    /**
2303
     * Returns a reference to the top level parent column.
2304
     * ```typescript
2305
     * let topLevelParent =  this.column.topLevelParent;
2306
     * ```
2307
     *
2308
     * @memberof IgxColumnComponent
2309
     */
2310
    public get topLevelParent() {
2311
        let parent = this.parent;
2312
        while (parent && parent.parent) {
2313
            parent = parent.parent;
2314
        }
2315
        return parent;
2316
    }
2317

2318
    /**
2319
     * Returns a reference to the header of the column.
2320
     * ```typescript
2321
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2322
     * let headerCell = column.headerCell;
2323
     * ```
2324
     *
2325
     * @memberof IgxColumnComponent
2326
     */
2327
    public get headerCell(): IgxGridHeaderComponent {
2328
        return this.grid.headerCellList.find((header) => header.column === this);
2329
    }
2330

2331
    /**
2332
     * Returns a reference to the filter cell of the column.
2333
     * ```typescript
2334
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2335
     * let filterell = column.filterell;
2336
     * ```
2337
     *
2338
     * @memberof IgxColumnComponent
2339
     */
2340
    public get filterCell(): IgxGridFilteringCellComponent {
2341
        return this.grid.filterCellList.find((filterCell) => filterCell.column === this);
2342
    }
2343

2344
    /**
2345
     * Returns a reference to the header group of the column.
2346
     *
2347
     * @memberof IgxColumnComponent
2348
     */
2349
    public get headerGroup(): IgxGridHeaderGroupComponent {
2350
        return this.grid.headerGroupsList.find(group => group.column === this);
2351
    }
2352

2353
    /**
2354
     * Autosize the column to the longest currently visible cell value, including the header cell.
2355
     * ```typescript
2356
     * @ViewChild('grid') grid: IgxGridComponent;
2357
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2358
     * column.autosize();
2359
     * ```
2360
     *
2361
     * @memberof IgxColumnComponent
2362
     * @param byHeaderOnly Set if column should be autosized based only on the header content.
2363
     */
2364
    public autosize(byHeaderOnly = false) {
2365
        if (!this.columnGroup) {
2366
            this.width = this.getAutoSize(byHeaderOnly);
2367
            this.grid.reflow();
2368
        }
2369
    }
2370

2371
    /**
2372
     * @hidden
2373
     */
2374
    public getAutoSize(byHeader = false): string {
2375
        const size = !byHeader ? this.getLargestCellWidth() :
2376
            (Object.values(this.getHeaderCellWidths()).reduce((a, b) => a + b) + 'px');
2377
        const isPercentageWidth = this.width && typeof this.width === 'string' && this.width.indexOf('%') !== -1;
2378

2379
        let newWidth;
2380
        if (isPercentageWidth) {
2381
            const gridAvailableSize = this.grid.calcWidth;
2382
            const percentageSize = parseFloat(size) / gridAvailableSize * 100;
2383
            newWidth = percentageSize + '%';
2384
        } else {
2385
            newWidth = size;
2386
        }
2387

2388
        const maxWidth = isPercentageWidth ? this.maxWidthPercent : this.maxWidthPx;
2389
        const minWidth = isPercentageWidth ? this.minWidthPercent : this.minWidthPx;
2390
        if (this.maxWidth && (parseFloat(newWidth) > maxWidth)) {
2391
            newWidth = isPercentageWidth ? maxWidth + '%' : maxWidth + 'px';
2392
        } else if (parseFloat(newWidth) < minWidth) {
2393
            newWidth = isPercentageWidth ? minWidth + '%' : minWidth + 'px';
2394
        }
2395

2396
        return newWidth;
2397
    }
2398

2399
    /**
2400
     * @hidden
2401
     */
2402
    public getCalcWidth(): any {
2403
        if (this._calcWidth && !isNaN(this.calcPixelWidth)) {
2404
            return this._calcWidth;
2405
        }
2406
        this.cacheCalcWidth();
2407
        return this._calcWidth;
2408
    }
2409

2410

2411
    /**
2412
     * @hidden
2413
     * Returns the width and padding of a header cell.
2414
     */
2415
    public getHeaderCellWidths() {
2416
        return this.grid.getHeaderCellWidth(this.headerCell.nativeElement);
2417
    }
2418

2419
    /**
2420
     * @hidden
2421
     * Returns the size (in pixels) of the longest currently visible cell, including the header cell.
2422
     * ```typescript
2423
     * @ViewChild('grid') grid: IgxGridComponent;
2424
     *
2425
     * let column = this.grid.columnList.filter(c => c.field === 'ID')[0];
2426
     * let size = column.getLargestCellWidth();
2427
     * ```
2428
     * @memberof IgxColumnComponent
2429
     */
2430
    public getLargestCellWidth(): string {
2431
        const range = this.grid.document.createRange();
2432
        const largest = new Map<number, number>();
2433

2434
        if (this._cells.length > 0) {
2435
            const cellsContentWidths = [];
2436
            this._cells.forEach((cell) => cellsContentWidths.push(cell.calculateSizeToFit(range)));
2437

2438
            const index = cellsContentWidths.indexOf(Math.max(...cellsContentWidths));
2439
            const cellStyle = this.grid.document.defaultView.getComputedStyle(this._cells[index].nativeElement);
2440
            const cellPadding = parseFloat(cellStyle.paddingLeft) + parseFloat(cellStyle.paddingRight) +
2441
                parseFloat(cellStyle.borderLeftWidth) + parseFloat(cellStyle.borderRightWidth);
2442

2443
            largest.set(Math.max(...cellsContentWidths), cellPadding);
2444
        }
2445

2446
        if (this.headerCell && this.autosizeHeader) {
2447
            const headerCellWidths = this.getHeaderCellWidths();
2448
            largest.set(headerCellWidths.width, headerCellWidths.padding);
2449
        }
2450

2451
        const largestCell = Math.max(...Array.from(largest.keys()));
2452
        const width = Math.ceil(largestCell + largest.get(largestCell));
2453

2454
        if (Number.isNaN(width)) {
2455
            return this.width;
2456
        } else {
2457
            return width + 'px';
2458
        }
2459
    }
2460

2461
    /**
2462
     * @hidden
2463
     */
2464
    public getCellWidth() {
2465
        const colWidth = this.width;
2466
        const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1;
2467

2468
        if (this.columnLayoutChild) {
2469
            return '';
2470
        }
2471

2472
        if (colWidth && !isPercentageWidth) {
2473

2474
            let cellWidth = colWidth;
2475
            if (typeof cellWidth !== 'string' || cellWidth.endsWith('px') === false) {
2476
                cellWidth += 'px';
2477
            }
2478

2479
            return cellWidth;
2480
        } else {
2481
            return colWidth;
2482
        }
2483
    }
2484

2485
    /**
2486
     * @hidden
2487
     */
2488
    public populateVisibleIndexes() { }
2489

2490
    protected getColumnSizesString(children: QueryList<IgxColumnComponent>): string {
2491
        const res = this.getFilledChildColumnSizes(children);
2492
        return res.join(' ');
2493
    }
2494

2495
    /**
2496
     * @hidden
2497
     * @internal
2498
     */
2499
    protected cacheCalcWidth(): any {
2500
        const colWidth = this.width;
2501
        const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1;
2502
        const isAutoWidth = colWidth && typeof colWidth === 'string' && colWidth === 'fit-content';
2503
        if (isPercentageWidth) {
2504
            this._calcWidth = Math.floor(parseFloat(colWidth) / 100 * this.grid.calcWidth);
2505
        } else if (!colWidth || isAutoWidth && !this.autoSize) {
2506
            // no width
2507
            this._calcWidth = this.defaultWidth || this.grid.getPossibleColumnWidth();
2508
        } else {
2509
            this._calcWidth = this.width;
2510
        }
2511
        this.calcPixelWidth = parseInt(this._calcWidth, 10);
2512
    }
2513

2514
    /**
2515
     * @hidden
2516
     * @internal
2517
     */
2518
    protected setExpandCollapseState() {
2519
        this.children.filter(col => (col.visibleWhenCollapsed !== undefined)).forEach(c => {
2520
            if (!this.collapsible) {
2521
                c.hidden = this.hidden; return;
2522
            }
2523
            c.hidden = this._expanded ? c.visibleWhenCollapsed : !c.visibleWhenCollapsed;
2524
        });
2525
    }
2526
    /**
2527
     * @hidden
2528
     * @internal
2529
     */
2530
    protected checkCollapsibleState() {
2531
        if (!this.children) {
2532
            return false;
2533
        }
2534
        const cols = this.children.map(child => child.visibleWhenCollapsed);
2535
        return (cols.some(c => c === true) && cols.some(c => c === false));
2536
    }
2537

2538
    /**
2539
     * @hidden
2540
     */
2541
    public get pinnable() {
2542
        return (this.grid as any)._init || !this.pinned;
2543
    }
2544

2545
    /**
2546
     * @hidden
2547
     */
2548
    public get applySelectableClass(): boolean {
2549
        return this._applySelectableClass;
2550
    }
2551

2552
    /**
2553
     * @hidden
2554
     */
2555
    public set applySelectableClass(value: boolean) {
2556
        if (this.selectable) {
2557
            this._applySelectableClass = value;
2558
        }
2559
    }
2560
}
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