UNPKG

@itwin/core-react

Version:

A react component library of iTwin.js UI general purpose components

177 lines 6.03 kB
/*--------------------------------------------------------------------------------------------- * 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