Keep number field in focus when pressing dialpad buttons (#6520)
parent
1a1a76e2d7
commit
1ad35b1564
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Reference in New Issue