source: trip-planner-front/node_modules/@angular/cdk/fesm2015/table.js@ ceaed42

Last change on this file since ceaed42 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

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