Skip to content

Commit 9149ed1

Browse files
committed
refactor(aria/grid): remove range selection to reduce computational overhead
1 parent 046e1a2 commit 9149ed1

File tree

8 files changed

+7
-265
lines changed

8 files changed

+7
-265
lines changed

goldens/aria/grid/index.api.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export class Grid {
1515
readonly colWrap: _angular_core.InputSignal<"continuous" | "loop" | "nowrap">;
1616
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
1717
readonly element: HTMLElement;
18-
readonly enableRangeSelection: _angular_core.InputSignalWithTransform<boolean, unknown>;
1918
readonly enableSelection: _angular_core.InputSignalWithTransform<boolean, unknown>;
2019
readonly focusMode: _angular_core.InputSignal<"roving" | "activedescendant">;
2120
readonly multi: _angular_core.InputSignalWithTransform<boolean, unknown>;
@@ -25,7 +24,7 @@ export class Grid {
2524
readonly softDisabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
2625
readonly textDirection: _angular_core.WritableSignal<_angular_cdk_bidi.Direction>;
2726
// (undocumented)
28-
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<Grid, "[ngGrid]", ["ngGrid"], { "enableSelection": { "alias": "enableSelection"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "softDisabled": { "alias": "softDisabled"; "required": false; "isSignal": true; }; "focusMode": { "alias": "focusMode"; "required": false; "isSignal": true; }; "rowWrap": { "alias": "rowWrap"; "required": false; "isSignal": true; }; "colWrap": { "alias": "colWrap"; "required": false; "isSignal": true; }; "multi": { "alias": "multi"; "required": false; "isSignal": true; }; "selectionMode": { "alias": "selectionMode"; "required": false; "isSignal": true; }; "enableRangeSelection": { "alias": "enableRangeSelection"; "required": false; "isSignal": true; }; }, {}, ["_rows"], never, true, never>;
27+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<Grid, "[ngGrid]", ["ngGrid"], { "enableSelection": { "alias": "enableSelection"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "softDisabled": { "alias": "softDisabled"; "required": false; "isSignal": true; }; "focusMode": { "alias": "focusMode"; "required": false; "isSignal": true; }; "rowWrap": { "alias": "rowWrap"; "required": false; "isSignal": true; }; "colWrap": { "alias": "colWrap"; "required": false; "isSignal": true; }; "multi": { "alias": "multi"; "required": false; "isSignal": true; }; "selectionMode": { "alias": "selectionMode"; "required": false; "isSignal": true; }; }, {}, ["_rows"], never, true, never>;
2928
// (undocumented)
3029
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Grid, never>;
3130
}
@@ -60,10 +59,10 @@ export class GridCell {
6059
export class GridCellWidget {
6160
constructor();
6261
activate(): void;
63-
readonly activated: _angular_core.OutputEmitterRef<FocusEvent | KeyboardEvent | undefined>;
62+
readonly activated: _angular_core.OutputEmitterRef<KeyboardEvent | FocusEvent | undefined>;
6463
readonly active: Signal<boolean>;
6564
deactivate(): void;
66-
readonly deactivated: _angular_core.OutputEmitterRef<FocusEvent | KeyboardEvent | undefined>;
65+
readonly deactivated: _angular_core.OutputEmitterRef<KeyboardEvent | FocusEvent | undefined>;
6766
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
6867
readonly element: HTMLElement;
6968
readonly focusTarget: _angular_core.InputSignal<ElementRef<any> | HTMLElement | undefined>;

goldens/aria/private/index.api.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,6 @@ export class GridCellWidgetPattern implements ListNavigationItem {
377377
// @public
378378
export interface GridInputs extends Omit<GridInputs$1<GridCellPattern>, 'cells'> {
379379
element: SignalLike<HTMLElement>;
380-
enableRangeSelection: SignalLike<boolean>;
381380
enableSelection: SignalLike<boolean>;
382381
getCell: (e: Element | null) => GridCellPattern | undefined;
383382
multi: SignalLike<boolean>;
@@ -389,7 +388,6 @@ export interface GridInputs extends Omit<GridInputs$1<GridCellPattern>, 'cells'>
389388
// @public
390389
export class GridPattern {
391390
constructor(inputs: GridInputs);
392-
readonly acceptsPointerMove: SignalLike<boolean>;
393391
readonly activeCell: SignalLike<GridCellPattern | undefined>;
394392
readonly activeDescendant: SignalLike<string | undefined>;
395393
readonly anchorCell: SignalLike<GridCellPattern | undefined>;
@@ -409,11 +407,8 @@ export class GridPattern {
409407
onFocusOut(event: FocusEvent): void;
410408
onKeydown(event: KeyboardEvent): void;
411409
onPointerdown(event: PointerEvent): void;
412-
onPointermove(event: PointerEvent): void;
413-
onPointerup(event: PointerEvent): void;
414410
readonly pauseNavigation: SignalLike<boolean>;
415411
readonly pointerdown: SignalLike<PointerEventManager<PointerEvent>>;
416-
readonly pointerup: SignalLike<PointerEventManager<PointerEvent>>;
417412
readonly prevColKey: SignalLike<"ArrowRight" | "ArrowLeft">;
418413
resetFocusEffect(): void;
419414
resetStateEffect(): void;

src/aria/grid/grid.spec.ts

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ describe('Grid directives', () => {
105105
softDisabled?: boolean;
106106
enableSelection?: boolean;
107107
selectionMode?: 'follow' | 'explicit';
108-
enableRangeSelection?: boolean;
109108
gridData?: RowConfig[];
110109
}) {
111110
TestBed.resetTestingModule();
@@ -122,8 +121,6 @@ describe('Grid directives', () => {
122121
if (opts?.enableSelection !== undefined)
123122
testComponent.enableSelection.set(opts.enableSelection);
124123
if (opts?.selectionMode !== undefined) testComponent.selectionMode.set(opts.selectionMode);
125-
if (opts?.enableRangeSelection !== undefined)
126-
testComponent.enableRangeSelection.set(opts.enableRangeSelection);
127124

128125
if (opts?.gridData !== undefined) {
129126
testComponent.gridData.set(opts.gridData);
@@ -388,7 +385,6 @@ describe('Grid directives', () => {
388385
enableSelection: true,
389386
selectionMode: 'explicit',
390387
multi: true,
391-
enableRangeSelection: true,
392388
});
393389
gridInstance._pattern.setDefaultStateEffect();
394390
fixture.detectChanges();
@@ -488,7 +484,6 @@ describe('Grid directives', () => {
488484
enableSelection: true,
489485
selectionMode: 'explicit',
490486
multi: true,
491-
enableRangeSelection: true,
492487
});
493488
gridInstance._pattern.setDefaultStateEffect();
494489
fixture.detectChanges();
@@ -503,41 +498,6 @@ describe('Grid directives', () => {
503498
expect(cell.getAttribute('aria-selected')).toBe('true');
504499
expect(getActiveCellId()).toBe('c1-1');
505500
});
506-
507-
it('should expand selection on pointermove while dragging without changing active cell', () => {
508-
const startCell = gridElement.querySelector('#c0-0') as HTMLElement;
509-
const dragCell = gridElement.querySelector('#c1-1') as HTMLElement;
510-
511-
pointerDown(startCell);
512-
pointerMove(dragCell);
513-
514-
expect(getActiveCellId()).toBe('c0-0');
515-
// Dragging expands selection
516-
expect(startCell.getAttribute('aria-selected')).toBe('true');
517-
expect(dragCell.getAttribute('aria-selected')).toBe('true');
518-
});
519-
520-
it('should stop dragging on pointerup', () => {
521-
const startCell = gridElement.querySelector('#c0-0') as HTMLElement;
522-
const endCell = gridElement.querySelector('#c1-1') as HTMLElement;
523-
524-
pointerDown(startCell);
525-
pointerUp(gridElement);
526-
pointerMove(endCell);
527-
528-
// Active cell should still be c0-0 because dragging stopped before moving to c1-1
529-
expect(getActiveCellId()).toBe('c0-0');
530-
expect(endCell.getAttribute('aria-selected')).toBe('false');
531-
});
532-
533-
it('should not change active cell on pointermove outside of the grid cells', () => {
534-
const startCell = gridElement.querySelector('#c0-0') as HTMLElement;
535-
536-
pointerDown(startCell);
537-
pointerMove(gridElement);
538-
539-
expect(getActiveCellId()).toBe('c0-0');
540-
});
541501
});
542502

543503
describe('configuration', () => {
@@ -1009,8 +969,7 @@ describe('Grid directives', () => {
1009969
[focusMode]="focusMode()"
1010970
[softDisabled]="softDisabled()"
1011971
[enableSelection]="enableSelection()"
1012-
[selectionMode]="selectionMode()"
1013-
[enableRangeSelection]="enableRangeSelection()">
972+
[selectionMode]="selectionMode()">
1014973
@for (row of gridData(); track $index; let rIndex = $index) {
1015974
<tr ngGridRow [rowIndex]="row.rowIndex">
1016975
@for (cell of row.cells; track $index; let cIndex = $index) {
@@ -1067,7 +1026,6 @@ class GridTestComponent {
10671026
readonly softDisabled = signal(true);
10681027
readonly enableSelection = signal(false);
10691028
readonly selectionMode = signal<'follow' | 'explicit'>('follow');
1070-
readonly enableRangeSelection = signal(false);
10711029
readonly gridData = signal<RowConfig[]>(createGridData());
10721030

10731031
onActivated = jasmine.createSpy('activated');

src/aria/grid/grid.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ import {GRID_ROW} from './grid-tokens';
5656
'[attr.aria-activedescendant]': '_pattern.activeDescendant()',
5757
'(keydown)': '_pattern.onKeydown($event)',
5858
'(pointerdown)': '_pattern.onPointerdown($event)',
59-
'(pointerup)': '_pattern.onPointerup($event)',
6059
'(focusin)': '_pattern.onFocusIn($event)',
6160
'(focusout)': '_pattern.onFocusOut($event)',
6261
},
@@ -122,9 +121,6 @@ export class Grid {
122121
*/
123122
readonly selectionMode = input<'follow' | 'explicit'>('follow');
124123

125-
/** Whether enable range selections (with modifier keys or dragging). */
126-
readonly enableRangeSelection = input(false, {transform: booleanAttribute});
127-
128124
/** The UI pattern for the grid. */
129125
readonly _pattern = new GridPattern({
130126
...this,
@@ -136,20 +132,6 @@ export class Grid {
136132
constructor() {
137133
const ngZone = inject(NgZone);
138134

139-
// Since `pointermove` fires on each pixel, we need to
140-
// be careful not to hit the zone unless it's necessary.
141-
ngZone.runOutsideAngular(() => {
142-
this.element.addEventListener(
143-
'pointermove',
144-
event => {
145-
if (this._pattern.acceptsPointerMove()) {
146-
ngZone.run(() => this._pattern.onPointermove(event));
147-
}
148-
},
149-
{passive: true},
150-
);
151-
});
152-
153135
afterRenderEffect(() => this._pattern.setDefaultStateEffect());
154136
afterRenderEffect(() => this._pattern.resetStateEffect());
155137
afterRenderEffect(() => this._pattern.resetFocusEffect());

src/aria/private/grid/grid.spec.ts

Lines changed: 0 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ function getDefaultGridInputs(): TestGridInputs {
141141
enableSelection: signal(false),
142142
multi: signal(false),
143143
selectionMode: signal('follow'),
144-
enableRangeSelection: signal(false),
145144
getCell: () => undefined,
146145
focusMode: signal('roving'),
147146
disabled: signal(false),
@@ -602,7 +601,6 @@ describe('Grid', () => {
602601

603602
it('should select all on Ctrl+A', () => {
604603
(gridInputs.multi as WritableSignalLike<boolean>).set(true);
605-
(gridInputs.enableRangeSelection as WritableSignalLike<boolean>).set(true);
606604
grid.onKeydown(a({control: true}));
607605
expect(
608606
grid
@@ -614,7 +612,6 @@ describe('Grid', () => {
614612

615613
it('should select row on Shift+Space', () => {
616614
(gridInputs.multi as WritableSignalLike<boolean>).set(true);
617-
(gridInputs.enableRangeSelection as WritableSignalLike<boolean>).set(true);
618615
const gridCells = grid.cells();
619616
grid.gridBehavior.focusBehavior.focusCell(gridCells[0][0]);
620617
grid.onKeydown(space({shift: true}));
@@ -627,7 +624,6 @@ describe('Grid', () => {
627624

628625
it('should select column on Ctrl+Space', () => {
629626
(gridInputs.multi as WritableSignalLike<boolean>).set(true);
630-
(gridInputs.enableRangeSelection as WritableSignalLike<boolean>).set(true);
631627
const gridCells = grid.cells();
632628
grid.gridBehavior.focusBehavior.focusCell(gridCells[0][0]);
633629
grid.onKeydown(space({control: true}));
@@ -638,66 +634,6 @@ describe('Grid', () => {
638634
expect(gridCells[1][1].selected()).toBe(false);
639635
});
640636
});
641-
642-
describe('Range Selection Logic', () => {
643-
let grid: GridPattern;
644-
645-
beforeEach(() => {
646-
(gridInputs.enableSelection as WritableSignalLike<boolean>).set(true);
647-
(gridInputs.multi as WritableSignalLike<boolean>).set(true);
648-
(gridInputs.enableRangeSelection as WritableSignalLike<boolean>).set(true);
649-
650-
const data = [{cells: [{}, {}, {}]}, {cells: [{}, {}, {}]}, {cells: [{}, {}, {}]}];
651-
const result = createGrid(data, gridInputs);
652-
grid = result.grid;
653-
grid.setDefaultStateEffect();
654-
});
655-
656-
it('should expand the selection range up on Shift+ArrowUp', () => {
657-
const cells = grid.cells();
658-
grid.gridBehavior.focusBehavior.focusCell(cells[1][1]);
659-
grid.onKeydown(shiftUp());
660-
expect(cells[1][1].selected()).toBe(true);
661-
expect(cells[0][1].selected()).toBe(true);
662-
});
663-
664-
it('should expand the selection range down on Shift+ArrowDown', () => {
665-
const cells = grid.cells();
666-
grid.gridBehavior.focusBehavior.focusCell(cells[1][1]);
667-
grid.onKeydown(shiftDown());
668-
expect(cells[1][1].selected()).toBe(true);
669-
expect(cells[2][1].selected()).toBe(true);
670-
});
671-
672-
it('should expand the selection range left on Shift+ArrowLeft', () => {
673-
const cells = grid.cells();
674-
grid.gridBehavior.focusBehavior.focusCell(cells[1][1]);
675-
grid.onKeydown(shiftLeft());
676-
expect(cells[1][1].selected()).toBe(true);
677-
expect(cells[1][0].selected()).toBe(true);
678-
});
679-
680-
it('should expand the selection range right on Shift+ArrowRight', () => {
681-
const cells = grid.cells();
682-
grid.gridBehavior.focusBehavior.focusCell(cells[1][1]);
683-
grid.onKeydown(shiftRight());
684-
expect(cells[1][1].selected()).toBe(true);
685-
expect(cells[1][2].selected()).toBe(true);
686-
});
687-
688-
it('should support range selection with Shift+Home/End', () => {
689-
const cells = grid.cells();
690-
grid.gridBehavior.focusBehavior.focusCell(cells[0][1]);
691-
grid.onKeydown(shiftHome());
692-
expect(cells[0][0].selected()).toBe(true);
693-
expect(cells[0][1].selected()).toBe(true);
694-
695-
grid.onKeydown(shiftEnd());
696-
expect(cells[0][0].selected()).toBe(false);
697-
expect(cells[0][1].selected()).toBe(true);
698-
expect(cells[0][2].selected()).toBe(true);
699-
});
700-
});
701637
});
702638

703639
describe('Pointer Events', () => {
@@ -745,42 +681,6 @@ describe('Grid', () => {
745681
expect(cells[0][0].selected()).toBe(true);
746682
expect(cells[0][1].selected()).toBe(true);
747683
});
748-
749-
it('should support range selection with Shift+pointerdown', () => {
750-
(gridInputs.multi as WritableSignalLike<boolean>).set(true);
751-
(gridInputs.enableRangeSelection as WritableSignalLike<boolean>).set(true);
752-
const cells = grid.cells();
753-
grid.onPointerdown(createClickEvent(cells[0][0].element()));
754-
grid.onPointerdown(createClickEvent(cells[1][1].element(), {shift: true}));
755-
expect(cells[0][0].selected()).toBe(true);
756-
expect(cells[0][1].selected()).toBe(true);
757-
expect(cells[1][0].selected()).toBe(true);
758-
expect(cells[1][1].selected()).toBe(true);
759-
});
760-
});
761-
762-
describe('Range Selection Dragging', () => {
763-
beforeEach(() => {
764-
(gridInputs.multi as WritableSignalLike<boolean>).set(true);
765-
(gridInputs.enableRangeSelection as WritableSignalLike<boolean>).set(true);
766-
});
767-
768-
it('should select range on pointermove', () => {
769-
const cells = grid.cells();
770-
grid.onPointerdown(createClickEvent(cells[0][0].element()));
771-
grid.onPointermove(createClickEvent(cells[0][1].element()));
772-
expect(cells[0][0].selected()).toBe(true);
773-
expect(cells[0][1].selected()).toBe(true);
774-
});
775-
776-
it('should stabilize selection on pointerup', () => {
777-
const cell = grid.cells()[0][1];
778-
grid.onPointerdown(createClickEvent(grid.cells()[0][0].element()));
779-
grid.onPointermove(createClickEvent(cell.element()));
780-
expect(grid.dragging()).toBe(true);
781-
grid.onPointerup(createClickEvent(cell.element()));
782-
expect(grid.dragging()).toBe(false);
783-
});
784684
});
785685
});
786686

0 commit comments

Comments
 (0)