123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- /*
- * SonarQube
- * Copyright (C) 2009-2021 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- import key from 'keymaster';
- import * as React from 'react';
- import PageActions from '../../components/ui/PageActions';
- import { getComponentMeasureUniqueKey } from '../../helpers/component';
- import { getWrappedDisplayName } from './utils';
-
- export interface WithKeyboardNavigationProps {
- components?: T.ComponentMeasure[];
- cycle?: boolean;
- isFile?: boolean;
- onEndOfList?: () => void;
- onGoToParent?: () => void;
- onHighlight?: (item: T.ComponentMeasure) => void;
- onSelect?: (item: T.ComponentMeasure) => void;
- selected?: T.ComponentMeasure;
- }
-
- const KEY_SCOPE = 'key_nav';
-
- export default function withKeyboardNavigation<P>(
- WrappedComponent: React.ComponentClass<P & Partial<WithKeyboardNavigationProps>>
- ) {
- return class Wrapper extends React.Component<P & WithKeyboardNavigationProps> {
- static displayName = getWrappedDisplayName(WrappedComponent, 'withKeyboardNavigation');
-
- componentDidMount() {
- this.attachShortcuts();
- }
-
- componentWillUnmount() {
- this.detachShortcuts();
- }
-
- attachShortcuts = () => {
- key.setScope(KEY_SCOPE);
- key('up', KEY_SCOPE, () => {
- return this.skipIfFile(this.handleHighlightPrevious);
- });
- key('down', KEY_SCOPE, () => {
- return this.skipIfFile(this.handleHighlightNext);
- });
- key('right,enter', KEY_SCOPE, () => {
- return this.skipIfFile(this.handleSelectCurrent);
- });
- key('left', KEY_SCOPE, () => {
- this.handleSelectParent();
- return false; // always hijack left
- });
- };
-
- detachShortcuts = () => {
- key.deleteScope(KEY_SCOPE);
- };
-
- getCurrentIndex = () => {
- const { selected, components = [] } = this.props;
- return selected
- ? components.findIndex(
- component =>
- getComponentMeasureUniqueKey(component) === getComponentMeasureUniqueKey(selected)
- )
- : -1;
- };
-
- skipIfFile = (handler: () => void) => {
- if (this.props.isFile) {
- return true;
- } else {
- handler();
- return false;
- }
- };
-
- handleHighlightNext = () => {
- if (this.props.onHighlight === undefined) {
- return;
- }
-
- const { components = [], cycle } = this.props;
- const index = this.getCurrentIndex();
- const first = cycle ? 0 : index;
-
- this.props.onHighlight(
- index < components.length - 1 ? components[index + 1] : components[first]
- );
-
- if (index + 1 === components.length - 1 && this.props.onEndOfList) {
- this.props.onEndOfList();
- }
- };
-
- handleHighlightPrevious = () => {
- if (this.props.onHighlight === undefined) {
- return;
- }
- const { components = [], cycle } = this.props;
- const index = this.getCurrentIndex();
- const last = cycle ? components.length - 1 : index;
-
- this.props.onHighlight(index > 0 ? components[index - 1] : components[last]);
- };
-
- handleSelectCurrent = () => {
- if (this.props.onSelect === undefined) {
- return;
- }
-
- const { selected } = this.props;
- if (selected !== undefined) {
- this.props.onSelect(selected as T.ComponentMeasure);
- }
- };
-
- handleSelectNext = () => {
- if (this.props.onSelect === undefined) {
- return;
- }
-
- const { components = [] } = this.props;
- const index = this.getCurrentIndex();
-
- if (index !== -1 && index < components.length - 1) {
- this.props.onSelect(components[index + 1]);
- }
- };
-
- handleSelectParent = () => {
- if (this.props.onGoToParent !== undefined) {
- this.props.onGoToParent();
- }
- };
-
- handleSelectPrevious = () => {
- if (this.props.onSelect === undefined) {
- return;
- }
-
- const { components = [] } = this.props;
- const index = this.getCurrentIndex();
-
- if (components.length && index > 0) {
- this.props.onSelect(components[index - 1]);
- }
- };
-
- render() {
- return (
- <>
- <PageActions showShortcuts={!this.props.isFile} />
-
- <WrappedComponent {...this.props} />
- </>
- );
- }
- };
- }
|