UNPKG

116 kBJavaScriptView Raw
1import * as i1 from '@angular/cdk/bidi';
2import { coerceBooleanProperty } from '@angular/cdk/coercion';
3import { _VIEW_REPEATER_STRATEGY, _RecycleViewRepeaterStrategy, isDataSource, _DisposeViewRepeaterStrategy } from '@angular/cdk/collections';
4export { DataSource } from '@angular/cdk/collections';
5import * as i2 from '@angular/cdk/platform';
6import * as i3 from '@angular/cdk/scrolling';
7import { ScrollingModule } from '@angular/cdk/scrolling';
8import { DOCUMENT } from '@angular/common';
9import * as i0 from '@angular/core';
10import { InjectionToken, Directive, Inject, Optional, Input, ContentChild, Injectable, Component, ChangeDetectionStrategy, ViewEncapsulation, EmbeddedViewRef, EventEmitter, NgZone, Attribute, SkipSelf, Output, ViewChild, ContentChildren, NgModule } from '@angular/core';
11import { Subject, from, BehaviorSubject, isObservable, of } from 'rxjs';
12import { takeUntil, take } from 'rxjs/operators';
13
14/**
15 * Mixin to provide a directive with a function that checks if the sticky input has been
16 * changed since the last time the function was called. Essentially adds a dirty-check to the
17 * sticky value.
18 * @docs-private
19 */
20function mixinHasStickyInput(base) {
21 return class extends base {
22 /** Whether sticky positioning should be applied. */
23 get sticky() {
24 return this._sticky;
25 }
26 set sticky(v) {
27 const prevValue = this._sticky;
28 this._sticky = coerceBooleanProperty(v);
29 this._hasStickyChanged = prevValue !== this._sticky;
30 }
31 /** Whether the sticky value has changed since this was last called. */
32 hasStickyChanged() {
33 const hasStickyChanged = this._hasStickyChanged;
34 this._hasStickyChanged = false;
35 return hasStickyChanged;
36 }
37 /** Resets the dirty check for cases where the sticky state has been used without checking. */
38 resetStickyChanged() {
39 this._hasStickyChanged = false;
40 }
41 constructor(...args) {
42 super(...args);
43 this._sticky = false;
44 /** Whether the sticky input has changed since it was last checked. */
45 this._hasStickyChanged = false;
46 }
47 };
48}
49
50/**
51 * Used to provide a table to some of the sub-components without causing a circular dependency.
52 * @docs-private
53 */
54const CDK_TABLE = new InjectionToken('CDK_TABLE');
55/** Injection token that can be used to specify the text column options. */
56const TEXT_COLUMN_OPTIONS = new InjectionToken('text-column-options');
57
58/**
59 * Cell definition for a CDK table.
60 * Captures the template of a column's data row cell as well as cell-specific properties.
61 */
62class CdkCellDef {
63 constructor(/** @docs-private */ template) {
64 this.template = template;
65 }
66 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkCellDef, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); }
67 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkCellDef, selector: "[cdkCellDef]", ngImport: i0 }); }
68}
69i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkCellDef, decorators: [{
70 type: Directive,
71 args: [{ selector: '[cdkCellDef]' }]
72 }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } });
73/**
74 * Header cell definition for a CDK table.
75 * Captures the template of a column's header cell and as well as cell-specific properties.
76 */
77class CdkHeaderCellDef {
78 constructor(/** @docs-private */ template) {
79 this.template = template;
80 }
81 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkHeaderCellDef, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); }
82 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkHeaderCellDef, selector: "[cdkHeaderCellDef]", ngImport: i0 }); }
83}
84i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkHeaderCellDef, decorators: [{
85 type: Directive,
86 args: [{ selector: '[cdkHeaderCellDef]' }]
87 }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } });
88/**
89 * Footer cell definition for a CDK table.
90 * Captures the template of a column's footer cell and as well as cell-specific properties.
91 */
92class CdkFooterCellDef {
93 constructor(/** @docs-private */ template) {
94 this.template = template;
95 }
96 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkFooterCellDef, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); }
97 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkFooterCellDef, selector: "[cdkFooterCellDef]", ngImport: i0 }); }
98}
99i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkFooterCellDef, decorators: [{
100 type: Directive,
101 args: [{ selector: '[cdkFooterCellDef]' }]
102 }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } });
103// Boilerplate for applying mixins to CdkColumnDef.
104/** @docs-private */
105class CdkColumnDefBase {
106}
107const _CdkColumnDefBase = mixinHasStickyInput(CdkColumnDefBase);
108/**
109 * Column definition for the CDK table.
110 * Defines a set of cells available for a table column.
111 */
112class CdkColumnDef extends _CdkColumnDefBase {
113 /** Unique name for this column. */
114 get name() {
115 return this._name;
116 }
117 set name(name) {
118 this._setNameInput(name);
119 }
120 /**
121 * Whether this column should be sticky positioned on the end of the row. Should make sure
122 * that it mimics the `CanStick` mixin such that `_hasStickyChanged` is set to true if the value
123 * has been changed.
124 */
125 get stickyEnd() {
126 return this._stickyEnd;
127 }
128 set stickyEnd(v) {
129 const prevValue = this._stickyEnd;
130 this._stickyEnd = coerceBooleanProperty(v);
131 this._hasStickyChanged = prevValue !== this._stickyEnd;
132 }
133 constructor(_table) {
134 super();
135 this._table = _table;
136 this._stickyEnd = false;
137 }
138 /**
139 * Overridable method that sets the css classes that will be added to every cell in this
140 * column.
141 * In the future, columnCssClassName will change from type string[] to string and this
142 * will set a single string value.
143 * @docs-private
144 */
145 _updateColumnCssClassName() {
146 this._columnCssClassName = [`cdk-column-${this.cssClassFriendlyName}`];
147 }
148 /**
149 * This has been extracted to a util because of TS 4 and VE.
150 * View Engine doesn't support property rename inheritance.
151 * TS 4.0 doesn't allow properties to override accessors or vice-versa.
152 * @docs-private
153 */
154 _setNameInput(value) {
155 // If the directive is set without a name (updated programmatically), then this setter will
156 // trigger with an empty string and should not overwrite the programmatically set value.
157 if (value) {
158 this._name = value;
159 this.cssClassFriendlyName = value.replace(/[^a-z0-9_-]/gi, '-');
160 this._updateColumnCssClassName();
161 }
162 }
163 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkColumnDef, deps: [{ token: CDK_TABLE, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
164 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkColumnDef, selector: "[cdkColumnDef]", inputs: { sticky: "sticky", name: ["cdkColumnDef", "name"], stickyEnd: "stickyEnd" }, providers: [{ provide: 'MAT_SORT_HEADER_COLUMN_DEF', useExisting: CdkColumnDef }], queries: [{ propertyName: "cell", first: true, predicate: CdkCellDef, descendants: true }, { propertyName: "headerCell", first: true, predicate: CdkHeaderCellDef, descendants: true }, { propertyName: "footerCell", first: true, predicate: CdkFooterCellDef, descendants: true }], usesInheritance: true, ngImport: i0 }); }
165}
166i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkColumnDef, decorators: [{
167 type: Directive,
168 args: [{
169 selector: '[cdkColumnDef]',
170 inputs: ['sticky'],
171 providers: [{ provide: 'MAT_SORT_HEADER_COLUMN_DEF', useExisting: CdkColumnDef }],
172 }]
173 }], ctorParameters: function () { return [{ type: undefined, decorators: [{
174 type: Inject,
175 args: [CDK_TABLE]
176 }, {
177 type: Optional
178 }] }]; }, propDecorators: { name: [{
179 type: Input,
180 args: ['cdkColumnDef']
181 }], stickyEnd: [{
182 type: Input,
183 args: ['stickyEnd']
184 }], cell: [{
185 type: ContentChild,
186 args: [CdkCellDef]
187 }], headerCell: [{
188 type: ContentChild,
189 args: [CdkHeaderCellDef]
190 }], footerCell: [{
191 type: ContentChild,
192 args: [CdkFooterCellDef]
193 }] } });
194/** Base class for the cells. Adds a CSS classname that identifies the column it renders in. */
195class BaseCdkCell {
196 constructor(columnDef, elementRef) {
197 elementRef.nativeElement.classList.add(...columnDef._columnCssClassName);
198 }
199}
200/** Header cell template container that adds the right classes and role. */
201class CdkHeaderCell extends BaseCdkCell {
202 constructor(columnDef, elementRef) {
203 super(columnDef, elementRef);
204 }
205 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkHeaderCell, deps: [{ token: CdkColumnDef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
206 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkHeaderCell, selector: "cdk-header-cell, th[cdk-header-cell]", host: { attributes: { "role": "columnheader" }, classAttribute: "cdk-header-cell" }, usesInheritance: true, ngImport: i0 }); }
207}
208i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkHeaderCell, decorators: [{
209 type: Directive,
210 args: [{
211 selector: 'cdk-header-cell, th[cdk-header-cell]',
212 host: {
213 'class': 'cdk-header-cell',
214 'role': 'columnheader',
215 },
216 }]
217 }], ctorParameters: function () { return [{ type: CdkColumnDef }, { type: i0.ElementRef }]; } });
218/** Footer cell template container that adds the right classes and role. */
219class CdkFooterCell extends BaseCdkCell {
220 constructor(columnDef, elementRef) {
221 super(columnDef, elementRef);
222 if (columnDef._table?._elementRef.nativeElement.nodeType === 1) {
223 const tableRole = columnDef._table._elementRef.nativeElement.getAttribute('role');
224 const role = tableRole === 'grid' || tableRole === 'treegrid' ? 'gridcell' : 'cell';
225 elementRef.nativeElement.setAttribute('role', role);
226 }
227 }
228 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkFooterCell, deps: [{ token: CdkColumnDef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
229 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkFooterCell, selector: "cdk-footer-cell, td[cdk-footer-cell]", host: { classAttribute: "cdk-footer-cell" }, usesInheritance: true, ngImport: i0 }); }
230}
231i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkFooterCell, decorators: [{
232 type: Directive,
233 args: [{
234 selector: 'cdk-footer-cell, td[cdk-footer-cell]',
235 host: {
236 'class': 'cdk-footer-cell',
237 },
238 }]
239 }], ctorParameters: function () { return [{ type: CdkColumnDef }, { type: i0.ElementRef }]; } });
240/** Cell template container that adds the right classes and role. */
241class CdkCell extends BaseCdkCell {
242 constructor(columnDef, elementRef) {
243 super(columnDef, elementRef);
244 if (columnDef._table?._elementRef.nativeElement.nodeType === 1) {
245 const tableRole = columnDef._table._elementRef.nativeElement.getAttribute('role');
246 const role = tableRole === 'grid' || tableRole === 'treegrid' ? 'gridcell' : 'cell';
247 elementRef.nativeElement.setAttribute('role', role);
248 }
249 }
250 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkCell, deps: [{ token: CdkColumnDef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
251 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkCell, selector: "cdk-cell, td[cdk-cell]", host: { classAttribute: "cdk-cell" }, usesInheritance: true, ngImport: i0 }); }
252}
253i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkCell, decorators: [{
254 type: Directive,
255 args: [{
256 selector: 'cdk-cell, td[cdk-cell]',
257 host: {
258 'class': 'cdk-cell',
259 },
260 }]
261 }], ctorParameters: function () { return [{ type: CdkColumnDef }, { type: i0.ElementRef }]; } });
262
263/**
264 * @docs-private
265 */
266class _Schedule {
267 constructor() {
268 this.tasks = [];
269 this.endTasks = [];
270 }
271}
272/** Injection token used to provide a coalesced style scheduler. */
273const _COALESCED_STYLE_SCHEDULER = new InjectionToken('_COALESCED_STYLE_SCHEDULER');
274/**
275 * Allows grouping up CSSDom mutations after the current execution context.
276 * This can significantly improve performance when separate consecutive functions are
277 * reading from the CSSDom and then mutating it.
278 *
279 * @docs-private
280 */
281class _CoalescedStyleScheduler {
282 constructor(_ngZone) {
283 this._ngZone = _ngZone;
284 this._currentSchedule = null;
285 this._destroyed = new Subject();
286 }
287 /**
288 * Schedules the specified task to run at the end of the current VM turn.
289 */
290 schedule(task) {
291 this._createScheduleIfNeeded();
292 this._currentSchedule.tasks.push(task);
293 }
294 /**
295 * Schedules the specified task to run after other scheduled tasks at the end of the current
296 * VM turn.
297 */
298 scheduleEnd(task) {
299 this._createScheduleIfNeeded();
300 this._currentSchedule.endTasks.push(task);
301 }
302 /** Prevent any further tasks from running. */
303 ngOnDestroy() {
304 this._destroyed.next();
305 this._destroyed.complete();
306 }
307 _createScheduleIfNeeded() {
308 if (this._currentSchedule) {
309 return;
310 }
311 this._currentSchedule = new _Schedule();
312 this._getScheduleObservable()
313 .pipe(takeUntil(this._destroyed))
314 .subscribe(() => {
315 while (this._currentSchedule.tasks.length || this._currentSchedule.endTasks.length) {
316 const schedule = this._currentSchedule;
317 // Capture new tasks scheduled by the current set of tasks.
318 this._currentSchedule = new _Schedule();
319 for (const task of schedule.tasks) {
320 task();
321 }
322 for (const task of schedule.endTasks) {
323 task();
324 }
325 }
326 this._currentSchedule = null;
327 });
328 }
329 _getScheduleObservable() {
330 // Use onStable when in the context of an ongoing change detection cycle so that we
331 // do not accidentally trigger additional cycles.
332 return this._ngZone.isStable
333 ? from(Promise.resolve(undefined))
334 : this._ngZone.onStable.pipe(take(1));
335 }
336 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: _CoalescedStyleScheduler, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable }); }
337 static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: _CoalescedStyleScheduler }); }
338}
339i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: _CoalescedStyleScheduler, decorators: [{
340 type: Injectable
341 }], ctorParameters: function () { return [{ type: i0.NgZone }]; } });
342
343/**
344 * The row template that can be used by the mat-table. Should not be used outside of the
345 * material library.
346 */
347const CDK_ROW_TEMPLATE = `<ng-container cdkCellOutlet></ng-container>`;
348/**
349 * Base class for the CdkHeaderRowDef and CdkRowDef that handles checking their columns inputs
350 * for changes and notifying the table.
351 */
352class BaseRowDef {
353 constructor(
354 /** @docs-private */ template, _differs) {
355 this.template = template;
356 this._differs = _differs;
357 }
358 ngOnChanges(changes) {
359 // Create a new columns differ if one does not yet exist. Initialize it based on initial value
360 // of the columns property or an empty array if none is provided.
361 if (!this._columnsDiffer) {
362 const columns = (changes['columns'] && changes['columns'].currentValue) || [];
363 this._columnsDiffer = this._differs.find(columns).create();
364 this._columnsDiffer.diff(columns);
365 }
366 }
367 /**
368 * Returns the difference between the current columns and the columns from the last diff, or null
369 * if there is no difference.
370 */
371 getColumnsDiff() {
372 return this._columnsDiffer.diff(this.columns);
373 }
374 /** Gets this row def's relevant cell template from the provided column def. */
375 extractCellTemplate(column) {
376 if (this instanceof CdkHeaderRowDef) {
377 return column.headerCell.template;
378 }
379 if (this instanceof CdkFooterRowDef) {
380 return column.footerCell.template;
381 }
382 else {
383 return column.cell.template;
384 }
385 }
386 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: BaseRowDef, deps: [{ token: i0.TemplateRef }, { token: i0.IterableDiffers }], target: i0.ɵɵFactoryTarget.Directive }); }
387 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: BaseRowDef, usesOnChanges: true, ngImport: i0 }); }
388}
389i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: BaseRowDef, decorators: [{
390 type: Directive
391 }], ctorParameters: function () { return [{ type: i0.TemplateRef }, { type: i0.IterableDiffers }]; } });
392// Boilerplate for applying mixins to CdkHeaderRowDef.
393/** @docs-private */
394class CdkHeaderRowDefBase extends BaseRowDef {
395}
396const _CdkHeaderRowDefBase = mixinHasStickyInput(CdkHeaderRowDefBase);
397/**
398 * Header row definition for the CDK table.
399 * Captures the header row's template and other header properties such as the columns to display.
400 */
401class CdkHeaderRowDef extends _CdkHeaderRowDefBase {
402 constructor(template, _differs, _table) {
403 super(template, _differs);
404 this._table = _table;
405 }
406 // Prerender fails to recognize that ngOnChanges in a part of this class through inheritance.
407 // Explicitly define it so that the method is called as part of the Angular lifecycle.
408 ngOnChanges(changes) {
409 super.ngOnChanges(changes);
410 }
411 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkHeaderRowDef, deps: [{ token: i0.TemplateRef }, { token: i0.IterableDiffers }, { token: CDK_TABLE, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
412 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkHeaderRowDef, selector: "[cdkHeaderRowDef]", inputs: { columns: ["cdkHeaderRowDef", "columns"], sticky: ["cdkHeaderRowDefSticky", "sticky"] }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
413}
414i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkHeaderRowDef, decorators: [{
415 type: Directive,
416 args: [{
417 selector: '[cdkHeaderRowDef]',
418 inputs: ['columns: cdkHeaderRowDef', 'sticky: cdkHeaderRowDefSticky'],
419 }]
420 }], ctorParameters: function () { return [{ type: i0.TemplateRef }, { type: i0.IterableDiffers }, { type: undefined, decorators: [{
421 type: Inject,
422 args: [CDK_TABLE]
423 }, {
424 type: Optional
425 }] }]; } });
426// Boilerplate for applying mixins to CdkFooterRowDef.
427/** @docs-private */
428class CdkFooterRowDefBase extends BaseRowDef {
429}
430const _CdkFooterRowDefBase = mixinHasStickyInput(CdkFooterRowDefBase);
431/**
432 * Footer row definition for the CDK table.
433 * Captures the footer row's template and other footer properties such as the columns to display.
434 */
435class CdkFooterRowDef extends _CdkFooterRowDefBase {
436 constructor(template, _differs, _table) {
437 super(template, _differs);
438 this._table = _table;
439 }
440 // Prerender fails to recognize that ngOnChanges in a part of this class through inheritance.
441 // Explicitly define it so that the method is called as part of the Angular lifecycle.
442 ngOnChanges(changes) {
443 super.ngOnChanges(changes);
444 }
445 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkFooterRowDef, deps: [{ token: i0.TemplateRef }, { token: i0.IterableDiffers }, { token: CDK_TABLE, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
446 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkFooterRowDef, selector: "[cdkFooterRowDef]", inputs: { columns: ["cdkFooterRowDef", "columns"], sticky: ["cdkFooterRowDefSticky", "sticky"] }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
447}
448i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkFooterRowDef, decorators: [{
449 type: Directive,
450 args: [{
451 selector: '[cdkFooterRowDef]',
452 inputs: ['columns: cdkFooterRowDef', 'sticky: cdkFooterRowDefSticky'],
453 }]
454 }], ctorParameters: function () { return [{ type: i0.TemplateRef }, { type: i0.IterableDiffers }, { type: undefined, decorators: [{
455 type: Inject,
456 args: [CDK_TABLE]
457 }, {
458 type: Optional
459 }] }]; } });
460/**
461 * Data row definition for the CDK table.
462 * Captures the header row's template and other row properties such as the columns to display and
463 * a when predicate that describes when this row should be used.
464 */
465class CdkRowDef extends BaseRowDef {
466 // TODO(andrewseguin): Add an input for providing a switch function to determine
467 // if this template should be used.
468 constructor(template, _differs, _table) {
469 super(template, _differs);
470 this._table = _table;
471 }
472 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkRowDef, deps: [{ token: i0.TemplateRef }, { token: i0.IterableDiffers }, { token: CDK_TABLE, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
473 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkRowDef, selector: "[cdkRowDef]", inputs: { columns: ["cdkRowDefColumns", "columns"], when: ["cdkRowDefWhen", "when"] }, usesInheritance: true, ngImport: i0 }); }
474}
475i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkRowDef, decorators: [{
476 type: Directive,
477 args: [{
478 selector: '[cdkRowDef]',
479 inputs: ['columns: cdkRowDefColumns', 'when: cdkRowDefWhen'],
480 }]
481 }], ctorParameters: function () { return [{ type: i0.TemplateRef }, { type: i0.IterableDiffers }, { type: undefined, decorators: [{
482 type: Inject,
483 args: [CDK_TABLE]
484 }, {
485 type: Optional
486 }] }]; } });
487/**
488 * Outlet for rendering cells inside of a row or header row.
489 * @docs-private
490 */
491class CdkCellOutlet {
492 /**
493 * Static property containing the latest constructed instance of this class.
494 * Used by the CDK table when each CdkHeaderRow and CdkRow component is created using
495 * createEmbeddedView. After one of these components are created, this property will provide
496 * a handle to provide that component's cells and context. After init, the CdkCellOutlet will
497 * construct the cells with the provided context.
498 */
499 static { this.mostRecentCellOutlet = null; }
500 constructor(_viewContainer) {
501 this._viewContainer = _viewContainer;
502 CdkCellOutlet.mostRecentCellOutlet = this;
503 }
504 ngOnDestroy() {
505 // If this was the last outlet being rendered in the view, remove the reference
506 // from the static property after it has been destroyed to avoid leaking memory.
507 if (CdkCellOutlet.mostRecentCellOutlet === this) {
508 CdkCellOutlet.mostRecentCellOutlet = null;
509 }
510 }
511 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkCellOutlet, deps: [{ token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
512 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkCellOutlet, selector: "[cdkCellOutlet]", ngImport: i0 }); }
513}
514i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkCellOutlet, decorators: [{
515 type: Directive,
516 args: [{ selector: '[cdkCellOutlet]' }]
517 }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }]; } });
518/** Header template container that contains the cell outlet. Adds the right class and role. */
519class CdkHeaderRow {
520 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkHeaderRow, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
521 static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: CdkHeaderRow, selector: "cdk-header-row, tr[cdk-header-row]", host: { attributes: { "role": "row" }, classAttribute: "cdk-header-row" }, ngImport: i0, template: "<ng-container cdkCellOutlet></ng-container>", isInline: true, dependencies: [{ kind: "directive", type: CdkCellOutlet, selector: "[cdkCellOutlet]" }], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None }); }
522}
523i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkHeaderRow, decorators: [{
524 type: Component,
525 args: [{
526 selector: 'cdk-header-row, tr[cdk-header-row]',
527 template: CDK_ROW_TEMPLATE,
528 host: {
529 'class': 'cdk-header-row',
530 'role': 'row',
531 },
532 // See note on CdkTable for explanation on why this uses the default change detection strategy.
533 // tslint:disable-next-line:validate-decorators
534 changeDetection: ChangeDetectionStrategy.Default,
535 encapsulation: ViewEncapsulation.None,
536 }]
537 }] });
538/** Footer template container that contains the cell outlet. Adds the right class and role. */
539class CdkFooterRow {
540 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkFooterRow, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
541 static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: CdkFooterRow, selector: "cdk-footer-row, tr[cdk-footer-row]", host: { attributes: { "role": "row" }, classAttribute: "cdk-footer-row" }, ngImport: i0, template: "<ng-container cdkCellOutlet></ng-container>", isInline: true, dependencies: [{ kind: "directive", type: CdkCellOutlet, selector: "[cdkCellOutlet]" }], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None }); }
542}
543i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkFooterRow, decorators: [{
544 type: Component,
545 args: [{
546 selector: 'cdk-footer-row, tr[cdk-footer-row]',
547 template: CDK_ROW_TEMPLATE,
548 host: {
549 'class': 'cdk-footer-row',
550 'role': 'row',
551 },
552 // See note on CdkTable for explanation on why this uses the default change detection strategy.
553 // tslint:disable-next-line:validate-decorators
554 changeDetection: ChangeDetectionStrategy.Default,
555 encapsulation: ViewEncapsulation.None,
556 }]
557 }] });
558/** Data row template container that contains the cell outlet. Adds the right class and role. */
559class CdkRow {
560 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkRow, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
561 static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: CdkRow, selector: "cdk-row, tr[cdk-row]", host: { attributes: { "role": "row" }, classAttribute: "cdk-row" }, ngImport: i0, template: "<ng-container cdkCellOutlet></ng-container>", isInline: true, dependencies: [{ kind: "directive", type: CdkCellOutlet, selector: "[cdkCellOutlet]" }], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None }); }
562}
563i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkRow, decorators: [{
564 type: Component,
565 args: [{
566 selector: 'cdk-row, tr[cdk-row]',
567 template: CDK_ROW_TEMPLATE,
568 host: {
569 'class': 'cdk-row',
570 'role': 'row',
571 },
572 // See note on CdkTable for explanation on why this uses the default change detection strategy.
573 // tslint:disable-next-line:validate-decorators
574 changeDetection: ChangeDetectionStrategy.Default,
575 encapsulation: ViewEncapsulation.None,
576 }]
577 }] });
578/** Row that can be used to display a message when no data is shown in the table. */
579class CdkNoDataRow {
580 constructor(templateRef) {
581 this.templateRef = templateRef;
582 this._contentClassName = 'cdk-no-data-row';
583 }
584 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkNoDataRow, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); }
585 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkNoDataRow, selector: "ng-template[cdkNoDataRow]", ngImport: i0 }); }
586}
587i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkNoDataRow, decorators: [{
588 type: Directive,
589 args: [{
590 selector: 'ng-template[cdkNoDataRow]',
591 }]
592 }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } });
593
594/**
595 * List of all possible directions that can be used for sticky positioning.
596 * @docs-private
597 */
598const STICKY_DIRECTIONS = ['top', 'bottom', 'left', 'right'];
599/**
600 * Applies and removes sticky positioning styles to the `CdkTable` rows and columns cells.
601 * @docs-private
602 */
603class StickyStyler {
604 /**
605 * @param _isNativeHtmlTable Whether the sticky logic should be based on a table
606 * that uses the native `<table>` element.
607 * @param _stickCellCss The CSS class that will be applied to every row/cell that has
608 * sticky positioning applied.
609 * @param direction The directionality context of the table (ltr/rtl); affects column positioning
610 * by reversing left/right positions.
611 * @param _isBrowser Whether the table is currently being rendered on the server or the client.
612 * @param _needsPositionStickyOnElement Whether we need to specify position: sticky on cells
613 * using inline styles. If false, it is assumed that position: sticky is included in
614 * the component stylesheet for _stickCellCss.
615 * @param _positionListener A listener that is notified of changes to sticky rows/columns
616 * and their dimensions.
617 */
618 constructor(_isNativeHtmlTable, _stickCellCss, direction, _coalescedStyleScheduler, _isBrowser = true, _needsPositionStickyOnElement = true, _positionListener) {
619 this._isNativeHtmlTable = _isNativeHtmlTable;
620 this._stickCellCss = _stickCellCss;
621 this.direction = direction;
622 this._coalescedStyleScheduler = _coalescedStyleScheduler;
623 this._isBrowser = _isBrowser;
624 this._needsPositionStickyOnElement = _needsPositionStickyOnElement;
625 this._positionListener = _positionListener;
626 this._cachedCellWidths = [];
627 this._borderCellCss = {
628 'top': `${_stickCellCss}-border-elem-top`,
629 'bottom': `${_stickCellCss}-border-elem-bottom`,
630 'left': `${_stickCellCss}-border-elem-left`,
631 'right': `${_stickCellCss}-border-elem-right`,
632 };
633 }
634 /**
635 * Clears the sticky positioning styles from the row and its cells by resetting the `position`
636 * style, setting the zIndex to 0, and unsetting each provided sticky direction.
637 * @param rows The list of rows that should be cleared from sticking in the provided directions
638 * @param stickyDirections The directions that should no longer be set as sticky on the rows.
639 */
640 clearStickyPositioning(rows, stickyDirections) {
641 const elementsToClear = [];
642 for (const row of rows) {
643 // If the row isn't an element (e.g. if it's an `ng-container`),
644 // it won't have inline styles or `children` so we skip it.
645 if (row.nodeType !== row.ELEMENT_NODE) {
646 continue;
647 }
648 elementsToClear.push(row);
649 for (let i = 0; i < row.children.length; i++) {
650 elementsToClear.push(row.children[i]);
651 }
652 }
653 // Coalesce with sticky row/column updates (and potentially other changes like column resize).
654 this._coalescedStyleScheduler.schedule(() => {
655 for (const element of elementsToClear) {
656 this._removeStickyStyle(element, stickyDirections);
657 }
658 });
659 }
660 /**
661 * Applies sticky left and right positions to the cells of each row according to the sticky
662 * states of the rendered column definitions.
663 * @param rows The rows that should have its set of cells stuck according to the sticky states.
664 * @param stickyStartStates A list of boolean states where each state represents whether the cell
665 * in this index position should be stuck to the start of the row.
666 * @param stickyEndStates A list of boolean states where each state represents whether the cell
667 * in this index position should be stuck to the end of the row.
668 * @param recalculateCellWidths Whether the sticky styler should recalculate the width of each
669 * column cell. If `false` cached widths will be used instead.
670 */
671 updateStickyColumns(rows, stickyStartStates, stickyEndStates, recalculateCellWidths = true) {
672 if (!rows.length ||
673 !this._isBrowser ||
674 !(stickyStartStates.some(state => state) || stickyEndStates.some(state => state))) {
675 if (this._positionListener) {
676 this._positionListener.stickyColumnsUpdated({ sizes: [] });
677 this._positionListener.stickyEndColumnsUpdated({ sizes: [] });
678 }
679 return;
680 }
681 const firstRow = rows[0];
682 const numCells = firstRow.children.length;
683 const cellWidths = this._getCellWidths(firstRow, recalculateCellWidths);
684 const startPositions = this._getStickyStartColumnPositions(cellWidths, stickyStartStates);
685 const endPositions = this._getStickyEndColumnPositions(cellWidths, stickyEndStates);
686 const lastStickyStart = stickyStartStates.lastIndexOf(true);
687 const firstStickyEnd = stickyEndStates.indexOf(true);
688 // Coalesce with sticky row updates (and potentially other changes like column resize).
689 this._coalescedStyleScheduler.schedule(() => {
690 const isRtl = this.direction === 'rtl';
691 const start = isRtl ? 'right' : 'left';
692 const end = isRtl ? 'left' : 'right';
693 for (const row of rows) {
694 for (let i = 0; i < numCells; i++) {
695 const cell = row.children[i];
696 if (stickyStartStates[i]) {
697 this._addStickyStyle(cell, start, startPositions[i], i === lastStickyStart);
698 }
699 if (stickyEndStates[i]) {
700 this._addStickyStyle(cell, end, endPositions[i], i === firstStickyEnd);
701 }
702 }
703 }
704 if (this._positionListener) {
705 this._positionListener.stickyColumnsUpdated({
706 sizes: lastStickyStart === -1
707 ? []
708 : cellWidths
709 .slice(0, lastStickyStart + 1)
710 .map((width, index) => (stickyStartStates[index] ? width : null)),
711 });
712 this._positionListener.stickyEndColumnsUpdated({
713 sizes: firstStickyEnd === -1
714 ? []
715 : cellWidths
716 .slice(firstStickyEnd)
717 .map((width, index) => (stickyEndStates[index + firstStickyEnd] ? width : null))
718 .reverse(),
719 });
720 }
721 });
722 }
723 /**
724 * Applies sticky positioning to the row's cells if using the native table layout, and to the
725 * row itself otherwise.
726 * @param rowsToStick The list of rows that should be stuck according to their corresponding
727 * sticky state and to the provided top or bottom position.
728 * @param stickyStates A list of boolean states where each state represents whether the row
729 * should be stuck in the particular top or bottom position.
730 * @param position The position direction in which the row should be stuck if that row should be
731 * sticky.
732 *
733 */
734 stickRows(rowsToStick, stickyStates, position) {
735 // Since we can't measure the rows on the server, we can't stick the rows properly.
736 if (!this._isBrowser) {
737 return;
738 }
739 // If positioning the rows to the bottom, reverse their order when evaluating the sticky
740 // position such that the last row stuck will be "bottom: 0px" and so on. Note that the
741 // sticky states need to be reversed as well.
742 const rows = position === 'bottom' ? rowsToStick.slice().reverse() : rowsToStick;
743 const states = position === 'bottom' ? stickyStates.slice().reverse() : stickyStates;
744 // Measure row heights all at once before adding sticky styles to reduce layout thrashing.
745 const stickyOffsets = [];
746 const stickyCellHeights = [];
747 const elementsToStick = [];
748 for (let rowIndex = 0, stickyOffset = 0; rowIndex < rows.length; rowIndex++) {
749 if (!states[rowIndex]) {
750 continue;
751 }
752 stickyOffsets[rowIndex] = stickyOffset;
753 const row = rows[rowIndex];
754 elementsToStick[rowIndex] = this._isNativeHtmlTable
755 ? Array.from(row.children)
756 : [row];
757 const height = row.getBoundingClientRect().height;
758 stickyOffset += height;
759 stickyCellHeights[rowIndex] = height;
760 }
761 const borderedRowIndex = states.lastIndexOf(true);
762 // Coalesce with other sticky row updates (top/bottom), sticky columns updates
763 // (and potentially other changes like column resize).
764 this._coalescedStyleScheduler.schedule(() => {
765 for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
766 if (!states[rowIndex]) {
767 continue;
768 }
769 const offset = stickyOffsets[rowIndex];
770 const isBorderedRowIndex = rowIndex === borderedRowIndex;
771 for (const element of elementsToStick[rowIndex]) {
772 this._addStickyStyle(element, position, offset, isBorderedRowIndex);
773 }
774 }
775 if (position === 'top') {
776 this._positionListener?.stickyHeaderRowsUpdated({
777 sizes: stickyCellHeights,
778 offsets: stickyOffsets,
779 elements: elementsToStick,
780 });
781 }
782 else {
783 this._positionListener?.stickyFooterRowsUpdated({
784 sizes: stickyCellHeights,
785 offsets: stickyOffsets,
786 elements: elementsToStick,
787 });
788 }
789 });
790 }
791 /**
792 * When using the native table in Safari, sticky footer cells do not stick. The only way to stick
793 * footer rows is to apply sticky styling to the tfoot container. This should only be done if
794 * all footer rows are sticky. If not all footer rows are sticky, remove sticky positioning from
795 * the tfoot element.
796 */
797 updateStickyFooterContainer(tableElement, stickyStates) {
798 if (!this._isNativeHtmlTable) {
799 return;
800 }
801 const tfoot = tableElement.querySelector('tfoot');
802 // Coalesce with other sticky updates (and potentially other changes like column resize).
803 this._coalescedStyleScheduler.schedule(() => {
804 if (stickyStates.some(state => !state)) {
805 this._removeStickyStyle(tfoot, ['bottom']);
806 }
807 else {
808 this._addStickyStyle(tfoot, 'bottom', 0, false);
809 }
810 });
811 }
812 /**
813 * Removes the sticky style on the element by removing the sticky cell CSS class, re-evaluating
814 * the zIndex, removing each of the provided sticky directions, and removing the
815 * sticky position if there are no more directions.
816 */
817 _removeStickyStyle(element, stickyDirections) {
818 for (const dir of stickyDirections) {
819 element.style[dir] = '';
820 element.classList.remove(this._borderCellCss[dir]);
821 }
822 // If the element no longer has any more sticky directions, remove sticky positioning and
823 // the sticky CSS class.
824 // Short-circuit checking element.style[dir] for stickyDirections as they
825 // were already removed above.
826 const hasDirection = STICKY_DIRECTIONS.some(dir => stickyDirections.indexOf(dir) === -1 && element.style[dir]);
827 if (hasDirection) {
828 element.style.zIndex = this._getCalculatedZIndex(element);
829 }
830 else {
831 // When not hasDirection, _getCalculatedZIndex will always return ''.
832 element.style.zIndex = '';
833 if (this._needsPositionStickyOnElement) {
834 element.style.position = '';
835 }
836 element.classList.remove(this._stickCellCss);
837 }
838 }
839 /**
840 * Adds the sticky styling to the element by adding the sticky style class, changing position
841 * to be sticky (and -webkit-sticky), setting the appropriate zIndex, and adding a sticky
842 * direction and value.
843 */
844 _addStickyStyle(element, dir, dirValue, isBorderElement) {
845 element.classList.add(this._stickCellCss);
846 if (isBorderElement) {
847 element.classList.add(this._borderCellCss[dir]);
848 }
849 element.style[dir] = `${dirValue}px`;
850 element.style.zIndex = this._getCalculatedZIndex(element);
851 if (this._needsPositionStickyOnElement) {
852 element.style.cssText += 'position: -webkit-sticky; position: sticky; ';
853 }
854 }
855 /**
856 * Calculate what the z-index should be for the element, depending on what directions (top,
857 * bottom, left, right) have been set. It should be true that elements with a top direction
858 * should have the highest index since these are elements like a table header. If any of those
859 * elements are also sticky in another direction, then they should appear above other elements
860 * that are only sticky top (e.g. a sticky column on a sticky header). Bottom-sticky elements
861 * (e.g. footer rows) should then be next in the ordering such that they are below the header
862 * but above any non-sticky elements. Finally, left/right sticky elements (e.g. sticky columns)
863 * should minimally increment so that they are above non-sticky elements but below top and bottom
864 * elements.
865 */
866 _getCalculatedZIndex(element) {
867 const zIndexIncrements = {
868 top: 100,
869 bottom: 10,
870 left: 1,
871 right: 1,
872 };
873 let zIndex = 0;
874 // Use `Iterable` instead of `Array` because TypeScript, as of 3.6.3,
875 // loses the array generic type in the `for of`. But we *also* have to use `Array` because
876 // typescript won't iterate over an `Iterable` unless you compile with `--downlevelIteration`
877 for (const dir of STICKY_DIRECTIONS) {
878 if (element.style[dir]) {
879 zIndex += zIndexIncrements[dir];
880 }
881 }
882 return zIndex ? `${zIndex}` : '';
883 }
884 /** Gets the widths for each cell in the provided row. */
885 _getCellWidths(row, recalculateCellWidths = true) {
886 if (!recalculateCellWidths && this._cachedCellWidths.length) {
887 return this._cachedCellWidths;
888 }
889 const cellWidths = [];
890 const firstRowCells = row.children;
891 for (let i = 0; i < firstRowCells.length; i++) {
892 let cell = firstRowCells[i];
893 cellWidths.push(cell.getBoundingClientRect().width);
894 }
895 this._cachedCellWidths = cellWidths;
896 return cellWidths;
897 }
898 /**
899 * Determines the left and right positions of each sticky column cell, which will be the
900 * accumulation of all sticky column cell widths to the left and right, respectively.
901 * Non-sticky cells do not need to have a value set since their positions will not be applied.
902 */
903 _getStickyStartColumnPositions(widths, stickyStates) {
904 const positions = [];
905 let nextPosition = 0;
906 for (let i = 0; i < widths.length; i++) {
907 if (stickyStates[i]) {
908 positions[i] = nextPosition;
909 nextPosition += widths[i];
910 }
911 }
912 return positions;
913 }
914 /**
915 * Determines the left and right positions of each sticky column cell, which will be the
916 * accumulation of all sticky column cell widths to the left and right, respectively.
917 * Non-sticky cells do not need to have a value set since their positions will not be applied.
918 */
919 _getStickyEndColumnPositions(widths, stickyStates) {
920 const positions = [];
921 let nextPosition = 0;
922 for (let i = widths.length; i > 0; i--) {
923 if (stickyStates[i]) {
924 positions[i] = nextPosition;
925 nextPosition += widths[i];
926 }
927 }
928 return positions;
929 }
930}
931
932/**
933 * Returns an error to be thrown when attempting to find an nonexistent column.
934 * @param id Id whose lookup failed.
935 * @docs-private
936 */
937function getTableUnknownColumnError(id) {
938 return Error(`Could not find column with id "${id}".`);
939}
940/**
941 * Returns an error to be thrown when two column definitions have the same name.
942 * @docs-private
943 */
944function getTableDuplicateColumnNameError(name) {
945 return Error(`Duplicate column definition name provided: "${name}".`);
946}
947/**
948 * Returns an error to be thrown when there are multiple rows that are missing a when function.
949 * @docs-private
950 */
951function getTableMultipleDefaultRowDefsError() {
952 return Error(`There can only be one default row without a when predicate function.`);
953}
954/**
955 * Returns an error to be thrown when there are no matching row defs for a particular set of data.
956 * @docs-private
957 */
958function getTableMissingMatchingRowDefError(data) {
959 return Error(`Could not find a matching row definition for the` +
960 `provided row data: ${JSON.stringify(data)}`);
961}
962/**
963 * Returns an error to be thrown when there is no row definitions present in the content.
964 * @docs-private
965 */
966function getTableMissingRowDefsError() {
967 return Error('Missing definitions for header, footer, and row; ' +
968 'cannot determine which columns should be rendered.');
969}
970/**
971 * Returns an error to be thrown when the data source does not match the compatible types.
972 * @docs-private
973 */
974function getTableUnknownDataSourceError() {
975 return Error(`Provided data source did not match an array, Observable, or DataSource`);
976}
977/**
978 * Returns an error to be thrown when the text column cannot find a parent table to inject.
979 * @docs-private
980 */
981function getTableTextColumnMissingParentTableError() {
982 return Error(`Text column could not find a parent table for registration.`);
983}
984/**
985 * Returns an error to be thrown when a table text column doesn't have a name.
986 * @docs-private
987 */
988function getTableTextColumnMissingNameError() {
989 return Error(`Table text column must have a name.`);
990}
991
992/** The injection token used to specify the StickyPositioningListener. */
993const STICKY_POSITIONING_LISTENER = new InjectionToken('CDK_SPL');
994
995/**
996 * Enables the recycle view repeater strategy, which reduces rendering latency. Not compatible with
997 * tables that animate rows.
998 */
999class CdkRecycleRows {
1000 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkRecycleRows, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1001 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkRecycleRows, selector: "cdk-table[recycleRows], table[cdk-table][recycleRows]", providers: [{ provide: _VIEW_REPEATER_STRATEGY, useClass: _RecycleViewRepeaterStrategy }], ngImport: i0 }); }
1002}
1003i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkRecycleRows, decorators: [{
1004 type: Directive,
1005 args: [{
1006 selector: 'cdk-table[recycleRows], table[cdk-table][recycleRows]',
1007 providers: [{ provide: _VIEW_REPEATER_STRATEGY, useClass: _RecycleViewRepeaterStrategy }],
1008 }]
1009 }] });
1010/**
1011 * Provides a handle for the table to grab the view container's ng-container to insert data rows.
1012 * @docs-private
1013 */
1014class DataRowOutlet {
1015 constructor(viewContainer, elementRef) {
1016 this.viewContainer = viewContainer;
1017 this.elementRef = elementRef;
1018 }
1019 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: DataRowOutlet, deps: [{ token: i0.ViewContainerRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
1020 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: DataRowOutlet, selector: "[rowOutlet]", ngImport: i0 }); }
1021}
1022i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: DataRowOutlet, decorators: [{
1023 type: Directive,
1024 args: [{ selector: '[rowOutlet]' }]
1025 }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ElementRef }]; } });
1026/**
1027 * Provides a handle for the table to grab the view container's ng-container to insert the header.
1028 * @docs-private
1029 */
1030class HeaderRowOutlet {
1031 constructor(viewContainer, elementRef) {
1032 this.viewContainer = viewContainer;
1033 this.elementRef = elementRef;
1034 }
1035 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: HeaderRowOutlet, deps: [{ token: i0.ViewContainerRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
1036 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: HeaderRowOutlet, selector: "[headerRowOutlet]", ngImport: i0 }); }
1037}
1038i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: HeaderRowOutlet, decorators: [{
1039 type: Directive,
1040 args: [{ selector: '[headerRowOutlet]' }]
1041 }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ElementRef }]; } });
1042/**
1043 * Provides a handle for the table to grab the view container's ng-container to insert the footer.
1044 * @docs-private
1045 */
1046class FooterRowOutlet {
1047 constructor(viewContainer, elementRef) {
1048 this.viewContainer = viewContainer;
1049 this.elementRef = elementRef;
1050 }
1051 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: FooterRowOutlet, deps: [{ token: i0.ViewContainerRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
1052 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: FooterRowOutlet, selector: "[footerRowOutlet]", ngImport: i0 }); }
1053}
1054i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: FooterRowOutlet, decorators: [{
1055 type: Directive,
1056 args: [{ selector: '[footerRowOutlet]' }]
1057 }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ElementRef }]; } });
1058/**
1059 * Provides a handle for the table to grab the view
1060 * container's ng-container to insert the no data row.
1061 * @docs-private
1062 */
1063class NoDataRowOutlet {
1064 constructor(viewContainer, elementRef) {
1065 this.viewContainer = viewContainer;
1066 this.elementRef = elementRef;
1067 }
1068 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NoDataRowOutlet, deps: [{ token: i0.ViewContainerRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
1069 static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: NoDataRowOutlet, selector: "[noDataRowOutlet]", ngImport: i0 }); }
1070}
1071i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NoDataRowOutlet, decorators: [{
1072 type: Directive,
1073 args: [{ selector: '[noDataRowOutlet]' }]
1074 }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ElementRef }]; } });
1075/**
1076 * The table template that can be used by the mat-table. Should not be used outside of the
1077 * material library.
1078 * @docs-private
1079 */
1080const CDK_TABLE_TEMPLATE =
1081// Note that according to MDN, the `caption` element has to be projected as the **first**
1082// element in the table. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption
1083`
1084 <ng-content select="caption"></ng-content>
1085 <ng-content select="colgroup, col"></ng-content>
1086 <ng-container headerRowOutlet></ng-container>
1087 <ng-container rowOutlet></ng-container>
1088 <ng-container noDataRowOutlet></ng-container>
1089 <ng-container footerRowOutlet></ng-container>
1090`;
1091/**
1092 * Class used to conveniently type the embedded view ref for rows with a context.
1093 * @docs-private
1094 */
1095class RowViewRef extends EmbeddedViewRef {
1096}
1097/**
1098 * A data table that can render a header row, data rows, and a footer row.
1099 * Uses the dataSource input to determine the data to be rendered. The data can be provided either
1100 * as a data array, an Observable stream that emits the data array to render, or a DataSource with a
1101 * connect function that will return an Observable stream that emits the data array to render.
1102 */
1103class CdkTable {
1104 /**
1105 * Tracking function that will be used to check the differences in data changes. Used similarly
1106 * to `ngFor` `trackBy` function. Optimize row operations by identifying a row based on its data
1107 * relative to the function to know if a row should be added/removed/moved.
1108 * Accepts a function that takes two parameters, `index` and `item`.
1109 */
1110 get trackBy() {
1111 return this._trackByFn;
1112 }
1113 set trackBy(fn) {
1114 if ((typeof ngDevMode === 'undefined' || ngDevMode) && fn != null && typeof fn !== 'function') {
1115 console.warn(`trackBy must be a function, but received ${JSON.stringify(fn)}.`);
1116 }
1117 this._trackByFn = fn;
1118 }
1119 /**
1120 * The table's source of data, which can be provided in three ways (in order of complexity):
1121 * - Simple data array (each object represents one table row)
1122 * - Stream that emits a data array each time the array changes
1123 * - `DataSource` object that implements the connect/disconnect interface.
1124 *
1125 * If a data array is provided, the table must be notified when the array's objects are
1126 * added, removed, or moved. This can be done by calling the `renderRows()` function which will
1127 * render the diff since the last table render. If the data array reference is changed, the table
1128 * will automatically trigger an update to the rows.
1129 *
1130 * When providing an Observable stream, the table will trigger an update automatically when the
1131 * stream emits a new array of data.
1132 *
1133 * Finally, when providing a `DataSource` object, the table will use the Observable stream
1134 * provided by the connect function and trigger updates when that stream emits new data array
1135 * values. During the table's ngOnDestroy or when the data source is removed from the table, the
1136 * table will call the DataSource's `disconnect` function (may be useful for cleaning up any
1137 * subscriptions registered during the connect process).
1138 */
1139 get dataSource() {
1140 return this._dataSource;
1141 }
1142 set dataSource(dataSource) {
1143 if (this._dataSource !== dataSource) {
1144 this._switchDataSource(dataSource);
1145 }
1146 }
1147 /**
1148 * Whether to allow multiple rows per data object by evaluating which rows evaluate their 'when'
1149 * predicate to true. If `multiTemplateDataRows` is false, which is the default value, then each
1150 * dataobject will render the first row that evaluates its when predicate to true, in the order
1151 * defined in the table, or otherwise the default row which does not have a when predicate.
1152 */
1153 get multiTemplateDataRows() {
1154 return this._multiTemplateDataRows;
1155 }
1156 set multiTemplateDataRows(v) {
1157 this._multiTemplateDataRows = coerceBooleanProperty(v);
1158 // In Ivy if this value is set via a static attribute (e.g. <table multiTemplateDataRows>),
1159 // this setter will be invoked before the row outlet has been defined hence the null check.
1160 if (this._rowOutlet && this._rowOutlet.viewContainer.length) {
1161 this._forceRenderDataRows();
1162 this.updateStickyColumnStyles();
1163 }
1164 }
1165 /**
1166 * Whether to use a fixed table layout. Enabling this option will enforce consistent column widths
1167 * and optimize rendering sticky styles for native tables. No-op for flex tables.
1168 */
1169 get fixedLayout() {
1170 return this._fixedLayout;
1171 }
1172 set fixedLayout(v) {
1173 this._fixedLayout = coerceBooleanProperty(v);
1174 // Toggling `fixedLayout` may change column widths. Sticky column styles should be recalculated.
1175 this._forceRecalculateCellWidths = true;
1176 this._stickyColumnStylesNeedReset = true;
1177 }
1178 constructor(_differs, _changeDetectorRef, _elementRef, role, _dir, _document, _platform, _viewRepeater, _coalescedStyleScheduler, _viewportRuler,
1179 /**
1180 * @deprecated `_stickyPositioningListener` parameter to become required.
1181 * @breaking-change 13.0.0
1182 */
1183 _stickyPositioningListener,
1184 /**
1185 * @deprecated `_ngZone` parameter to become required.
1186 * @breaking-change 14.0.0
1187 */
1188 _ngZone) {
1189 this._differs = _differs;
1190 this._changeDetectorRef = _changeDetectorRef;
1191 this._elementRef = _elementRef;
1192 this._dir = _dir;
1193 this._platform = _platform;
1194 this._viewRepeater = _viewRepeater;
1195 this._coalescedStyleScheduler = _coalescedStyleScheduler;
1196 this._viewportRuler = _viewportRuler;
1197 this._stickyPositioningListener = _stickyPositioningListener;
1198 this._ngZone = _ngZone;
1199 /** Subject that emits when the component has been destroyed. */
1200 this._onDestroy = new Subject();
1201 /**
1202 * Map of all the user's defined columns (header, data, and footer cell template) identified by
1203 * name. Collection populated by the column definitions gathered by `ContentChildren` as well as
1204 * any custom column definitions added to `_customColumnDefs`.
1205 */
1206 this._columnDefsByName = new Map();
1207 /**
1208 * Column definitions that were defined outside of the direct content children of the table.
1209 * These will be defined when, e.g., creating a wrapper around the cdkTable that has
1210 * column definitions as *its* content child.
1211 */
1212 this._customColumnDefs = new Set();
1213 /**
1214 * Data row definitions that were defined outside of the direct content children of the table.
1215 * These will be defined when, e.g., creating a wrapper around the cdkTable that has
1216 * built-in data rows as *its* content child.
1217 */
1218 this._customRowDefs = new Set();
1219 /**
1220 * Header row definitions that were defined outside of the direct content children of the table.
1221 * These will be defined when, e.g., creating a wrapper around the cdkTable that has
1222 * built-in header rows as *its* content child.
1223 */
1224 this._customHeaderRowDefs = new Set();
1225 /**
1226 * Footer row definitions that were defined outside of the direct content children of the table.
1227 * These will be defined when, e.g., creating a wrapper around the cdkTable that has a
1228 * built-in footer row as *its* content child.
1229 */
1230 this._customFooterRowDefs = new Set();
1231 /**
1232 * Whether the header row definition has been changed. Triggers an update to the header row after
1233 * content is checked. Initialized as true so that the table renders the initial set of rows.
1234 */
1235 this._headerRowDefChanged = true;
1236 /**
1237 * Whether the footer row definition has been changed. Triggers an update to the footer row after
1238 * content is checked. Initialized as true so that the table renders the initial set of rows.
1239 */
1240 this._footerRowDefChanged = true;
1241 /**
1242 * Whether the sticky column styles need to be updated. Set to `true` when the visible columns
1243 * change.
1244 */
1245 this._stickyColumnStylesNeedReset = true;
1246 /**
1247 * Whether the sticky styler should recalculate cell widths when applying sticky styles. If
1248 * `false`, cached values will be used instead. This is only applicable to tables with
1249 * {@link fixedLayout} enabled. For other tables, cell widths will always be recalculated.
1250 */
1251 this._forceRecalculateCellWidths = true;
1252 /**
1253 * Cache of the latest rendered `RenderRow` objects as a map for easy retrieval when constructing
1254 * a new list of `RenderRow` objects for rendering rows. Since the new list is constructed with
1255 * the cached `RenderRow` objects when possible, the row identity is preserved when the data
1256 * and row template matches, which allows the `IterableDiffer` to check rows by reference
1257 * and understand which rows are added/moved/removed.
1258 *
1259 * Implemented as a map of maps where the first key is the `data: T` object and the second is the
1260 * `CdkRowDef<T>` object. With the two keys, the cache points to a `RenderRow<T>` object that
1261 * contains an array of created pairs. The array is necessary to handle cases where the data
1262 * array contains multiple duplicate data objects and each instantiated `RenderRow` must be
1263 * stored.
1264 */
1265 this._cachedRenderRowsMap = new Map();
1266 /**
1267 * CSS class added to any row or cell that has sticky positioning applied. May be overridden by
1268 * table subclasses.
1269 */
1270 this.stickyCssClass = 'cdk-table-sticky';
1271 /**
1272 * Whether to manually add position: sticky to all sticky cell elements. Not needed if
1273 * the position is set in a selector associated with the value of stickyCssClass. May be
1274 * overridden by table subclasses
1275 */
1276 this.needsPositionStickyOnElement = true;
1277 /** Whether the no data row is currently showing anything. */
1278 this._isShowingNoDataRow = false;
1279 this._multiTemplateDataRows = false;
1280 this._fixedLayout = false;
1281 /**
1282 * Emits when the table completes rendering a set of data rows based on the latest data from the
1283 * data source, even if the set of rows is empty.
1284 */
1285 this.contentChanged = new EventEmitter();
1286 // TODO(andrewseguin): Remove max value as the end index
1287 // and instead calculate the view on init and scroll.
1288 /**
1289 * Stream containing the latest information on what rows are being displayed on screen.
1290 * Can be used by the data source to as a heuristic of what data should be provided.
1291 *
1292 * @docs-private
1293 */
1294 this.viewChange = new BehaviorSubject({
1295 start: 0,
1296 end: Number.MAX_VALUE,
1297 });
1298 if (!role) {
1299 this._elementRef.nativeElement.setAttribute('role', 'table');
1300 }
1301 this._document = _document;
1302 this._isNativeHtmlTable = this._elementRef.nativeElement.nodeName === 'TABLE';
1303 }
1304 ngOnInit() {
1305 this._setupStickyStyler();
1306 if (this._isNativeHtmlTable) {
1307 this._applyNativeTableSections();
1308 }
1309 // Set up the trackBy function so that it uses the `RenderRow` as its identity by default. If
1310 // the user has provided a custom trackBy, return the result of that function as evaluated
1311 // with the values of the `RenderRow`'s data and index.
1312 this._dataDiffer = this._differs.find([]).create((_i, dataRow) => {
1313 return this.trackBy ? this.trackBy(dataRow.dataIndex, dataRow.data) : dataRow;
1314 });
1315 this._viewportRuler
1316 .change()
1317 .pipe(takeUntil(this._onDestroy))
1318 .subscribe(() => {
1319 this._forceRecalculateCellWidths = true;
1320 });
1321 }
1322 ngAfterContentChecked() {
1323 // Cache the row and column definitions gathered by ContentChildren and programmatic injection.
1324 this._cacheRowDefs();
1325 this._cacheColumnDefs();
1326 // Make sure that the user has at least added header, footer, or data row def.
1327 if (!this._headerRowDefs.length &&
1328 !this._footerRowDefs.length &&
1329 !this._rowDefs.length &&
1330 (typeof ngDevMode === 'undefined' || ngDevMode)) {
1331 throw getTableMissingRowDefsError();
1332 }
1333 // Render updates if the list of columns have been changed for the header, row, or footer defs.
1334 const columnsChanged = this._renderUpdatedColumns();
1335 const rowDefsChanged = columnsChanged || this._headerRowDefChanged || this._footerRowDefChanged;
1336 // Ensure sticky column styles are reset if set to `true` elsewhere.
1337 this._stickyColumnStylesNeedReset = this._stickyColumnStylesNeedReset || rowDefsChanged;
1338 this._forceRecalculateCellWidths = rowDefsChanged;
1339 // If the header row definition has been changed, trigger a render to the header row.
1340 if (this._headerRowDefChanged) {
1341 this._forceRenderHeaderRows();
1342 this._headerRowDefChanged = false;
1343 }
1344 // If the footer row definition has been changed, trigger a render to the footer row.
1345 if (this._footerRowDefChanged) {
1346 this._forceRenderFooterRows();
1347 this._footerRowDefChanged = false;
1348 }
1349 // If there is a data source and row definitions, connect to the data source unless a
1350 // connection has already been made.
1351 if (this.dataSource && this._rowDefs.length > 0 && !this._renderChangeSubscription) {
1352 this._observeRenderChanges();
1353 }
1354 else if (this._stickyColumnStylesNeedReset) {
1355 // In the above case, _observeRenderChanges will result in updateStickyColumnStyles being
1356 // called when it row data arrives. Otherwise, we need to call it proactively.
1357 this.updateStickyColumnStyles();
1358 }
1359 this._checkStickyStates();
1360 }
1361 ngOnDestroy() {
1362 [
1363 this._rowOutlet.viewContainer,
1364 this._headerRowOutlet.viewContainer,
1365 this._footerRowOutlet.viewContainer,
1366 this._cachedRenderRowsMap,
1367 this._customColumnDefs,
1368 this._customRowDefs,
1369 this._customHeaderRowDefs,
1370 this._customFooterRowDefs,
1371 this._columnDefsByName,
1372 ].forEach(def => {
1373 def.clear();
1374 });
1375 this._headerRowDefs = [];
1376 this._footerRowDefs = [];
1377 this._defaultRowDef = null;
1378 this._onDestroy.next();
1379 this._onDestroy.complete();
1380 if (isDataSource(this.dataSource)) {
1381 this.dataSource.disconnect(this);
1382 }
1383 }
1384 /**
1385 * Renders rows based on the table's latest set of data, which was either provided directly as an
1386 * input or retrieved through an Observable stream (directly or from a DataSource).
1387 * Checks for differences in the data since the last diff to perform only the necessary
1388 * changes (add/remove/move rows).
1389 *
1390 * If the table's data source is a DataSource or Observable, this will be invoked automatically
1391 * each time the provided Observable stream emits a new data array. Otherwise if your data is
1392 * an array, this function will need to be called to render any changes.
1393 */
1394 renderRows() {
1395 this._renderRows = this._getAllRenderRows();
1396 const changes = this._dataDiffer.diff(this._renderRows);
1397 if (!changes) {
1398 this._updateNoDataRow();
1399 this.contentChanged.next();
1400 return;
1401 }
1402 const viewContainer = this._rowOutlet.viewContainer;
1403 this._viewRepeater.applyChanges(changes, viewContainer, (record, _adjustedPreviousIndex, currentIndex) => this._getEmbeddedViewArgs(record.item, currentIndex), record => record.item.data, (change) => {
1404 if (change.operation === 1 /* _ViewRepeaterOperation.INSERTED */ && change.context) {
1405 this._renderCellTemplateForItem(change.record.item.rowDef, change.context);
1406 }
1407 });
1408 // Update the meta context of a row's context data (index, count, first, last, ...)
1409 this._updateRowIndexContext();
1410 // Update rows that did not get added/removed/moved but may have had their identity changed,
1411 // e.g. if trackBy matched data on some property but the actual data reference changed.
1412 changes.forEachIdentityChange((record) => {
1413 const rowView = viewContainer.get(record.currentIndex);
1414 rowView.context.$implicit = record.item.data;
1415 });
1416 this._updateNoDataRow();
1417 // Allow the new row data to render before measuring it.
1418 // @breaking-change 14.0.0 Remove undefined check once _ngZone is required.
1419 if (this._ngZone && NgZone.isInAngularZone()) {
1420 this._ngZone.onStable.pipe(take(1), takeUntil(this._onDestroy)).subscribe(() => {
1421 this.updateStickyColumnStyles();
1422 });
1423 }
1424 else {
1425 this.updateStickyColumnStyles();
1426 }
1427 this.contentChanged.next();
1428 }
1429 /** Adds a column definition that was not included as part of the content children. */
1430 addColumnDef(columnDef) {
1431 this._customColumnDefs.add(columnDef);
1432 }
1433 /** Removes a column definition that was not included as part of the content children. */
1434 removeColumnDef(columnDef) {
1435 this._customColumnDefs.delete(columnDef);
1436 }
1437 /** Adds a row definition that was not included as part of the content children. */
1438 addRowDef(rowDef) {
1439 this._customRowDefs.add(rowDef);
1440 }
1441 /** Removes a row definition that was not included as part of the content children. */
1442 removeRowDef(rowDef) {
1443 this._customRowDefs.delete(rowDef);
1444 }
1445 /** Adds a header row definition that was not included as part of the content children. */
1446 addHeaderRowDef(headerRowDef) {
1447 this._customHeaderRowDefs.add(headerRowDef);
1448 this._headerRowDefChanged = true;
1449 }
1450 /** Removes a header row definition that was not included as part of the content children. */
1451 removeHeaderRowDef(headerRowDef) {
1452 this._customHeaderRowDefs.delete(headerRowDef);
1453 this._headerRowDefChanged = true;
1454 }
1455 /** Adds a footer row definition that was not included as part of the content children. */
1456 addFooterRowDef(footerRowDef) {
1457 this._customFooterRowDefs.add(footerRowDef);
1458 this._footerRowDefChanged = true;
1459 }
1460 /** Removes a footer row definition that was not included as part of the content children. */
1461 removeFooterRowDef(footerRowDef) {
1462 this._customFooterRowDefs.delete(footerRowDef);
1463 this._footerRowDefChanged = true;
1464 }
1465 /** Sets a no data row definition that was not included as a part of the content children. */
1466 setNoDataRow(noDataRow) {
1467 this._customNoDataRow = noDataRow;
1468 }
1469 /**
1470 * Updates the header sticky styles. First resets all applied styles with respect to the cells
1471 * sticking to the top. Then, evaluating which cells need to be stuck to the top. This is
1472 * automatically called when the header row changes its displayed set of columns, or if its
1473 * sticky input changes. May be called manually for cases where the cell content changes outside
1474 * of these events.
1475 */
1476 updateStickyHeaderRowStyles() {
1477 const headerRows = this._getRenderedRows(this._headerRowOutlet);
1478 const tableElement = this._elementRef.nativeElement;
1479 // Hide the thead element if there are no header rows. This is necessary to satisfy
1480 // overzealous a11y checkers that fail because the `rowgroup` element does not contain
1481 // required child `row`.
1482 const thead = tableElement.querySelector('thead');
1483 if (thead) {
1484 thead.style.display = headerRows.length ? '' : 'none';
1485 }
1486 const stickyStates = this._headerRowDefs.map(def => def.sticky);
1487 this._stickyStyler.clearStickyPositioning(headerRows, ['top']);
1488 this._stickyStyler.stickRows(headerRows, stickyStates, 'top');
1489 // Reset the dirty state of the sticky input change since it has been used.
1490 this._headerRowDefs.forEach(def => def.resetStickyChanged());
1491 }
1492 /**
1493 * Updates the footer sticky styles. First resets all applied styles with respect to the cells
1494 * sticking to the bottom. Then, evaluating which cells need to be stuck to the bottom. This is
1495 * automatically called when the footer row changes its displayed set of columns, or if its
1496 * sticky input changes. May be called manually for cases where the cell content changes outside
1497 * of these events.
1498 */
1499 updateStickyFooterRowStyles() {
1500 const footerRows = this._getRenderedRows(this._footerRowOutlet);
1501 const tableElement = this._elementRef.nativeElement;
1502 // Hide the tfoot element if there are no footer rows. This is necessary to satisfy
1503 // overzealous a11y checkers that fail because the `rowgroup` element does not contain
1504 // required child `row`.
1505 const tfoot = tableElement.querySelector('tfoot');
1506 if (tfoot) {
1507 tfoot.style.display = footerRows.length ? '' : 'none';
1508 }
1509 const stickyStates = this._footerRowDefs.map(def => def.sticky);
1510 this._stickyStyler.clearStickyPositioning(footerRows, ['bottom']);
1511 this._stickyStyler.stickRows(footerRows, stickyStates, 'bottom');
1512 this._stickyStyler.updateStickyFooterContainer(this._elementRef.nativeElement, stickyStates);
1513 // Reset the dirty state of the sticky input change since it has been used.
1514 this._footerRowDefs.forEach(def => def.resetStickyChanged());
1515 }
1516 /**
1517 * Updates the column sticky styles. First resets all applied styles with respect to the cells
1518 * sticking to the left and right. Then sticky styles are added for the left and right according
1519 * to the column definitions for each cell in each row. This is automatically called when
1520 * the data source provides a new set of data or when a column definition changes its sticky
1521 * input. May be called manually for cases where the cell content changes outside of these events.
1522 */
1523 updateStickyColumnStyles() {
1524 const headerRows = this._getRenderedRows(this._headerRowOutlet);
1525 const dataRows = this._getRenderedRows(this._rowOutlet);
1526 const footerRows = this._getRenderedRows(this._footerRowOutlet);
1527 // For tables not using a fixed layout, the column widths may change when new rows are rendered.
1528 // In a table using a fixed layout, row content won't affect column width, so sticky styles
1529 // don't need to be cleared unless either the sticky column config changes or one of the row
1530 // defs change.
1531 if ((this._isNativeHtmlTable && !this._fixedLayout) || this._stickyColumnStylesNeedReset) {
1532 // Clear the left and right positioning from all columns in the table across all rows since
1533 // sticky columns span across all table sections (header, data, footer)
1534 this._stickyStyler.clearStickyPositioning([...headerRows, ...dataRows, ...footerRows], ['left', 'right']);
1535 this._stickyColumnStylesNeedReset = false;
1536 }
1537 // Update the sticky styles for each header row depending on the def's sticky state
1538 headerRows.forEach((headerRow, i) => {
1539 this._addStickyColumnStyles([headerRow], this._headerRowDefs[i]);
1540 });
1541 // Update the sticky styles for each data row depending on its def's sticky state
1542 this._rowDefs.forEach(rowDef => {
1543 // Collect all the rows rendered with this row definition.
1544 const rows = [];
1545 for (let i = 0; i < dataRows.length; i++) {
1546 if (this._renderRows[i].rowDef === rowDef) {
1547 rows.push(dataRows[i]);
1548 }
1549 }
1550 this._addStickyColumnStyles(rows, rowDef);
1551 });
1552 // Update the sticky styles for each footer row depending on the def's sticky state
1553 footerRows.forEach((footerRow, i) => {
1554 this._addStickyColumnStyles([footerRow], this._footerRowDefs[i]);
1555 });
1556 // Reset the dirty state of the sticky input change since it has been used.
1557 Array.from(this._columnDefsByName.values()).forEach(def => def.resetStickyChanged());
1558 }
1559 /**
1560 * Get the list of RenderRow objects to render according to the current list of data and defined
1561 * row definitions. If the previous list already contained a particular pair, it should be reused
1562 * so that the differ equates their references.
1563 */
1564 _getAllRenderRows() {
1565 const renderRows = [];
1566 // Store the cache and create a new one. Any re-used RenderRow objects will be moved into the
1567 // new cache while unused ones can be picked up by garbage collection.
1568 const prevCachedRenderRows = this._cachedRenderRowsMap;
1569 this._cachedRenderRowsMap = new Map();
1570 // For each data object, get the list of rows that should be rendered, represented by the
1571 // respective `RenderRow` object which is the pair of `data` and `CdkRowDef`.
1572 for (let i = 0; i < this._data.length; i++) {
1573 let data = this._data[i];
1574 const renderRowsForData = this._getRenderRowsForData(data, i, prevCachedRenderRows.get(data));
1575 if (!this._cachedRenderRowsMap.has(data)) {
1576 this._cachedRenderRowsMap.set(data, new WeakMap());
1577 }
1578 for (let j = 0; j < renderRowsForData.length; j++) {
1579 let renderRow = renderRowsForData[j];
1580 const cache = this._cachedRenderRowsMap.get(renderRow.data);
1581 if (cache.has(renderRow.rowDef)) {
1582 cache.get(renderRow.rowDef).push(renderRow);
1583 }
1584 else {
1585 cache.set(renderRow.rowDef, [renderRow]);
1586 }
1587 renderRows.push(renderRow);
1588 }
1589 }
1590 return renderRows;
1591 }
1592 /**
1593 * Gets a list of `RenderRow<T>` for the provided data object and any `CdkRowDef` objects that
1594 * should be rendered for this data. Reuses the cached RenderRow objects if they match the same
1595 * `(T, CdkRowDef)` pair.
1596 */
1597 _getRenderRowsForData(data, dataIndex, cache) {
1598 const rowDefs = this._getRowDefs(data, dataIndex);
1599 return rowDefs.map(rowDef => {
1600 const cachedRenderRows = cache && cache.has(rowDef) ? cache.get(rowDef) : [];
1601 if (cachedRenderRows.length) {
1602 const dataRow = cachedRenderRows.shift();
1603 dataRow.dataIndex = dataIndex;
1604 return dataRow;
1605 }
1606 else {
1607 return { data, rowDef, dataIndex };
1608 }
1609 });
1610 }
1611 /** Update the map containing the content's column definitions. */
1612 _cacheColumnDefs() {
1613 this._columnDefsByName.clear();
1614 const columnDefs = mergeArrayAndSet(this._getOwnDefs(this._contentColumnDefs), this._customColumnDefs);
1615 columnDefs.forEach(columnDef => {
1616 if (this._columnDefsByName.has(columnDef.name) &&
1617 (typeof ngDevMode === 'undefined' || ngDevMode)) {
1618 throw getTableDuplicateColumnNameError(columnDef.name);
1619 }
1620 this._columnDefsByName.set(columnDef.name, columnDef);
1621 });
1622 }
1623 /** Update the list of all available row definitions that can be used. */
1624 _cacheRowDefs() {
1625 this._headerRowDefs = mergeArrayAndSet(this._getOwnDefs(this._contentHeaderRowDefs), this._customHeaderRowDefs);
1626 this._footerRowDefs = mergeArrayAndSet(this._getOwnDefs(this._contentFooterRowDefs), this._customFooterRowDefs);
1627 this._rowDefs = mergeArrayAndSet(this._getOwnDefs(this._contentRowDefs), this._customRowDefs);
1628 // After all row definitions are determined, find the row definition to be considered default.
1629 const defaultRowDefs = this._rowDefs.filter(def => !def.when);
1630 if (!this.multiTemplateDataRows &&
1631 defaultRowDefs.length > 1 &&
1632 (typeof ngDevMode === 'undefined' || ngDevMode)) {
1633 throw getTableMultipleDefaultRowDefsError();
1634 }
1635 this._defaultRowDef = defaultRowDefs[0];
1636 }
1637 /**
1638 * Check if the header, data, or footer rows have changed what columns they want to display or
1639 * whether the sticky states have changed for the header or footer. If there is a diff, then
1640 * re-render that section.
1641 */
1642 _renderUpdatedColumns() {
1643 const columnsDiffReducer = (acc, def) => acc || !!def.getColumnsDiff();
1644 // Force re-render data rows if the list of column definitions have changed.
1645 const dataColumnsChanged = this._rowDefs.reduce(columnsDiffReducer, false);
1646 if (dataColumnsChanged) {
1647 this._forceRenderDataRows();
1648 }
1649 // Force re-render header/footer rows if the list of column definitions have changed.
1650 const headerColumnsChanged = this._headerRowDefs.reduce(columnsDiffReducer, false);
1651 if (headerColumnsChanged) {
1652 this._forceRenderHeaderRows();
1653 }
1654 const footerColumnsChanged = this._footerRowDefs.reduce(columnsDiffReducer, false);
1655 if (footerColumnsChanged) {
1656 this._forceRenderFooterRows();
1657 }
1658 return dataColumnsChanged || headerColumnsChanged || footerColumnsChanged;
1659 }
1660 /**
1661 * Switch to the provided data source by resetting the data and unsubscribing from the current
1662 * render change subscription if one exists. If the data source is null, interpret this by
1663 * clearing the row outlet. Otherwise start listening for new data.
1664 */
1665 _switchDataSource(dataSource) {
1666 this._data = [];
1667 if (isDataSource(this.dataSource)) {
1668 this.dataSource.disconnect(this);
1669 }
1670 // Stop listening for data from the previous data source.
1671 if (this._renderChangeSubscription) {
1672 this._renderChangeSubscription.unsubscribe();
1673 this._renderChangeSubscription = null;
1674 }
1675 if (!dataSource) {
1676 if (this._dataDiffer) {
1677 this._dataDiffer.diff([]);
1678 }
1679 this._rowOutlet.viewContainer.clear();
1680 }
1681 this._dataSource = dataSource;
1682 }
1683 /** Set up a subscription for the data provided by the data source. */
1684 _observeRenderChanges() {
1685 // If no data source has been set, there is nothing to observe for changes.
1686 if (!this.dataSource) {
1687 return;
1688 }
1689 let dataStream;
1690 if (isDataSource(this.dataSource)) {
1691 dataStream = this.dataSource.connect(this);
1692 }
1693 else if (isObservable(this.dataSource)) {
1694 dataStream = this.dataSource;
1695 }
1696 else if (Array.isArray(this.dataSource)) {
1697 dataStream = of(this.dataSource);
1698 }
1699 if (dataStream === undefined && (typeof ngDevMode === 'undefined' || ngDevMode)) {
1700 throw getTableUnknownDataSourceError();
1701 }
1702 this._renderChangeSubscription = dataStream
1703 .pipe(takeUntil(this._onDestroy))
1704 .subscribe(data => {
1705 this._data = data || [];
1706 this.renderRows();
1707 });
1708 }
1709 /**
1710 * Clears any existing content in the header row outlet and creates a new embedded view
1711 * in the outlet using the header row definition.
1712 */
1713 _forceRenderHeaderRows() {
1714 // Clear the header row outlet if any content exists.
1715 if (this._headerRowOutlet.viewContainer.length > 0) {
1716 this._headerRowOutlet.viewContainer.clear();
1717 }
1718 this._headerRowDefs.forEach((def, i) => this._renderRow(this._headerRowOutlet, def, i));
1719 this.updateStickyHeaderRowStyles();
1720 }
1721 /**
1722 * Clears any existing content in the footer row outlet and creates a new embedded view
1723 * in the outlet using the footer row definition.
1724 */
1725 _forceRenderFooterRows() {
1726 // Clear the footer row outlet if any content exists.
1727 if (this._footerRowOutlet.viewContainer.length > 0) {
1728 this._footerRowOutlet.viewContainer.clear();
1729 }
1730 this._footerRowDefs.forEach((def, i) => this._renderRow(this._footerRowOutlet, def, i));
1731 this.updateStickyFooterRowStyles();
1732 }
1733 /** Adds the sticky column styles for the rows according to the columns' stick states. */
1734 _addStickyColumnStyles(rows, rowDef) {
1735 const columnDefs = Array.from(rowDef.columns || []).map(columnName => {
1736 const columnDef = this._columnDefsByName.get(columnName);
1737 if (!columnDef && (typeof ngDevMode === 'undefined' || ngDevMode)) {
1738 throw getTableUnknownColumnError(columnName);
1739 }
1740 return columnDef;
1741 });
1742 const stickyStartStates = columnDefs.map(columnDef => columnDef.sticky);
1743 const stickyEndStates = columnDefs.map(columnDef => columnDef.stickyEnd);
1744 this._stickyStyler.updateStickyColumns(rows, stickyStartStates, stickyEndStates, !this._fixedLayout || this._forceRecalculateCellWidths);
1745 }
1746 /** Gets the list of rows that have been rendered in the row outlet. */
1747 _getRenderedRows(rowOutlet) {
1748 const renderedRows = [];
1749 for (let i = 0; i < rowOutlet.viewContainer.length; i++) {
1750 const viewRef = rowOutlet.viewContainer.get(i);
1751 renderedRows.push(viewRef.rootNodes[0]);
1752 }
1753 return renderedRows;
1754 }
1755 /**
1756 * Get the matching row definitions that should be used for this row data. If there is only
1757 * one row definition, it is returned. Otherwise, find the row definitions that has a when
1758 * predicate that returns true with the data. If none return true, return the default row
1759 * definition.
1760 */
1761 _getRowDefs(data, dataIndex) {
1762 if (this._rowDefs.length == 1) {
1763 return [this._rowDefs[0]];
1764 }
1765 let rowDefs = [];
1766 if (this.multiTemplateDataRows) {
1767 rowDefs = this._rowDefs.filter(def => !def.when || def.when(dataIndex, data));
1768 }
1769 else {
1770 let rowDef = this._rowDefs.find(def => def.when && def.when(dataIndex, data)) || this._defaultRowDef;
1771 if (rowDef) {
1772 rowDefs.push(rowDef);
1773 }
1774 }
1775 if (!rowDefs.length && (typeof ngDevMode === 'undefined' || ngDevMode)) {
1776 throw getTableMissingMatchingRowDefError(data);
1777 }
1778 return rowDefs;
1779 }
1780 _getEmbeddedViewArgs(renderRow, index) {
1781 const rowDef = renderRow.rowDef;
1782 const context = { $implicit: renderRow.data };
1783 return {
1784 templateRef: rowDef.template,
1785 context,
1786 index,
1787 };
1788 }
1789 /**
1790 * Creates a new row template in the outlet and fills it with the set of cell templates.
1791 * Optionally takes a context to provide to the row and cells, as well as an optional index
1792 * of where to place the new row template in the outlet.
1793 */
1794 _renderRow(outlet, rowDef, index, context = {}) {
1795 // TODO(andrewseguin): enforce that one outlet was instantiated from createEmbeddedView
1796 const view = outlet.viewContainer.createEmbeddedView(rowDef.template, context, index);
1797 this._renderCellTemplateForItem(rowDef, context);
1798 return view;
1799 }
1800 _renderCellTemplateForItem(rowDef, context) {
1801 for (let cellTemplate of this._getCellTemplates(rowDef)) {
1802 if (CdkCellOutlet.mostRecentCellOutlet) {
1803 CdkCellOutlet.mostRecentCellOutlet._viewContainer.createEmbeddedView(cellTemplate, context);
1804 }
1805 }
1806 this._changeDetectorRef.markForCheck();
1807 }
1808 /**
1809 * Updates the index-related context for each row to reflect any changes in the index of the rows,
1810 * e.g. first/last/even/odd.
1811 */
1812 _updateRowIndexContext() {
1813 const viewContainer = this._rowOutlet.viewContainer;
1814 for (let renderIndex = 0, count = viewContainer.length; renderIndex < count; renderIndex++) {
1815 const viewRef = viewContainer.get(renderIndex);
1816 const context = viewRef.context;
1817 context.count = count;
1818 context.first = renderIndex === 0;
1819 context.last = renderIndex === count - 1;
1820 context.even = renderIndex % 2 === 0;
1821 context.odd = !context.even;
1822 if (this.multiTemplateDataRows) {
1823 context.dataIndex = this._renderRows[renderIndex].dataIndex;
1824 context.renderIndex = renderIndex;
1825 }
1826 else {
1827 context.index = this._renderRows[renderIndex].dataIndex;
1828 }
1829 }
1830 }
1831 /** Gets the column definitions for the provided row def. */
1832 _getCellTemplates(rowDef) {
1833 if (!rowDef || !rowDef.columns) {
1834 return [];
1835 }
1836 return Array.from(rowDef.columns, columnId => {
1837 const column = this._columnDefsByName.get(columnId);
1838 if (!column && (typeof ngDevMode === 'undefined' || ngDevMode)) {
1839 throw getTableUnknownColumnError(columnId);
1840 }
1841 return rowDef.extractCellTemplate(column);
1842 });
1843 }
1844 /** Adds native table sections (e.g. tbody) and moves the row outlets into them. */
1845 _applyNativeTableSections() {
1846 const documentFragment = this._document.createDocumentFragment();
1847 const sections = [
1848 { tag: 'thead', outlets: [this._headerRowOutlet] },
1849 { tag: 'tbody', outlets: [this._rowOutlet, this._noDataRowOutlet] },
1850 { tag: 'tfoot', outlets: [this._footerRowOutlet] },
1851 ];
1852 for (const section of sections) {
1853 const element = this._document.createElement(section.tag);
1854 element.setAttribute('role', 'rowgroup');
1855 for (const outlet of section.outlets) {
1856 element.appendChild(outlet.elementRef.nativeElement);
1857 }
1858 documentFragment.appendChild(element);
1859 }
1860 // Use a DocumentFragment so we don't hit the DOM on each iteration.
1861 this._elementRef.nativeElement.appendChild(documentFragment);
1862 }
1863 /**
1864 * Forces a re-render of the data rows. Should be called in cases where there has been an input
1865 * change that affects the evaluation of which rows should be rendered, e.g. toggling
1866 * `multiTemplateDataRows` or adding/removing row definitions.
1867 */
1868 _forceRenderDataRows() {
1869 this._dataDiffer.diff([]);
1870 this._rowOutlet.viewContainer.clear();
1871 this.renderRows();
1872 }
1873 /**
1874 * Checks if there has been a change in sticky states since last check and applies the correct
1875 * sticky styles. Since checking resets the "dirty" state, this should only be performed once
1876 * during a change detection and after the inputs are settled (after content check).
1877 */
1878 _checkStickyStates() {
1879 const stickyCheckReducer = (acc, d) => {
1880 return acc || d.hasStickyChanged();
1881 };
1882 // Note that the check needs to occur for every definition since it notifies the definition
1883 // that it can reset its dirty state. Using another operator like `some` may short-circuit
1884 // remaining definitions and leave them in an unchecked state.
1885 if (this._headerRowDefs.reduce(stickyCheckReducer, false)) {
1886 this.updateStickyHeaderRowStyles();
1887 }
1888 if (this._footerRowDefs.reduce(stickyCheckReducer, false)) {
1889 this.updateStickyFooterRowStyles();
1890 }
1891 if (Array.from(this._columnDefsByName.values()).reduce(stickyCheckReducer, false)) {
1892 this._stickyColumnStylesNeedReset = true;
1893 this.updateStickyColumnStyles();
1894 }
1895 }
1896 /**
1897 * Creates the sticky styler that will be used for sticky rows and columns. Listens
1898 * for directionality changes and provides the latest direction to the styler. Re-applies column
1899 * stickiness when directionality changes.
1900 */
1901 _setupStickyStyler() {
1902 const direction = this._dir ? this._dir.value : 'ltr';
1903 this._stickyStyler = new StickyStyler(this._isNativeHtmlTable, this.stickyCssClass, direction, this._coalescedStyleScheduler, this._platform.isBrowser, this.needsPositionStickyOnElement, this._stickyPositioningListener);
1904 (this._dir ? this._dir.change : of())
1905 .pipe(takeUntil(this._onDestroy))
1906 .subscribe(value => {
1907 this._stickyStyler.direction = value;
1908 this.updateStickyColumnStyles();
1909 });
1910 }
1911 /** Filters definitions that belong to this table from a QueryList. */
1912 _getOwnDefs(items) {
1913 return items.filter(item => !item._table || item._table === this);
1914 }
1915 /** Creates or removes the no data row, depending on whether any data is being shown. */
1916 _updateNoDataRow() {
1917 const noDataRow = this._customNoDataRow || this._noDataRow;
1918 if (!noDataRow) {
1919 return;
1920 }
1921 const shouldShow = this._rowOutlet.viewContainer.length === 0;
1922 if (shouldShow === this._isShowingNoDataRow) {
1923 return;
1924 }
1925 const container = this._noDataRowOutlet.viewContainer;
1926 if (shouldShow) {
1927 const view = container.createEmbeddedView(noDataRow.templateRef);
1928 const rootNode = view.rootNodes[0];
1929 // Only add the attributes if we have a single root node since it's hard
1930 // to figure out which one to add it to when there are multiple.
1931 if (view.rootNodes.length === 1 && rootNode?.nodeType === this._document.ELEMENT_NODE) {
1932 rootNode.setAttribute('role', 'row');
1933 rootNode.classList.add(noDataRow._contentClassName);
1934 }
1935 }
1936 else {
1937 container.clear();
1938 }
1939 this._isShowingNoDataRow = shouldShow;
1940 }
1941 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTable, deps: [{ token: i0.IterableDiffers }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: 'role', attribute: true }, { token: i1.Directionality, optional: true }, { token: DOCUMENT }, { token: i2.Platform }, { token: _VIEW_REPEATER_STRATEGY }, { token: _COALESCED_STYLE_SCHEDULER }, { token: i3.ViewportRuler }, { token: STICKY_POSITIONING_LISTENER, optional: true, skipSelf: true }, { token: i0.NgZone, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
1942 static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: CdkTable, selector: "cdk-table, table[cdk-table]", inputs: { trackBy: "trackBy", dataSource: "dataSource", multiTemplateDataRows: "multiTemplateDataRows", fixedLayout: "fixedLayout" }, outputs: { contentChanged: "contentChanged" }, host: { attributes: { "ngSkipHydration": "" }, properties: { "class.cdk-table-fixed-layout": "fixedLayout" }, classAttribute: "cdk-table" }, providers: [
1943 { provide: CDK_TABLE, useExisting: CdkTable },
1944 { provide: _VIEW_REPEATER_STRATEGY, useClass: _DisposeViewRepeaterStrategy },
1945 { provide: _COALESCED_STYLE_SCHEDULER, useClass: _CoalescedStyleScheduler },
1946 // Prevent nested tables from seeing this table's StickyPositioningListener.
1947 { provide: STICKY_POSITIONING_LISTENER, useValue: null },
1948 ], queries: [{ propertyName: "_noDataRow", first: true, predicate: CdkNoDataRow, descendants: true }, { propertyName: "_contentColumnDefs", predicate: CdkColumnDef, descendants: true }, { propertyName: "_contentRowDefs", predicate: CdkRowDef, descendants: true }, { propertyName: "_contentHeaderRowDefs", predicate: CdkHeaderRowDef, descendants: true }, { propertyName: "_contentFooterRowDefs", predicate: CdkFooterRowDef, descendants: true }], viewQueries: [{ propertyName: "_rowOutlet", first: true, predicate: DataRowOutlet, descendants: true, static: true }, { propertyName: "_headerRowOutlet", first: true, predicate: HeaderRowOutlet, descendants: true, static: true }, { propertyName: "_footerRowOutlet", first: true, predicate: FooterRowOutlet, descendants: true, static: true }, { propertyName: "_noDataRowOutlet", first: true, predicate: NoDataRowOutlet, descendants: true, static: true }], exportAs: ["cdkTable"], ngImport: i0, template: "\n <ng-content select=\"caption\"></ng-content>\n <ng-content select=\"colgroup, col\"></ng-content>\n <ng-container headerRowOutlet></ng-container>\n <ng-container rowOutlet></ng-container>\n <ng-container noDataRowOutlet></ng-container>\n <ng-container footerRowOutlet></ng-container>\n", isInline: true, styles: [".cdk-table-fixed-layout{table-layout:fixed}"], dependencies: [{ kind: "directive", type: DataRowOutlet, selector: "[rowOutlet]" }, { kind: "directive", type: HeaderRowOutlet, selector: "[headerRowOutlet]" }, { kind: "directive", type: FooterRowOutlet, selector: "[footerRowOutlet]" }, { kind: "directive", type: NoDataRowOutlet, selector: "[noDataRowOutlet]" }], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None }); }
1949}
1950i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTable, decorators: [{
1951 type: Component,
1952 args: [{ selector: 'cdk-table, table[cdk-table]', exportAs: 'cdkTable', template: CDK_TABLE_TEMPLATE, host: {
1953 'class': 'cdk-table',
1954 '[class.cdk-table-fixed-layout]': 'fixedLayout',
1955 'ngSkipHydration': '',
1956 }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.Default, providers: [
1957 { provide: CDK_TABLE, useExisting: CdkTable },
1958 { provide: _VIEW_REPEATER_STRATEGY, useClass: _DisposeViewRepeaterStrategy },
1959 { provide: _COALESCED_STYLE_SCHEDULER, useClass: _CoalescedStyleScheduler },
1960 // Prevent nested tables from seeing this table's StickyPositioningListener.
1961 { provide: STICKY_POSITIONING_LISTENER, useValue: null },
1962 ], styles: [".cdk-table-fixed-layout{table-layout:fixed}"] }]
1963 }], ctorParameters: function () { return [{ type: i0.IterableDiffers }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: undefined, decorators: [{
1964 type: Attribute,
1965 args: ['role']
1966 }] }, { type: i1.Directionality, decorators: [{
1967 type: Optional
1968 }] }, { type: undefined, decorators: [{
1969 type: Inject,
1970 args: [DOCUMENT]
1971 }] }, { type: i2.Platform }, { type: undefined, decorators: [{
1972 type: Inject,
1973 args: [_VIEW_REPEATER_STRATEGY]
1974 }] }, { type: _CoalescedStyleScheduler, decorators: [{
1975 type: Inject,
1976 args: [_COALESCED_STYLE_SCHEDULER]
1977 }] }, { type: i3.ViewportRuler }, { type: undefined, decorators: [{
1978 type: Optional
1979 }, {
1980 type: SkipSelf
1981 }, {
1982 type: Inject,
1983 args: [STICKY_POSITIONING_LISTENER]
1984 }] }, { type: i0.NgZone, decorators: [{
1985 type: Optional
1986 }] }]; }, propDecorators: { trackBy: [{
1987 type: Input
1988 }], dataSource: [{
1989 type: Input
1990 }], multiTemplateDataRows: [{
1991 type: Input
1992 }], fixedLayout: [{
1993 type: Input
1994 }], contentChanged: [{
1995 type: Output
1996 }], _rowOutlet: [{
1997 type: ViewChild,
1998 args: [DataRowOutlet, { static: true }]
1999 }], _headerRowOutlet: [{
2000 type: ViewChild,
2001 args: [HeaderRowOutlet, { static: true }]
2002 }], _footerRowOutlet: [{
2003 type: ViewChild,
2004 args: [FooterRowOutlet, { static: true }]
2005 }], _noDataRowOutlet: [{
2006 type: ViewChild,
2007 args: [NoDataRowOutlet, { static: true }]
2008 }], _contentColumnDefs: [{
2009 type: ContentChildren,
2010 args: [CdkColumnDef, { descendants: true }]
2011 }], _contentRowDefs: [{
2012 type: ContentChildren,
2013 args: [CdkRowDef, { descendants: true }]
2014 }], _contentHeaderRowDefs: [{
2015 type: ContentChildren,
2016 args: [CdkHeaderRowDef, {
2017 descendants: true,
2018 }]
2019 }], _contentFooterRowDefs: [{
2020 type: ContentChildren,
2021 args: [CdkFooterRowDef, {
2022 descendants: true,
2023 }]
2024 }], _noDataRow: [{
2025 type: ContentChild,
2026 args: [CdkNoDataRow]
2027 }] } });
2028/** Utility function that gets a merged list of the entries in an array and values of a Set. */
2029function mergeArrayAndSet(array, set) {
2030 return array.concat(Array.from(set));
2031}
2032
2033/**
2034 * Column that simply shows text content for the header and row cells. Assumes that the table
2035 * is using the native table implementation (`<table>`).
2036 *
2037 * By default, the name of this column will be the header text and data property accessor.
2038 * The header text can be overridden with the `headerText` input. Cell values can be overridden with
2039 * the `dataAccessor` input. Change the text justification to the start or end using the `justify`
2040 * input.
2041 */
2042class CdkTextColumn {
2043 /** Column name that should be used to reference this column. */
2044 get name() {
2045 return this._name;
2046 }
2047 set name(name) {
2048 this._name = name;
2049 // With Ivy, inputs can be initialized before static query results are
2050 // available. In that case, we defer the synchronization until "ngOnInit" fires.
2051 this._syncColumnDefName();
2052 }
2053 constructor(
2054 // `CdkTextColumn` is always requiring a table, but we just assert it manually
2055 // for better error reporting.
2056 // tslint:disable-next-line: lightweight-tokens
2057 _table, _options) {
2058 this._table = _table;
2059 this._options = _options;
2060 /** Alignment of the cell values. */
2061 this.justify = 'start';
2062 this._options = _options || {};
2063 }
2064 ngOnInit() {
2065 this._syncColumnDefName();
2066 if (this.headerText === undefined) {
2067 this.headerText = this._createDefaultHeaderText();
2068 }
2069 if (!this.dataAccessor) {
2070 this.dataAccessor =
2071 this._options.defaultDataAccessor || ((data, name) => data[name]);
2072 }
2073 if (this._table) {
2074 // Provide the cell and headerCell directly to the table with the static `ViewChild` query,
2075 // since the columnDef will not pick up its content by the time the table finishes checking
2076 // its content and initializing the rows.
2077 this.columnDef.cell = this.cell;
2078 this.columnDef.headerCell = this.headerCell;
2079 this._table.addColumnDef(this.columnDef);
2080 }
2081 else if (typeof ngDevMode === 'undefined' || ngDevMode) {
2082 throw getTableTextColumnMissingParentTableError();
2083 }
2084 }
2085 ngOnDestroy() {
2086 if (this._table) {
2087 this._table.removeColumnDef(this.columnDef);
2088 }
2089 }
2090 /**
2091 * Creates a default header text. Use the options' header text transformation function if one
2092 * has been provided. Otherwise simply capitalize the column name.
2093 */
2094 _createDefaultHeaderText() {
2095 const name = this.name;
2096 if (!name && (typeof ngDevMode === 'undefined' || ngDevMode)) {
2097 throw getTableTextColumnMissingNameError();
2098 }
2099 if (this._options && this._options.defaultHeaderTextTransform) {
2100 return this._options.defaultHeaderTextTransform(name);
2101 }
2102 return name[0].toUpperCase() + name.slice(1);
2103 }
2104 /** Synchronizes the column definition name with the text column name. */
2105 _syncColumnDefName() {
2106 if (this.columnDef) {
2107 this.columnDef.name = this.name;
2108 }
2109 }
2110 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTextColumn, deps: [{ token: CdkTable, optional: true }, { token: TEXT_COLUMN_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
2111 static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: CdkTextColumn, selector: "cdk-text-column", inputs: { name: "name", headerText: "headerText", dataAccessor: "dataAccessor", justify: "justify" }, viewQueries: [{ propertyName: "columnDef", first: true, predicate: CdkColumnDef, descendants: true, static: true }, { propertyName: "cell", first: true, predicate: CdkCellDef, descendants: true, static: true }, { propertyName: "headerCell", first: true, predicate: CdkHeaderCellDef, descendants: true, static: true }], ngImport: i0, template: `
2112 <ng-container cdkColumnDef>
2113 <th cdk-header-cell *cdkHeaderCellDef [style.text-align]="justify">
2114 {{headerText}}
2115 </th>
2116 <td cdk-cell *cdkCellDef="let data" [style.text-align]="justify">
2117 {{dataAccessor(data, name)}}
2118 </td>
2119 </ng-container>
2120 `, isInline: true, dependencies: [{ kind: "directive", type: CdkCellDef, selector: "[cdkCellDef]" }, { kind: "directive", type: CdkHeaderCellDef, selector: "[cdkHeaderCellDef]" }, { kind: "directive", type: CdkColumnDef, selector: "[cdkColumnDef]", inputs: ["sticky", "cdkColumnDef", "stickyEnd"] }, { kind: "directive", type: CdkCell, selector: "cdk-cell, td[cdk-cell]" }, { kind: "directive", type: CdkHeaderCell, selector: "cdk-header-cell, th[cdk-header-cell]" }], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None }); }
2121}
2122i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTextColumn, decorators: [{
2123 type: Component,
2124 args: [{
2125 selector: 'cdk-text-column',
2126 template: `
2127 <ng-container cdkColumnDef>
2128 <th cdk-header-cell *cdkHeaderCellDef [style.text-align]="justify">
2129 {{headerText}}
2130 </th>
2131 <td cdk-cell *cdkCellDef="let data" [style.text-align]="justify">
2132 {{dataAccessor(data, name)}}
2133 </td>
2134 </ng-container>
2135 `,
2136 encapsulation: ViewEncapsulation.None,
2137 // Change detection is intentionally not set to OnPush. This component's template will be provided
2138 // to the table to be inserted into its view. This is problematic when change detection runs since
2139 // the bindings in this template will be evaluated _after_ the table's view is evaluated, which
2140 // mean's the template in the table's view will not have the updated value (and in fact will cause
2141 // an ExpressionChangedAfterItHasBeenCheckedError).
2142 // tslint:disable-next-line:validate-decorators
2143 changeDetection: ChangeDetectionStrategy.Default,
2144 }]
2145 }], ctorParameters: function () { return [{ type: CdkTable, decorators: [{
2146 type: Optional
2147 }] }, { type: undefined, decorators: [{
2148 type: Optional
2149 }, {
2150 type: Inject,
2151 args: [TEXT_COLUMN_OPTIONS]
2152 }] }]; }, propDecorators: { name: [{
2153 type: Input
2154 }], headerText: [{
2155 type: Input
2156 }], dataAccessor: [{
2157 type: Input
2158 }], justify: [{
2159 type: Input
2160 }], columnDef: [{
2161 type: ViewChild,
2162 args: [CdkColumnDef, { static: true }]
2163 }], cell: [{
2164 type: ViewChild,
2165 args: [CdkCellDef, { static: true }]
2166 }], headerCell: [{
2167 type: ViewChild,
2168 args: [CdkHeaderCellDef, { static: true }]
2169 }] } });
2170
2171const EXPORTED_DECLARATIONS = [
2172 CdkTable,
2173 CdkRowDef,
2174 CdkCellDef,
2175 CdkCellOutlet,
2176 CdkHeaderCellDef,
2177 CdkFooterCellDef,
2178 CdkColumnDef,
2179 CdkCell,
2180 CdkRow,
2181 CdkHeaderCell,
2182 CdkFooterCell,
2183 CdkHeaderRow,
2184 CdkHeaderRowDef,
2185 CdkFooterRow,
2186 CdkFooterRowDef,
2187 DataRowOutlet,
2188 HeaderRowOutlet,
2189 FooterRowOutlet,
2190 CdkTextColumn,
2191 CdkNoDataRow,
2192 CdkRecycleRows,
2193 NoDataRowOutlet,
2194];
2195class CdkTableModule {
2196 static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2197 static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.0.0", ngImport: i0, type: CdkTableModule, declarations: [CdkTable,
2198 CdkRowDef,
2199 CdkCellDef,
2200 CdkCellOutlet,
2201 CdkHeaderCellDef,
2202 CdkFooterCellDef,
2203 CdkColumnDef,
2204 CdkCell,
2205 CdkRow,
2206 CdkHeaderCell,
2207 CdkFooterCell,
2208 CdkHeaderRow,
2209 CdkHeaderRowDef,
2210 CdkFooterRow,
2211 CdkFooterRowDef,
2212 DataRowOutlet,
2213 HeaderRowOutlet,
2214 FooterRowOutlet,
2215 CdkTextColumn,
2216 CdkNoDataRow,
2217 CdkRecycleRows,
2218 NoDataRowOutlet], imports: [ScrollingModule], exports: [CdkTable,
2219 CdkRowDef,
2220 CdkCellDef,
2221 CdkCellOutlet,
2222 CdkHeaderCellDef,
2223 CdkFooterCellDef,
2224 CdkColumnDef,
2225 CdkCell,
2226 CdkRow,
2227 CdkHeaderCell,
2228 CdkFooterCell,
2229 CdkHeaderRow,
2230 CdkHeaderRowDef,
2231 CdkFooterRow,
2232 CdkFooterRowDef,
2233 DataRowOutlet,
2234 HeaderRowOutlet,
2235 FooterRowOutlet,
2236 CdkTextColumn,
2237 CdkNoDataRow,
2238 CdkRecycleRows,
2239 NoDataRowOutlet] }); }
2240 static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTableModule, imports: [ScrollingModule] }); }
2241}
2242i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTableModule, decorators: [{
2243 type: NgModule,
2244 args: [{
2245 exports: EXPORTED_DECLARATIONS,
2246 declarations: EXPORTED_DECLARATIONS,
2247 imports: [ScrollingModule],
2248 }]
2249 }] });
2250
2251/**
2252 * Generated bundle index. Do not edit.
2253 */
2254
2255export { BaseCdkCell, BaseRowDef, CDK_ROW_TEMPLATE, CDK_TABLE, CDK_TABLE_TEMPLATE, CdkCell, CdkCellDef, CdkCellOutlet, CdkColumnDef, CdkFooterCell, CdkFooterCellDef, CdkFooterRow, CdkFooterRowDef, CdkHeaderCell, CdkHeaderCellDef, CdkHeaderRow, CdkHeaderRowDef, CdkNoDataRow, CdkRecycleRows, CdkRow, CdkRowDef, CdkTable, CdkTableModule, CdkTextColumn, DataRowOutlet, FooterRowOutlet, HeaderRowOutlet, NoDataRowOutlet, STICKY_DIRECTIONS, STICKY_POSITIONING_LISTENER, StickyStyler, TEXT_COLUMN_OPTIONS, _COALESCED_STYLE_SCHEDULER, _CoalescedStyleScheduler, _Schedule, mixinHasStickyInput };
2256//# sourceMappingURL=table.mjs.map