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

@ -17,7 +17,8 @@
import React, { ReactHTML } from 'react';
import classnames from 'classnames';
import { Key } from '../../../Keyboard';
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
export type ButtonEvent = React.MouseEvent<Element> | React.KeyboardEvent<Element> | React.FormEvent<Element>;
@ -92,29 +93,36 @@ export default function AccessibleButton({
// Browsers handle space and enter keypresses differently and we are only adjusting to the
// inconsistencies here
newProps.onKeyDown = (e) => {
if (e.key === Key.ENTER) {
e.stopPropagation();
e.preventDefault();
return onClick(e);
}
if (e.key === Key.SPACE) {
e.stopPropagation();
e.preventDefault();
} else {
onKeyDown?.(e);
const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) {
case KeyBindingAction.Enter:
e.stopPropagation();
e.preventDefault();
return onClick(e);
case KeyBindingAction.Space:
e.stopPropagation();
e.preventDefault();
break;
default:
onKeyDown?.(e);
}
};
newProps.onKeyUp = (e) => {
if (e.key === Key.SPACE) {
e.stopPropagation();
e.preventDefault();
return onClick(e);
}
if (e.key === Key.ENTER) {
e.stopPropagation();
e.preventDefault();
} else {
onKeyUp?.(e);
const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) {
case KeyBindingAction.Enter:
e.stopPropagation();
e.preventDefault();
break;
case KeyBindingAction.Space:
e.stopPropagation();
e.preventDefault();
return onClick(e);
default:
onKeyUp?.(e);
break;
}
};
}

View file

@ -20,8 +20,9 @@ import classnames from 'classnames';
import AccessibleButton, { ButtonEvent } from './AccessibleButton';
import { _t } from '../../../languageHandler';
import { Key } from "../../../Keyboard";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
interface IMenuOptionProps {
children: ReactElement;
@ -181,10 +182,12 @@ export default class Dropdown extends React.Component<IProps, IState> {
private onAccessibleButtonClick = (ev: ButtonEvent) => {
if (this.props.disabled) return;
const action = getKeyBindingsManager().getAccessibilityAction(ev as React.KeyboardEvent);
if (!this.state.expanded) {
this.setState({ expanded: true });
ev.preventDefault();
} else if ((ev as React.KeyboardEvent).key === Key.ENTER) {
} else if (action === KeyBindingAction.Enter) {
// the accessible button consumes enter onKeyDown for firing onClick, so handle it here
this.props.onOptionChange(this.state.highlightedOption);
this.close();
@ -214,14 +217,15 @@ export default class Dropdown extends React.Component<IProps, IState> {
let handled = true;
// These keys don't generate keypress events and so needs to be on keyup
switch (e.key) {
case Key.ENTER:
const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) {
case KeyBindingAction.Enter:
this.props.onOptionChange(this.state.highlightedOption);
// fallthrough
case Key.ESCAPE:
case KeyBindingAction.Escape:
this.close();
break;
case Key.ARROW_DOWN:
case KeyBindingAction.ArrowDown:
if (this.state.expanded) {
this.setState({
highlightedOption: this.nextOption(this.state.highlightedOption),
@ -230,7 +234,7 @@ export default class Dropdown extends React.Component<IProps, IState> {
this.setState({ expanded: true });
}
break;
case Key.ARROW_UP:
case KeyBindingAction.ArrowUp:
if (this.state.expanded) {
this.setState({
highlightedOption: this.prevOption(this.state.highlightedOption),

View file

@ -17,7 +17,8 @@ limitations under the License.
import React, { createRef } from 'react';
import { Key } from "../../../Keyboard";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
import { replaceableComponent } from "../../../utils/replaceableComponent";
enum Phases {
@ -124,9 +125,12 @@ export default class EditableText extends React.Component<IProps, IState> {
this.showPlaceholder(false);
}
if (ev.key === Key.ENTER) {
ev.stopPropagation();
ev.preventDefault();
const action = getKeyBindingsManager().getAccessibilityAction(ev);
switch (action) {
case KeyBindingAction.Enter:
ev.stopPropagation();
ev.preventDefault();
break;
}
// console.log("keyDown: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder);
@ -141,10 +145,14 @@ export default class EditableText extends React.Component<IProps, IState> {
this.value = (ev.target as HTMLDivElement).textContent;
}
if (ev.key === Key.ENTER) {
this.onFinish(ev);
} else if (ev.key === Key.ESCAPE) {
this.cancelEdit();
const action = getKeyBindingsManager().getAccessibilityAction(ev);
switch (action) {
case KeyBindingAction.Escape:
this.cancelEdit();
break;
case KeyBindingAction.Enter:
this.onFinish(ev);
break;
}
// console.log("keyUp: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder);
@ -179,7 +187,8 @@ export default class EditableText extends React.Component<IProps, IState> {
): void => {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
const submit = ("key" in ev && ev.key === Key.ENTER) || shouldSubmit;
const action = getKeyBindingsManager().getAccessibilityAction(ev as React.KeyboardEvent);
const submit = action === KeyBindingAction.Enter || shouldSubmit;
this.setState({
phase: Phases.Display,
}, () => {

View file

@ -22,7 +22,6 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { _t } from '../../../languageHandler';
import AccessibleTooltipButton from "./AccessibleTooltipButton";
import { Key } from "../../../Keyboard";
import MemberAvatar from "../avatars/MemberAvatar";
import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton";
import MessageContextMenu from "../context_menus/MessageContextMenu";
@ -38,6 +37,8 @@ import { normalizeWheelEvent } from "../../../utils/Mouse";
import { IDialogProps } from '../dialogs/IDialogProps';
import UIStore from '../../../stores/UIStore';
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
// Max scale to keep gaps around the image
const MAX_SCALE = 0.95;
@ -292,10 +293,13 @@ export default class ImageView extends React.Component<IProps, IState> {
};
private onKeyDown = (ev: KeyboardEvent) => {
if (ev.key === Key.ESCAPE) {
ev.stopPropagation();
ev.preventDefault();
this.props.onFinished();
const action = getKeyBindingsManager().getAccessibilityAction(ev);
switch (action) {
case KeyBindingAction.Escape:
ev.stopPropagation();
ev.preventDefault();
this.props.onFinished();
break;
}
};

View file

@ -19,8 +19,9 @@ import React from 'react';
import * as Roles from '../../../Roles';
import { _t } from '../../../languageHandler';
import Field from "./Field";
import { Key } from "../../../Keyboard";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
const CUSTOM_VALUE = "SELECT_VALUE_CUSTOM";
@ -130,16 +131,19 @@ export default class PowerSelector extends React.Component<IProps, IState> {
};
private onCustomKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {
if (event.key === Key.ENTER) {
event.preventDefault();
event.stopPropagation();
const action = getKeyBindingsManager().getAccessibilityAction(event);
switch (action) {
case KeyBindingAction.Enter:
event.preventDefault();
event.stopPropagation();
// Do not call the onChange handler directly here - it can cause an infinite loop.
// Long story short, a user hits Enter to submit the value which onChange handles as
// raising a dialog which causes a blur which causes a dialog which causes a blur and
// so on. By not causing the onChange to be called here, we avoid the loop because we
// handle the onBlur safely.
(event.target as HTMLInputElement).blur();
// Do not call the onChange handler directly here - it can cause an infinite loop.
// Long story short, a user hits Enter to submit the value which onChange handles as
// raising a dialog which causes a blur which causes a dialog which causes a blur and
// so on. By not causing the onChange to be called here, we avoid the loop because we
// handle the onBlur safely.
(event.target as HTMLInputElement).blur();
break;
}
};