Skip to content

Commit 0395ca2

Browse files
authored
perf(aria/grid): avoid excessive change detections (#32705)
Fixes that the grid was running change detection on `pointermove` events even when the user isn't dragging to select cells. Fixes #32700.
1 parent c70a473 commit 0395ca2

File tree

4 files changed

+36
-16
lines changed

4 files changed

+36
-16
lines changed

goldens/aria/grid/index.api.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ export class GridCellWidget {
6767
readonly focusTarget: _angular_core.InputSignal<ElementRef<any> | HTMLElement | undefined>;
6868
readonly id: _angular_core.InputSignal<string>;
6969
get isActivated(): Signal<boolean>;
70-
readonly onActivate: _angular_core.OutputEmitterRef<KeyboardEvent | FocusEvent | undefined>;
71-
readonly onDeactivate: _angular_core.OutputEmitterRef<KeyboardEvent | FocusEvent | undefined>;
70+
readonly onActivate: _angular_core.OutputEmitterRef<FocusEvent | KeyboardEvent | undefined>;
71+
readonly onDeactivate: _angular_core.OutputEmitterRef<FocusEvent | KeyboardEvent | undefined>;
7272
readonly _pattern: GridCellWidgetPattern;
7373
protected readonly _tabIndex: Signal<number>;
7474
readonly tabindex: _angular_core.InputSignal<number | undefined>;

goldens/aria/private/index.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ export interface GridInputs extends Omit<GridInputs$1<GridCellPattern>, 'cells'>
407407
// @public
408408
export class GridPattern {
409409
constructor(inputs: GridInputs);
410+
readonly acceptsPointerMove: SignalLike<boolean>;
410411
readonly activeCell: SignalLike<GridCellPattern | undefined>;
411412
readonly activeDescendant: SignalLike<string | undefined>;
412413
readonly anchorCell: SignalLike<GridCellPattern | undefined>;

src/aria/grid/grid.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
ElementRef,
1616
inject,
1717
input,
18+
NgZone,
1819
Signal,
1920
} from '@angular/core';
2021
import {Directionality} from '@angular/cdk/bidi';
@@ -54,7 +55,6 @@ import {GRID_ROW} from './grid-tokens';
5455
'[attr.aria-activedescendant]': '_pattern.activeDescendant()',
5556
'(keydown)': '_pattern.onKeydown($event)',
5657
'(pointerdown)': '_pattern.onPointerdown($event)',
57-
'(pointermove)': '_pattern.onPointermove($event)',
5858
'(pointerup)': '_pattern.onPointerup($event)',
5959
'(focusin)': '_pattern.onFocusIn($event)',
6060
'(focusout)': '_pattern.onFocusOut($event)',
@@ -133,6 +133,22 @@ export class Grid {
133133
});
134134

135135
constructor() {
136+
const ngZone = inject(NgZone);
137+
138+
// Since `pointermove` fires on each pixel, we need to
139+
// be careful not to hit the zone unless it's necessary.
140+
ngZone.runOutsideAngular(() => {
141+
this.element.addEventListener(
142+
'pointermove',
143+
event => {
144+
if (this._pattern.acceptsPointerMove()) {
145+
ngZone.run(() => this._pattern.onPointermove(event));
146+
}
147+
},
148+
{passive: true},
149+
);
150+
});
151+
136152
afterRenderEffect(() => this._pattern.setDefaultStateEffect());
137153
afterRenderEffect(() => this._pattern.resetStateEffect());
138154
afterRenderEffect(() => this._pattern.resetFocusEffect());

src/aria/private/grid/grid.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ export class GridPattern {
9393
this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight',
9494
);
9595

96+
/** Whether the grid pattern is currently accepting `pointermove` events. */
97+
readonly acceptsPointerMove = computed(() => {
98+
return (
99+
!this.disabled() &&
100+
this.inputs.enableSelection() &&
101+
this.inputs.enableRangeSelection() &&
102+
this.dragging()
103+
);
104+
});
105+
96106
/** The keydown event manager for the grid. */
97107
readonly keydown = computed(() => {
98108
const manager = new KeyboardEventManager();
@@ -252,20 +262,13 @@ export class GridPattern {
252262

253263
/** Handles pointermove events on the grid. */
254264
onPointermove(event: PointerEvent) {
255-
if (
256-
this.disabled() ||
257-
!this.inputs.enableSelection() ||
258-
!this.inputs.enableRangeSelection() ||
259-
!this.dragging()
260-
) {
261-
return;
262-
}
265+
if (this.acceptsPointerMove()) {
266+
const cell = this.inputs.getCell(event.target as Element);
263267

264-
const cell = this.inputs.getCell(event.target as Element);
265-
266-
// Dragging anchor.
267-
if (cell !== undefined) {
268-
this.gridBehavior.gotoCell(cell, {anchor: true});
268+
// Dragging anchor.
269+
if (cell !== undefined) {
270+
this.gridBehavior.gotoCell(cell, {anchor: true});
271+
}
269272
}
270273
}
271274

0 commit comments

Comments
 (0)