Skip to content

Commit 7e2079a

Browse files
kseamonmmalerba
authored andcommitted
perf(column-resize): Coalesce style updates along with sticky styler (#20086)
(cherry picked from commit e2bc083)
1 parent 81d904d commit 7e2079a

File tree

19 files changed

+190
-87
lines changed

19 files changed

+190
-87
lines changed

src/cdk-experimental/column-resize/column-resize-directives/column-resize-flex.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
import {Directive, ElementRef, NgZone} from '@angular/core';
10+
import {CdkTable} from '@angular/cdk/table';
1011

1112
import {ColumnResize} from '../column-resize';
1213
import {ColumnResizeNotifier, ColumnResizeNotifierSource} from '../column-resize-notifier';
@@ -30,7 +31,8 @@ export class CdkColumnResizeFlex extends ColumnResize {
3031
readonly elementRef: ElementRef<HTMLElement>,
3132
protected readonly eventDispatcher: HeaderRowEventDispatcher,
3233
protected readonly ngZone: NgZone,
33-
protected readonly notifier: ColumnResizeNotifierSource) {
34+
protected readonly notifier: ColumnResizeNotifierSource,
35+
protected readonly table: CdkTable<unknown>) {
3436
super();
3537
}
3638
}

src/cdk-experimental/column-resize/column-resize-directives/column-resize.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
import {Directive, ElementRef, NgZone} from '@angular/core';
10+
import {CdkTable} from '@angular/cdk/table';
1011

1112
import {ColumnResize} from '../column-resize';
1213
import {ColumnResizeNotifier, ColumnResizeNotifierSource} from '../column-resize-notifier';
@@ -30,7 +31,8 @@ export class CdkColumnResize extends ColumnResize {
3031
readonly elementRef: ElementRef<HTMLElement>,
3132
protected readonly eventDispatcher: HeaderRowEventDispatcher,
3233
protected readonly ngZone: NgZone,
33-
protected readonly notifier: ColumnResizeNotifierSource) {
34+
protected readonly notifier: ColumnResizeNotifierSource,
35+
protected readonly table: CdkTable<unknown>) {
3436
super();
3537
}
3638
}

src/cdk-experimental/column-resize/column-resize-directives/default-enabled-column-resize-flex.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
import {Directive, ElementRef, NgZone} from '@angular/core';
10+
import {CdkTable} from '@angular/cdk/table';
1011

1112
import {ColumnResize} from '../column-resize';
1213
import {ColumnResizeNotifier, ColumnResizeNotifierSource} from '../column-resize-notifier';
@@ -30,7 +31,8 @@ export class CdkDefaultEnabledColumnResizeFlex extends ColumnResize {
3031
readonly elementRef: ElementRef<HTMLElement>,
3132
protected readonly eventDispatcher: HeaderRowEventDispatcher,
3233
protected readonly ngZone: NgZone,
33-
protected readonly notifier: ColumnResizeNotifierSource) {
34+
protected readonly notifier: ColumnResizeNotifierSource,
35+
protected readonly table: CdkTable<unknown>) {
3436
super();
3537
}
3638
}

src/cdk-experimental/column-resize/column-resize-directives/default-enabled-column-resize.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
import {Directive, ElementRef, NgZone} from '@angular/core';
10+
import {CdkTable} from '@angular/cdk/table';
1011

1112
import {ColumnResize} from '../column-resize';
1213
import {ColumnResizeNotifier, ColumnResizeNotifierSource} from '../column-resize-notifier';
@@ -30,7 +31,8 @@ export class CdkDefaultEnabledColumnResize extends ColumnResize {
3031
readonly elementRef: ElementRef<HTMLElement>,
3132
protected readonly eventDispatcher: HeaderRowEventDispatcher,
3233
protected readonly ngZone: NgZone,
33-
protected readonly notifier: ColumnResizeNotifierSource) {
34+
protected readonly notifier: ColumnResizeNotifierSource,
35+
protected readonly table: CdkTable<unknown>) {
3436
super();
3537
}
3638
}

src/cdk-experimental/column-resize/column-resize-notifier.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ export interface ColumnSizeAction extends ColumnSize {
2929
* for all programatically triggered resizes.
3030
*/
3131
readonly completeImmediately?: boolean;
32+
33+
/**
34+
* Whether the resize action is being applied to a sticky/stickyEnd column.
35+
*/
36+
readonly isStickyColumn?: boolean;
3237
}
3338

3439
/**
@@ -57,6 +62,7 @@ export class ColumnResizeNotifier {
5762

5863
/** Instantly resizes the specified column. */
5964
resize(columnId: string, size: number): void {
60-
this._source.triggerResize.next({columnId, size, completeImmediately: true});
65+
this._source.triggerResize.next(
66+
{columnId, size, completeImmediately: true, isStickyColumn: true});
6167
}
6268
}

