Merge branch 't3chguy/accesibility' into release-v1.6.0
This commit is contained in:
commit
5a8943353f
13 changed files with 161 additions and 92 deletions
|
@ -75,6 +75,7 @@
|
||||||
"filesize": "3.5.6",
|
"filesize": "3.5.6",
|
||||||
"flux": "2.1.1",
|
"flux": "2.1.1",
|
||||||
"focus-trap-react": "^3.0.5",
|
"focus-trap-react": "^3.0.5",
|
||||||
|
"focus-visible": "^5.0.2",
|
||||||
"fuse.js": "^2.2.0",
|
"fuse.js": "^2.2.0",
|
||||||
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566",
|
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566",
|
||||||
"gfm.css": "^1.1.1",
|
"gfm.css": "^1.1.1",
|
||||||
|
|
|
@ -18,7 +18,7 @@ limitations under the License.
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton:focus {
|
.mx_AccessibleButton:focus:not(.focus-visible) {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import ReactDOM from 'react-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {focusCapturedRef} from "../../utils/Accessibility";
|
import {focusCapturedRef} from "../../utils/Accessibility";
|
||||||
|
import {KeyCode} from "../../Keyboard";
|
||||||
|
|
||||||
// Shamelessly ripped off Modal.js. There's probably a better way
|
// Shamelessly ripped off Modal.js. There's probably a better way
|
||||||
// of doing reusable widgets like dialog boxes & menus where we go and
|
// of doing reusable widgets like dialog boxes & menus where we go and
|
||||||
|
@ -67,7 +68,7 @@ export default class ContextualMenu extends React.Component {
|
||||||
// on resize callback
|
// on resize callback
|
||||||
windowResize: PropTypes.func,
|
windowResize: PropTypes.func,
|
||||||
// method to close menu
|
// method to close menu
|
||||||
closeMenu: PropTypes.func,
|
closeMenu: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -114,6 +115,14 @@ export default class ContextualMenu extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onKeyDown = (ev) => {
|
||||||
|
if (ev.keyCode === KeyCode.ESCAPE) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
this.props.closeMenu();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const position = {};
|
const position = {};
|
||||||
let chevronFace = null;
|
let chevronFace = null;
|
||||||
|
@ -210,7 +219,7 @@ export default class ContextualMenu extends React.Component {
|
||||||
|
|
||||||
// FIXME: If a menu uses getDefaultProps it clobbers the onFinished
|
// FIXME: If a menu uses getDefaultProps it clobbers the onFinished
|
||||||
// property set here so you can't close the menu from a button click!
|
// property set here so you can't close the menu from a button click!
|
||||||
return <div className={className} style={{...position, ...wrapperStyle}}>
|
return <div className={className} style={{...position, ...wrapperStyle}} onKeyDown={this._onKeyDown}>
|
||||||
<div className={menuClasses} style={menuStyle} ref={this.collectContextMenuRect} tabIndex={0}>
|
<div className={menuClasses} style={menuStyle} ref={this.collectContextMenuRect} tabIndex={0}>
|
||||||
{ chevron }
|
{ chevron }
|
||||||
<ElementClass {...props} onFinished={props.closeMenu} onResize={props.windowResize} />
|
<ElementClass {...props} onFinished={props.closeMenu} onResize={props.windowResize} />
|
||||||
|
|
|
@ -24,6 +24,9 @@ import createReactClass from 'create-react-class';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Matrix from "matrix-js-sdk";
|
import Matrix from "matrix-js-sdk";
|
||||||
|
|
||||||
|
// focus-visible is a Polyfill for the :focus-visible CSS pseudo-attribute used by _AccessibleButton.scss
|
||||||
|
import 'focus-visible';
|
||||||
|
|
||||||
import Analytics from "../../Analytics";
|
import Analytics from "../../Analytics";
|
||||||
import { DecryptionFailureTracker } from "../../DecryptionFailureTracker";
|
import { DecryptionFailureTracker } from "../../DecryptionFailureTracker";
|
||||||
import MatrixClientPeg from "../../MatrixClientPeg";
|
import MatrixClientPeg from "../../MatrixClientPeg";
|
||||||
|
@ -41,7 +44,7 @@ import * as Rooms from '../../Rooms';
|
||||||
import linkifyMatrix from "../../linkify-matrix";
|
import linkifyMatrix from "../../linkify-matrix";
|
||||||
import * as Lifecycle from '../../Lifecycle';
|
import * as Lifecycle from '../../Lifecycle';
|
||||||
// LifecycleStore is not used but does listen to and dispatch actions
|
// LifecycleStore is not used but does listen to and dispatch actions
|
||||||
require('../../stores/LifecycleStore');
|
import '../../stores/LifecycleStore';
|
||||||
import PageTypes from '../../PageTypes';
|
import PageTypes from '../../PageTypes';
|
||||||
import { getHomePageUrl } from '../../utils/pages';
|
import { getHomePageUrl } from '../../utils/pages';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 Travis Ralston
|
Copyright 2017 Travis Ralston
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -18,6 +19,7 @@ limitations under the License.
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {_t} from '../../languageHandler';
|
import {_t} from '../../languageHandler';
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
import sdk from "../../index";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a tab for the TabbedView.
|
* Represents a tab for the TabbedView.
|
||||||
|
@ -70,6 +72,8 @@ export class TabbedView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderTabLabel(tab) {
|
_renderTabLabel(tab) {
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
|
||||||
let classes = "mx_TabbedView_tabLabel ";
|
let classes = "mx_TabbedView_tabLabel ";
|
||||||
|
|
||||||
const idx = this.props.tabs.indexOf(tab);
|
const idx = this.props.tabs.indexOf(tab);
|
||||||
|
@ -84,19 +88,12 @@ export class TabbedView extends React.Component {
|
||||||
|
|
||||||
const label = _t(tab.label);
|
const label = _t(tab.label);
|
||||||
return (
|
return (
|
||||||
<span
|
<AccessibleButton className={classes} key={"tab_label_" + tab.label} onClick={onClickHandler}>
|
||||||
className={classes}
|
|
||||||
key={"tab_label_" + tab.label}
|
|
||||||
onClick={onClickHandler}
|
|
||||||
role="button"
|
|
||||||
aria-label={label}
|
|
||||||
tabIndex={0}
|
|
||||||
>
|
|
||||||
{tabIcon}
|
{tabIcon}
|
||||||
<span className="mx_TabbedView_tabLabel_text">
|
<span className="mx_TabbedView_tabLabel_text">
|
||||||
{ label }
|
{ label }
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,10 +109,11 @@ export default class TopLeftMenuButton extends React.Component {
|
||||||
return (
|
return (
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
className="mx_TopLeftMenuButton"
|
className="mx_TopLeftMenuButton"
|
||||||
role="button"
|
|
||||||
onClick={this.onToggleMenu}
|
onClick={this.onToggleMenu}
|
||||||
inputRef={(r) => this._buttonRef = r}
|
inputRef={(r) => this._buttonRef = r}
|
||||||
aria-label={_t("Your profile")}
|
aria-label={_t("Your profile")}
|
||||||
|
aria-haspopup={true}
|
||||||
|
aria-expanded={this.state.menuDisplayed}
|
||||||
>
|
>
|
||||||
<BaseAvatar
|
<BaseAvatar
|
||||||
idName={MatrixClientPeg.get().getUserId()}
|
idName={MatrixClientPeg.get().getUserId()}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2018 Vector Creations Ltd
|
Copyright 2018 Vector Creations Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -77,11 +78,12 @@ export default class GroupInviteTileContextMenu extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
return <div>
|
return <div>
|
||||||
<div className="mx_RoomTileContextMenu_leave" onClick={this._onClickReject} >
|
<AccessibleButton className="mx_RoomTileContextMenu_leave" onClick={this._onClickReject} >
|
||||||
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_delete.svg")} width="15" height="15" />
|
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_delete.svg")} width="15" height="15" />
|
||||||
{ _t('Reject') }
|
{ _t('Reject') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -288,6 +289,8 @@ module.exports = createReactClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const me = cli.getUserId();
|
const me = cli.getUserId();
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent;
|
||||||
|
@ -319,89 +322,89 @@ module.exports = createReactClass({
|
||||||
if (!mxEvent.isRedacted()) {
|
if (!mxEvent.isRedacted()) {
|
||||||
if (eventStatus === EventStatus.NOT_SENT) {
|
if (eventStatus === EventStatus.NOT_SENT) {
|
||||||
resendButton = (
|
resendButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onResendClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onResendClick}>
|
||||||
{ _t('Resend') }
|
{ _t('Resend') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editStatus === EventStatus.NOT_SENT) {
|
if (editStatus === EventStatus.NOT_SENT) {
|
||||||
resendEditButton = (
|
resendEditButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onResendEditClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onResendEditClick}>
|
||||||
{ _t('Resend edit') }
|
{ _t('Resend edit') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unsentReactionsCount !== 0) {
|
if (unsentReactionsCount !== 0) {
|
||||||
resendReactionsButton = (
|
resendReactionsButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onResendReactionsClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onResendReactionsClick}>
|
||||||
{ _t('Resend %(unsentCount)s reaction(s)', {unsentCount: unsentReactionsCount}) }
|
{ _t('Resend %(unsentCount)s reaction(s)', {unsentCount: unsentReactionsCount}) }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redactStatus === EventStatus.NOT_SENT) {
|
if (redactStatus === EventStatus.NOT_SENT) {
|
||||||
resendRedactionButton = (
|
resendRedactionButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onResendRedactionClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onResendRedactionClick}>
|
||||||
{ _t('Resend removal') }
|
{ _t('Resend removal') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSent && this.state.canRedact) {
|
if (isSent && this.state.canRedact) {
|
||||||
redactButton = (
|
redactButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onRedactClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onRedactClick}>
|
||||||
{ _t('Remove') }
|
{ _t('Remove') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowCancel) {
|
if (allowCancel) {
|
||||||
cancelButton = (
|
cancelButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onCancelSendClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onCancelSendClick}>
|
||||||
{ _t('Cancel Sending') }
|
{ _t('Cancel Sending') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isContentActionable(mxEvent)) {
|
if (isContentActionable(mxEvent)) {
|
||||||
forwardButton = (
|
forwardButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onForwardClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onForwardClick}>
|
||||||
{ _t('Forward Message') }
|
{ _t('Forward Message') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.state.canPin) {
|
if (this.state.canPin) {
|
||||||
pinButton = (
|
pinButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onPinClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onPinClick}>
|
||||||
{ this._isPinned() ? _t('Unpin Message') : _t('Pin Message') }
|
{ this._isPinned() ? _t('Unpin Message') : _t('Pin Message') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewSourceButton = (
|
const viewSourceButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onViewSourceClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onViewSourceClick}>
|
||||||
{ _t('View Source') }
|
{ _t('View Source') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (mxEvent.getType() !== mxEvent.getWireType()) {
|
if (mxEvent.getType() !== mxEvent.getWireType()) {
|
||||||
viewClearSourceButton = (
|
viewClearSourceButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onViewClearSourceClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onViewClearSourceClick}>
|
||||||
{ _t('View Decrypted Source') }
|
{ _t('View Decrypted Source') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.eventTileOps) {
|
if (this.props.eventTileOps) {
|
||||||
if (this.props.eventTileOps.isWidgetHidden()) {
|
if (this.props.eventTileOps.isWidgetHidden()) {
|
||||||
unhidePreviewButton = (
|
unhidePreviewButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onUnhidePreviewClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onUnhidePreviewClick}>
|
||||||
{ _t('Unhide Preview') }
|
{ _t('Unhide Preview') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,20 +415,19 @@ module.exports = createReactClass({
|
||||||
}
|
}
|
||||||
// XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID)
|
// XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID)
|
||||||
const permalinkButton = (
|
const permalinkButton = (
|
||||||
<div className="mx_MessageContextMenu_field">
|
<AccessibleButton className="mx_MessageContextMenu_field">
|
||||||
<a href={permalink}
|
<a href={permalink} target="_blank" rel="noopener" onClick={this.onPermalinkClick} tabIndex={-1}>
|
||||||
target="_blank" rel="noopener" onClick={this.onPermalinkClick}>
|
|
||||||
{ mxEvent.isRedacted() || mxEvent.getType() !== 'm.room.message'
|
{ mxEvent.isRedacted() || mxEvent.getType() !== 'm.room.message'
|
||||||
? _t('Share Permalink') : _t('Share Message') }
|
? _t('Share Permalink') : _t('Share Message') }
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.props.eventTileOps && this.props.eventTileOps.getInnerText) {
|
if (this.props.eventTileOps && this.props.eventTileOps.getInnerText) {
|
||||||
quoteButton = (
|
quoteButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onQuoteClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onQuoteClick}>
|
||||||
{ _t('Quote') }
|
{ _t('Quote') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,34 +437,43 @@ module.exports = createReactClass({
|
||||||
isUrlPermitted(mxEvent.event.content.external_url)
|
isUrlPermitted(mxEvent.event.content.external_url)
|
||||||
) {
|
) {
|
||||||
externalURLButton = (
|
externalURLButton = (
|
||||||
<div className="mx_MessageContextMenu_field">
|
<AccessibleButton className="mx_MessageContextMenu_field">
|
||||||
<a href={mxEvent.event.content.external_url}
|
<a
|
||||||
rel="noopener" target="_blank" onClick={this.closeMenu}>{ _t('Source URL') }</a>
|
href={mxEvent.event.content.external_url}
|
||||||
</div>
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
onClick={this.closeMenu}
|
||||||
|
tabIndex={-1}
|
||||||
|
>
|
||||||
|
{ _t('Source URL') }
|
||||||
|
</a>
|
||||||
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.collapseReplyThread) {
|
if (this.props.collapseReplyThread) {
|
||||||
collapseReplyThread = (
|
collapseReplyThread = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onCollapseReplyThreadClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onCollapseReplyThreadClick}>
|
||||||
{ _t('Collapse Reply Thread') }
|
{ _t('Collapse Reply Thread') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let e2eInfo;
|
let e2eInfo;
|
||||||
if (this.props.e2eInfoCallback) {
|
if (this.props.e2eInfoCallback) {
|
||||||
e2eInfo = <div className="mx_MessageContextMenu_field" onClick={this.e2eInfoClicked}>
|
e2eInfo = (
|
||||||
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.e2eInfoClicked}>
|
||||||
{ _t('End-to-end encryption information') }
|
{ _t('End-to-end encryption information') }
|
||||||
</div>;
|
</AccessibleButton>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let reportEventButton;
|
let reportEventButton;
|
||||||
if (mxEvent.getSender() !== me) {
|
if (mxEvent.getSender() !== me) {
|
||||||
reportEventButton = (
|
reportEventButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onReportEventClick}>
|
<AccessibleButton className="mx_MessageContextMenu_field" onClick={this.onReportEventClick}>
|
||||||
{ _t('Report Content') }
|
{ _t('Report Content') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -227,6 +228,8 @@ module.exports = createReactClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderNotifMenu: function() {
|
_renderNotifMenu: function() {
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
|
||||||
const alertMeClasses = classNames({
|
const alertMeClasses = classNames({
|
||||||
'mx_RoomTileContextMenu_notif_field': true,
|
'mx_RoomTileContextMenu_notif_field': true,
|
||||||
'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES_LOUD,
|
'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES_LOUD,
|
||||||
|
@ -249,29 +252,29 @@ module.exports = createReactClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomTileContextMenu">
|
<div className="mx_RoomTileContextMenu">
|
||||||
<div className="mx_RoomTileContextMenu_notif_picker" >
|
<div className="mx_RoomTileContextMenu_notif_picker">
|
||||||
<img src={require("../../../../res/img/notif-slider.svg")} width="20" height="107" />
|
<img src={require("../../../../res/img/notif-slider.svg")} width="20" height="107" />
|
||||||
</div>
|
</div>
|
||||||
<div className={alertMeClasses} onClick={this._onClickAlertMe} >
|
<AccessibleButton className={alertMeClasses} onClick={this._onClickAlertMe}>
|
||||||
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
|
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
|
||||||
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute-off-copy.svg")} width="16" height="12" />
|
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute-off-copy.svg")} width="16" height="12" />
|
||||||
{ _t('All messages (noisy)') }
|
{ _t('All messages (noisy)') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
<div className={allNotifsClasses} onClick={this._onClickAllNotifs} >
|
<AccessibleButton className={allNotifsClasses} onClick={this._onClickAllNotifs}>
|
||||||
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
|
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
|
||||||
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute-off.svg")} width="16" height="12" />
|
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute-off.svg")} width="16" height="12" />
|
||||||
{ _t('All messages') }
|
{ _t('All messages') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
<div className={mentionsClasses} onClick={this._onClickMentions} >
|
<AccessibleButton className={mentionsClasses} onClick={this._onClickMentions}>
|
||||||
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
|
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
|
||||||
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute-mentions.svg")} width="16" height="12" />
|
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute-mentions.svg")} width="16" height="12" />
|
||||||
{ _t('Mentions only') }
|
{ _t('Mentions only') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
<div className={muteNotifsClasses} onClick={this._onClickMute} >
|
<AccessibleButton className={muteNotifsClasses} onClick={this._onClickMute}>
|
||||||
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
|
<img className="mx_RoomTileContextMenu_notif_activeIcon" src={require("../../../../res/img/notif-active.svg")} width="12" height="12" />
|
||||||
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute.svg")} width="16" height="12" />
|
<img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src={require("../../../../res/img/icon-context-mute.svg")} width="16" height="12" />
|
||||||
{ _t('Mute') }
|
{ _t('Mute') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -287,12 +290,13 @@ module.exports = createReactClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderSettingsMenu: function() {
|
_renderSettingsMenu: function() {
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="mx_RoomTileContextMenu_tag_field" onClick={this._onClickSettings} >
|
<AccessibleButton className="mx_RoomTileContextMenu_tag_field" onClick={this._onClickSettings} >
|
||||||
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icons-settings-room.svg")} width="15" height="15" />
|
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icons-settings-room.svg")} width="15" height="15" />
|
||||||
{ _t('Settings') }
|
{ _t('Settings') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -302,6 +306,8 @@ module.exports = createReactClass({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
|
||||||
let leaveClickHandler = null;
|
let leaveClickHandler = null;
|
||||||
let leaveText = null;
|
let leaveText = null;
|
||||||
|
|
||||||
|
@ -323,15 +329,17 @@ module.exports = createReactClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="mx_RoomTileContextMenu_leave" onClick={leaveClickHandler} >
|
<AccessibleButton className="mx_RoomTileContextMenu_leave" onClick={leaveClickHandler} >
|
||||||
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_delete.svg")} width="15" height="15" />
|
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_delete.svg")} width="15" height="15" />
|
||||||
{ leaveText }
|
{ leaveText }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderRoomTagMenu: function() {
|
_renderRoomTagMenu: function() {
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
|
||||||
const favouriteClasses = classNames({
|
const favouriteClasses = classNames({
|
||||||
'mx_RoomTileContextMenu_tag_field': true,
|
'mx_RoomTileContextMenu_tag_field': true,
|
||||||
'mx_RoomTileContextMenu_tag_fieldSet': this.state.isFavourite,
|
'mx_RoomTileContextMenu_tag_fieldSet': this.state.isFavourite,
|
||||||
|
@ -352,21 +360,21 @@ module.exports = createReactClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={favouriteClasses} onClick={this._onClickFavourite} >
|
<AccessibleButton className={favouriteClasses} onClick={this._onClickFavourite} >
|
||||||
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_fave.svg")} width="15" height="15" />
|
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_fave.svg")} width="15" height="15" />
|
||||||
<img className="mx_RoomTileContextMenu_tag_icon_set" src={require("../../../../res/img/icon_context_fave_on.svg")} width="15" height="15" />
|
<img className="mx_RoomTileContextMenu_tag_icon_set" src={require("../../../../res/img/icon_context_fave_on.svg")} width="15" height="15" />
|
||||||
{ _t('Favourite') }
|
{ _t('Favourite') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
<div className={lowPriorityClasses} onClick={this._onClickLowPriority} >
|
<AccessibleButton className={lowPriorityClasses} onClick={this._onClickLowPriority} >
|
||||||
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_low.svg")} width="15" height="15" />
|
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_low.svg")} width="15" height="15" />
|
||||||
<img className="mx_RoomTileContextMenu_tag_icon_set" src={require("../../../../res/img/icon_context_low_on.svg")} width="15" height="15" />
|
<img className="mx_RoomTileContextMenu_tag_icon_set" src={require("../../../../res/img/icon_context_low_on.svg")} width="15" height="15" />
|
||||||
{ _t('Low Priority') }
|
{ _t('Low Priority') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
<div className={dmClasses} onClick={this._onClickDM} >
|
<AccessibleButton className={dmClasses} onClick={this._onClickDM} >
|
||||||
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_person.svg")} width="15" height="15" />
|
<img className="mx_RoomTileContextMenu_tag_icon" src={require("../../../../res/img/icon_context_person.svg")} width="15" height="15" />
|
||||||
<img className="mx_RoomTileContextMenu_tag_icon_set" src={require("../../../../res/img/icon_context_person_on.svg")} width="15" height="15" />
|
<img className="mx_RoomTileContextMenu_tag_icon_set" src={require("../../../../res/img/icon_context_person_on.svg")} width="15" height="15" />
|
||||||
{ _t('Direct Chat') }
|
{ _t('Direct Chat') }
|
||||||
</div>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -24,6 +24,7 @@ import Modal from "../../../Modal";
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import { getHostingLink } from '../../../utils/HostingLink';
|
import { getHostingLink } from '../../../utils/HostingLink';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
|
import sdk from "../../../index";
|
||||||
|
|
||||||
export class TopLeftMenu extends React.Component {
|
export class TopLeftMenu extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -57,6 +58,8 @@ export class TopLeftMenu extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
|
||||||
const isGuest = MatrixClientPeg.get().isGuest();
|
const isGuest = MatrixClientPeg.get().isGuest();
|
||||||
|
|
||||||
const hostingSignupLink = getHostingLink('user-context-menu');
|
const hostingSignupLink = getHostingLink('user-context-menu');
|
||||||
|
@ -77,25 +80,33 @@ export class TopLeftMenu extends React.Component {
|
||||||
|
|
||||||
let homePageItem = null;
|
let homePageItem = null;
|
||||||
if (this.hasHomePage()) {
|
if (this.hasHomePage()) {
|
||||||
homePageItem = <li className="mx_TopLeftMenu_icon_home" onClick={this.viewHomePage} tabIndex={0}>
|
homePageItem = (
|
||||||
{_t("Home")}
|
<AccessibleButton element="li" className="mx_TopLeftMenu_icon_home" onClick={this.viewHomePage}>
|
||||||
</li>;
|
{_t("Home")}
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let signInOutItem;
|
let signInOutItem;
|
||||||
if (isGuest) {
|
if (isGuest) {
|
||||||
signInOutItem = <li className="mx_TopLeftMenu_icon_signin" onClick={this.signIn} tabIndex={0}>
|
signInOutItem = (
|
||||||
{_t("Sign in")}
|
<AccessibleButton element="li" className="mx_TopLeftMenu_icon_signin" onClick={this.signIn}>
|
||||||
</li>;
|
{_t("Sign in")}
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
signInOutItem = <li className="mx_TopLeftMenu_icon_signout" onClick={this.signOut} tabIndex={0}>
|
signInOutItem = (
|
||||||
{_t("Sign out")}
|
<AccessibleButton element="li" className="mx_TopLeftMenu_icon_signout" onClick={this.signOut}>
|
||||||
</li>;
|
{_t("Sign out")}
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsItem = <li className="mx_TopLeftMenu_icon_settings" onClick={this.openSettings} tabIndex={0}>
|
const settingsItem = (
|
||||||
{_t("Settings")}
|
<AccessibleButton element="li" className="mx_TopLeftMenu_icon_settings" onClick={this.openSettings}>
|
||||||
</li>;
|
{_t("Settings")}
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
|
|
||||||
return <div className="mx_TopLeftMenu mx_HiddenFocusable" tabIndex={0} ref={this.props.containerRef}>
|
return <div className="mx_TopLeftMenu mx_HiddenFocusable" tabIndex={0} ref={this.props.containerRef}>
|
||||||
<div className="mx_TopLeftMenu_section_noIcon" aria-readonly={true}>
|
<div className="mx_TopLeftMenu_section_noIcon" aria-readonly={true}>
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import {KeyCode} from "../../../Keyboard";
|
||||||
|
|
||||||
export default class ToggleSwitch extends React.Component {
|
export default class ToggleSwitch extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -44,10 +45,7 @@ export default class ToggleSwitch extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onClick = (e) => {
|
_toggle = () => {
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (this.props.disabled) return;
|
if (this.props.disabled) return;
|
||||||
|
|
||||||
const newState = !this.state.checked;
|
const newState = !this.state.checked;
|
||||||
|
@ -57,6 +55,22 @@ export default class ToggleSwitch extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_onClick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this._toggle();
|
||||||
|
};
|
||||||
|
|
||||||
|
_onKeyDown = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (e.keyCode === KeyCode.ENTER || e.keyCode === KeyCode.SPACE) {
|
||||||
|
this._toggle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
const {checked, disabled, onChange, ...props} = this.props;
|
const {checked, disabled, onChange, ...props} = this.props;
|
||||||
|
@ -71,6 +85,7 @@ export default class ToggleSwitch extends React.Component {
|
||||||
{...props}
|
{...props}
|
||||||
className={classes}
|
className={classes}
|
||||||
onClick={this._onClick}
|
onClick={this._onClick}
|
||||||
|
onKeyDown={this._onKeyDown}
|
||||||
role="checkbox"
|
role="checkbox"
|
||||||
aria-checked={this.state.checked}
|
aria-checked={this.state.checked}
|
||||||
aria-disabled={disabled}
|
aria-disabled={disabled}
|
||||||
|
|
|
@ -140,6 +140,8 @@ export default class MessageActionBar extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
|
||||||
let reactButton;
|
let reactButton;
|
||||||
let replyButton;
|
let replyButton;
|
||||||
let editButton;
|
let editButton;
|
||||||
|
@ -149,14 +151,16 @@ export default class MessageActionBar extends React.PureComponent {
|
||||||
reactButton = this.renderReactButton();
|
reactButton = this.renderReactButton();
|
||||||
}
|
}
|
||||||
if (this.context.room.canReply) {
|
if (this.context.room.canReply) {
|
||||||
replyButton = <span className="mx_MessageActionBar_maskButton mx_MessageActionBar_replyButton"
|
replyButton = <AccessibleButton
|
||||||
|
className="mx_MessageActionBar_maskButton mx_MessageActionBar_replyButton"
|
||||||
title={_t("Reply")}
|
title={_t("Reply")}
|
||||||
onClick={this.onReplyClick}
|
onClick={this.onReplyClick}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (canEditContent(this.props.mxEvent)) {
|
if (canEditContent(this.props.mxEvent)) {
|
||||||
editButton = <span className="mx_MessageActionBar_maskButton mx_MessageActionBar_editButton"
|
editButton = <AccessibleButton
|
||||||
|
className="mx_MessageActionBar_maskButton mx_MessageActionBar_editButton"
|
||||||
title={_t("Edit")}
|
title={_t("Edit")}
|
||||||
onClick={this.onEditClick}
|
onClick={this.onEditClick}
|
||||||
/>;
|
/>;
|
||||||
|
@ -166,9 +170,11 @@ export default class MessageActionBar extends React.PureComponent {
|
||||||
{reactButton}
|
{reactButton}
|
||||||
{replyButton}
|
{replyButton}
|
||||||
{editButton}
|
{editButton}
|
||||||
<span className="mx_MessageActionBar_maskButton mx_MessageActionBar_optionsButton"
|
<AccessibleButton
|
||||||
|
className="mx_MessageActionBar_maskButton mx_MessageActionBar_optionsButton"
|
||||||
title={_t("Options")}
|
title={_t("Options")}
|
||||||
onClick={this.onOptionsClick}
|
onClick={this.onOptionsClick}
|
||||||
|
aria-haspopup={true}
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3367,6 +3367,11 @@ focus-trap@^2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
tabbable "^1.0.3"
|
tabbable "^1.0.3"
|
||||||
|
|
||||||
|
focus-visible@^5.0.2:
|
||||||
|
version "5.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/focus-visible/-/focus-visible-5.0.2.tgz#4fae9cf40458b73c10701c9774c462e3ccd53caf"
|
||||||
|
integrity sha512-zT2fj/bmOgEBjqGbURGlowTmCwsIs3bRDMr/sFZz8Ly7VkEiwuCn9swNTL3pPuf8Oua2de7CLuKdnuNajWdDsQ==
|
||||||
|
|
||||||
follow-redirects@^1.0.0:
|
follow-redirects@^1.0.0:
|
||||||
version "1.7.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76"
|
||||||
|
|
Loading…
Reference in a new issue