Support CTRL+I for opening TopLeftMenu

This commit is contained in:
Travis Ralston 2019-05-17 15:28:12 -06:00
parent f1aa2875e1
commit c5757d8303
4 changed files with 45 additions and 5 deletions

View file

@ -322,6 +322,14 @@ const LoggedInView = React.createClass({
handled = true; handled = true;
} }
break; break;
case KeyCode.KEY_I:
if (ctrlCmdOnly) {
dis.dispatch({
action: 'toggle_top_left_menu',
});
handled = true;
}
break;
} }
if (handled) { if (handled) {

View file

@ -23,6 +23,7 @@ import BaseAvatar from '../views/avatars/BaseAvatar';
import MatrixClientPeg from '../../MatrixClientPeg'; import MatrixClientPeg from '../../MatrixClientPeg';
import Avatar from '../../Avatar'; import Avatar from '../../Avatar';
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
import dis from "../../dispatcher";
const AVATAR_SIZE = 28; const AVATAR_SIZE = 28;
@ -37,6 +38,7 @@ export default class TopLeftMenuButton extends React.Component {
super(); super();
this.state = { this.state = {
menuDisplayed: false, menuDisplayed: false,
menuFunctions: null, // should be { close: fn }
profileInfo: null, profileInfo: null,
}; };
@ -59,6 +61,8 @@ export default class TopLeftMenuButton extends React.Component {
} }
async componentDidMount() { async componentDidMount() {
this._dispatcherRef = dis.register(this.onAction);
try { try {
const profileInfo = await this._getProfileInfo(); const profileInfo = await this._getProfileInfo();
this.setState({profileInfo}); this.setState({profileInfo});
@ -68,6 +72,17 @@ export default class TopLeftMenuButton extends React.Component {
} }
} }
componentWillUnmount() {
dis.unregister(this._dispatcherRef);
}
onAction = (payload) => {
// For accessibility
if (payload.action === "toggle_top_left_menu") {
if (this._buttonRef) this._buttonRef.click();
}
};
_getDisplayName() { _getDisplayName() {
if (MatrixClientPeg.get().isGuest()) { if (MatrixClientPeg.get().isGuest()) {
return _t("Guest"); return _t("Guest");
@ -88,7 +103,13 @@ export default class TopLeftMenuButton extends React.Component {
} }
return ( return (
<AccessibleButton className="mx_TopLeftMenuButton" onClick={this.onToggleMenu}> <AccessibleButton
className="mx_TopLeftMenuButton"
role="button"
onClick={this.onToggleMenu}
inputRef={(r) => this._buttonRef = r}
aria-label={_t("Your profile")}
>
<BaseAvatar <BaseAvatar
idName={MatrixClientPeg.get().getUserId()} idName={MatrixClientPeg.get().getUserId()}
name={name} name={name}
@ -98,7 +119,7 @@ export default class TopLeftMenuButton extends React.Component {
resizeMethod="crop" resizeMethod="crop"
/> />
{ nameElement } { nameElement }
<span className="mx_TopLeftMenuButton_chevron"></span> <span className="mx_TopLeftMenuButton_chevron" />
</AccessibleButton> </AccessibleButton>
); );
} }
@ -107,20 +128,25 @@ export default class TopLeftMenuButton extends React.Component {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
if (this.state.menuDisplayed && this.state.menuFunctions) {
this.state.menuFunctions.close();
return;
}
const elementRect = e.currentTarget.getBoundingClientRect(); const elementRect = e.currentTarget.getBoundingClientRect();
const x = elementRect.left; const x = elementRect.left;
const y = elementRect.top + elementRect.height; const y = elementRect.top + elementRect.height;
ContextualMenu.createMenu(TopLeftMenu, { const menuFunctions = ContextualMenu.createMenu(TopLeftMenu, {
chevronFace: "none", chevronFace: "none",
left: x, left: x,
top: y, top: y,
userId: MatrixClientPeg.get().getUserId(), userId: MatrixClientPeg.get().getUserId(),
displayName: this._getDisplayName(), displayName: this._getDisplayName(),
onFinished: () => { onFinished: () => {
this.setState({ menuDisplayed: false }); this.setState({ menuDisplayed: false, menuFunctions: null });
}, },
}); });
this.setState({ menuDisplayed: true }); this.setState({ menuDisplayed: true, menuFunctions });
} }
} }

View file

@ -63,6 +63,10 @@ export default function AccessibleButton(props) {
}; };
} }
// Pass through the ref - used for keyboard shortcut access to some buttons
restProps.ref = restProps.inputRef;
delete restProps.inputRef;
restProps.tabIndex = restProps.tabIndex || "0"; restProps.tabIndex = restProps.tabIndex || "0";
restProps.role = "button"; restProps.role = "button";
restProps.className = (restProps.className ? restProps.className + " " : "") + restProps.className = (restProps.className ? restProps.className + " " : "") +
@ -89,6 +93,7 @@ export default function AccessibleButton(props) {
*/ */
AccessibleButton.propTypes = { AccessibleButton.propTypes = {
children: PropTypes.node, children: PropTypes.node,
inputRef: PropTypes.func,
element: PropTypes.string, element: PropTypes.string,
onClick: PropTypes.func.isRequired, onClick: PropTypes.func.isRequired,

View file

@ -1493,6 +1493,7 @@
"Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.", "Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.",
"Failed to load timeline position": "Failed to load timeline position", "Failed to load timeline position": "Failed to load timeline position",
"Guest": "Guest", "Guest": "Guest",
"Your profile": "Your profile",
"Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others", "Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others",
"Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s", "Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s",
"Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other", "Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other",