Keep number field in focus when pressing dialpad buttons (#6520)

pull/21833/head
Andrew Morgan 2021-08-12 18:58:06 +01:00 committed by GitHub
parent 1a1a76e2d7
commit 1ad35b1564
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 65 additions and 18 deletions

View File

@ -14,8 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import * as React from "react";
import AccessibleButton from "../elements/AccessibleButton"; import { createRef } from "react";
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import { ContextMenu, IProps as IContextMenuProps } from '../../structures/ContextMenu'; import { ContextMenu, IProps as IContextMenuProps } from '../../structures/ContextMenu';
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call'; import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
import Field from "../elements/Field"; import Field from "../elements/Field";
@ -32,6 +33,8 @@ interface IState {
@replaceableComponent("views.context_menus.DialpadContextMenu") @replaceableComponent("views.context_menus.DialpadContextMenu")
export default class DialpadContextMenu extends React.Component<IProps, IState> { export default class DialpadContextMenu extends React.Component<IProps, IState> {
private numberEntryFieldRef: React.RefObject<Field> = createRef();
constructor(props) { constructor(props) {
super(props); super(props);
@ -40,9 +43,16 @@ export default class DialpadContextMenu extends React.Component<IProps, IState>
}; };
} }
onDigitPress = (digit) => { onDigitPress = (digit: string, ev: ButtonEvent) => {
this.props.call.sendDtmfDigit(digit); this.props.call.sendDtmfDigit(digit);
this.setState({ value: this.state.value + digit }); this.setState({ value: this.state.value + digit });
// Keep the number field focused so that keyboard entry is still available
// However, don't focus if this wasn't the result of directly clicking on the button,
// i.e someone using keyboard navigation.
if (ev.type === "click") {
this.numberEntryFieldRef.current?.focus();
}
}; };
onCancelClick = () => { onCancelClick = () => {
@ -68,6 +78,7 @@ export default class DialpadContextMenu extends React.Component<IProps, IState>
</div> </div>
<div className="mx_DialPadContextMenu_header"> <div className="mx_DialPadContextMenu_header">
<Field <Field
ref={this.numberEntryFieldRef}
className="mx_DialPadContextMenu_dialled" className="mx_DialPadContextMenu_dialled"
value={this.state.value} value={this.state.value}
autoFocus={true} autoFocus={true}

View File

@ -55,7 +55,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
import { mediaFromMxc } from "../../../customisations/Media"; import { mediaFromMxc } from "../../../customisations/Media";
import { getAddressType } from "../../../UserAddress"; import { getAddressType } from "../../../UserAddress";
import BaseAvatar from '../avatars/BaseAvatar'; import BaseAvatar from '../avatars/BaseAvatar';
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton';
import { compare } from '../../../utils/strings'; import { compare } from '../../../utils/strings';
import { IInvite3PID } from "matrix-js-sdk/src/@types/requests"; import { IInvite3PID } from "matrix-js-sdk/src/@types/requests";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
@ -394,6 +394,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
private closeCopiedTooltip: () => void; private closeCopiedTooltip: () => void;
private debounceTimer: number = null; // actually number because we're in the browser private debounceTimer: number = null; // actually number because we're in the browser
private editorRef = createRef<HTMLInputElement>(); private editorRef = createRef<HTMLInputElement>();
private numberEntryFieldRef: React.RefObject<Field> = createRef();
private unmounted = false; private unmounted = false;
constructor(props) { constructor(props) {
@ -1283,13 +1284,27 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
this.setState({ dialPadValue: ev.currentTarget.value }); this.setState({ dialPadValue: ev.currentTarget.value });
}; };
private onDigitPress = digit => { private onDigitPress = (digit: string, ev: ButtonEvent) => {
this.setState({ dialPadValue: this.state.dialPadValue + digit }); this.setState({ dialPadValue: this.state.dialPadValue + digit });
// Keep the number field focused so that keyboard entry is still available
// However, don't focus if this wasn't the result of directly clicking on the button,
// i.e someone using keyboard navigation.
if (ev.type === "click") {
this.numberEntryFieldRef.current?.focus();
}
}; };
private onDeletePress = () => { private onDeletePress = (ev: ButtonEvent) => {
if (this.state.dialPadValue.length === 0) return; if (this.state.dialPadValue.length === 0) return;
this.setState({ dialPadValue: this.state.dialPadValue.slice(0, -1) }); this.setState({ dialPadValue: this.state.dialPadValue.slice(0, -1) });
// Keep the number field focused so that keyboard entry is still available
// However, don't focus if this wasn't the result of directly clicking on the button,
// i.e someone using keyboard navigation.
if (ev.type === "click") {
this.numberEntryFieldRef.current?.focus();
}
}; };
private onTabChange = (tabId: TabId) => { private onTabChange = (tabId: TabId) => {
@ -1543,6 +1558,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
let dialPadField; let dialPadField;
if (this.state.dialPadValue.length !== 0) { if (this.state.dialPadValue.length !== 0) {
dialPadField = <Field dialPadField = <Field
ref={this.numberEntryFieldRef}
className="mx_InviteDialog_dialPadField" className="mx_InviteDialog_dialPadField"
id="dialpad_number" id="dialpad_number"
value={this.state.dialPadValue} value={this.state.dialPadValue}
@ -1552,6 +1568,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
/>; />;
} else { } else {
dialPadField = <Field dialPadField = <Field
ref={this.numberEntryFieldRef}
className="mx_InviteDialog_dialPadField" className="mx_InviteDialog_dialPadField"
id="dialpad_number" id="dialpad_number"
value={this.state.dialPadValue} value={this.state.dialPadValue}

View File

@ -15,11 +15,11 @@ limitations under the License.
*/ */
import * as React from "react"; import * as React from "react";
import AccessibleButton from "./AccessibleButton"; import AccessibleButton, { ButtonEvent } from "./AccessibleButton";
interface IProps { interface IProps {
// Callback for when the button is pressed // Callback for when the button is pressed
onBackspacePress: () => void; onBackspacePress: (ev: ButtonEvent) => void;
} }
export default class DialPadBackspaceButton extends React.PureComponent<IProps> { export default class DialPadBackspaceButton extends React.PureComponent<IProps> {

View File

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import * as React from "react"; import * as React from "react";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
const BUTTONS = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '0', '#']; const BUTTONS = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '0', '#'];
@ -30,12 +30,12 @@ interface IButtonProps {
kind: DialPadButtonKind; kind: DialPadButtonKind;
digit?: string; digit?: string;
digitSubtext?: string; digitSubtext?: string;
onButtonPress: (string) => void; onButtonPress: (digit: string, ev: ButtonEvent) => void;
} }
class DialPadButton extends React.PureComponent<IButtonProps> { class DialPadButton extends React.PureComponent<IButtonProps> {
onClick = () => { onClick = (ev: ButtonEvent) => {
this.props.onButtonPress(this.props.digit); this.props.onButtonPress(this.props.digit, ev);
}; };
render() { render() {
@ -54,10 +54,10 @@ class DialPadButton extends React.PureComponent<IButtonProps> {
} }
interface IProps { interface IProps {
onDigitPress: (string) => void; onDigitPress: (digit: string, ev: ButtonEvent) => void;
hasDial: boolean; hasDial: boolean;
onDeletePress?: (string) => void; onDeletePress?: (ev: ButtonEvent) => void;
onDialPress?: (string) => void; onDialPress?: () => void;
} }
@replaceableComponent("views.voip.DialPad") @replaceableComponent("views.voip.DialPad")

View File

@ -15,7 +15,8 @@ limitations under the License.
*/ */
import * as React from "react"; import * as React from "react";
import AccessibleButton from "../elements/AccessibleButton"; import { createRef } from "react";
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import Field from "../elements/Field"; import Field from "../elements/Field";
import DialPad from './DialPad'; import DialPad from './DialPad';
import dis from '../../../dispatcher/dispatcher'; import dis from '../../../dispatcher/dispatcher';
@ -34,6 +35,8 @@ interface IState {
@replaceableComponent("views.voip.DialPadModal") @replaceableComponent("views.voip.DialPadModal")
export default class DialpadModal extends React.PureComponent<IProps, IState> { export default class DialpadModal extends React.PureComponent<IProps, IState> {
private numberEntryFieldRef: React.RefObject<Field> = createRef();
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -54,13 +57,27 @@ export default class DialpadModal extends React.PureComponent<IProps, IState> {
this.onDialPress(); this.onDialPress();
}; };
onDigitPress = (digit) => { onDigitPress = (digit: string, ev: ButtonEvent) => {
this.setState({ value: this.state.value + digit }); this.setState({ value: this.state.value + digit });
// Keep the number field focused so that keyboard entry is still available.
// However, don't focus if this wasn't the result of directly clicking on the button,
// i.e someone using keyboard navigation.
if (ev.type === "click") {
this.numberEntryFieldRef.current?.focus();
}
}; };
onDeletePress = () => { onDeletePress = (ev: ButtonEvent) => {
if (this.state.value.length === 0) return; if (this.state.value.length === 0) return;
this.setState({ value: this.state.value.slice(0, -1) }); this.setState({ value: this.state.value.slice(0, -1) });
// Keep the number field focused so that keyboard entry is still available
// However, don't focus if this wasn't the result of directly clicking on the button,
// i.e someone using keyboard navigation.
if (ev.type === "click") {
this.numberEntryFieldRef.current?.focus();
}
}; };
onDialPress = async () => { onDialPress = async () => {
@ -82,6 +99,7 @@ export default class DialpadModal extends React.PureComponent<IProps, IState> {
let dialPadField; let dialPadField;
if (this.state.value.length !== 0) { if (this.state.value.length !== 0) {
dialPadField = <Field dialPadField = <Field
ref={this.numberEntryFieldRef}
className="mx_DialPadModal_field" className="mx_DialPadModal_field"
id="dialpad_number" id="dialpad_number"
value={this.state.value} value={this.state.value}
@ -91,6 +109,7 @@ export default class DialpadModal extends React.PureComponent<IProps, IState> {
/>; />;
} else { } else {
dialPadField = <Field dialPadField = <Field
ref={this.numberEntryFieldRef}
className="mx_DialPadModal_field" className="mx_DialPadModal_field"
id="dialpad_number" id="dialpad_number"
value={this.state.value} value={this.state.value}