Initial styling for new room list

This is a work in progress, but covers the coarse areas. This uses all-new classes to better describe what everything is, and to reduce the number of selectors we keep track of.

This is primarily layout for the list and not actually the final structure. For example, some buttons are missing and other areas are not styled correctly - the idea in this commit was to get things roughly in the right place and work on it.
This commit is contained in:
Travis Ralston 2020-06-04 16:34:04 -06:00
parent 02c131e3ff
commit 4c1bc50649
11 changed files with 205 additions and 60 deletions

View file

@ -12,6 +12,7 @@
@import "./structures/_HeaderButtons.scss";
@import "./structures/_HomePage.scss";
@import "./structures/_LeftPanel.scss";
@import "./structures/_LeftPanel2.scss";
@import "./structures/_MainSplit.scss";
@import "./structures/_MatrixChat.scss";
@import "./structures/_MyGroups.scss";
@ -177,10 +178,12 @@
@import "./views/rooms/_RoomDropTarget.scss";
@import "./views/rooms/_RoomHeader.scss";
@import "./views/rooms/_RoomList.scss";
@import "./views/rooms/_RoomList2.scss";
@import "./views/rooms/_RoomPreviewBar.scss";
@import "./views/rooms/_RoomRecoveryReminder.scss";
@import "./views/rooms/_RoomSublist2.scss";
@import "./views/rooms/_RoomTile.scss";
@import "./views/rooms/_RoomTile2.scss";
@import "./views/rooms/_RoomUpgradeWarningBar.scss";
@import "./views/rooms/_SearchBar.scss";
@import "./views/rooms/_SendMessageComposer.scss";

View file

