Make everything use the KeyBindingManager (#7907)

This commit is contained in:
Šimon Brandner 2022-02-28 17:05:52 +01:00 committed by GitHub
parent 5f8441216c
commit df591ee835
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 529 additions and 277 deletions

View file

@ -21,11 +21,12 @@ import ReactDOM from "react-dom";
import classNames from "classnames";
import FocusLock from "react-focus-lock";
import { Key } from "../../Keyboard";
import { Writeable } from "../../@types/common";
import { replaceableComponent } from "../../utils/replaceableComponent";
import UIStore from "../../stores/UIStore";
import { checkInputableElement, RovingTabIndexProvider } from "../../accessibility/RovingTabIndex";
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
import { getKeyBindingsManager } from "../../KeyBindingsManager";
// Shamelessly ripped off Modal.js. There's probably a better way
// of doing reusable widgets like dialog boxes & menus where we go and
@ -191,30 +192,32 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
private onKeyDown = (ev: React.KeyboardEvent) => {
ev.stopPropagation(); // prevent keyboard propagating out of the context menu, we're focus-locked
const action = getKeyBindingsManager().getAccessibilityAction(ev);
// If someone is managing their own focus, we will only exit for them with Escape.
// They are probably using props.focusLock along with this option as well.
if (!this.props.managed) {
if (ev.key === Key.ESCAPE) {
if (action === KeyBindingAction.Escape) {
this.props.onFinished();
}
return;
}
// When an <input> is focused, only handle the Escape key
if (checkInputableElement(ev.target as HTMLElement) && ev.key !== Key.ESCAPE) {
if (checkInputableElement(ev.target as HTMLElement) && action !== KeyBindingAction.Escape) {
return;
}
if (
ev.key === Key.ESCAPE ||
if ([
KeyBindingAction.Escape,
// You can only navigate the ContextMenu by arrow keys and Home/End (see RovingTabIndex).
// Tabbing to the next section of the page, will close the ContextMenu.
ev.key === Key.TAB ||
KeyBindingAction.Tab,
// When someone moves left or right along a <Toolbar /> (like the
// MessageActionBar), we should close any ContextMenu that is open.
ev.key === Key.ARROW_LEFT ||
ev.key === Key.ARROW_RIGHT
) {
KeyBindingAction.ArrowLeft,
KeyBindingAction.ArrowRight,
].includes(action)) {
this.props.onFinished();
}
};

View file

@ -35,7 +35,6 @@ import { getKeyBindingsManager } from "../../KeyBindingsManager";
import UIStore from "../../stores/UIStore";
import { findSiblingElement, IState as IRovingTabIndexState } from "../../accessibility/RovingTabIndex";
import RoomListHeader from "../views/rooms/RoomListHeader";
import { Key } from "../../Keyboard";
import RecentlyViewedButton from "../views/rooms/RecentlyViewedButton";
import { BreadcrumbsStore } from "../../stores/BreadcrumbsStore";
import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomListStore";
@ -316,12 +315,15 @@ export default class LeftPanel extends React.Component<IProps, IState> {
private onRoomListKeydown = (ev: React.KeyboardEvent) => {
if (ev.altKey || ev.ctrlKey || ev.metaKey) return;
if (SettingsStore.getValue("feature_spotlight")) return;
const action = getKeyBindingsManager().getAccessibilityAction(ev);
// we cannot handle Space as that is an activation key for all focusable elements in this widget
if (ev.key.length === 1) {
ev.preventDefault();
ev.stopPropagation();
this.roomSearchRef.current?.appendChar(ev.key);
} else if (ev.key === Key.BACKSPACE) {
} else if (action === KeyBindingAction.Backspace) {
ev.preventDefault();
ev.stopPropagation();
this.roomSearchRef.current?.backspace();

View file

@ -20,7 +20,6 @@ import classNames from "classnames";
import AccessibleButton from "../views/elements/AccessibleButton";
import { useRovingTabIndex } from "../../accessibility/RovingTabIndex";
import { Key } from "../../Keyboard";
import { useLocalStorageState } from "../../hooks/useLocalStorageState";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import WidgetUtils, { IWidgetEvent } from "../../utils/WidgetUtils";
@ -28,6 +27,8 @@ import { useAccountData } from "../../hooks/useAccountData";
import AppTile from "../views/elements/AppTile";
import { useSettingValue } from "../../hooks/useSettings";
import UIStore from "../../stores/UIStore";
import { getKeyBindingsManager } from "../../KeyBindingsManager";
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
const MIN_HEIGHT = 100;
const MAX_HEIGHT = 500; // or 50% of the window height
@ -91,16 +92,16 @@ const LeftPanelWidget: React.FC = () => {
onFocus={onFocus}
className="mx_LeftPanelWidget_headerContainer"
onKeyDown={(ev: React.KeyboardEvent) => {
switch (ev.key) {
case Key.ARROW_LEFT:
const action = getKeyBindingsManager().getAccessibilityAction(ev);
switch (action) {
case KeyBindingAction.ArrowLeft:
ev.stopPropagation();
setExpanded(false);
break;
case Key.ARROW_RIGHT: {
case KeyBindingAction.ArrowRight:
ev.stopPropagation();
setExpanded(true);
break;
}
}
}}
>

View file

@ -19,9 +19,10 @@ import React, { createRef, HTMLProps } from 'react';
import { throttle } from 'lodash';
import classNames from 'classnames';
import { Key } from '../../Keyboard';
import AccessibleButton from '../../components/views/elements/AccessibleButton';
import { replaceableComponent } from "../../utils/replaceableComponent";
import { getKeyBindingsManager } from "../../KeyBindingsManager";
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
interface IProps extends HTMLProps<HTMLInputElement> {
onSearch?: (query: string) => void;
@ -66,8 +67,9 @@ export default class SearchBox extends React.Component<IProps, IState> {
}, 200, { trailing: true, leading: true });
private onKeyDown = (ev: React.KeyboardEvent): void => {
switch (ev.key) {
case Key.ESCAPE:
const action = getKeyBindingsManager().getAccessibilityAction(ev);
switch (action) {
case KeyBindingAction.Escape:
this.clearSearch("keyboard");
break;
}

View file

@ -54,7 +54,6 @@ import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import { linkifyElement } from "../../HtmlUtils";
import { useDispatcher } from "../../hooks/useDispatcher";
import { Action } from "../../dispatcher/actions";
import { Key } from "../../Keyboard";
import { IState, RovingTabIndexProvider, useRovingTabIndex } from "../../accessibility/RovingTabIndex";
import { getDisplayAliasForRoom } from "./RoomDirectory";
import MatrixClientContext from "../../contexts/MatrixClientContext";
@ -64,6 +63,8 @@ import { awaitRoomDownSync } from "../../utils/RoomUpgrade";
import RoomViewStore from "../../stores/RoomViewStore";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
import { JoinRoomReadyPayload } from "../../dispatcher/payloads/JoinRoomReadyPayload";
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
import { getKeyBindingsManager } from "../../KeyBindingsManager";
interface IProps {
space: Room;
@ -247,10 +248,13 @@ const Tile: React.FC<ITileProps> = ({
if (showChildren) {
const onChildrenKeyDown = (e) => {
if (e.key === Key.ARROW_LEFT) {
e.preventDefault();
e.stopPropagation();
ref.current?.focus();
const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) {
case KeyBindingAction.ArrowLeft:
e.preventDefault();
e.stopPropagation();
ref.current?.focus();
break;
}
};
@ -266,15 +270,16 @@ const Tile: React.FC<ITileProps> = ({
onKeyDown = (e) => {
let handled = false;
switch (e.key) {
case Key.ARROW_LEFT:
const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) {
case KeyBindingAction.ArrowLeft:
if (showChildren) {
handled = true;
toggleShowChildren();
}
break;
case Key.ARROW_RIGHT:
case KeyBindingAction.ArrowRight:
handled = true;
if (showChildren) {
const childSection = ref.current?.nextElementSibling;
@ -700,7 +705,11 @@ const SpaceHierarchy = ({
}
const onKeyDown = (ev: KeyboardEvent, state: IState): void => {
if (ev.key === Key.ARROW_DOWN && ev.currentTarget.classList.contains("mx_SpaceHierarchy_search")) {
const action = getKeyBindingsManager().getAccessibilityAction(ev);
if (
action === KeyBindingAction.ArrowDown &&
ev.currentTarget.classList.contains("mx_SpaceHierarchy_search")
) {
state.refs[0]?.current?.focus();
}
};

View file

@ -37,7 +37,6 @@ import UserActivity from "../../UserActivity";
import Modal from "../../Modal";
import dis from "../../dispatcher/dispatcher";
import { Action } from '../../dispatcher/actions';
import { Key } from '../../Keyboard';
import Timer from '../../utils/Timer';
import shouldHideEvent from '../../shouldHideEvent';
import { haveTileForEvent } from "../views/rooms/EventTile";
@ -54,6 +53,8 @@ import EditorStateTransfer from '../../utils/EditorStateTransfer';
import ErrorDialog from '../views/dialogs/ErrorDialog';
import CallEventGrouper, { buildCallEventGroupers } from "./CallEventGrouper";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
import { getKeyBindingsManager } from "../../KeyBindingsManager";
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
const PAGINATE_SIZE = 20;
const INITIAL_SIZE = 20;
@ -1086,11 +1087,12 @@ class TimelinePanel extends React.Component<IProps, IState> {
* We pass it down to the scroll panel.
*/
public handleScrollKey = ev => {
if (!this.messagePanel.current) { return; }
if (!this.messagePanel.current) return;
// jump to the live timeline on ctrl-end, rather than the end of the
// timeline window.
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey && ev.key === Key.END) {
const action = getKeyBindingsManager().getRoomAction(ev);
if (action === KeyBindingAction.JumpToLatestMessage) {
this.jumpToLiveTimeline();
} else {
this.messagePanel.current.handleScrollKey(ev);