src/cdk-experimental/column-resize/overlay-handle.ts

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {AfterViewInit, Directive, ElementRef, OnDestroy, NgZone} from '@angular/
1010
import {coerceCssPixelValue} from '@angular/cdk/coercion';
1111
import {Directionality} from '@angular/cdk/bidi';
1212
import {ESCAPE} from '@angular/cdk/keycodes';
13-
import {CdkColumnDef} from '@angular/cdk/table';
13+
import {CdkColumnDef, _CoalescedStyleScheduler} from '@angular/cdk/table';
1414
import {fromEvent, Subject, merge} from 'rxjs';
1515
import {
1616
distinctUntilChanged,
@@ -47,6 +47,7 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
4747
protected abstract readonly ngZone: NgZone;
4848
protected abstract readonly resizeNotifier: ColumnResizeNotifierSource;
4949
protected abstract readonly resizeRef: ResizeRef;
50+
protected abstract readonly styleScheduler: _CoalescedStyleScheduler;
5051

5152
ngAfterViewInit() {
5253
this._listenForMouseEvents();
@@ -91,15 +92,17 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
9192
const startX = mousedownEvent.screenX;
9293

9394
const initialSize = this._getOriginWidth();
94-
let overlayOffset = this._getOverlayOffset();
95+
let overlayOffset = 0;
9596
let originOffset = this._getOriginOffset();
9697
let size = initialSize;
9798
let overshot = 0;
9899

99100
this.updateResizeActive(true);
100101

101102
mouseup.pipe(takeUntil(merge(escape, this.destroyed))).subscribe(({screenX}) => {
102-
this._notifyResizeEnded(size, screenX !== startX);
103+
this.styleScheduler.scheduleEnd(() => {
104+
this._notifyResizeEnded(size, screenX !== startX);
105+
});
103106
});
104107

105108
escape.pipe(takeUntil(merge(mouseup, this.destroyed))).subscribe(() => {
@@ -137,20 +140,26 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
137140
computedNewSize = Math.min(
138141
Math.max(computedNewSize, this.resizeRef.minWidthPx, 0), this.resizeRef.maxWidthPx);
139142

140-
this.resizeNotifier.triggerResize.next(
141-
{columnId: this.columnDef.name, size: computedNewSize, previousSize: size});
143+
this.resizeNotifier.triggerResize.next({
144+
columnId: this.columnDef.name,
145+
size: computedNewSize,
146+
previousSize: size,
147+
isStickyColumn: this.columnDef.sticky || this.columnDef.stickyEnd,
148+
});
142149

143-
const originNewSize = this._getOriginWidth();
144-
const originNewOffset = this._getOriginOffset();
145-
const originOffsetDeltaX = originNewOffset - originOffset;
146-
const originSizeDeltaX = originNewSize - size;
147-
size = originNewSize;
148-
originOffset = originNewOffset;
150+
this.styleScheduler.scheduleEnd(() => {
151+
const originNewSize = this._getOriginWidth();
152+
const originNewOffset = this._getOriginOffset();
153+
const originOffsetDeltaX = originNewOffset - originOffset;
154+
const originSizeDeltaX = originNewSize - size;
155+
size = originNewSize;
156+
originOffset = originNewOffset;
149157

150-
overshot += deltaX + (this._isLtr() ? -originSizeDeltaX : originSizeDeltaX);
151-
overlayOffset += originOffsetDeltaX + (this._isLtr() ? originSizeDeltaX : 0);
158+
overshot += deltaX + (this._isLtr() ? -originSizeDeltaX : originSizeDeltaX);
159+
overlayOffset += originOffsetDeltaX + (this._isLtr() ? originSizeDeltaX : 0);
152160

153-
this._updateOverlayOffset(overlayOffset);
161+
this._updateOverlayOffset(overlayOffset);
162+
});
154163
});
155164
}
156165

@@ -167,12 +176,9 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
167176
return this.resizeRef.origin.nativeElement!.offsetLeft;
168177
}
169178

170-
private _getOverlayOffset(): number {
171-
return parseInt(this.resizeRef.overlayRef.overlayElement.style.left!, 10);
172-
}
173-
174179
private _updateOverlayOffset(offset: number): void {
175-
this.resizeRef.overlayRef.overlayElement.style.left = coerceCssPixelValue(offset);
180+
this.resizeRef.overlayRef.overlayElement.style.transform =
181+
`translateX(${coerceCssPixelValue(offset)})`;
176182
}
177183