@ -23,14 +23,6 @@ limitations under the License.
flex: 0 0 auto;
}
// TODO: Remove temporary indicator of new room list implementation.
// This border is meant to visually distinguish between the two components when the
// user has turned on the new room list implementation, at least until the designs
// themselves give it away.
.mx_LeftPanel2 .mx_LeftPanel {
border-left: 5px #e26dff solid;
}
.mx_LeftPanel_container.collapsed {
min-width: unset;
/* Collapsed LeftPanel 50px */

View file

@ -0,0 +1,91 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
$tagPanelWidth: 70px;
$roomListMinimizedWidth: 50px;
.mx_LeftPanel2 {
background-color: $header-panel-bg-color;
min-width: 260px;
max-width: 50%;
// Create a row-based flexbox for the TagPanel and the room list
display: flex;
.mx_LeftPanel2_tagPanelContainer {
flex-grow: 0;
flex-shrink: 0;
flex-basis: $tagPanelWidth;
height: 100%;
// Create another flexbox so the TagPanel fills the container
display: flex;
// TagPanel handles its own CSS
}
// Note: The 'room list' in this context is actually everything that isn't the tag
// panel, such as the menu options, breadcrumbs, filtering, etc
.mx_LeftPanel2_roomListContainer {
width: calc(100% - $tagPanelWidth);
// Create another flexbox (this time a column) for the room list components
display: flex;
flex-direction: column;
.mx_LeftPanel2_userHeader {
padding: 14px 12px 20px; // 14px top, 12px sides, 20px bottom
// Create another flexbox column for the rows to stack within
display: flex;
flex-direction: column;
// There's 2 rows when breadcrumbs are present: the top bit and the breadcrumbs
.mx_LeftPanel2_headerRow {
// Create yet another flexbox, this time within the row, to ensure items stay
// aligned correctly. This is also a row-based flexbox.
display: flex;
align-items: center;
}
.mx_LeftPanel2_userAvatarContainer {
position: relative; // to make default avatars work
margin-right: 8px;
}
.mx_LeftPanel2_userName {
font-weight: 600;
font-size: $font-15px;
line-height: $font-20px;
}
.mx_LeftPanel2_breadcrumbsContainer {
// TODO: Improve CSS for breadcrumbs (currently shoved into the view rather than placed)
width: 100%;
overflow: hidden;
}
}
.mx_LeftPanel2_filterContainer {
// TODO: Improve CSS for filtering and its input
}
.mx_LeftPanel2_actualRoomListContainer {
flex-grow: 1; // fill the available space
overflow-y: auto;
}
}
}

View file

@ -66,7 +66,7 @@ limitations under the License.
}
/* not the left panel, and not the resize handle, so the roomview/groupview/... */
.mx_MatrixChat > :not(.mx_LeftPanel_container):not(.mx_ResizeHandle) {
.mx_MatrixChat > :not(.mx_LeftPanel_container):not(.mx_LeftPanel2):not(.mx_ResizeHandle) {
background-color: $primary-bg-color;
flex: 1 1 0;

View file

@ -0,0 +1,23 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_RoomList2 {
// Create a column-based flexbox for the sublists. That's pretty much all we have to
// worry about in this stylesheet.
display: flex;
flex-direction: column;
flex-wrap: wrap;
}

View file

@ -16,7 +16,7 @@ limitations under the License.
@import "../../../../node_modules/react-resizable/css/styles.css";
.mx_RoomList2 .mx_RoomSubList_labelContainer {
.mx_RoomList2 .mx_RoomSubList2_labelContainer {
z-index: 12;
background-color: purple;
}

View file

@ -0,0 +1,18 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_RoomTile2 {
}

View file

@ -24,6 +24,9 @@ import SearchBox from "./SearchBox";
import RoomList2 from "../views/rooms/RoomList2";
import TopLeftMenuButton from "./TopLeftMenuButton";
import { Action } from "../../dispatcher/actions";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import BaseAvatar from '../views/avatars/BaseAvatar';
import RoomBreadcrumbs from "../views/rooms/RoomBreadcrumbs";
/*******************************************************************
* CAUTION *
@ -82,24 +85,44 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
}
}
private renderHeader(): React.ReactNode {
// TODO: Use real profile info
// TODO: Presence
// TODO: Breadcrumbs toggle
// TODO: Menu button
const avatarSize = 32;
return (
<div className="mx_LeftPanel2_userHeader">
<div className="mx_LeftPanel2_headerRow">
<span className="mx_LeftPanel2_userAvatarContainer">
<BaseAvatar
idName={MatrixClientPeg.get().getUserId()}
name={"TODO: Display Name"}
url={null}
width={avatarSize}
height={avatarSize}
resizeMethod="crop"
className="mx_LeftPanel2_userAvatar"
/>
</span>
<span className="mx_LeftPanel2_userName">Irene</span>
</div>
<div className="mx_LeftPanel2_headerRow mx_LeftPanel2_breadcrumbsContainer">
<RoomBreadcrumbs />
</div>
</div>
);
}
public render(): React.ReactNode {
const tagPanel = (
<div className="mx_LeftPanel_tagPanelContainer">
<div className="mx_LeftPanel2_tagPanelContainer">
<TagPanel/>
</div>
);
const exploreButton = (
<div
className={classNames("mx_LeftPanel_explore", {"mx_LeftPanel_explore_hidden": this.state.searchExpanded})}>
<AccessibleButton onClick={() => dis.dispatch({action: 'view_room_directory'})}>
{_t("Explore")}
</AccessibleButton>
</div>
);
const searchBox = (<SearchBox
className="mx_LeftPanel_filterRooms"
className="mx_LeftPanel2_TODO_filterRooms"
enableRoomSearchFocus={true}
blurredPlaceholder={_t('Filter')}
placeholder={_t('Filter rooms…')}
@ -124,29 +147,25 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
// TODO: Conference handling / calls
const containerClasses = classNames({
"mx_LeftPanel_container": true,
"mx_fadable": true,
"collapsed": false, // TODO: Collapsed support
"mx_LeftPanel_container_hasTagPanel": true, // TODO: TagPanel support
"mx_fadable_faded": false,
"mx_LeftPanel2": true, // TODO: Remove flag when RoomList2 ships (used as an indicator)
"mx_LeftPanel2": true,
});
return (
<div className={containerClasses}>
{tagPanel}
<aside className="mx_LeftPanel dark-panel">
<TopLeftMenuButton collapsed={false}/>
<aside className="mx_LeftPanel2_roomListContainer">
{this.renderHeader()}
<div
className="mx_LeftPanel_exploreAndFilterRow"
className="mx_LeftPanel2_filterContainer"
onKeyDown={() => {/*TODO*/}}
onFocus={() => {/*TODO*/}}
onBlur={() => {/*TODO*/}}
>
{exploreButton}
{searchBox}
</div>
{roomList}
<div className="mx_LeftPanel2_actualRoomListContainer">
{roomList}
</div>
</aside>
</div>
);

View file

