From db08c357ae0a0442fa11e8e901668a4730f6f634 Mon Sep 17 00:00:00 2001 From: Karl Seamon Date: Tue, 23 Jun 2020 11:23:41 -0400 Subject: [PATCH] fix,perf(column-resize): Coalesce style updates along with sticky styler --- .../column-resize-flex.ts | 4 +- .../column-resize-directives/column-resize.ts | 4 +- .../default-enabled-column-resize-flex.ts | 4 +- .../default-enabled-column-resize.ts | 4 +- .../column-resize/column-resize-notifier.ts | 8 ++- .../column-resize/overlay-handle.ts | 44 +++++++------ .../column-resize/resizable.ts | 27 +++++--- .../column-resize/resize-strategy.ts | 64 +++++++++++++++---- src/cdk/table/coalesced-style-scheduler.ts | 34 +++++++--- ...lt-enabled-column-resize-flex-example.html | 4 +- ...default-enabled-column-resize-example.html | 4 +- .../opt-in-column-resize-example.html | 4 +- .../column-resize/BUILD.bazel | 1 + .../column-resize/_column-resize.scss | 1 + .../column-resize/column-resize.spec.ts | 56 ++++++++++------ .../column-resize/overlay-handle.ts | 3 +- .../default-enabled-resizable.ts | 3 +- .../resizable-directives/resizable.ts | 3 +- .../column-resize/resize-strategy.ts | 5 +- 19 files changed, 190 insertions(+), 87 deletions(-) diff --git a/src/cdk-experimental/column-resize/column-resize-directives/column-resize-flex.ts b/src/cdk-experimental/column-resize/column-resize-directives/column-resize-flex.ts index 7013bc25312f..0389c5db60bf 100644 --- a/src/cdk-experimental/column-resize/column-resize-directives/column-resize-flex.ts +++ b/src/cdk-experimental/column-resize/column-resize-directives/column-resize-flex.ts @@ -7,6 +7,7 @@ */ import {Directive, ElementRef, NgZone} from '@angular/core'; +import {CdkTable} from '@angular/cdk/table'; import {ColumnResize} from '../column-resize'; import {ColumnResizeNotifier, ColumnResizeNotifierSource} from '../column-resize-notifier'; @@ -30,7 +31,8 @@ export class CdkColumnResizeFlex extends ColumnResize { readonly elementRef: ElementRef, protected readonly eventDispatcher: HeaderRowEventDispatcher, protected readonly ngZone: NgZone, - protected readonly notifier: ColumnResizeNotifierSource) { + protected readonly notifier: ColumnResizeNotifierSource, + protected readonly table: CdkTable) { super(); } } diff --git a/src/cdk-experimental/column-resize/column-resize-directives/column-resize.ts b/src/cdk-experimental/column-resize/column-resize-directives/column-resize.ts index 2e487c1df88c..5ee4e7df1aff 100644 --- a/src/cdk-experimental/column-resize/column-resize-directives/column-resize.ts +++ b/src/cdk-experimental/column-resize/column-resize-directives/column-resize.ts @@ -7,6 +7,7 @@ */ import {Directive, ElementRef, NgZone} from '@angular/core'; +import {CdkTable} from '@angular/cdk/table'; import {ColumnResize} from '../column-resize'; import {ColumnResizeNotifier, ColumnResizeNotifierSource} from '../column-resize-notifier'; @@ -30,7 +31,8 @@ export class CdkColumnResize extends ColumnResize { readonly elementRef: ElementRef, protected readonly eventDispatcher: HeaderRowEventDispatcher, protected readonly ngZone: NgZone, - protected readonly notifier: ColumnResizeNotifierSource) { + protected readonly notifier: ColumnResizeNotifierSource, + protected readonly table: CdkTable) { super(); } } diff --git a/src/cdk-experimental/column-resize/column-resize-directives/default-enabled-column-resize-flex.ts b/src/cdk-experimental/column-resize/column-resize-directives/default-enabled-column-resize-flex.ts index 86a600ca259f..bbf0b142f767 100644 --- a/src/cdk-experimental/column-resize/column-resize-directives/default-enabled-column-resize-flex.ts +++ b/src/cdk-experimental/column-resize/column-resize-directives/default-enabled-column-resize-flex.ts @@ -7,6 +7,7 @@ */ import {Directive, ElementRef, NgZone} from '@angular/core'; +import {CdkTable} from '@angular/cdk/table'; import {ColumnResize} from '../column-resize'; import {ColumnResizeNotifier, ColumnResizeNotifierSource} from '../column-resize-notifier'; @@ -30,7 +31,8 @@ export class CdkDefaultEnabledColumnResizeFlex extends ColumnResize { readonly elementRef: ElementRef, protected readonly eventDispatcher: HeaderRowEventDispatcher, protected readonly ngZone: NgZone, - protected readonly notifier: ColumnResizeNotifierSource) { + protected readonly notifier: ColumnResizeNotifierSource, + protected readonly table: CdkTable) { super(); } } diff --git a/src/cdk-experimental/column-resize/column-resize-directives/default-enabled-column-resize.ts b/src/cdk-experimental/column-resize/column-resize-directives/default-enabled-column-resize.ts index 264195edfa7b..bf62d2892d70 100644 --- a/src/cdk-experimental/column-resize/column-resize-directives/default-enabled-column-resize.ts +++ b/src/cdk-experimental/column-resize/column-resize-directives/default-enabled-column-resize.ts @@ -7,6 +7,7 @@ */ import {Directive, ElementRef, NgZone} from '@angular/core'; +import {CdkTable} from '@angular/cdk/table'; import {ColumnResize} from '../column-resize'; import {ColumnResizeNotifier, ColumnResizeNotifierSource} from '../column-resize-notifier'; @@ -30,7 +31,8 @@ export class CdkDefaultEnabledColumnResize extends ColumnResize { readonly elementRef: ElementRef, protected readonly eventDispatcher: HeaderRowEventDispatcher, protected readonly ngZone: NgZone, - protected readonly notifier: ColumnResizeNotifierSource) { + protected readonly notifier: ColumnResizeNotifierSource, + protected readonly table: CdkTable) { super(); } } diff --git a/src/cdk-experimental/column-resize/column-resize-notifier.ts b/src/cdk-experimental/column-resize/column-resize-notifier.ts index 220df8456efa..a695ea7e88f4 100644 --- a/src/cdk-experimental/column-resize/column-resize-notifier.ts +++ b/src/cdk-experimental/column-resize/column-resize-notifier.ts @@ -29,6 +29,11 @@ export interface ColumnSizeAction extends ColumnSize { * for all programatically triggered resizes. */ readonly completeImmediately?: boolean; + + /** + * Whether the resize action is being applied to a sticky/stickyEnd column. + */ + readonly isStickyColumn?: boolean; } /** @@ -57,6 +62,7 @@ export class ColumnResizeNotifier { /** Instantly resizes the specified column. */ resize(columnId: string, size: number): void { - this._source.triggerResize.next({columnId, size, completeImmediately: true}); + this._source.triggerResize.next( + {columnId, size, completeImmediately: true, isStickyColumn: true}); } } diff --git a/src/cdk-experimental/column-resize/overlay-handle.ts b/src/cdk-experimental/column-resize/overlay-handle.ts index 71894d053516..cc4c4277b594 100644 --- a/src/cdk-experimental/column-resize/overlay-handle.ts +++ b/src/cdk-experimental/column-resize/overlay-handle.ts @@ -10,7 +10,7 @@ import {AfterViewInit, Directive, ElementRef, OnDestroy, NgZone} from '@angular/ import {coerceCssPixelValue} from '@angular/cdk/coercion'; import {Directionality} from '@angular/cdk/bidi'; import {ESCAPE} from '@angular/cdk/keycodes'; -import {CdkColumnDef} from '@angular/cdk/table'; +import {CdkColumnDef, _CoalescedStyleScheduler} from '@angular/cdk/table'; import {fromEvent, Subject, merge} from 'rxjs'; import { distinctUntilChanged, @@ -47,6 +47,7 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy { protected abstract readonly ngZone: NgZone; protected abstract readonly resizeNotifier: ColumnResizeNotifierSource; protected abstract readonly resizeRef: ResizeRef; + protected abstract readonly styleScheduler: _CoalescedStyleScheduler; ngAfterViewInit() { this._listenForMouseEvents(); @@ -91,7 +92,7 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy { const startX = mousedownEvent.screenX; const initialSize = this._getOriginWidth(); - let overlayOffset = this._getOverlayOffset(); + let overlayOffset = 0; let originOffset = this._getOriginOffset(); let size = initialSize; let overshot = 0; @@ -99,7 +100,9 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy { this.updateResizeActive(true); mouseup.pipe(takeUntil(merge(escape, this.destroyed))).subscribe(({screenX}) => { - this._notifyResizeEnded(size, screenX !== startX); + this.styleScheduler.scheduleEnd(() => { + this._notifyResizeEnded(size, screenX !== startX); + }); }); escape.pipe(takeUntil(merge(mouseup, this.destroyed))).subscribe(() => { @@ -137,20 +140,26 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy { computedNewSize = Math.min( Math.max(computedNewSize, this.resizeRef.minWidthPx, 0), this.resizeRef.maxWidthPx); - this.resizeNotifier.triggerResize.next( - {columnId: this.columnDef.name, size: computedNewSize, previousSize: size}); + this.resizeNotifier.triggerResize.next({ + columnId: this.columnDef.name, + size: computedNewSize, + previousSize: size, + isStickyColumn: this.columnDef.sticky || this.columnDef.stickyEnd, + }); - const originNewSize = this._getOriginWidth(); - const originNewOffset = this._getOriginOffset(); - const originOffsetDeltaX = originNewOffset - originOffset; - const originSizeDeltaX = originNewSize - size; - size = originNewSize; - originOffset = originNewOffset; + this.styleScheduler.scheduleEnd(() => { + const originNewSize = this._getOriginWidth(); + const originNewOffset = this._getOriginOffset(); + const originOffsetDeltaX = originNewOffset - originOffset; + const originSizeDeltaX = originNewSize - size; + size = originNewSize; + originOffset = originNewOffset; - overshot += deltaX + (this._isLtr() ? -originSizeDeltaX : originSizeDeltaX); - overlayOffset += originOffsetDeltaX + (this._isLtr() ? originSizeDeltaX : 0); + overshot += deltaX + (this._isLtr() ? -originSizeDeltaX : originSizeDeltaX); + overlayOffset += originOffsetDeltaX + (this._isLtr() ? originSizeDeltaX : 0); - this._updateOverlayOffset(overlayOffset); + this._updateOverlayOffset(overlayOffset); + }); }); } @@ -167,12 +176,9 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy { return this.resizeRef.origin.nativeElement!.offsetLeft; } - private _getOverlayOffset(): number { - return parseInt(this.resizeRef.overlayRef.overlayElement.style.left!, 10); - } - private _updateOverlayOffset(offset: number): void { - this.resizeRef.overlayRef.overlayElement.style.left = coerceCssPixelValue(offset); + this.resizeRef.overlayRef.overlayElement.style.transform = + `translateX(${coerceCssPixelValue(offset)})`; } private _isLtr(): boolean { diff --git a/src/cdk-experimental/column-resize/resizable.ts b/src/cdk-experimental/column-resize/resizable.ts index 4fba32dca448..7784e6c15579 100644 --- a/src/cdk-experimental/column-resize/resizable.ts +++ b/src/cdk-experimental/column-resize/resizable.ts @@ -20,7 +20,7 @@ import { import {Directionality} from '@angular/cdk/bidi'; import {ComponentPortal} from '@angular/cdk/portal'; import {Overlay, OverlayRef} from '@angular/cdk/overlay'; -import {CdkColumnDef} from '@angular/cdk/table'; +import {CdkColumnDef, _CoalescedStyleScheduler} from '@angular/cdk/table'; import {merge, Subject} from 'rxjs'; import {filter, takeUntil} from 'rxjs/operators'; @@ -61,9 +61,12 @@ export abstract class Resizable protected abstract readonly overlay: Overlay; protected abstract readonly resizeNotifier: ColumnResizeNotifierSource; protected abstract readonly resizeStrategy: ResizeStrategy; + protected abstract readonly styleScheduler: _CoalescedStyleScheduler; protected abstract readonly viewContainerRef: ViewContainerRef; protected abstract readonly changeDetectorRef: ChangeDetectorRef; + private _viewInitialized = false; + /** The minimum width to allow the column to be sized to. */ get minWidthPx(): number { return this.minWidthPxInternal; @@ -71,8 +74,8 @@ export abstract class Resizable set minWidthPx(value: number) { this.minWidthPxInternal = value; - if (this.elementRef.nativeElement) { - this.columnResize.setResized(); + this.columnResize.setResized(); + if (this.elementRef.nativeElement && this._viewInitialized) { this._applyMinWidthPx(); } } @@ -84,13 +87,15 @@ export abstract class Resizable set maxWidthPx(value: number) { this.maxWidthPxInternal = value; - if (this.elementRef.nativeElement) { - this.columnResize.setResized(); + this.columnResize.setResized(); + if (this.elementRef.nativeElement && this._viewInitialized) { this._applyMaxWidthPx(); } } ngAfterViewInit() { + this._viewInitialized = true; + this._listenForRowHoverEvents(); this._listenForResizeEvents(); this._appendInlineHandle(); @@ -255,12 +260,14 @@ export abstract class Resizable } private _appendInlineHandle(): void { - this.inlineHandle = this.document.createElement('div'); - this.inlineHandle.tabIndex = 0; - this.inlineHandle.className = this.getInlineHandleCssClassName(); + this.styleScheduler.schedule(() => { + this.inlineHandle = this.document.createElement('div'); + this.inlineHandle.tabIndex = 0; + this.inlineHandle.className = this.getInlineHandleCssClassName(); - // TODO: Apply correct aria role (probably slider) after a11y spec questions resolved. + // TODO: Apply correct aria role (probably slider) after a11y spec questions resolved. - this.elementRef.nativeElement!.appendChild(this.inlineHandle); + this.elementRef.nativeElement!.appendChild(this.inlineHandle); + }); } } diff --git a/src/cdk-experimental/column-resize/resize-strategy.ts b/src/cdk-experimental/column-resize/resize-strategy.ts index d91bab5ece1e..905c78ffb535 100644 --- a/src/cdk-experimental/column-resize/resize-strategy.ts +++ b/src/cdk-experimental/column-resize/resize-strategy.ts @@ -9,6 +9,7 @@ import {Inject, Injectable, OnDestroy, Provider} from '@angular/core'; import {DOCUMENT} from '@angular/common'; import {coerceCssPixelValue} from '@angular/cdk/coercion'; +import {CdkTable, _CoalescedStyleScheduler} from '@angular/cdk/table'; import {ColumnResize} from './column-resize'; @@ -19,6 +20,10 @@ import {ColumnResize} from './column-resize'; @Injectable() export abstract class ResizeStrategy { protected abstract readonly columnResize: ColumnResize; + protected abstract readonly styleScheduler: _CoalescedStyleScheduler; + protected abstract readonly table: CdkTable; + + private _pendingResizeDelta: number|null = null; /** Updates the width of the specified column. */ abstract applyColumnSize( @@ -40,11 +45,23 @@ export abstract class ResizeStrategy { minSizeInPx: number): void; /** Adjusts the width of the table element by the specified delta. */ - protected updateTableWidth(delta: number): void { - const table = this.columnResize.elementRef.nativeElement; - const tableWidth = getElementWidth(table); + protected updateTableWidthAndStickyColumns(delta: number): void { + if (this._pendingResizeDelta === null) { + const tableElement = this.columnResize.elementRef.nativeElement; + const tableWidth = getElementWidth(tableElement); + + this.styleScheduler.schedule(() => { + tableElement.style.width = coerceCssPixelValue(tableWidth + this._pendingResizeDelta!); - table.style.width = coerceCssPixelValue(tableWidth + delta); + this._pendingResizeDelta = null; + }); + + this.styleScheduler.scheduleEnd(() => { + this.table.updateStickyColumnStyles(); + }); + } + + this._pendingResizeDelta = (this._pendingResizeDelta ?? 0) + delta; } } @@ -57,7 +74,10 @@ export abstract class ResizeStrategy { */ @Injectable() export class TableLayoutFixedResizeStrategy extends ResizeStrategy { - constructor(protected readonly columnResize: ColumnResize) { + constructor( + protected readonly columnResize: ColumnResize, + protected readonly styleScheduler: _CoalescedStyleScheduler, + protected readonly table: CdkTable) { super(); } @@ -65,9 +85,15 @@ export class TableLayoutFixedResizeStrategy extends ResizeStrategy { previousSizeInPx?: number): void { const delta = sizeInPx - (previousSizeInPx ?? getElementWidth(columnHeader)); - columnHeader.style.width = coerceCssPixelValue(sizeInPx); + if (delta === 0) { + return; + } + + this.styleScheduler.schedule(() => { + columnHeader.style.width = coerceCssPixelValue(sizeInPx); + }); - this.updateTableWidth(delta); + this.updateTableWidthAndStickyColumns(delta); } applyMinColumnSize(_: string, columnHeader: HTMLElement, sizeInPx: number): void { @@ -105,6 +131,8 @@ export class CdkFlexTableResizeStrategy extends ResizeStrategy implements OnDest constructor( protected readonly columnResize: ColumnResize, + protected readonly styleScheduler: _CoalescedStyleScheduler, + protected readonly table: CdkTable, @Inject(DOCUMENT) document: any) { super(); this._document = document; @@ -117,10 +145,14 @@ export class CdkFlexTableResizeStrategy extends ResizeStrategy implements OnDest const delta = sizeInPx - (previousSizeInPx ?? (this._getAppliedWidth(cssFriendlyColumnName) || columnHeader.offsetWidth)); + if (delta === 0) { + return; + } + const cssSize = coerceCssPixelValue(sizeInPx); this._applyProperty(cssFriendlyColumnName, 'flex', `0 0.01 ${cssSize}`); - this.updateTableWidth(delta); + this.updateTableWidthAndStickyColumns(delta); } applyMinColumnSize(cssFriendlyColumnName: string, _: HTMLElement, sizeInPx: number): void { @@ -128,6 +160,7 @@ export class CdkFlexTableResizeStrategy extends ResizeStrategy implements OnDest this._applyProperty(cssFriendlyColumnName, 'min-width', cssSize, sizeInPx !== this.defaultMinSize); + this.updateTableWidthAndStickyColumns(0); } applyMaxColumnSize(cssFriendlyColumnName: string, _: HTMLElement, sizeInPx: number): void { @@ -135,6 +168,7 @@ export class CdkFlexTableResizeStrategy extends ResizeStrategy implements OnDest this._applyProperty(cssFriendlyColumnName, 'max-width', cssSize, sizeInPx !== this.defaultMaxSize); + this.updateTableWidthAndStickyColumns(0); } protected getColumnCssClass(cssFriendlyColumnName: string): string { @@ -165,12 +199,14 @@ export class CdkFlexTableResizeStrategy extends ResizeStrategy implements OnDest enable = true): void { const properties = this._getColumnPropertiesMap(cssFriendlyColumnName); - if (enable) { - properties.set(key, value); - } else { - properties.delete(key); - } - this._applySizeCss(cssFriendlyColumnName); + this.styleScheduler.schedule(() => { + if (enable) { + properties.set(key, value); + } else { + properties.delete(key); + } + this._applySizeCss(cssFriendlyColumnName); + }); } private _getStyleSheet(): CSSStyleSheet { diff --git a/src/cdk/table/coalesced-style-scheduler.ts b/src/cdk/table/coalesced-style-scheduler.ts index 5e8f985009ac..d19517a59e81 100644 --- a/src/cdk/table/coalesced-style-scheduler.ts +++ b/src/cdk/table/coalesced-style-scheduler.ts @@ -7,7 +7,7 @@ */ import {Injectable, NgZone, OnDestroy} from '@angular/core'; -import {Subject} from 'rxjs'; +import {from, Subject} from 'rxjs'; import {take, takeUntil} from 'rxjs/operators'; /** @@ -62,19 +62,33 @@ export class _CoalescedStyleScheduler implements OnDestroy { this._currentSchedule = new _Schedule(); - this._ngZone.onStable.pipe( - take(1), + this._getScheduleObservable().pipe( takeUntil(this._destroyed), ).subscribe(() => { - const schedule = this._currentSchedule!; - this._currentSchedule = null; + while (this._currentSchedule!.tasks.length || this._currentSchedule!.endTasks.length) { + const schedule = this._currentSchedule!; - for (const task of schedule.tasks) { - task(); - } - for (const task of schedule.endTasks) { - task(); + // Capture new tasks scheduled by the current set of tasks. + this._currentSchedule = new _Schedule(); + + for (const task of schedule.tasks) { + task(); + } + + for (const task of schedule.endTasks) { + task(); + } } + + this._currentSchedule = null; }); } + + private _getScheduleObservable() { + // Use onStable when in the context of an ongoing change detection cycle so that we + // do not accidentally trigger additional cycles. + return this._ngZone.isStable ? + from(Promise.resolve(undefined)) : + this._ngZone.onStable.pipe(take(1)); + } } diff --git a/src/components-examples/material-experimental/column-resize/default-enabled-column-resize-flex/default-enabled-column-resize-flex-example.html b/src/components-examples/material-experimental/column-resize/default-enabled-column-resize-flex/default-enabled-column-resize-flex-example.html index 06a7c3966410..7e4768f218f2 100644 --- a/src/components-examples/material-experimental/column-resize/default-enabled-column-resize-flex/default-enabled-column-resize-flex-example.html +++ b/src/components-examples/material-experimental/column-resize/default-enabled-column-resize-flex/default-enabled-column-resize-flex-example.html @@ -1,12 +1,12 @@ - + No. {{element.position}} - + Name {{element.name}} diff --git a/src/components-examples/material-experimental/column-resize/default-enabled-column-resize/default-enabled-column-resize-example.html b/src/components-examples/material-experimental/column-resize/default-enabled-column-resize/default-enabled-column-resize-example.html index 0cbbc035a947..ca64fbb09959 100644 --- a/src/components-examples/material-experimental/column-resize/default-enabled-column-resize/default-enabled-column-resize-example.html +++ b/src/components-examples/material-experimental/column-resize/default-enabled-column-resize/default-enabled-column-resize-example.html @@ -1,12 +1,12 @@ - + - + diff --git a/src/components-examples/material-experimental/column-resize/opt-in-column-resize/opt-in-column-resize-example.html b/src/components-examples/material-experimental/column-resize/opt-in-column-resize/opt-in-column-resize-example.html index 381dcffd68ee..3761945f42c5 100644 --- a/src/components-examples/material-experimental/column-resize/opt-in-column-resize/opt-in-column-resize-example.html +++ b/src/components-examples/material-experimental/column-resize/opt-in-column-resize/opt-in-column-resize-example.html @@ -1,12 +1,12 @@
No. {{element.position}} Name {{element.name}}
- + - + diff --git a/src/material-experimental/column-resize/BUILD.bazel b/src/material-experimental/column-resize/BUILD.bazel index 0bf19b8f6ebf..8824945e5bc5 100644 --- a/src/material-experimental/column-resize/BUILD.bazel +++ b/src/material-experimental/column-resize/BUILD.bazel @@ -13,6 +13,7 @@ ng_module( deps = [ "//src/cdk-experimental/column-resize", "//src/cdk/overlay", + "//src/cdk/table", "//src/material/table", "@npm//@angular/core", ], diff --git a/src/material-experimental/column-resize/_column-resize.scss b/src/material-experimental/column-resize/_column-resize.scss index 51ff6bf8b1da..887a5300446d 100644 --- a/src/material-experimental/column-resize/_column-resize.scss +++ b/src/material-experimental/column-resize/_column-resize.scss @@ -93,6 +93,7 @@ transparent, transparent 7px, $resizable-active-divider, $resizable-active-divider 1px, transparent 8px, transparent); + will-change: transform; } } } diff --git a/src/material-experimental/column-resize/column-resize.spec.ts b/src/material-experimental/column-resize/column-resize.spec.ts index b6538be3d05f..4debab341b01 100644 --- a/src/material-experimental/column-resize/column-resize.spec.ts +++ b/src/material-experimental/column-resize/column-resize.spec.ts @@ -62,21 +62,21 @@ function getTableTemplate(defaultEnabled: boolean) {
No. {{element.position}} Name {{element.name}}
- + - + - + @@ -117,21 +117,21 @@ function getFlexTemplate(defaultEnabled: boolean) {
- + No. {{element.position}} - + Name {{element.name}} - + Weight (Not resizable) @@ -170,7 +170,7 @@ abstract class BaseTestComponent { } getColumnElement(index: number): HTMLElement { - return this.table.nativeElement!.querySelectorAll('.mat-resizable')[index] as HTMLElement; + return this.table.nativeElement!.querySelectorAll('.mat-header-cell')[index] as HTMLElement; } getColumnWidth(index: number): number { @@ -196,8 +196,11 @@ abstract class BaseTestComponent { } getOverlayThumbPosition(index: number): number { - const thumbElement = this.getOverlayThumbElement(index); - return parseInt((thumbElement.parentNode as HTMLElement).style.left!, 10); + const thumbPositionElement = this.getOverlayThumbElement(index)!.parentNode as HTMLElement; + const left = parseInt(thumbPositionElement.style.left!, 10); + const translateX = Number(/translateX\((-?\d+)px\)/.exec( + thumbPositionElement.style.transform)?.[1] ?? 0); + return left + translateX; } beginColumnResizeWithMouse(index: number, button = 0): void { @@ -329,7 +332,8 @@ const testCases: ReadonlyArray<[Type, Type, string]> ], [ MatDefaultEnabledColumnResizeModule, MatResizeDefaultRtlTest, - 'default enabled rtl table-based mat-table'], + 'default enabled rtl table-based mat-table' + ], [ MatDefaultEnabledColumnResizeModule, MatResizeDefaultFlexTest, 'default enabled flex-based mat-table' @@ -392,6 +396,7 @@ describe('Material Popover Edit', () => { component.completeResizeWithMouseInProgress(0); component.endHoverState(); fixture.detectChanges(); + flushMicrotasks(); expect(component.getOverlayThumbElement(0)).toBeUndefined(); })); @@ -400,6 +405,7 @@ describe('Material Popover Edit', () => { const initialTableWidth = component.getTableWidth(); const initialColumnWidth = component.getColumnWidth(1); const initialColumnPosition = component.getColumnOriginPosition(1); + const initialNextColumnPosition = component.getColumnOriginPosition(2); component.triggerHoverState(); fixture.detectChanges(); @@ -407,24 +413,30 @@ describe('Material Popover Edit', () => { const initialThumbPosition = component.getOverlayThumbPosition(1); component.updateResizeWithMouseInProgress(5); + flushMicrotasks(); let thumbPositionDelta = component.getOverlayThumbPosition(1) - initialThumbPosition; let columnPositionDelta = component.getColumnOriginPosition(1) - initialColumnPosition; - expect(thumbPositionDelta).toBe(columnPositionDelta); + let nextColumnPositionDelta = + component.getColumnOriginPosition(2) - initialNextColumnPosition; + (expect(thumbPositionDelta) as any).isApproximately(columnPositionDelta); + (expect(nextColumnPositionDelta) as any).isApproximately(columnPositionDelta); (expect(component.getTableWidth()) as any).isApproximately(initialTableWidth + 5); (expect(component.getColumnWidth(1)) as any).isApproximately(initialColumnWidth + 5); component.updateResizeWithMouseInProgress(1); + flushMicrotasks(); thumbPositionDelta = component.getOverlayThumbPosition(1) - initialThumbPosition; columnPositionDelta = component.getColumnOriginPosition(1) - initialColumnPosition; - expect(thumbPositionDelta).toBe(columnPositionDelta); + (expect(thumbPositionDelta) as any).isApproximately(columnPositionDelta); (expect(component.getTableWidth()) as any).isApproximately(initialTableWidth + 1); (expect(component.getColumnWidth(1)) as any).isApproximately(initialColumnWidth + 1); component.completeResizeWithMouseInProgress(1); + flushMicrotasks(); (expect(component.getColumnWidth(1)) as any).isApproximately(initialColumnWidth + 1); @@ -459,15 +471,17 @@ describe('Material Popover Edit', () => { const initialThumbPosition = component.getOverlayThumbPosition(1); component.updateResizeWithMouseInProgress(5); + flushMicrotasks(); let thumbPositionDelta = component.getOverlayThumbPosition(1) - initialThumbPosition; let columnPositionDelta = component.getColumnOriginPosition(1) - initialColumnPosition; - expect(thumbPositionDelta).toBe(columnPositionDelta); + (expect(thumbPositionDelta) as any).isApproximately(columnPositionDelta); (expect(component.getColumnWidth(1)) as any).isApproximately(initialColumnWidth + 5); (expect(component.getTableWidth()) as any).isApproximately(initialTableWidth + 5); dispatchKeyboardEvent(document, 'keyup', ESCAPE); + flushMicrotasks(); (expect(component.getColumnWidth(1)) as any).isApproximately(initialColumnWidth); (expect(component.getTableWidth()) as any).isApproximately(initialTableWidth); @@ -476,7 +490,7 @@ describe('Material Popover Edit', () => { fixture.detectChanges(); })); - it('notifies subscribers of a completed resize via ColumnResizeNotifier', () => { + it('notifies subscribers of a completed resize via ColumnResizeNotifier', fakeAsync(() => { const initialColumnWidth = component.getColumnWidth(1); let resize: ColumnSize|null = null; @@ -490,14 +504,15 @@ describe('Material Popover Edit', () => { expect(resize).toBe(null); component.resizeColumnWithMouse(1, 5); + flushMicrotasks(); expect(resize).toEqual({columnId: 'name', size: initialColumnWidth + 5} as any); component.endHoverState(); fixture.detectChanges(); - }); + })); - it('does not notify subscribers of a canceled resize', () => { + it('does not notify subscribers of a canceled resize', fakeAsync(() => { let resize: ColumnSize|null = null; component.columnResize.columnResizeNotifier.resizeCompleted.subscribe(size => { resize = size; @@ -508,23 +523,26 @@ describe('Material Popover Edit', () => { component.beginColumnResizeWithMouse(0); component.updateResizeWithMouseInProgress(5); + flushMicrotasks(); dispatchKeyboardEvent(document, 'keyup', ESCAPE); + flushMicrotasks(); component.endHoverState(); fixture.detectChanges(); expect(resize).toBe(null); - }); + })); - it('performs a column resize triggered via ColumnResizeNotifier', () => { + it('performs a column resize triggered via ColumnResizeNotifier', fakeAsync(() => { // Pre-verify that we are not updating the size to the initial size. (expect(component.getColumnWidth(1)) as any).not.isApproximately(173); component.columnResize.columnResizeNotifier.resize('name', 173); + flushMicrotasks(); (expect(component.getColumnWidth(1)) as any).isApproximately(173); - }); + })); }); } }); diff --git a/src/material-experimental/column-resize/overlay-handle.ts b/src/material-experimental/column-resize/overlay-handle.ts index 27d2cb2b0b3b..6c106148751d 100644 --- a/src/material-experimental/column-resize/overlay-handle.ts +++ b/src/material-experimental/column-resize/overlay-handle.ts @@ -15,7 +15,7 @@ import { ViewEncapsulation, } from '@angular/core'; import {DOCUMENT} from '@angular/common'; -import {CdkColumnDef} from '@angular/cdk/table'; +import {CdkColumnDef, _CoalescedStyleScheduler} from '@angular/cdk/table'; import {Directionality} from '@angular/cdk/bidi'; import { ColumnResize, @@ -49,6 +49,7 @@ export class MatColumnResizeOverlayHandle extends ResizeOverlayHandle { protected readonly ngZone: NgZone, protected readonly resizeNotifier: ColumnResizeNotifierSource, protected readonly resizeRef: ResizeRef, + protected readonly styleScheduler: _CoalescedStyleScheduler, @Inject(DOCUMENT) document: any) { super(); this.document = document; diff --git a/src/material-experimental/column-resize/resizable-directives/default-enabled-resizable.ts b/src/material-experimental/column-resize/resizable-directives/default-enabled-resizable.ts index e071e471963f..57d0d329ad70 100644 --- a/src/material-experimental/column-resize/resizable-directives/default-enabled-resizable.ts +++ b/src/material-experimental/column-resize/resizable-directives/default-enabled-resizable.ts @@ -18,7 +18,7 @@ import { import {DOCUMENT} from '@angular/common'; import {Directionality} from '@angular/cdk/bidi'; import {Overlay} from '@angular/cdk/overlay'; -import {CdkColumnDef} from '@angular/cdk/table'; +import {CdkColumnDef, _CoalescedStyleScheduler} from '@angular/cdk/table'; import { ColumnResize, ColumnResizeNotifierSource, @@ -52,6 +52,7 @@ export class MatDefaultResizable extends AbstractMatResizable { protected readonly overlay: Overlay, protected readonly resizeNotifier: ColumnResizeNotifierSource, protected readonly resizeStrategy: ResizeStrategy, + protected readonly styleScheduler: _CoalescedStyleScheduler, protected readonly viewContainerRef: ViewContainerRef, protected readonly changeDetectorRef: ChangeDetectorRef) { super(); diff --git a/src/material-experimental/column-resize/resizable-directives/resizable.ts b/src/material-experimental/column-resize/resizable-directives/resizable.ts index 756e8bcf3666..e23343b69694 100644 --- a/src/material-experimental/column-resize/resizable-directives/resizable.ts +++ b/src/material-experimental/column-resize/resizable-directives/resizable.ts @@ -18,7 +18,7 @@ import { import {DOCUMENT} from '@angular/common'; import {Directionality} from '@angular/cdk/bidi'; import {Overlay} from '@angular/cdk/overlay'; -import {CdkColumnDef} from '@angular/cdk/table'; +import {CdkColumnDef, _CoalescedStyleScheduler} from '@angular/cdk/table'; import { ColumnResize, ColumnResizeNotifierSource, @@ -51,6 +51,7 @@ export class MatResizable extends AbstractMatResizable { protected readonly overlay: Overlay, protected readonly resizeNotifier: ColumnResizeNotifierSource, protected readonly resizeStrategy: ResizeStrategy, + protected readonly styleScheduler: _CoalescedStyleScheduler, protected readonly viewContainerRef: ViewContainerRef, protected readonly changeDetectorRef: ChangeDetectorRef) { super(); diff --git a/src/material-experimental/column-resize/resize-strategy.ts b/src/material-experimental/column-resize/resize-strategy.ts index 0806d8fe7b1d..df5b7c370dbd 100644 --- a/src/material-experimental/column-resize/resize-strategy.ts +++ b/src/material-experimental/column-resize/resize-strategy.ts @@ -8,6 +8,7 @@ import {Inject, Injectable, Provider} from '@angular/core'; import {DOCUMENT} from '@angular/common'; +import {CdkTable, _CoalescedStyleScheduler} from '@angular/cdk/table'; import { ColumnResize, @@ -25,8 +26,10 @@ export {TABLE_LAYOUT_FIXED_RESIZE_STRATEGY_PROVIDER}; export class MatFlexTableResizeStrategy extends CdkFlexTableResizeStrategy { constructor( columnResize: ColumnResize, + styleScheduler: _CoalescedStyleScheduler, + table: CdkTable, @Inject(DOCUMENT) document: any) { - super(columnResize, document); + super(columnResize, styleScheduler, table, document); } protected getColumnCssClass(cssFriendlyColumnName: string): string {
No. {{element.position}} Name {{element.name}} Weight (Not resizable)