178184
private _isLtr(): boolean {

src/cdk-experimental/column-resize/resizable.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
import {Directionality} from '@angular/cdk/bidi';
2121
import {ComponentPortal, PortalInjector} from '@angular/cdk/portal';
2222
import {Overlay, OverlayRef} from '@angular/cdk/overlay';
23-
import {CdkColumnDef} from '@angular/cdk/table';
23+
import {CdkColumnDef, _CoalescedStyleScheduler} from '@angular/cdk/table';
2424
import {merge, Subject} from 'rxjs';
2525
import {filter, takeUntil} from 'rxjs/operators';
2626

@@ -61,18 +61,21 @@ export abstract class Resizable<HandleComponent extends ResizeOverlayHandle>
6161
protected abstract readonly overlay: Overlay;
6262
protected abstract readonly resizeNotifier: ColumnResizeNotifierSource;
6363
protected abstract readonly resizeStrategy: ResizeStrategy;
64+
protected abstract readonly styleScheduler: _CoalescedStyleScheduler;
6465
protected abstract readonly viewContainerRef: ViewContainerRef;
6566
protected abstract readonly changeDetectorRef: ChangeDetectorRef;
6667

68+
private _viewInitialized = false;
69+
6770
/** The minimum width to allow the column to be sized to. */
6871
get minWidthPx(): number {
6972
return this.minWidthPxInternal;
7073
}
7174
set minWidthPx(value: number) {
7275
this.minWidthPxInternal = value;
7376

74-
if (this.elementRef.nativeElement) {
75-
this.columnResize.setResized();
77+
this.columnResize.setResized();
78+
if (this.elementRef.nativeElement && this._viewInitialized) {
7679
this._applyMinWidthPx();
7780
}
7881
}
@@ -84,13 +87,15 @@ export abstract class Resizable<HandleComponent extends ResizeOverlayHandle>
8487
set maxWidthPx(value: number) {
8588
this.maxWidthPxInternal = value;
8689

87-
if (this.elementRef.nativeElement) {
88-
this.columnResize.setResized();
90+
this.columnResize.setResized();
91+
if (this.elementRef.nativeElement && this._viewInitialized) {
8992
this._applyMaxWidthPx();
9093
}
9194
}
9295

9396
ngAfterViewInit() {
97+
this._viewInitialized = true;
98+
9499
this._listenForRowHoverEvents();
95100
this._listenForResizeEvents();
96101
this._appendInlineHandle();
@@ -251,12 +256,14 @@ export abstract class Resizable<HandleComponent extends ResizeOverlayHandle>
251256
}
252257

253258
private _appendInlineHandle(): void {
254-
this.inlineHandle = this.document.createElement('div');
255-
this.inlineHandle.tabIndex = 0;
256-
this.inlineHandle.className = this.getInlineHandleCssClassName();
259+
this.styleScheduler.schedule(() => {
260+
this.inlineHandle = this.document.createElement('div');
261+
this.inlineHandle.tabIndex = 0;
262+
this.inlineHandle.className = this.getInlineHandleCssClassName();
257263

258-
// TODO: Apply correct aria role (probably slider) after a11y spec questions resolved.
264+
// TODO: Apply correct aria role (probably slider) after a11y spec questions resolved.
259265

260-
this.elementRef.nativeElement!.appendChild(this.inlineHandle);
266+
this.elementRef.nativeElement!.appendChild(this.inlineHandle);
267+
});
261268
}
262269
}

src/cdk-experimental/column-resize/resize-strategy.ts

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import {Inject, Injectable, OnDestroy, Provider} from '@angular/core';
1010
import {DOCUMENT} from '@angular/common';
1111
import {coerceCssPixelValue} from '@angular/cdk/coercion';
12+
import {CdkTable, _CoalescedStyleScheduler} from '@angular/cdk/table';
1213

1314
import {ColumnResize} from './column-resize';
1415

@@ -19,6 +20,10 @@ import {ColumnResize} from './column-resize';
1920
@Injectable()
2021
export abstract class ResizeStrategy {
2122
protected abstract readonly columnResize: ColumnResize;
23+
protected abstract readonly styleScheduler: _CoalescedStyleScheduler;
24+
protected abstract readonly table: CdkTable<unknown>;
25+
26+
private _pendingResizeDelta: number|null = null;
2227

2328
/** Updates the width of the specified column. */
2429
abstract applyColumnSize(
@@ -40,11 +45,23 @@ export abstract class ResizeStrategy {
4045
minSizeInPx: number): void;
4146

4247
/** Adjusts the width of the table element by the specified delta. */
43-
protected updateTableWidth(delta: number): void {
44-
const table = this.columnResize.elementRef.nativeElement;
45-
const tableWidth = getElementWidth(table);
48+
protected updateTableWidthAndStickyColumns(delta: number): void {
49+
if (this._pendingResizeDelta === null) {
50+
const tableElement = this.columnResize.elementRef.nativeElement;
51+
const tableWidth = getElementWidth(tableElement);
52+
53+
this.styleScheduler.schedule(() => {
54+
tableElement.style.width = coerceCssPixelValue(tableWidth + this._pendingResizeDelta!);
4655

47-
table.style.width = coerceCssPixelValue(tableWidth + delta);
56+
this._pendingResizeDelta = null;
57+
});
58+
59+
this.styleScheduler.scheduleEnd(() => {
60+
this.table.updateStickyColumnStyles();
61+
});
62+
}
63+
64+
this._pendingResizeDelta = (this._pendingResizeDelta ?? 0) + delta;
4865
}
4966
}
5067

@@ -57,17 +74,26 @@ export abstract class ResizeStrategy {
5774
*/
5875
@Injectable()
5976
export class TableLayoutFixedResizeStrategy extends ResizeStrategy {
60-
constructor(protected readonly columnResize: ColumnResize) {
77+
constructor(
78+
protected readonly columnResize: ColumnResize,
79+
protected readonly styleScheduler: _CoalescedStyleScheduler,
80+
protected readonly table: CdkTable<unknown>) {
6181
super();
6282
}
6383

6484
applyColumnSize(_: string, columnHeader: HTMLElement, sizeInPx: number,
6585
previousSizeInPx?: number): void {
6686
const delta = sizeInPx - (previousSizeInPx ?? getElementWidth(columnHeader));
6787

68-
columnHeader.style.width = coerceCssPixelValue(sizeInPx);
88+
if (delta === 0) {
89+
return;
90+
}
91+
92+
this.styleScheduler.schedule(() => {
93+
columnHeader.style.width = coerceCssPixelValue(sizeInPx);
94+
});
6995

70-
this.updateTableWidth(delta);
96+
this.updateTableWidthAndStickyColumns(delta);
7197
}
7298

7399
applyMinColumnSize(_: string, columnHeader: HTMLElement, sizeInPx: number): void {
@@ -105,6 +131,8 @@ export class CdkFlexTableResizeStrategy extends ResizeStrategy implements OnDest
105131

106132
constructor(
107133
protected readonly columnResize: ColumnResize,
134+
protected readonly styleScheduler: _CoalescedStyleScheduler,
135+
protected readonly table: CdkTable<unknown>,
108136
@Inject(DOCUMENT) document: any) {
109137
super();
110138
this._document = document;
@@ -117,24 +145,30 @@ export class CdkFlexTableResizeStrategy extends ResizeStrategy implements OnDest
117145
const delta = sizeInPx - (previousSizeInPx ??
118146
(this._getAppliedWidth(cssFriendlyColumnName) || columnHeader.offsetWidth));
119147

148+
if (delta === 0) {
149+
return;
150+
}
151+
120152
const cssSize = coerceCssPixelValue(sizeInPx);
121153

122154
this._applyProperty(cssFriendlyColumnName, 'flex', `0 0.01 ${cssSize}`);
123-
this.updateTableWidth(delta);
155+
this.updateTableWidthAndStickyColumns(delta);
124156
}
125157

126158
applyMinColumnSize(cssFriendlyColumnName: string, _: HTMLElement, sizeInPx: number): void {
127159
const cssSize = coerceCssPixelValue(sizeInPx);
128160

129161
this._applyProperty(cssFriendlyColumnName, 'min-width', cssSize,
130162
sizeInPx !== this.defaultMinSize);
163+
this.updateTableWidthAndStickyColumns(0);
131164
}
132165

133166
applyMaxColumnSize(cssFriendlyColumnName: string, _: HTMLElement, sizeInPx: number): void {
134167
const cssSize = coerceCssPixelValue(sizeInPx);
135168

136169
this._applyProperty(cssFriendlyColumnName, 'max-width', cssSize,
137170
sizeInPx !== this.defaultMaxSize);
171+
this.updateTableWidthAndStickyColumns(0);
138172
}
139173

140174
protected getColumnCssClass(cssFriendlyColumnName: string): string {
@@ -165,12 +199,14 @@ export class CdkFlexTableResizeStrategy extends ResizeStrategy implements OnDest
165199
enable = true): void {
166200
const properties = this._getColumnPropertiesMap(cssFriendlyColumnName);
167201

168-
if (enable) {
169-
properties.set(key, value);
170-
} else {
171-
properties.delete(key);
172-
}
173-
this._applySizeCss(cssFriendlyColumnName);
202+
this.styleScheduler.schedule(() => {
203+
if (enable) {
204+
properties.set(key, value);
205+
} else {
206+
properties.delete(key);
207+
}
208+
this._applySizeCss(cssFriendlyColumnName);
209+
});
174210
}
175211

176212
private _getStyleSheet(): CSSStyleSheet {

0 commit comments

Comments
 (0)