@ -216,7 +216,7 @@ export default class RoomList2 extends React.Component<IProps, IState> {
onFocus={this.props.onFocus}
onBlur={this.props.onBlur}
onKeyDown={onKeyDownHandler}
className="mx_RoomList mx_RoomList2"
className="mx_RoomList2"
role="tree"
aria-label={_t("Rooms")}
// Firefox sometimes makes this element focusable due to

View file

@ -113,9 +113,9 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
let chevron = null;
if (this.hasTiles()) {
const chevronClasses = classNames({
'mx_RoomSubList_chevron': true,
'mx_RoomSubList_chevronRight': false, // isCollapsed
'mx_RoomSubList_chevronDown': true, // !isCollapsed
'mx_RoomSublist2_chevron': true,
'mx_RoomSublist2_chevronRight': false, // isCollapsed
'mx_RoomSublist2_chevronDown': true, // !isCollapsed
});
chevron = (<div className={chevronClasses}/>);
}
@ -130,8 +130,8 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
let badge;
if (true) { // !isCollapsed
const badgeClasses = classNames({
'mx_RoomSubList_badge': true,
'mx_RoomSubList_badgeHighlight': notifHighlight,
'mx_RoomSublist2_badge': true,
'mx_RoomSublist2_badgeHighlight': notifHighlight,
});
// Wrap the contents in a div and apply styles to the child div so that the browser default outline works
if (notifCount > 0) {
@ -168,7 +168,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
<AccessibleTooltipButton
tabIndex={tabIndex}
onClick={this.onAddRoom}
className="mx_RoomSubList_addRoom"
className="mx_RoomSublist2_addButton"
title={this.props.addRoomLabel || _t("Add room")}
/>
);
@ -176,11 +176,11 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
// TODO: a11y (see old component)
return (
<div className={"mx_RoomSubList_labelContainer"}>
<div className={"mx_RoomSublist2_headerContainer"}>
<AccessibleButton
inputRef={ref}
tabIndex={tabIndex}
className={"mx_RoomSubList_label"}
className={"mx_RoomSublist2_headerText"}
role="treeitem"
aria-level="1"
>
@ -204,9 +204,8 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
const classes = classNames({
// TODO: Proper collapse support
'mx_RoomSubList': true,
'mx_RoomSubList_hidden': false, // len && isCollapsed
'mx_RoomSubList_nonEmpty': this.hasTiles(), // len && !isCollapsed
'mx_RoomSublist2': true,
'mx_RoomSublist2_collapsed': false, // len && isCollapsed
});
let content = null;

View file

@ -195,28 +195,28 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
const hasBadge = this.state.notificationState.color > NotificationColor.Bold;
const isUnread = this.state.notificationState.color > NotificationColor.None;
const classes = classNames({
'mx_RoomTile': true,
'mx_RoomTile2': true,
// 'mx_RoomTile_selected': this.state.selected,
'mx_RoomTile_unread': isUnread,
'mx_RoomTile_unreadNotify': this.state.notificationState.color >= NotificationColor.Grey,
'mx_RoomTile_highlight': this.state.notificationState.color >= NotificationColor.Red,
'mx_RoomTile_invited': this.roomIsInvite,
'mx_RoomTile2_unread': isUnread,
'mx_RoomTile2_unreadNotify': this.state.notificationState.color >= NotificationColor.Grey,
'mx_RoomTile2_highlight': this.state.notificationState.color >= NotificationColor.Red,
'mx_RoomTile2_invited': this.roomIsInvite,
// 'mx_RoomTile_menuDisplayed': isMenuDisplayed,
'mx_RoomTile_noBadges': !hasBadge,
'mx_RoomTile2_noBadges': !hasBadge,
// 'mx_RoomTile_transparent': this.props.transparent,
// 'mx_RoomTile_hasSubtext': subtext && !this.props.collapsed,
});
const avatarClasses = classNames({
'mx_RoomTile_avatar': true,
'mx_RoomTile2_avatar': true,
});
let badge;
if (hasBadge) {
const badgeClasses = classNames({
'mx_RoomTile_badge': true,
'mx_RoomTile_badgeButton': false, // this.state.badgeHover || isMenuDisplayed
'mx_RoomTile2_badge': true,
'mx_RoomTile2_badgeButton': false, // this.state.badgeHover || isMenuDisplayed
});
badge = <div className={badgeClasses}>{this.state.notificationState.symbol}</div>;
}
@ -227,16 +227,16 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
const nameClasses = classNames({
'mx_RoomTile_name': true,
'mx_RoomTile_invite': this.roomIsInvite,
'mx_RoomTile_badgeShown': hasBadge,
'mx_RoomTile2_name': true,
'mx_RoomTile2_invite': this.roomIsInvite,
'mx_RoomTile2_badgeShown': hasBadge,
});
// TODO: Support collapsed state properly
let tooltip = null;
if (false) { // isCollapsed
if (this.state.hover) {
tooltip = <Tooltip className="mx_RoomTile_tooltip" label={this.props.room.name} />
tooltip = <Tooltip className="mx_RoomTile2_tooltip" label={this.props.room.name} />
}
}
@ -255,12 +255,12 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
role="treeitem"
>
<div className={avatarClasses}>
<div className="mx_RoomTile_avatar_container">
<div className="mx_RoomTile2_avatarContainer">
<RoomAvatar room={this.props.room} width={24} height={24}/>
</div>
</div>
<div className="mx_RoomTile_nameContainer">
<div className="mx_RoomTile_labelContainer">
<div className="mx_RoomTile2_nameContainer">
<div className="mx_RoomTile2_labelContainer">
<div title={name} className={nameClasses} tabIndex={-1} dir="auto">
{name}
</div>