UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

1 lines 180 kB
{"version":3,"file":"table.mjs","sources":["../../../../../k8-fastbuild-ST-46c76129e412/bin/src/cdk/table/tokens.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/cdk/table/cell.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/cdk/table/coalesced-style-scheduler.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/cdk/table/row.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/cdk/table/sticky-styler.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/cdk/table/table-errors.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/cdk/table/sticky-position-listener.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/cdk/table/table.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/cdk/table/text-column.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/cdk/table/table-module.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/cdk/table/can-stick.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {InjectionToken} from '@angular/core';\n\n/**\n * Used to provide a table to some of the sub-components without causing a circular dependency.\n * @docs-private\n */\nexport const CDK_TABLE = new InjectionToken<any>('CDK_TABLE');\n\n/** Configurable options for `CdkTextColumn`. */\nexport interface TextColumnOptions<T> {\n /**\n * Default function that provides the header text based on the column name if a header\n * text is not provided.\n */\n defaultHeaderTextTransform?: (name: string) => string;\n\n /** Default data accessor to use if one is not provided. */\n defaultDataAccessor?: (data: T, name: string) => string;\n}\n\n/** Injection token that can be used to specify the text column options. */\nexport const TEXT_COLUMN_OPTIONS = new InjectionToken<TextColumnOptions<any>>(\n 'text-column-options',\n);\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n ContentChild,\n Directive,\n ElementRef,\n Input,\n TemplateRef,\n booleanAttribute,\n inject,\n} from '@angular/core';\nimport {CanStick} from './can-stick';\nimport {CDK_TABLE} from './tokens';\n\n/** Base interface for a cell definition. Captures a column's cell template definition. */\nexport interface CellDef {\n template: TemplateRef<any>;\n}\n\n/**\n * Cell definition for a CDK table.\n * Captures the template of a column's data row cell as well as cell-specific properties.\n */\n@Directive({\n selector: '[cdkCellDef]',\n})\nexport class CdkCellDef implements CellDef {\n /** @docs-private */\n template = inject<TemplateRef<any>>(TemplateRef);\n\n constructor(...args: unknown[]);\n constructor() {}\n}\n\n/**\n * Header cell definition for a CDK table.\n * Captures the template of a column's header cell and as well as cell-specific properties.\n */\n@Directive({\n selector: '[cdkHeaderCellDef]',\n})\nexport class CdkHeaderCellDef implements CellDef {\n /** @docs-private */\n template = inject<TemplateRef<any>>(TemplateRef);\n\n constructor(...args: unknown[]);\n constructor() {}\n}\n\n/**\n * Footer cell definition for a CDK table.\n * Captures the template of a column's footer cell and as well as cell-specific properties.\n */\n@Directive({\n selector: '[cdkFooterCellDef]',\n})\nexport class CdkFooterCellDef implements CellDef {\n /** @docs-private */\n template = inject<TemplateRef<any>>(TemplateRef);\n\n constructor(...args: unknown[]);\n constructor() {}\n}\n\n/**\n * Column definition for the CDK table.\n * Defines a set of cells available for a table column.\n */\n@Directive({\n selector: '[cdkColumnDef]',\n providers: [{provide: 'MAT_SORT_HEADER_COLUMN_DEF', useExisting: CdkColumnDef}],\n})\nexport class CdkColumnDef implements CanStick {\n _table? = inject(CDK_TABLE, {optional: true});\n\n private _hasStickyChanged = false;\n\n /** Unique name for this column. */\n @Input('cdkColumnDef')\n get name(): string {\n return this._name;\n }\n set name(name: string) {\n this._setNameInput(name);\n }\n protected _name: string;\n\n /** Whether the cell is sticky. */\n @Input({transform: booleanAttribute})\n get sticky(): boolean {\n return this._sticky;\n }\n set sticky(value: boolean) {\n if (value !== this._sticky) {\n this._sticky = value;\n this._hasStickyChanged = true;\n }\n }\n private _sticky = false;\n\n /**\n * Whether this column should be sticky positioned on the end of the row. Should make sure\n * that it mimics the `CanStick` mixin such that `_hasStickyChanged` is set to true if the value\n * has been changed.\n */\n @Input({transform: booleanAttribute})\n get stickyEnd(): boolean {\n return this._stickyEnd;\n }\n set stickyEnd(value: boolean) {\n if (value !== this._stickyEnd) {\n this._stickyEnd = value;\n this._hasStickyChanged = true;\n }\n }\n _stickyEnd: boolean = false;\n\n /** @docs-private */\n @ContentChild(CdkCellDef) cell: CdkCellDef;\n\n /** @docs-private */\n @ContentChild(CdkHeaderCellDef) headerCell: CdkHeaderCellDef;\n\n /** @docs-private */\n @ContentChild(CdkFooterCellDef) footerCell: CdkFooterCellDef;\n\n /**\n * Transformed version of the column name that can be used as part of a CSS classname. Excludes\n * all non-alphanumeric characters and the special characters '-' and '_'. Any characters that\n * do not match are replaced by the '-' character.\n */\n cssClassFriendlyName: string;\n\n /**\n * Class name for cells in this column.\n * @docs-private\n */\n _columnCssClassName: string[];\n\n constructor(...args: unknown[]);\n constructor() {}\n\n /** Whether the sticky state has changed. */\n hasStickyChanged(): boolean {\n const hasStickyChanged = this._hasStickyChanged;\n this.resetStickyChanged();\n return hasStickyChanged;\n }\n\n /** Resets the sticky changed state. */\n resetStickyChanged(): void {\n this._hasStickyChanged = false;\n }\n\n /**\n * Overridable method that sets the css classes that will be added to every cell in this\n * column.\n * In the future, columnCssClassName will change from type string[] to string and this\n * will set a single string value.\n * @docs-private\n */\n protected _updateColumnCssClassName() {\n this._columnCssClassName = [`cdk-column-${this.cssClassFriendlyName}`];\n }\n\n /**\n * This has been extracted to a util because of TS 4 and VE.\n * View Engine doesn't support property rename inheritance.\n * TS 4.0 doesn't allow properties to override accessors or vice-versa.\n * @docs-private\n */\n protected _setNameInput(value: string) {\n // If the directive is set without a name (updated programmatically), then this setter will\n // trigger with an empty string and should not overwrite the programmatically set value.\n if (value) {\n this._name = value;\n this.cssClassFriendlyName = value.replace(/[^a-z0-9_-]/gi, '-');\n this._updateColumnCssClassName();\n }\n }\n}\n\n/** Base class for the cells. Adds a CSS classname that identifies the column it renders in. */\nexport class BaseCdkCell {\n constructor(columnDef: CdkColumnDef, elementRef: ElementRef) {\n elementRef.nativeElement.classList.add(...columnDef._columnCssClassName);\n }\n}\n\n/** Header cell template container that adds the right classes and role. */\n@Directive({\n selector: 'cdk-header-cell, th[cdk-header-cell]',\n host: {\n 'class': 'cdk-header-cell',\n 'role': 'columnheader',\n },\n})\nexport class CdkHeaderCell extends BaseCdkCell {\n constructor(...args: unknown[]);\n\n constructor() {\n super(inject(CdkColumnDef), inject(ElementRef));\n }\n}\n\n/** Footer cell template container that adds the right classes and role. */\n@Directive({\n selector: 'cdk-footer-cell, td[cdk-footer-cell]',\n host: {\n 'class': 'cdk-footer-cell',\n },\n})\nexport class CdkFooterCell extends BaseCdkCell {\n constructor(...args: unknown[]);\n\n constructor() {\n const columnDef = inject(CdkColumnDef);\n const elementRef = inject(ElementRef);\n\n super(columnDef, elementRef);\n\n const role = columnDef._table?._getCellRole();\n if (role) {\n elementRef.nativeElement.setAttribute('role', role);\n }\n }\n}\n\n/** Cell template container that adds the right classes and role. */\n@Directive({\n selector: 'cdk-cell, td[cdk-cell]',\n host: {\n 'class': 'cdk-cell',\n },\n})\nexport class CdkCell extends BaseCdkCell {\n constructor(...args: unknown[]);\n\n constructor() {\n const columnDef = inject(CdkColumnDef);\n const elementRef = inject(ElementRef);\n\n super(columnDef, elementRef);\n\n const role = columnDef._table?._getCellRole();\n if (role) {\n elementRef.nativeElement.setAttribute('role', role);\n }\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Injectable, InjectionToken, NgZone, inject} from '@angular/core';\n\n/**\n * @docs-private\n */\nexport class _Schedule {\n tasks: (() => unknown)[] = [];\n endTasks: (() => unknown)[] = [];\n}\n\n/** Injection token used to provide a coalesced style scheduler. */\nexport const _COALESCED_STYLE_SCHEDULER = new InjectionToken<_CoalescedStyleScheduler>(\n '_COALESCED_STYLE_SCHEDULER',\n);\n\n/**\n * Allows grouping up CSSDom mutations after the current execution context.\n * This can significantly improve performance when separate consecutive functions are\n * reading from the CSSDom and then mutating it.\n *\n * @docs-private\n */\n@Injectable()\nexport class _CoalescedStyleScheduler {\n private _currentSchedule: _Schedule | null = null;\n private _ngZone = inject(NgZone);\n\n constructor(...args: unknown[]);\n constructor() {}\n\n /**\n * Schedules the specified task to run at the end of the current VM turn.\n */\n schedule(task: () => unknown): void {\n this._createScheduleIfNeeded();\n\n this._currentSchedule!.tasks.push(task);\n }\n\n /**\n * Schedules the specified task to run after other scheduled tasks at the end of the current\n * VM turn.\n */\n scheduleEnd(task: () => unknown): void {\n this._createScheduleIfNeeded();\n\n this._currentSchedule!.endTasks.push(task);\n }\n\n private _createScheduleIfNeeded() {\n if (this._currentSchedule) {\n return;\n }\n\n this._currentSchedule = new _Schedule();\n\n this._ngZone.runOutsideAngular(() =>\n // TODO(mmalerba): Scheduling this using something that runs less frequently\n // (e.g. requestAnimationFrame, setTimeout, etc.) causes noticeable jank with the column\n // resizer. We should audit the usages of schedule / scheduleEnd in that component and see\n // if we can refactor it so that we don't need to flush the tasks quite so frequently.\n queueMicrotask(() => {\n while (this._currentSchedule!.tasks.length || this._currentSchedule!.endTasks.length) {\n const schedule = this._currentSchedule!;\n\n // Capture new tasks scheduled by the current set of tasks.\n this._currentSchedule = new _Schedule();\n\n for (const task of schedule.tasks) {\n task();\n }\n\n for (const task of schedule.endTasks) {\n task();\n }\n }\n\n this._currentSchedule = null;\n }),\n );\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n ChangeDetectionStrategy,\n Component,\n Directive,\n IterableChanges,\n IterableDiffer,\n IterableDiffers,\n OnChanges,\n OnDestroy,\n SimpleChanges,\n TemplateRef,\n ViewContainerRef,\n ViewEncapsulation,\n Input,\n booleanAttribute,\n inject,\n} from '@angular/core';\nimport {CanStick} from './can-stick';\nimport {CdkCellDef, CdkColumnDef} from './cell';\nimport {CDK_TABLE} from './tokens';\n\n/**\n * The row template that can be used by the mat-table. Should not be used outside of the\n * material library.\n */\nexport const CDK_ROW_TEMPLATE = `<ng-container cdkCellOutlet></ng-container>`;\n\n/**\n * Base class for the CdkHeaderRowDef and CdkRowDef that handles checking their columns inputs\n * for changes and notifying the table.\n */\n@Directive()\nexport abstract class BaseRowDef implements OnChanges {\n template = inject<TemplateRef<any>>(TemplateRef);\n protected _differs = inject(IterableDiffers);\n\n /** The columns to be displayed on this row. */\n columns: Iterable<string>;\n\n /** Differ used to check if any changes were made to the columns. */\n protected _columnsDiffer: IterableDiffer<any>;\n\n constructor(...args: unknown[]);\n constructor() {}\n\n ngOnChanges(changes: SimpleChanges): void {\n // Create a new columns differ if one does not yet exist. Initialize it based on initial value\n // of the columns property or an empty array if none is provided.\n if (!this._columnsDiffer) {\n const columns = (changes['columns'] && changes['columns'].currentValue) || [];\n this._columnsDiffer = this._differs.find(columns).create();\n this._columnsDiffer.diff(columns);\n }\n }\n\n /**\n * Returns the difference between the current columns and the columns from the last diff, or null\n * if there is no difference.\n */\n getColumnsDiff(): IterableChanges<any> | null {\n return this._columnsDiffer.diff(this.columns);\n }\n\n /** Gets this row def's relevant cell template from the provided column def. */\n extractCellTemplate(column: CdkColumnDef): TemplateRef<any> {\n if (this instanceof CdkHeaderRowDef) {\n return column.headerCell.template;\n }\n if (this instanceof CdkFooterRowDef) {\n return column.footerCell.template;\n } else {\n return column.cell.template;\n }\n }\n}\n\n/**\n * Header row definition for the CDK table.\n * Captures the header row's template and other header properties such as the columns to display.\n */\n@Directive({\n selector: '[cdkHeaderRowDef]',\n inputs: [{name: 'columns', alias: 'cdkHeaderRowDef'}],\n})\nexport class CdkHeaderRowDef extends BaseRowDef implements CanStick, OnChanges {\n _table? = inject(CDK_TABLE, {optional: true});\n\n private _hasStickyChanged = false;\n\n /** Whether the row is sticky. */\n @Input({alias: 'cdkHeaderRowDefSticky', transform: booleanAttribute})\n get sticky(): boolean {\n return this._sticky;\n }\n set sticky(value: boolean) {\n if (value !== this._sticky) {\n this._sticky = value;\n this._hasStickyChanged = true;\n }\n }\n private _sticky = false;\n\n constructor(...args: unknown[]);\n\n constructor() {\n super(inject<TemplateRef<any>>(TemplateRef), inject(IterableDiffers));\n }\n\n // Prerender fails to recognize that ngOnChanges in a part of this class through inheritance.\n // Explicitly define it so that the method is called as part of the Angular lifecycle.\n override ngOnChanges(changes: SimpleChanges): void {\n super.ngOnChanges(changes);\n }\n\n /** Whether the sticky state has changed. */\n hasStickyChanged(): boolean {\n const hasStickyChanged = this._hasStickyChanged;\n this.resetStickyChanged();\n return hasStickyChanged;\n }\n\n /** Resets the sticky changed state. */\n resetStickyChanged(): void {\n this._hasStickyChanged = false;\n }\n}\n\n/**\n * Footer row definition for the CDK table.\n * Captures the footer row's template and other footer properties such as the columns to display.\n */\n@Directive({\n selector: '[cdkFooterRowDef]',\n inputs: [{name: 'columns', alias: 'cdkFooterRowDef'}],\n})\nexport class CdkFooterRowDef extends BaseRowDef implements CanStick, OnChanges {\n _table? = inject(CDK_TABLE, {optional: true});\n\n private _hasStickyChanged = false;\n\n /** Whether the row is sticky. */\n @Input({alias: 'cdkFooterRowDefSticky', transform: booleanAttribute})\n get sticky(): boolean {\n return this._sticky;\n }\n set sticky(value: boolean) {\n if (value !== this._sticky) {\n this._sticky = value;\n this._hasStickyChanged = true;\n }\n }\n private _sticky = false;\n\n constructor(...args: unknown[]);\n\n constructor() {\n super(inject<TemplateRef<any>>(TemplateRef), inject(IterableDiffers));\n }\n\n // Prerender fails to recognize that ngOnChanges in a part of this class through inheritance.\n // Explicitly define it so that the method is called as part of the Angular lifecycle.\n override ngOnChanges(changes: SimpleChanges): void {\n super.ngOnChanges(changes);\n }\n\n /** Whether the sticky state has changed. */\n hasStickyChanged(): boolean {\n const hasStickyChanged = this._hasStickyChanged;\n this.resetStickyChanged();\n return hasStickyChanged;\n }\n\n /** Resets the sticky changed state. */\n resetStickyChanged(): void {\n this._hasStickyChanged = false;\n }\n}\n\n/**\n * Data row definition for the CDK table.\n * Captures the header row's template and other row properties such as the columns to display and\n * a when predicate that describes when this row should be used.\n */\n@Directive({\n selector: '[cdkRowDef]',\n inputs: [\n {name: 'columns', alias: 'cdkRowDefColumns'},\n {name: 'when', alias: 'cdkRowDefWhen'},\n ],\n})\nexport class CdkRowDef<T> extends BaseRowDef {\n _table? = inject(CDK_TABLE, {optional: true});\n\n /**\n * Function that should return true if this row template should be used for the provided index\n * and row data. If left undefined, this row will be considered the default row template to use\n * when no other when functions return true for the data.\n * For every row, there must be at least one when function that passes or an undefined to default.\n */\n when: (index: number, rowData: T) => boolean;\n\n constructor(...args: unknown[]);\n\n constructor() {\n // TODO(andrewseguin): Add an input for providing a switch function to determine\n // if this template should be used.\n super(inject<TemplateRef<any>>(TemplateRef), inject(IterableDiffers));\n }\n}\n\n/** Context provided to the row cells when `multiTemplateDataRows` is false */\nexport interface CdkCellOutletRowContext<T> {\n /** Data for the row that this cell is located within. */\n $implicit?: T;\n\n /** Index of the data object in the provided data array. */\n index?: number;\n\n /** Length of the number of total rows. */\n count?: number;\n\n /** True if this cell is contained in the first row. */\n first?: boolean;\n\n /** True if this cell is contained in the last row. */\n last?: boolean;\n\n /** True if this cell is contained in a row with an even-numbered index. */\n even?: boolean;\n\n /** True if this cell is contained in a row with an odd-numbered index. */\n odd?: boolean;\n}\n\n/**\n * Context provided to the row cells when `multiTemplateDataRows` is true. This context is the same\n * as CdkCellOutletRowContext except that the single `index` value is replaced by `dataIndex` and\n * `renderIndex`.\n */\nexport interface CdkCellOutletMultiRowContext<T> {\n /** Data for the row that this cell is located within. */\n $implicit?: T;\n\n /** Index of the data object in the provided data array. */\n dataIndex?: number;\n\n /** Index location of the rendered row that this cell is located within. */\n renderIndex?: number;\n\n /** Length of the number of total rows. */\n count?: number;\n\n /** True if this cell is contained in the first row. */\n first?: boolean;\n\n /** True if this cell is contained in the last row. */\n last?: boolean;\n\n /** True if this cell is contained in a row with an even-numbered index. */\n even?: boolean;\n\n /** True if this cell is contained in a row with an odd-numbered index. */\n odd?: boolean;\n}\n\n/**\n * Outlet for rendering cells inside of a row or header row.\n * @docs-private\n */\n@Directive({\n selector: '[cdkCellOutlet]',\n})\nexport class CdkCellOutlet implements OnDestroy {\n _viewContainer = inject(ViewContainerRef);\n\n /** The ordered list of cells to render within this outlet's view container */\n cells: CdkCellDef[];\n\n /** The data context to be provided to each cell */\n context: any;\n\n /**\n * Static property containing the latest constructed instance of this class.\n * Used by the CDK table when each CdkHeaderRow and CdkRow component is created using\n * createEmbeddedView. After one of these components are created, this property will provide\n * a handle to provide that component's cells and context. After init, the CdkCellOutlet will\n * construct the cells with the provided context.\n */\n static mostRecentCellOutlet: CdkCellOutlet | null = null;\n\n constructor(...args: unknown[]);\n\n constructor() {\n CdkCellOutlet.mostRecentCellOutlet = this;\n }\n\n ngOnDestroy() {\n // If this was the last outlet being rendered in the view, remove the reference\n // from the static property after it has been destroyed to avoid leaking memory.\n if (CdkCellOutlet.mostRecentCellOutlet === this) {\n CdkCellOutlet.mostRecentCellOutlet = null;\n }\n }\n}\n\n/** Header template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'cdk-header-row, tr[cdk-header-row]',\n template: CDK_ROW_TEMPLATE,\n host: {\n 'class': 'cdk-header-row',\n 'role': 'row',\n },\n // See note on CdkTable for explanation on why this uses the default change detection strategy.\n // tslint:disable-next-line:validate-decorators\n changeDetection: ChangeDetectionStrategy.Default,\n encapsulation: ViewEncapsulation.None,\n imports: [CdkCellOutlet],\n})\nexport class CdkHeaderRow {}\n\n/** Footer template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'cdk-footer-row, tr[cdk-footer-row]',\n template: CDK_ROW_TEMPLATE,\n host: {\n 'class': 'cdk-footer-row',\n 'role': 'row',\n },\n // See note on CdkTable for explanation on why this uses the default change detection strategy.\n // tslint:disable-next-line:validate-decorators\n changeDetection: ChangeDetectionStrategy.Default,\n encapsulation: ViewEncapsulation.None,\n imports: [CdkCellOutlet],\n})\nexport class CdkFooterRow {}\n\n/** Data row template container that contains the cell outlet. Adds the right class and role. */\n@Component({\n selector: 'cdk-row, tr[cdk-row]',\n template: CDK_ROW_TEMPLATE,\n host: {\n 'class': 'cdk-row',\n 'role': 'row',\n },\n // See note on CdkTable for explanation on why this uses the default change detection strategy.\n // tslint:disable-next-line:validate-decorators\n changeDetection: ChangeDetectionStrategy.Default,\n encapsulation: ViewEncapsulation.None,\n imports: [CdkCellOutlet],\n})\nexport class CdkRow {}\n\n/** Row that can be used to display a message when no data is shown in the table. */\n@Directive({\n selector: 'ng-template[cdkNoDataRow]',\n})\nexport class CdkNoDataRow {\n templateRef = inject<TemplateRef<any>>(TemplateRef);\n\n _contentClassName = 'cdk-no-data-row';\n\n constructor(...args: unknown[]);\n constructor() {}\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n/**\n * Directions that can be used when setting sticky positioning.\n * @docs-private\n */\nimport {afterNextRender, Injector} from '@angular/core';\nimport {Direction} from '../bidi';\nimport {_CoalescedStyleScheduler} from './coalesced-style-scheduler';\nimport {StickyPositioningListener} from './sticky-position-listener';\n\nexport type StickyDirection = 'top' | 'bottom' | 'left' | 'right';\n\ninterface UpdateStickyColumnsParams {\n rows: HTMLElement[];\n stickyStartStates: boolean[];\n stickyEndStates: boolean[];\n}\n\n/**\n * List of all possible directions that can be used for sticky positioning.\n * @docs-private\n */\nexport const STICKY_DIRECTIONS: StickyDirection[] = ['top', 'bottom', 'left', 'right'];\n\n/**\n * Applies and removes sticky positioning styles to the `CdkTable` rows and columns cells.\n * @docs-private\n */\nexport class StickyStyler {\n private _elemSizeCache = new WeakMap<HTMLElement, {width: number; height: number}>();\n private _resizeObserver = globalThis?.ResizeObserver\n ? new globalThis.ResizeObserver(entries => this._updateCachedSizes(entries))\n : null;\n private _updatedStickyColumnsParamsToReplay: UpdateStickyColumnsParams[] = [];\n private _stickyColumnsReplayTimeout: ReturnType<typeof setTimeout> | null = null;\n private _cachedCellWidths: number[] = [];\n private readonly _borderCellCss: Readonly<{[d in StickyDirection]: string}>;\n private _destroyed = false;\n\n /**\n * @param _isNativeHtmlTable Whether the sticky logic should be based on a table\n * that uses the native `<table>` element.\n * @param _stickCellCss The CSS class that will be applied to every row/cell that has\n * sticky positioning applied.\n * @param direction The directionality context of the table (ltr/rtl); affects column positioning\n * by reversing left/right positions.\n * @param _isBrowser Whether the table is currently being rendered on the server or the client.\n * @param _needsPositionStickyOnElement Whether we need to specify position: sticky on cells\n * using inline styles. If false, it is assumed that position: sticky is included in\n * the component stylesheet for _stickCellCss.\n * @param _positionListener A listener that is notified of changes to sticky rows/columns\n * and their dimensions.\n * @param _tableInjector The table's Injector.\n */\n constructor(\n private _isNativeHtmlTable: boolean,\n private _stickCellCss: string,\n public direction: Direction,\n private _coalescedStyleScheduler: _CoalescedStyleScheduler,\n private _isBrowser = true,\n private readonly _needsPositionStickyOnElement = true,\n private readonly _positionListener?: StickyPositioningListener,\n private readonly _tableInjector?: Injector,\n ) {\n this._borderCellCss = {\n 'top': `${_stickCellCss}-border-elem-top`,\n 'bottom': `${_stickCellCss}-border-elem-bottom`,\n 'left': `${_stickCellCss}-border-elem-left`,\n 'right': `${_stickCellCss}-border-elem-right`,\n };\n }\n\n /**\n * Clears the sticky positioning styles from the row and its cells by resetting the `position`\n * style, setting the zIndex to 0, and unsetting each provided sticky direction.\n * @param rows The list of rows that should be cleared from sticking in the provided directions\n * @param stickyDirections The directions that should no longer be set as sticky on the rows.\n */\n clearStickyPositioning(rows: HTMLElement[], stickyDirections: StickyDirection[]) {\n if (stickyDirections.includes('left') || stickyDirections.includes('right')) {\n this._removeFromStickyColumnReplayQueue(rows);\n }\n\n const elementsToClear: HTMLElement[] = [];\n for (const row of rows) {\n // If the row isn't an element (e.g. if it's an `ng-container`),\n // it won't have inline styles or `children` so we skip it.\n if (row.nodeType !== row.ELEMENT_NODE) {\n continue;\n }\n\n elementsToClear.push(row, ...(Array.from(row.children) as HTMLElement[]));\n }\n\n // Coalesce with sticky row/column updates (and potentially other changes like column resize).\n this._afterNextRender({\n write: () => {\n for (const element of elementsToClear) {\n this._removeStickyStyle(element, stickyDirections);\n }\n },\n });\n }\n\n /**\n * Applies sticky left and right positions to the cells of each row according to the sticky\n * states of the rendered column definitions.\n * @param rows The rows that should have its set of cells stuck according to the sticky states.\n * @param stickyStartStates A list of boolean states where each state represents whether the cell\n * in this index position should be stuck to the start of the row.\n * @param stickyEndStates A list of boolean states where each state represents whether the cell\n * in this index position should be stuck to the end of the row.\n * @param recalculateCellWidths Whether the sticky styler should recalculate the width of each\n * column cell. If `false` cached widths will be used instead.\n * @param replay Whether to enqueue this call for replay after a ResizeObserver update.\n */\n updateStickyColumns(\n rows: HTMLElement[],\n stickyStartStates: boolean[],\n stickyEndStates: boolean[],\n recalculateCellWidths = true,\n replay = true,\n ) {\n // Don't cache any state if none of the columns are sticky.\n if (\n !rows.length ||\n !this._isBrowser ||\n !(stickyStartStates.some(state => state) || stickyEndStates.some(state => state))\n ) {\n this._positionListener?.stickyColumnsUpdated({sizes: []});\n this._positionListener?.stickyEndColumnsUpdated({sizes: []});\n return;\n }\n\n // Coalesce with sticky row updates (and potentially other changes like column resize).\n const firstRow = rows[0];\n const numCells = firstRow.children.length;\n\n const isRtl = this.direction === 'rtl';\n const start = isRtl ? 'right' : 'left';\n const end = isRtl ? 'left' : 'right';\n\n const lastStickyStart = stickyStartStates.lastIndexOf(true);\n const firstStickyEnd = stickyEndStates.indexOf(true);\n\n let cellWidths: number[];\n let startPositions: number[];\n let endPositions: number[];\n\n if (replay) {\n this._updateStickyColumnReplayQueue({\n rows: [...rows],\n stickyStartStates: [...stickyStartStates],\n stickyEndStates: [...stickyEndStates],\n });\n }\n\n this._afterNextRender({\n earlyRead: () => {\n cellWidths = this._getCellWidths(firstRow, recalculateCellWidths);\n\n startPositions = this._getStickyStartColumnPositions(cellWidths, stickyStartStates);\n endPositions = this._getStickyEndColumnPositions(cellWidths, stickyEndStates);\n },\n write: () => {\n for (const row of rows) {\n for (let i = 0; i < numCells; i++) {\n const cell = row.children[i] as HTMLElement;\n if (stickyStartStates[i]) {\n this._addStickyStyle(cell, start, startPositions[i], i === lastStickyStart);\n }\n\n if (stickyEndStates[i]) {\n this._addStickyStyle(cell, end, endPositions[i], i === firstStickyEnd);\n }\n }\n }\n\n if (this._positionListener && cellWidths.some(w => !!w)) {\n this._positionListener.stickyColumnsUpdated({\n sizes:\n lastStickyStart === -1\n ? []\n : cellWidths\n .slice(0, lastStickyStart + 1)\n .map((width, index) => (stickyStartStates[index] ? width : null)),\n });\n this._positionListener.stickyEndColumnsUpdated({\n sizes:\n firstStickyEnd === -1\n ? []\n : cellWidths\n .slice(firstStickyEnd)\n .map((width, index) => (stickyEndStates[index + firstStickyEnd] ? width : null))\n .reverse(),\n });\n }\n },\n });\n }\n\n /**\n * Applies sticky positioning to the row's cells if using the native table layout, and to the\n * row itself otherwise.\n * @param rowsToStick The list of rows that should be stuck according to their corresponding\n * sticky state and to the provided top or bottom position.\n * @param stickyStates A list of boolean states where each state represents whether the row\n * should be stuck in the particular top or bottom position.\n * @param position The position direction in which the row should be stuck if that row should be\n * sticky.\n *\n */\n stickRows(rowsToStick: HTMLElement[], stickyStates: boolean[], position: 'top' | 'bottom') {\n // Since we can't measure the rows on the server, we can't stick the rows properly.\n if (!this._isBrowser) {\n return;\n }\n\n // If positioning the rows to the bottom, reverse their order when evaluating the sticky\n // position such that the last row stuck will be \"bottom: 0px\" and so on. Note that the\n // sticky states need to be reversed as well.\n const rows = position === 'bottom' ? rowsToStick.slice().reverse() : rowsToStick;\n const states = position === 'bottom' ? stickyStates.slice().reverse() : stickyStates;\n\n // Measure row heights all at once before adding sticky styles to reduce layout thrashing.\n const stickyOffsets: number[] = [];\n const stickyCellHeights: (number | undefined)[] = [];\n const elementsToStick: HTMLElement[][] = [];\n\n // Coalesce with other sticky row updates (top/bottom), sticky columns updates\n // (and potentially other changes like column resize).\n this._afterNextRender({\n earlyRead: () => {\n for (let rowIndex = 0, stickyOffset = 0; rowIndex < rows.length; rowIndex++) {\n if (!states[rowIndex]) {\n continue;\n }\n\n stickyOffsets[rowIndex] = stickyOffset;\n const row = rows[rowIndex];\n elementsToStick[rowIndex] = this._isNativeHtmlTable\n ? (Array.from(row.children) as HTMLElement[])\n : [row];\n\n const height = this._retrieveElementSize(row).height;\n stickyOffset += height;\n stickyCellHeights[rowIndex] = height;\n }\n },\n write: () => {\n const borderedRowIndex = states.lastIndexOf(true);\n\n for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {\n if (!states[rowIndex]) {\n continue;\n }\n\n const offset = stickyOffsets[rowIndex];\n const isBorderedRowIndex = rowIndex === borderedRowIndex;\n for (const element of elementsToStick[rowIndex]) {\n this._addStickyStyle(element, position, offset, isBorderedRowIndex);\n }\n }\n\n if (position === 'top') {\n this._positionListener?.stickyHeaderRowsUpdated({\n sizes: stickyCellHeights,\n offsets: stickyOffsets,\n elements: elementsToStick,\n });\n } else {\n this._positionListener?.stickyFooterRowsUpdated({\n sizes: stickyCellHeights,\n offsets: stickyOffsets,\n elements: elementsToStick,\n });\n }\n },\n });\n }\n\n /**\n * When using the native table in Safari, sticky footer cells do not stick. The only way to stick\n * footer rows is to apply sticky styling to the tfoot container. This should only be done if\n * all footer rows are sticky. If not all footer rows are sticky, remove sticky positioning from\n * the tfoot element.\n */\n updateStickyFooterContainer(tableElement: Element, stickyStates: boolean[]) {\n if (!this._isNativeHtmlTable) {\n return;\n }\n\n // Coalesce with other sticky updates (and potentially other changes like column resize).\n this._afterNextRender({\n write: () => {\n const tfoot = tableElement.querySelector('tfoot')!;\n\n if (tfoot) {\n if (stickyStates.some(state => !state)) {\n this._removeStickyStyle(tfoot, ['bottom']);\n } else {\n this._addStickyStyle(tfoot, 'bottom', 0, false);\n }\n }\n },\n });\n }\n\n /** Triggered by the table's OnDestroy hook. */\n destroy() {\n if (this._stickyColumnsReplayTimeout) {\n clearTimeout(this._stickyColumnsReplayTimeout);\n }\n\n this._resizeObserver?.disconnect();\n this._destroyed = true;\n }\n\n /**\n * Removes the sticky style on the element by removing the sticky cell CSS class, re-evaluating\n * the zIndex, removing each of the provided sticky directions, and removing the\n * sticky position if there are no more directions.\n */\n _removeStickyStyle(element: HTMLElement, stickyDirections: StickyDirection[]) {\n if (!element.classList.contains(this._stickCellCss)) {\n return;\n }\n\n for (const dir of stickyDirections) {\n element.style[dir] = '';\n element.classList.remove(this._borderCellCss[dir]);\n }\n\n // If the element no longer has any more sticky directions, remove sticky positioning and\n // the sticky CSS class.\n // Short-circuit checking element.style[dir] for stickyDirections as they\n // were already removed above.\n const hasDirection = STICKY_DIRECTIONS.some(\n dir => stickyDirections.indexOf(dir) === -1 && element.style[dir],\n );\n if (hasDirection) {\n element.style.zIndex = this._getCalculatedZIndex(element);\n } else {\n // When not hasDirection, _getCalculatedZIndex will always return ''.\n element.style.zIndex = '';\n if (this._needsPositionStickyOnElement) {\n element.style.position = '';\n }\n element.classList.remove(this._stickCellCss);\n }\n }\n\n /**\n * Adds the sticky styling to the element by adding the sticky style class, changing position\n * to be sticky (and -webkit-sticky), setting the appropriate zIndex, and adding a sticky\n * direction and value.\n */\n _addStickyStyle(\n element: HTMLElement,\n dir: StickyDirection,\n dirValue: number,\n isBorderElement: boolean,\n ) {\n element.classList.add(this._stickCellCss);\n if (isBorderElement) {\n element.classList.add(this._borderCellCss[dir]);\n }\n element.style[dir] = `${dirValue}px`;\n element.style.zIndex = this._getCalculatedZIndex(element);\n if (this._needsPositionStickyOnElement) {\n element.style.cssText += 'position: -webkit-sticky; position: sticky; ';\n }\n }\n\n /**\n * Calculate what the z-index should be for the element, depending on what directions (top,\n * bottom, left, right) have been set. It should be true that elements with a top direction\n * should have the highest index since these are elements like a table header. If any of those\n * elements are also sticky in another direction, then they should appear above other elements\n * that are only sticky top (e.g. a sticky column on a sticky header). Bottom-sticky elements\n * (e.g. footer rows) should then be next in the ordering such that they are below the header\n * but above any non-sticky elements. Finally, left/right sticky elements (e.g. sticky columns)\n * should minimally increment so that they are above non-sticky elements but below top and bottom\n * elements.\n */\n _getCalculatedZIndex(element: HTMLElement): string {\n const zIndexIncrements = {\n top: 100,\n bottom: 10,\n left: 1,\n right: 1,\n };\n\n let zIndex = 0;\n // Use `Iterable` instead of `Array` because TypeScript, as of 3.6.3,\n // loses the array generic type in the `for of`. But we *also* have to use `Array` because\n // typescript won't iterate over an `Iterable` unless you compile with `--downlevelIteration`\n for (const dir of STICKY_DIRECTIONS as Iterable<StickyDirection> & StickyDirection[]) {\n if (element.style[dir]) {\n zIndex += zIndexIncrements[dir];\n }\n }\n\n return zIndex ? `${zIndex}` : '';\n }\n\n /** Gets the widths for each cell in the provided row. */\n _getCellWidths(row: HTMLElement, recalculateCellWidths = true): number[] {\n if (!recalculateCellWidths && this._cachedCellWidths.length) {\n return this._cachedCellWidths;\n }\n\n const cellWidths: number[] = [];\n const firstRowCells = row.children;\n for (let i = 0; i < firstRowCells.length; i++) {\n const cell = firstRowCells[i] as HTMLElement;\n cellWidths.push(this._retrieveElementSize(cell).width);\n }\n\n this._cachedCellWidths = cellWidths;\n return cellWidths;\n }\n\n /**\n * Determines the left and right positions of each sticky column cell, which will be the\n * accumulation of all sticky column cell widths to the left and right, respectively.\n * Non-sticky cells do not need to have a value set since their positions will not be applied.\n */\n _getStickyStartColumnPositions(widths: number[], stickyStates: boolean[]): number[] {\n const positions: number[] = [];\n let nextPosition = 0;\n\n for (let i = 0; i < widths.length; i++) {\n if (stickyStates[i]) {\n positions[i] = nextPosition;\n nextPosition += widths[i];\n }\n }\n\n return positions;\n }\n\n /**\n * Determines the left and right positions of each sticky column cell, which will be the\n * accumulation of all sticky column cell widths to the left and right, respectively.\n * Non-sticky cells do not need to have a value set since their positions will not be applied.\n */\n _getStickyEndColumnPositions(widths: number[], stickyStates: boolean[]): number[] {\n const positions: number[] = [];\n let nextPosition = 0;\n\n for (let i = widths.length; i > 0; i--) {\n if (stickyStates[i]) {\n positions[i] = nextPosition;\n nextPosition += widths[i];\n }\n }\n\n return positions;\n }\n\n /**\n * Retreives the most recently observed size of the specified element from the cache, or\n * meaures it directly if not yet cached.\n */\n private _retrieveElementSize(element: HTMLElement): {width: number; height: number} {\n const cachedSize = this._elemSizeCache.get(element);\n if (cachedSize) {\n return cachedSize;\n }\n\n const clientRect = element.getBoundingClientRect();\n const size = {width: clientRect.width, height: clientRect.height};\n\n if (!this._resizeObserver) {\n return size;\n }\n\n this._elemSizeCache.set(element, size);\n this._resizeObserver.observe(element, {box: 'border-box'});\n return size;\n }\n\n /**\n * Conditionally enqueue the requested sticky update and clear previously queued updates\n * for the same rows.\n */\n private _updateStickyColumnReplayQueue(params: UpdateStickyColumnsParams) {\n this._removeFromStickyColumnReplayQueue(params.rows);\n\n // No need to replay if a flush is pending.\n if (!this._stickyColumnsReplayTimeout) {\n this._updatedStickyColumnsParamsToReplay.push(params);\n }\n }\n\n /** Remove updates for the specified rows from the queue. */\n private _removeFromStickyColumnReplayQueue(rows: HTMLElement[]) {\n const rowsSet = new Set(rows);\n for (const update of this._updatedStickyColumnsParamsToReplay) {\n update.rows = update.rows.filter(row => !rowsSet.has(row));\n }\n this._updatedStickyColumnsParamsToReplay = this._updatedStickyColumnsParamsToReplay.filter(\n update => !!update.rows.length,\n );\n }\n\n /** Update _elemSizeCache with the observed sizes. */\n private _updateCachedSizes(entries: ResizeObserverEntry[]) {\n let needsColumnUpdate = false;\n for (const entry of entries) {\n const newEntry = entry.borderBoxSize?.length\n ? {\n width: entry.borderBoxSize[0].inlineSize,\n height: entry.borderBoxSize[0].blockSize,\n }\n : {\n width: entry.contentRect.width,\n height: entry.contentRect.height,\n };\n\n if (\n newEntry.width !== this._elemSizeCache.get(entry.target as HTMLElement)?.width &&\n isCell(entry.target)\n ) {\n needsColumnUpdate = true;\n }\n\n this._elemSizeCache.set(entry.target as HTMLElement, newEntry);\n }\n\n if (needsColumnUpdate && this._updatedStickyColumnsParamsToReplay.length) {\n if (this._stickyColumnsReplayTimeout) {\n clearTimeout(this._stickyColumnsReplayTimeout);\n }\n\n this._stickyColumnsReplayTimeout = setTimeout(() => {\n if (this._destroyed) {\n return;\n }\n\n for (const update of this._updatedStickyColumnsParamsToReplay) {\n this.updateStickyColumns(\n update.rows,\n update.stickyStartStates,\n update.stickyEndStates,\n true,\n false,\n );\n }\n this._updatedStickyColumnsParamsToReplay = [];\n this._stickyColumnsReplayTimeout = null;\n }, 0);\n }\n }\n\n /**\n * Invoke afterNextRender with the table's injector, falling back to CoalescedStyleScheduler\n * if the injector was not provided.\n */\n private _afterNextRender(spec: {earlyRead?: () => void; write: () => void}) {\n if (this._tableInjector) {\n afterNextRender(spec, {injector: this._tableInjector});\n } else {\n this._coalescedStyleScheduler.schedule(() => {\n spec.earlyRead?.();\n spec.write();\n });\n }\n }\n}\n\nfunction isCell(element: Element) {\n return ['cdk-cell', 'cdk-header-cell', 'cdk-footer-cell'].some(klass =>\n element.classList.contains(klass),\n );\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n/**\n * Returns an error to be thrown when attempting to find an nonexistent column.\n * @param id Id whose lookup failed.\n * @docs-private\n */\nexport function getTableUnknownColumnError(id: string) {\n return Error(`Could not find column with id \"${id}\".`);\n}\n\n/**\n * Returns an error to be thrown when two column definitions have the same name.\n * @docs-private\n */\nexport function getTableDuplicateColumnNameError(name: string) {\n return Error(`Duplicate column definition name provided: \"${name}\".`);\n}\n\n/**\n * Returns an error to be thrown when there are multiple rows that are missing a when function.\n * @docs-private\n */\nexport function getTableMultipleDefaultRowDefsError() {\n return Error(`There can only be one default row without a when predicate function.`);\n}\n\n/**\n * Returns an error to be thrown when there are no matching row defs for a particular set of data.\n * @docs-private\n */\nexport function getTableMissingMatchingRowDefError(data: any) {\n return Error(\n `Could not find a matching row definition for the` +\n `provided row data: ${JSON.stringify(data)}`,\n );\n}\n\n/**\n * Returns an error to be thrown when there is no row definitions present in the content.\n * @docs-private\n */\nexport function getTableMissingRowDefsError() {\n return Error(\n 'Missing definitions for header, footer, and row; ' +\n 'cannot determine which columns should be rendered.',\n );\n}\n\n/**\n * Returns an error to be thrown when the data source does not match the compatible types.\n * @docs-private\n */\nexport function getTableUnknownDataSourceError() {\n return Error(`Provided data source did not match an array, Observable, or DataSource`);\n}\n\n/**\n * Returns an error to be thrown when the text column cannot find a parent table to inject.\n * @docs-private\n */\nexport function getTableTextColumnMissingParentTableError() {\n return Error(`Text column could not find a parent table for registration.`);\n}\n\n/**\n * Returns an error to be thrown when a table text column doesn't have a name.\n * @docs-private\n */\nexport function getTableTextColumnMissingNameError() {\n return Error(`Table text column must have a name.`);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {InjectionToken} from '@angular/core';\n\n/** The injection token used to specify the StickyPositioningListener. */\nexport const STICKY_POSITIONING_LISTENER = new InjectionToken<StickyPositioningListener>('CDK_SPL');\n\nexport type StickySize = number | null | undefined;\nexport type StickyOffset = number | null | undefined;\n\nexport interface StickyUpdate {\n elements?: readonly (HTMLElement[] | undefined)[];\n offsets?: StickyOffset[];\n sizes: StickySize[];\n}\n\n/**\n * If provided, CdkTable will call the methods below when it updates the size/\n * position/etc of its sticky rows and columns.\n */\nexport interface StickyPositioningListener {\n /** Called when CdkTable updates its sticky start columns. */\n stickyColumnsUpdated(update: StickyUpdate): void;\n\n /** Called when CdkTable updates its sticky end columns. */\n stickyEndColumnsUpdated(update: StickyUpdate): void;\n\n /** Called when CdkTable updates its sticky header rows. */\n stickyHeaderRowsUpdated(update: StickyUpdate): void;\n\n /** Called when CdkTable updates its sticky footer rows. */\n stickyFooterRowsUpdated(update: StickyUpdate): void;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Direction, Directionality} from '../bidi';\nimport {\n CollectionViewer,\n DataSource,\n _DisposeViewRepeaterStrategy,\n _RecycleViewRepeaterStrategy,\n isDataSource,\n _VIEW_REPEATER_STRATEGY,\n _ViewRepeater,\n _ViewRepeaterItemChange,\n _ViewRepeaterItemInsertArgs,\n _ViewRepeaterOperation,\n} from '../collections';\nimport {Platform} from '../platform';\nimport {ViewportRuler} from '../scrolling';\nimport {DOCUMENT} from