@itwin/core-react
Version:
A react component library of iTwin.js UI general purpose components
177 lines • 6.03 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Focus
*/
import { Key } from "ts-key-enum";
import { Orientation } from "../enums/Orientation.js";
/** Keyboard Navigator for parent components
* @internal
*/
export class ItemKeyboardNavigator {
onFocusItem;
onActivateItem;
_direction;
_itemCount = 0;
_orientation = Orientation.Horizontal;
_allowWrap = true;
_crossAxisArrowKeyHandler;
constructor(onFocusItem, onActivateItem) {
this.onFocusItem = onFocusItem;
this.onActivateItem = onActivateItem;
this._direction = new Map([
[Key.ArrowLeft, -1],
[Key.ArrowUp, -1],
[Key.ArrowRight, 1],
[Key.ArrowDown, 1],
]);
}
/** The item count */
get itemCount() {
return this._itemCount;
}
set itemCount(count) {
this._itemCount = count;
}
/** The primary orientation */
get orientation() {
return this._orientation;
}
set orientation(orientation) {
this._orientation = orientation;
}
/** The allow wrap property controls whether the movement will stop at the first and last items or wrap */
get allowWrap() {
return this._allowWrap;
}
set allowWrap(v) {
this._allowWrap = v;
}
/** Called when the arrow keys that run perpendicular to the primary orientation are pressed */
get crossAxisArrowKeyHandler() {
return this._crossAxisArrowKeyHandler;
}
set crossAxisArrowKeyHandler(v) {
this._crossAxisArrowKeyHandler = v;
}
/** Handle KeyDown on items */
handleKeyDownEvent(event, index) {
const key = event.key;
switch (key) {
case Key.Home.valueOf():
event.preventDefault();
// Activate first item
this.focusFirstItem();
break;
case Key.End.valueOf():
event.preventDefault();
// Activate last item
this.focusLastItem();
break;
// Up and down are in keydown
// because we need to prevent page scroll >:)
case Key.ArrowUp.valueOf():
case Key.ArrowDown.valueOf():
this.determineOrientation(event, index);
break;
case Key.Enter.valueOf():
case " ":
this.activateItem(index);
break;
}
}
/** Handle KeyUp on items */
handleKeyUpEvent(event, index) {
const key = event.key;
switch (key) {
case Key.ArrowLeft.valueOf():
case Key.ArrowRight.valueOf():
this.determineOrientation(event, index);
break;
}
}
focusFirstItem() {
this.onFocusItem(0);
}
focusLastItem() {
const index = this._itemCount - 1;
this.onFocusItem(index);
}
/** When an item list's orientation is set to vertical,
* only up and down arrow should function.
* In all other cases only left and right arrow function.
*/
determineOrientation(event, index) {
const key = event.key;
const vertical = this._orientation === Orientation.Vertical;
let proceed = false;
if (vertical) {
if (key === Key.ArrowUp.valueOf() || key === Key.ArrowDown.valueOf()) {
event.preventDefault();
proceed = true;
}
else if (this.crossAxisArrowKeyHandler &&
(key === Key.ArrowLeft.valueOf() || key === Key.ArrowRight.valueOf())) {
this.crossAxisArrowKeyHandler(key === Key.ArrowRight.valueOf());
}
}
else {
if (key === Key.ArrowLeft.valueOf() || key === Key.ArrowRight.valueOf()) {
proceed = true;
}
else if (this.crossAxisArrowKeyHandler &&
(key === Key.ArrowUp.valueOf() || key === Key.ArrowDown.valueOf())) {
this.crossAxisArrowKeyHandler(key === Key.ArrowDown.valueOf());
}
}
if (proceed) {
this.switchItemOnArrowPress(event, index);
}
}
/** Either focus the next, previous, first, or last item depending on key pressed
*/
switchItemOnArrowPress(event, index) {
// Add or subtract depending on key pressed
const pressed = event.key;
const targetDirection = this._direction.get(pressed);
if (targetDirection) {
const newIndex = index + targetDirection;
if (0 <= newIndex && newIndex < this._itemCount) {
this.onFocusItem(newIndex);
}
else {
if (this._allowWrap) {
if (pressed === Key.ArrowLeft.valueOf() ||
pressed === Key.ArrowUp.valueOf()) {
this.focusLastItem();
}
else {
this.focusFirstItem();
}
}
}
}
}
activateItem(index) {
this.onActivateItem(index);
}
}
/** Determines if a KeyboardEvent.key is an Item Navigation key
* @internal
*/
export function isNavigationKey(key) {
return (isArrowKey(key) ||
key === Key.Home.valueOf() ||
key === Key.End.valueOf() ||
key === " " ||
key === Key.Enter.valueOf());
}
function isArrowKey(key) {
return (key === Key.ArrowLeft.valueOf() ||
key === Key.ArrowRight.valueOf() ||
key === Key.ArrowUp.valueOf() ||
key === Key.ArrowDown.valueOf());
}
//# sourceMappingURL=ItemKeyboardNavigator.js.map