Merge pull request #3556 from matrix-org/t3chguy/a11y4
Use Navigation Treeview pattern for RoomList Accessibility
This commit is contained in:
commit
e7f292794c
9 changed files with 145 additions and 93 deletions
|
@ -20,7 +20,7 @@ limitations under the License.
|
||||||
so they ideally wouldn't affect each other.
|
so they ideally wouldn't affect each other.
|
||||||
lowest category: .mx_RoomSubList
|
lowest category: .mx_RoomSubList
|
||||||
flex-shrink: 10000000
|
flex-shrink: 10000000
|
||||||
distribute size of items within the same categery by their size
|
distribute size of items within the same category by their size
|
||||||
middle category: .mx_RoomSubList.resized-sized
|
middle category: .mx_RoomSubList.resized-sized
|
||||||
flex-shrink: 1000
|
flex-shrink: 1000
|
||||||
applied when using the resizer, will have a max-height set to it,
|
applied when using the resizer, will have a max-height set to it,
|
||||||
|
@ -46,10 +46,15 @@ limitations under the License.
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
margin: 0 16px;
|
margin: 0 8px;
|
||||||
|
padding: 0 8px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomSubList_labelContainer:focus-within {
|
||||||
|
background-color: $roomtile-focused-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomSubList_label {
|
.mx_RoomSubList_label {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -67,7 +72,7 @@ limitations under the License.
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomSubList_badge {
|
.mx_RoomSubList_badge > div {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
@ -103,7 +108,7 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomSubList_badgeHighlight {
|
.mx_RoomSubList_badgeHighlight > div {
|
||||||
color: $accent-fg-color;
|
color: $accent-fg-color;
|
||||||
background-color: $warning-color;
|
background-color: $warning-color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,8 @@ limitations under the License.
|
||||||
|
|
||||||
// toggle menuButton and badge on hover/menu displayed
|
// toggle menuButton and badge on hover/menu displayed
|
||||||
.mx_RoomTile_menuDisplayed,
|
.mx_RoomTile_menuDisplayed,
|
||||||
|
// or on keyboard focus of room tile
|
||||||
|
.mx_RoomTile.focus-visible:focus-within,
|
||||||
.mx_LeftPanel_container:not(.collapsed) .mx_RoomTile:hover {
|
.mx_LeftPanel_container:not(.collapsed) .mx_RoomTile:hover {
|
||||||
.mx_RoomTile_menuButton {
|
.mx_RoomTile_menuButton {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -69,6 +69,8 @@ export const Key = {
|
||||||
BACKSPACE: "Backspace",
|
BACKSPACE: "Backspace",
|
||||||
ARROW_UP: "ArrowUp",
|
ARROW_UP: "ArrowUp",
|
||||||
ARROW_DOWN: "ArrowDown",
|
ARROW_DOWN: "ArrowDown",
|
||||||
|
ARROW_LEFT: "ArrowLeft",
|
||||||
|
ARROW_RIGHT: "ArrowRight",
|
||||||
TAB: "Tab",
|
TAB: "Tab",
|
||||||
ESCAPE: "Escape",
|
ESCAPE: "Escape",
|
||||||
ENTER: "Enter",
|
ENTER: "Enter",
|
||||||
|
|
|
@ -186,6 +186,7 @@ const LeftPanel = createReactClass({
|
||||||
}
|
}
|
||||||
} while (element && !(
|
} while (element && !(
|
||||||
classes.contains("mx_RoomTile") ||
|
classes.contains("mx_RoomTile") ||
|
||||||
|
classes.contains("mx_RoomSubList_label") ||
|
||||||
classes.contains("mx_textinput_search")));
|
classes.contains("mx_textinput_search")));
|
||||||
|
|
||||||
if (element) {
|
if (element) {
|
||||||
|
|
|
@ -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 2018, 2019 New Vector Ltd
|
Copyright 2018, 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.
|
||||||
|
@ -16,7 +17,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {createRef} from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
import createReactClass from 'create-react-class';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import sdk from '../../index';
|
import sdk from '../../index';
|
||||||
|
@ -25,7 +26,7 @@ import Unread from '../../Unread';
|
||||||
import * as RoomNotifs from '../../RoomNotifs';
|
import * as RoomNotifs from '../../RoomNotifs';
|
||||||
import * as FormattingUtils from '../../utils/FormattingUtils';
|
import * as FormattingUtils from '../../utils/FormattingUtils';
|
||||||
import IndicatorScrollbar from './IndicatorScrollbar';
|
import IndicatorScrollbar from './IndicatorScrollbar';
|
||||||
import { KeyCode } from '../../Keyboard';
|
import {Key, KeyCode} from '../../Keyboard';
|
||||||
import { Group } from 'matrix-js-sdk';
|
import { Group } from 'matrix-js-sdk';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import RoomTile from "../views/rooms/RoomTile";
|
import RoomTile from "../views/rooms/RoomTile";
|
||||||
|
@ -56,9 +57,8 @@ const RoomSubList = createReactClass({
|
||||||
collapsed: PropTypes.bool.isRequired, // is LeftPanel collapsed?
|
collapsed: PropTypes.bool.isRequired, // is LeftPanel collapsed?
|
||||||
onHeaderClick: PropTypes.func,
|
onHeaderClick: PropTypes.func,
|
||||||
incomingCall: PropTypes.object,
|
incomingCall: PropTypes.object,
|
||||||
isFiltered: PropTypes.bool,
|
|
||||||
headerItems: PropTypes.node, // content shown in the sublist header
|
|
||||||
extraTiles: PropTypes.arrayOf(PropTypes.node), // extra elements added beneath tiles
|
extraTiles: PropTypes.arrayOf(PropTypes.node), // extra elements added beneath tiles
|
||||||
|
forceExpand: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -80,6 +80,7 @@ const RoomSubList = createReactClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
|
this._headerButton = createRef();
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -87,9 +88,9 @@ const RoomSubList = createReactClass({
|
||||||
dis.unregister(this.dispatcherRef);
|
dis.unregister(this.dispatcherRef);
|
||||||
},
|
},
|
||||||
|
|
||||||
// The header is collapsable if it is hidden or not stuck
|
// The header is collapsible if it is hidden or not stuck
|
||||||
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
|
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
|
||||||
isCollapsableOnClick: function() {
|
isCollapsibleOnClick: function() {
|
||||||
const stuck = this.refs.header.dataset.stuck;
|
const stuck = this.refs.header.dataset.stuck;
|
||||||
if (!this.props.forceExpand && (this.state.hidden || stuck === undefined || stuck === "none")) {
|
if (!this.props.forceExpand && (this.state.hidden || stuck === undefined || stuck === "none")) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -114,8 +115,8 @@ const RoomSubList = createReactClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick: function(ev) {
|
onClick: function(ev) {
|
||||||
if (this.isCollapsableOnClick()) {
|
if (this.isCollapsibleOnClick()) {
|
||||||
// The header isCollapsable, so the click is to be interpreted as collapse and truncation logic
|
// The header isCollapsible, so the click is to be interpreted as collapse and truncation logic
|
||||||
const isHidden = !this.state.hidden;
|
const isHidden = !this.state.hidden;
|
||||||
this.setState({hidden: isHidden}, () => {
|
this.setState({hidden: isHidden}, () => {
|
||||||
this.props.onHeaderClick(isHidden);
|
this.props.onHeaderClick(isHidden);
|
||||||
|
@ -126,6 +127,49 @@ const RoomSubList = createReactClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onHeaderKeyDown: function(ev) {
|
||||||
|
switch (ev.key) {
|
||||||
|
case Key.TAB:
|
||||||
|
// Prevent LeftPanel handling Tab if focus is on the sublist header itself
|
||||||
|
ev.stopPropagation();
|
||||||
|
break;
|
||||||
|
case Key.ARROW_LEFT:
|
||||||
|
// On ARROW_LEFT collapse the room sublist
|
||||||
|
if (!this.state.hidden && !this.props.forceExpand) {
|
||||||
|
this.onClick();
|
||||||
|
}
|
||||||
|
ev.stopPropagation();
|
||||||
|
break;
|
||||||
|
case Key.ARROW_RIGHT: {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (this.state.hidden && !this.props.forceExpand) {
|
||||||
|
// sublist is collapsed, expand it
|
||||||
|
this.onClick();
|
||||||
|
} else if (!this.props.forceExpand) {
|
||||||
|
// sublist is expanded, go to first room
|
||||||
|
const element = this.refs.subList && this.refs.subList.querySelector(".mx_RoomTile");
|
||||||
|
if (element) {
|
||||||
|
element.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onKeyDown: function(ev) {
|
||||||
|
switch (ev.key) {
|
||||||
|
// On ARROW_LEFT go to the sublist header
|
||||||
|
case Key.ARROW_LEFT:
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._headerButton.current.focus();
|
||||||
|
break;
|
||||||
|
// Consume ARROW_RIGHT so it doesn't cause focus to get sent to composer
|
||||||
|
case Key.ARROW_RIGHT:
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onRoomTileClick(roomId, ev) {
|
onRoomTileClick(roomId, ev) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
|
@ -193,6 +237,11 @@ const RoomSubList = createReactClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onAddRoom: function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (this.props.onAddRoom) this.props.onAddRoom();
|
||||||
|
},
|
||||||
|
|
||||||
_getHeaderJsx: function(isCollapsed) {
|
_getHeaderJsx: function(isCollapsed) {
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const AccessibleTooltipButton = sdk.getComponent('elements.AccessibleTooltipButton');
|
const AccessibleTooltipButton = sdk.getComponent('elements.AccessibleTooltipButton');
|
||||||
|
@ -208,13 +257,24 @@ const RoomSubList = createReactClass({
|
||||||
'mx_RoomSubList_badge': true,
|
'mx_RoomSubList_badge': true,
|
||||||
'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
|
'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
|
||||||
});
|
});
|
||||||
|
// Wrap the contents in a div and apply styles to the child div so that the browser default outline works
|
||||||
if (subListNotifCount > 0) {
|
if (subListNotifCount > 0) {
|
||||||
badge = <div className={badgeClasses} onClick={this._onNotifBadgeClick}>
|
badge = (
|
||||||
|
<AccessibleButton className={badgeClasses} onClick={this._onNotifBadgeClick} aria-label={_t("Jump to first unread room.")}>
|
||||||
|
<div>
|
||||||
{ FormattingUtils.formatCount(subListNotifCount) }
|
{ FormattingUtils.formatCount(subListNotifCount) }
|
||||||
</div>;
|
</div>
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
} else if (this.props.isInvite && this.props.list.length) {
|
} else if (this.props.isInvite && this.props.list.length) {
|
||||||
// no notifications but highlight anyway because this is an invite badge
|
// no notifications but highlight anyway because this is an invite badge
|
||||||
badge = <div className={badgeClasses} onClick={this._onInviteBadgeClick}>{this.props.list.length}</div>;
|
badge = (
|
||||||
|
<AccessibleButton className={badgeClasses} onClick={this._onInviteBadgeClick} aria-label={_t("Jump to first invite.")}>
|
||||||
|
<div>
|
||||||
|
{ this.props.list.length }
|
||||||
|
</div>
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +297,7 @@ const RoomSubList = createReactClass({
|
||||||
if (this.props.onAddRoom) {
|
if (this.props.onAddRoom) {
|
||||||
addRoomButton = (
|
addRoomButton = (
|
||||||
<AccessibleTooltipButton
|
<AccessibleTooltipButton
|
||||||
onClick={ this.props.onAddRoom }
|
onClick={this.onAddRoom}
|
||||||
className="mx_RoomSubList_addRoom"
|
className="mx_RoomSubList_addRoom"
|
||||||
title={this.props.addRoomLabel || _t("Add room")}
|
title={this.props.addRoomLabel || _t("Add room")}
|
||||||
/>
|
/>
|
||||||
|
@ -255,10 +315,16 @@ const RoomSubList = createReactClass({
|
||||||
chevron = (<div className={chevronClasses} />);
|
chevron = (<div className={chevronClasses} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabindex = this.props.isFiltered ? "0" : "-1";
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
|
<div className="mx_RoomSubList_labelContainer" title={title} ref="header" onKeyDown={this.onHeaderKeyDown}>
|
||||||
<AccessibleButton onClick={this.onClick} className="mx_RoomSubList_label" tabIndex={tabindex} aria-expanded={!isCollapsed}>
|
<AccessibleButton
|
||||||
|
onClick={this.onClick}
|
||||||
|
className="mx_RoomSubList_label"
|
||||||
|
tabIndex={0}
|
||||||
|
aria-expanded={!isCollapsed}
|
||||||
|
inputRef={this._headerButton}
|
||||||
|
role="treeitem"
|
||||||
|
>
|
||||||
{ chevron }
|
{ chevron }
|
||||||
<span>{this.props.label}</span>
|
<span>{this.props.label}</span>
|
||||||
{ incomingCall }
|
{ incomingCall }
|
||||||
|
@ -299,20 +365,19 @@ const RoomSubList = createReactClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
const len = this.props.list.length + this.props.extraTiles.length;
|
const len = this.props.list.length + this.props.extraTiles.length;
|
||||||
const isCollapsed = this.state.hidden && !this.props.forceExpand;
|
const isCollapsed = this.state.hidden && !this.props.forceExpand;
|
||||||
if (len) {
|
|
||||||
const subListClasses = classNames({
|
const subListClasses = classNames({
|
||||||
"mx_RoomSubList": true,
|
"mx_RoomSubList": true,
|
||||||
"mx_RoomSubList_hidden": isCollapsed,
|
"mx_RoomSubList_hidden": len && isCollapsed,
|
||||||
"mx_RoomSubList_nonEmpty": len && !isCollapsed,
|
"mx_RoomSubList_nonEmpty": len && !isCollapsed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let content;
|
||||||
|
if (len) {
|
||||||
if (isCollapsed) {
|
if (isCollapsed) {
|
||||||
return <div ref="subList" className={subListClasses} role="group" aria-label={this.props.label}>
|
// no body
|
||||||
{this._getHeaderJsx(isCollapsed)}
|
|
||||||
</div>;
|
|
||||||
} else if (this._canUseLazyListRendering()) {
|
} else if (this._canUseLazyListRendering()) {
|
||||||
return <div ref="subList" className={subListClasses} role="group" aria-label={this.props.label}>
|
content = (
|
||||||
{this._getHeaderJsx(isCollapsed)}
|
|
||||||
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={this._onScroll}>
|
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={this._onScroll}>
|
||||||
<LazyRenderList
|
<LazyRenderList
|
||||||
scrollTop={this.state.scrollTop }
|
scrollTop={this.state.scrollTop }
|
||||||
|
@ -321,31 +386,35 @@ const RoomSubList = createReactClass({
|
||||||
itemHeight={34}
|
itemHeight={34}
|
||||||
items={ this.props.list } />
|
items={ this.props.list } />
|
||||||
</IndicatorScrollbar>
|
</IndicatorScrollbar>
|
||||||
</div>;
|
);
|
||||||
} else {
|
} else {
|
||||||
const roomTiles = this.props.list.map(r => this.makeRoomTile(r));
|
const roomTiles = this.props.list.map(r => this.makeRoomTile(r));
|
||||||
const tiles = roomTiles.concat(this.props.extraTiles);
|
const tiles = roomTiles.concat(this.props.extraTiles);
|
||||||
return <div ref="subList" className={subListClasses} role="group" aria-label={this.props.label}>
|
content = (
|
||||||
{this._getHeaderJsx(isCollapsed)}
|
|
||||||
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={this._onScroll}>
|
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={this._onScroll}>
|
||||||
{ tiles }
|
{ tiles }
|
||||||
</IndicatorScrollbar>
|
</IndicatorScrollbar>
|
||||||
</div>;
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
|
||||||
let content;
|
|
||||||
if (this.props.showSpinner && !isCollapsed) {
|
if (this.props.showSpinner && !isCollapsed) {
|
||||||
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
content = <Loader />;
|
content = <Loader />;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref="subList" className="mx_RoomSubList" role="group" aria-label={this.props.label}>
|
<div
|
||||||
|
ref="subList"
|
||||||
|
className={subListClasses}
|
||||||
|
role="group"
|
||||||
|
aria-label={this.props.label}
|
||||||
|
onKeyDown={this.onKeyDown}
|
||||||
|
>
|
||||||
{ this._getHeaderJsx(isCollapsed) }
|
{ this._getHeaderJsx(isCollapsed) }
|
||||||
{ content }
|
{ content }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,6 @@ export default function AccessibleButton(props) {
|
||||||
restProps.ref = restProps.inputRef;
|
restProps.ref = restProps.inputRef;
|
||||||
delete restProps.inputRef;
|
delete restProps.inputRef;
|
||||||
|
|
||||||
restProps.tabIndex = restProps.tabIndex || "0";
|
|
||||||
restProps.role = restProps.role || "button";
|
|
||||||
restProps.className = (restProps.className ? restProps.className + " " : "") + "mx_AccessibleButton";
|
restProps.className = (restProps.className ? restProps.className + " " : "") + "mx_AccessibleButton";
|
||||||
|
|
||||||
if (kind) {
|
if (kind) {
|
||||||
|
@ -93,19 +91,30 @@ export default function AccessibleButton(props) {
|
||||||
*/
|
*/
|
||||||
AccessibleButton.propTypes = {
|
AccessibleButton.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
inputRef: PropTypes.func,
|
inputRef: PropTypes.oneOfType([
|
||||||
|
// Either a function
|
||||||
|
PropTypes.func,
|
||||||
|
// Or the instance of a DOM native element
|
||||||
|
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
|
||||||
|
]),
|
||||||
element: PropTypes.string,
|
element: PropTypes.string,
|
||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
|
|
||||||
// The kind of button, similar to how Bootstrap works.
|
// The kind of button, similar to how Bootstrap works.
|
||||||
// See available classes for AccessibleButton for options.
|
// See available classes for AccessibleButton for options.
|
||||||
kind: PropTypes.string,
|
kind: PropTypes.string,
|
||||||
|
// The ARIA role
|
||||||
|
role: PropTypes.string,
|
||||||
|
// The tabIndex
|
||||||
|
tabIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||||
|
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
AccessibleButton.defaultProps = {
|
AccessibleButton.defaultProps = {
|
||||||
element: 'div',
|
element: 'div',
|
||||||
|
role: 'button',
|
||||||
|
tabIndex: "0",
|
||||||
};
|
};
|
||||||
|
|
||||||
AccessibleButton.displayName = "AccessibleButton";
|
AccessibleButton.displayName = "AccessibleButton";
|
||||||
|
|
|
@ -49,21 +49,6 @@ function labelForTagName(tagName) {
|
||||||
return tagName;
|
return tagName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function phraseForSection(section) {
|
|
||||||
switch (section) {
|
|
||||||
case 'm.favourite':
|
|
||||||
return _t('Drop here to favourite');
|
|
||||||
case 'im.vector.fake.direct':
|
|
||||||
return _t('Drop here to tag direct chat');
|
|
||||||
case 'im.vector.fake.recent':
|
|
||||||
return _t('Drop here to restore');
|
|
||||||
case 'm.lowpriority':
|
|
||||||
return _t('Drop here to demote');
|
|
||||||
default:
|
|
||||||
return _t('Drop here to tag %(section)s', {section: section});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = createReactClass({
|
module.exports = createReactClass({
|
||||||
displayName: 'RoomList',
|
displayName: 'RoomList',
|
||||||
|
|
||||||
|
@ -203,7 +188,7 @@ module.exports = createReactClass({
|
||||||
this.resizer.setClassNames({
|
this.resizer.setClassNames({
|
||||||
handle: "mx_ResizeHandle",
|
handle: "mx_ResizeHandle",
|
||||||
vertical: "mx_ResizeHandle_vertical",
|
vertical: "mx_ResizeHandle_vertical",
|
||||||
reverse: "mx_ResizeHandle_reverse"
|
reverse: "mx_ResizeHandle_reverse",
|
||||||
});
|
});
|
||||||
this._layout.update(
|
this._layout.update(
|
||||||
this._layoutSections,
|
this._layoutSections,
|
||||||
|
@ -584,23 +569,6 @@ module.exports = createReactClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_getHeaderItems: function(section) {
|
|
||||||
const StartChatButton = sdk.getComponent('elements.StartChatButton');
|
|
||||||
const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton');
|
|
||||||
const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton');
|
|
||||||
switch (section) {
|
|
||||||
case 'im.vector.fake.direct':
|
|
||||||
return <span className="mx_RoomList_headerButtons">
|
|
||||||
<StartChatButton size="16" />
|
|
||||||
</span>;
|
|
||||||
case 'im.vector.fake.recent':
|
|
||||||
return <span className="mx_RoomList_headerButtons">
|
|
||||||
<RoomDirectoryButton size="16" />
|
|
||||||
<CreateRoomButton size="16" />
|
|
||||||
</span>;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_makeGroupInviteTiles(filter) {
|
_makeGroupInviteTiles(filter) {
|
||||||
const ret = [];
|
const ret = [];
|
||||||
const lcFilter = filter && filter.toLowerCase();
|
const lcFilter = filter && filter.toLowerCase();
|
||||||
|
@ -746,16 +714,14 @@ module.exports = createReactClass({
|
||||||
list: this.state.lists['im.vector.fake.direct'],
|
list: this.state.lists['im.vector.fake.direct'],
|
||||||
label: _t('People'),
|
label: _t('People'),
|
||||||
tagName: "im.vector.fake.direct",
|
tagName: "im.vector.fake.direct",
|
||||||
headerItems: this._getHeaderItems('im.vector.fake.direct'),
|
|
||||||
order: "recent",
|
order: "recent",
|
||||||
incomingCall: incomingCallIfTaggedAs('im.vector.fake.direct'),
|
incomingCall: incomingCallIfTaggedAs('im.vector.fake.direct'),
|
||||||
onAddRoom: () => {dis.dispatch({action: 'view_create_chat'})},
|
onAddRoom: () => {dis.dispatch({action: 'view_create_chat'});},
|
||||||
addRoomLabel: _t("Start chat"),
|
addRoomLabel: _t("Start chat"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
list: this.state.lists['im.vector.fake.recent'],
|
list: this.state.lists['im.vector.fake.recent'],
|
||||||
label: _t('Rooms'),
|
label: _t('Rooms'),
|
||||||
headerItems: this._getHeaderItems('im.vector.fake.recent'),
|
|
||||||
order: "recent",
|
order: "recent",
|
||||||
incomingCall: incomingCallIfTaggedAs('im.vector.fake.recent'),
|
incomingCall: incomingCallIfTaggedAs('im.vector.fake.recent'),
|
||||||
onAddRoom: () => {dis.dispatch({action: 'view_create_room'});},
|
onAddRoom: () => {dis.dispatch({action: 'view_create_room'});},
|
||||||
|
@ -805,7 +771,7 @@ module.exports = createReactClass({
|
||||||
const subListComponents = this._mapSubListProps(subLists);
|
const subListComponents = this._mapSubListProps(subLists);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={this._collectResizeContainer} className="mx_RoomList" role="listbox" aria-label={_t("Rooms")}
|
<div ref={this._collectResizeContainer} className="mx_RoomList" role="tree" aria-label={_t("Rooms")}
|
||||||
onMouseMove={this.onMouseMove} onMouseLeave={this.onMouseLeave}>
|
onMouseMove={this.onMouseMove} onMouseLeave={this.onMouseLeave}>
|
||||||
{ subListComponents }
|
{ subListComponents }
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -398,7 +398,8 @@ module.exports = createReactClass({
|
||||||
onMouseLeave={this.onMouseLeave}
|
onMouseLeave={this.onMouseLeave}
|
||||||
onContextMenu={this.onContextMenu}
|
onContextMenu={this.onContextMenu}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
role="option"
|
aria-selected={this.state.selected}
|
||||||
|
role="treeitem"
|
||||||
>
|
>
|
||||||
<div className={avatarClasses}>
|
<div className={avatarClasses}>
|
||||||
<div className="mx_RoomTile_avatar_container">
|
<div className="mx_RoomTile_avatar_container">
|
||||||
|
|
|
@ -907,11 +907,6 @@
|
||||||
"Forget room": "Forget room",
|
"Forget room": "Forget room",
|
||||||
"Search": "Search",
|
"Search": "Search",
|
||||||
"Share room": "Share room",
|
"Share room": "Share room",
|
||||||
"Drop here to favourite": "Drop here to favourite",
|
|
||||||
"Drop here to tag direct chat": "Drop here to tag direct chat",
|
|
||||||
"Drop here to restore": "Drop here to restore",
|
|
||||||
"Drop here to demote": "Drop here to demote",
|
|
||||||
"Drop here to tag %(section)s": "Drop here to tag %(section)s",
|
|
||||||
"Community Invites": "Community Invites",
|
"Community Invites": "Community Invites",
|
||||||
"Invites": "Invites",
|
"Invites": "Invites",
|
||||||
"Favourites": "Favourites",
|
"Favourites": "Favourites",
|
||||||
|
@ -1664,6 +1659,8 @@
|
||||||
"Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
|
"Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
|
||||||
"Active call": "Active call",
|
"Active call": "Active call",
|
||||||
"There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?",
|
"There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?": "There's no one else here! Would you like to <inviteText>invite others</inviteText> or <nowarnText>stop warning about the empty room</nowarnText>?",
|
||||||
|
"Jump to first unread room.": "Jump to first unread room.",
|
||||||
|
"Jump to first invite.": "Jump to first invite.",
|
||||||
"Add room": "Add room",
|
"Add room": "Add room",
|
||||||
"You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?",
|
"You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?",
|
||||||
"You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?",
|
"You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?",
|
||||||
|
|
Loading…
Reference in a new issue