Skip to content

feat(popover-edit): Arrow keys move focus to neighboring cells #15762

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/cdk-experimental/popover-edit/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ ng_module(
"@npm//@angular/forms",
"@npm//rxjs",
"//src/cdk/a11y",
"//src/cdk/bidi",
"//src/cdk/overlay",
"//src/cdk/keycodes",
"//src/cdk/portal",
],
)
Expand All @@ -25,8 +27,11 @@ ng_test_library(
"@npm//@angular/common",
"@npm//@angular/forms",
"@npm//rxjs",
"//src/cdk/bidi",
"//src/cdk/collections",
"//src/cdk/keycodes",
"//src/cdk/table",
"//src/cdk/testing",
],
)

Expand Down
6 changes: 6 additions & 0 deletions src/cdk-experimental/popover-edit/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@
/** Selector for finding table cells. */
export const CELL_SELECTOR = '.cdk-cell, .mat-cell, td';

/** Selector for finding editable table cells. */
export const EDITABLE_CELL_SELECTOR = '.cdk-popover-edit-cell, .mat-popover-edit-cell';

/** Selector for finding table rows. */
export const ROW_SELECTOR = '.cdk-row, .mat-row, tr';

/** Selector for finding the table element. */
export const TABLE_SELECTOR = 'table, cdk-table, mat-table';

/** CSS class added to the edit lens pane. */
export const EDIT_PANE_CLASS = 'cdk-edit-pane';

Expand Down
92 changes: 92 additions & 0 deletions src/cdk-experimental/popover-edit/focus-dispatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {Directionality} from '@angular/cdk/bidi';
import {LEFT_ARROW, UP_ARROW, RIGHT_ARROW, DOWN_ARROW} from '@angular/cdk/keycodes';
import {Injectable} from '@angular/core';
import {PartialObserver} from 'rxjs';

import {EDITABLE_CELL_SELECTOR, ROW_SELECTOR, TABLE_SELECTOR} from './constants';
import {closest} from './polyfill';

/**
* Service responsible for moving cell focus around in response to keyboard events.
* May be overridden to customize the keyboard behavior of popover edit.
*/
@Injectable({providedIn: 'root'})
export class FocusDispatcher {
/** Observes keydown events triggered from the table. */
readonly keyObserver: PartialObserver<KeyboardEvent>;

constructor(protected readonly directionality: Directionality) {
this.keyObserver = {next: (event) => this.handleKeyboardEvent(event)};
}

/**
* Moves focus to earlier or later cells (in dom order) by offset cells relative to
* currentCell.
*/
moveFocusHorizontally(currentCell: HTMLElement, offset: number): void {
const cells = Array.from(closest(currentCell, TABLE_SELECTOR)!.querySelectorAll(
EDITABLE_CELL_SELECTOR)) as HTMLElement[];
const currentIndex = cells.indexOf(currentCell);
const newIndex = currentIndex + offset;

if (cells[newIndex]) {
cells[newIndex].focus();
}
}

/** Moves focus to up or down by row by offset cells relative to currentCell. */
moveFocusVertically(currentCell: HTMLElement, offset: number): void {
const currentRow = closest(currentCell, ROW_SELECTOR)!;
const rows = Array.from(closest(currentRow, TABLE_SELECTOR)!.querySelectorAll(ROW_SELECTOR));
const currentRowIndex = rows.indexOf(currentRow);
const currentIndexWithinRow =
Array.from(currentRow.querySelectorAll(EDITABLE_CELL_SELECTOR)).indexOf(currentCell);
const newRowIndex = currentRowIndex + offset;

if (rows[newRowIndex]) {
const rowToFocus =
Array.from(rows[newRowIndex].querySelectorAll(EDITABLE_CELL_SELECTOR)) as HTMLElement[];

if (rowToFocus[currentIndexWithinRow]) {
rowToFocus[currentIndexWithinRow].focus();
}
}
}

/** Translates arrow keydown events into focus move operations. */
protected handleKeyboardEvent(event: KeyboardEvent): void {
const cell = closest(event.target, EDITABLE_CELL_SELECTOR) as HTMLElement | null;

if (!cell) {
return;
}

switch (event.keyCode) {
case UP_ARROW:
this.moveFocusVertically(cell, -1);
break;
case DOWN_ARROW:
this.moveFocusVertically(cell, 1);
break;
case LEFT_ARROW:
this.moveFocusHorizontally(cell, this.directionality.value === 'ltr' ? -1 : 1);
break;
case RIGHT_ARROW:
this.moveFocusHorizontally(cell, this.directionality.value === 'ltr' ? 1 : -1);
break;
default:
// If the keyboard event is not handled, return now so that we don't `preventDefault`.
return;
}

event.preventDefault();
}
}
Loading