From 2c04a5678462fa9c95d9af9fd7dcd3c19706ef51 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 11 Jun 2020 14:39:28 -0600 Subject: [PATCH] Add a minimized view to the new room list This covers everything except breadcrumbs, as those are somewhat undecided from a design perspective. --- res/css/structures/_LeftPanel2.scss | 34 +++++++++ res/css/structures/_RoomSearch.scss | 11 +++ res/css/views/rooms/_RoomSublist2.scss | 82 +++++++++++++++++++-- res/css/views/rooms/_RoomTile2.scss | 39 +++++++--- src/components/structures/LeftPanel2.tsx | 25 +++++-- src/components/structures/LoggedInView.tsx | 2 +- src/components/structures/RoomSearch.tsx | 62 +++++++++++----- src/components/views/rooms/RoomList2.tsx | 2 + src/components/views/rooms/RoomSublist2.tsx | 13 +++- src/components/views/rooms/RoomTile2.tsx | 23 ++++-- 10 files changed, 243 insertions(+), 50 deletions(-) diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index bd2a3ba96e..3b8fb58be5 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -138,4 +138,38 @@ $roomListMinimizedWidth: 50px; display: flex; } } + + // These styles override the defaults for the minimized (66px) layout + &.mx_LeftPanel2_minimized { + min-width: unset; + + // We have to forcefully set the width to override the resizer's style attribute. + width: calc(68px + $tagPanelWidth) !important; + + .mx_LeftPanel2_roomListContainer { + width: 68px; + + .mx_LeftPanel2_userHeader { + .mx_LeftPanel2_headerRow { + justify-content: center; + } + + .mx_LeftPanel2_userAvatarContainer { + margin-right: 0; + } + } + + .mx_LeftPanel2_filterContainer { + // Organize the flexbox into a centered column layout + flex-direction: column; + justify-content: center; + + .mx_LeftPanel2_exploreButton { + margin-left: 0; + margin-top: 8px; + background-color: transparent; + } + } + } + } } diff --git a/res/css/structures/_RoomSearch.scss b/res/css/structures/_RoomSearch.scss index d078031090..31061f7a16 100644 --- a/res/css/structures/_RoomSearch.scss +++ b/res/css/structures/_RoomSearch.scss @@ -67,4 +67,15 @@ limitations under the License. width: 0; height: 0; } + + &.mx_RoomSearch_minimized { + border-radius: 32px; + height: auto; + width: auto; + padding: 8px; + + .mx_RoomSearch_icon { + margin-left: 0; + } + } } diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index ed17c071b6..d1e9c0459c 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -51,6 +51,7 @@ limitations under the License. margin: 0; visibility: hidden; position: relative; + border-radius: 32px; &::before { content: ''; @@ -172,7 +173,7 @@ limitations under the License. border: 2px solid $primary-fg-color; } - .mx_RoomSublist2_headerContainer { + &:not(.mx_RoomSublist2_minimized) > .mx_RoomSublist2_headerContainer { // If the header doesn't have an aux button we still need to hide the badge for // the menu button. .mx_RoomSublist2_badgeContainer { @@ -195,18 +196,89 @@ limitations under the License. .mx_RoomSublist2_menuButton { width: 24px; height: 24px; - border-radius: 32px; margin-left: 16px; - background-color: #fff; // TODO: Variable and theme visibility: visible; + background-color: #fff; // TODO: Variable and theme + } + } + } + + &.mx_RoomSublist2_minimized { + .mx_RoomSublist2_headerContainer { + height: auto; + flex-direction: column; + position: relative; + + .mx_RoomSublist2_badgeContainer { + order: 1; + align-self: flex-end; + margin-right: 0; + } + + .mx_RoomSublist2_headerText { + order: 2; + max-width: 100%; + } + + .mx_RoomSublist2_auxButton { + order: 4; + visibility: visible; + width: 32px !important; // !important to override hover styles + height: 32px !important; // !important to override hover styles + margin-left: 0 !important; // !important to override hover styles + background-color: #fff; // TODO: Variable and theme + margin-top: 8px; + + &::before { + top: 8px; + left: 8px; + } + } + } + + .mx_RoomSublist2_resizeBox { + align-items: center; + + .mx_RoomSublist2_showMoreButton { + .mx_RoomSublist2_showMoreButtonChevron { + margin-right: 12px; // to center + } + } + } + + &:hover, &.mx_RoomSublist2_hasMenuOpen { + .mx_RoomSublist2_menuButton { + visibility: visible; + position: absolute; + bottom: 48px; // align to middle of name, 40px for aux button (with padding) and 8px for alignment + right: 0; + width: 16px; + height: 16px; + border-radius: 0; + z-index: 1; // occlude the list name + + // This is the same color as the left panel background because it needs + // to occlude the sublist title + background-color: $header-panel-bg-color; + + &::before { + top: 0; + left: 0; + } + } + + .mx_RoomSublist2_headerContainer:not(.mx_RoomSublist2_headerContainer_withAux) { + .mx_RoomSublist2_menuButton { + bottom: 8px; // align to the middle of name, 40px less than the `bottom` above. + } } } } } // We have a hover style on the room list with no specific list hovered, so account for that -.mx_RoomList2:hover .mx_RoomSublist2, -.mx_RoomSublist2_hasMenuOpen { +.mx_RoomList2:hover .mx_RoomSublist2:not(.mx_RoomSublist2_minimized), +.mx_RoomSublist2_hasMenuOpen:not(.mx_RoomSublist2_minimized) { .mx_RoomSublist2_headerContainer_withAux { .mx_RoomSublist2_badgeContainer { // Completely hide the badge diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss index 67b1470550..f74d0ff5a4 100644 --- a/res/css/views/rooms/_RoomTile2.scss +++ b/res/css/views/rooms/_RoomTile2.scss @@ -115,18 +115,37 @@ limitations under the License. mask-image: url('$(res)/img/feather-customised/more-horizontal.svg'); } - &:hover, &.mx_RoomTile2_hasMenuOpen { - // Hide the badge container on hover because it'll be a menu button - .mx_RoomTile2_badgeContainer { - width: 0; - height: 0; - visibility: hidden; + &:not(.mx_RoomTile2_minimized) { + &:hover, &.mx_RoomTile2_hasMenuOpen { + // Hide the badge container on hover because it'll be a menu button + .mx_RoomTile2_badgeContainer { + width: 0; + height: 0; + visibility: hidden; + } + + .mx_RoomTile2_menuButton { + width: 18px; + height: 32px; + visibility: visible; + } + } + } + + &.mx_RoomTile2_minimized { + flex-direction: column; + align-items: center; + position: relative; + + .mx_RoomTile2_avatarContainer { + margin-right: 0; } - .mx_RoomTile2_menuButton { - width: 18px; - height: 32px; - visibility: visible; + .mx_RoomTile2_badgeContainer { + position: absolute; + top: 0; + right: 0; + height: 18px; } } } diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index 302d71afa8..a644aa4837 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -40,7 +40,7 @@ import { UPDATE_EVENT } from "../../stores/AsyncStore"; *******************************************************************/ interface IProps { - // TODO: Support collapsed state + isMinimized: boolean; } interface IState { @@ -106,11 +106,22 @@ export default class LeftPanel2 extends React.Component { if (this.state.showBreadcrumbs) { breadcrumbs = (
- + {this.props.isMinimized ? null : }
); } + let name = {displayName}; + let buttons = ( + + + + ); + if (this.props.isMinimized) { + name = null; + buttons = null; + } + return (
@@ -125,10 +136,8 @@ export default class LeftPanel2 extends React.Component { className="mx_LeftPanel2_userAvatar" /> - {displayName} - - - + {name} + {buttons}
{breadcrumbs}
@@ -140,7 +149,7 @@ export default class LeftPanel2 extends React.Component { return (
- + { searchFilter={this.state.searchFilter} onFocus={() => {/*TODO*/}} onBlur={() => {/*TODO*/}} + isMinimized={this.props.isMinimized} />; // TODO: Conference handling / calls const containerClasses = classNames({ "mx_LeftPanel2": true, + "mx_LeftPanel2_minimized": this.props.isMinimized, }); return ( diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 19edf505e0..f37f77b31b 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -677,7 +677,7 @@ class LoggedInView extends React.PureComponent { if (SettingsStore.isFeatureEnabled("feature_new_room_list")) { // TODO: Supply props like collapsed and disabled to LeftPanel2 leftPanel = ( - + ); } diff --git a/src/components/structures/RoomSearch.tsx b/src/components/structures/RoomSearch.tsx index ebfb4e6056..345cf83d31 100644 --- a/src/components/structures/RoomSearch.tsx +++ b/src/components/structures/RoomSearch.tsx @@ -35,6 +35,7 @@ import { Action } from "../../dispatcher/actions"; interface IProps { onQueryUpdate: (newQuery: string) => void; + isMinimized: boolean; } interface IState { @@ -75,6 +76,10 @@ export default class RoomSearch extends React.PureComponent { this.onChange(); }; + private openSearch = () => { + defaultDispatcher.dispatch({action: "show_left_panel"}); + }; + private onChange = () => { if (!this.inputRef.current) return; this.setState({query: this.inputRef.current.value}); @@ -111,6 +116,7 @@ export default class RoomSearch extends React.PureComponent { const classes = classNames({ 'mx_RoomSearch': true, 'mx_RoomSearch_expanded': this.state.query || this.state.focused, + 'mx_RoomSearch_minimized': this.props.isMinimized, }); const inputClasses = classNames({ @@ -118,26 +124,48 @@ export default class RoomSearch extends React.PureComponent { 'mx_RoomSearch_inputExpanded': this.state.query || this.state.focused, }); - return ( -
-
- + let icon = ( +
+ ); + let input = ( + + ); + let clearButton = ( + + ); + + if (this.props.isMinimized) { + icon = ( + ); + input = null; + clearButton = null; + } + + return ( +
+ {icon} + {input} + {clearButton}
); } diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index 42fb829b25..4fdce360c1 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -45,6 +45,7 @@ interface IProps { resizeNotifier: ResizeNotifier; collapsed: boolean; searchFilter: string; + isMinimized: boolean; } interface IState { @@ -200,6 +201,7 @@ export default class RoomList2 extends React.Component { addRoomLabel={aesthetics.addRoomLabel} isInvite={aesthetics.isInvite} layout={this.state.layouts.get(orderedTagId)} + isMinimized={this.props.isMinimized} /> ); } diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index fa37eef975..aea5b4ae23 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -47,6 +47,7 @@ interface IProps { addRoomLabel: string; isInvite: boolean; layout: ListLayout; + isMinimized: boolean; // TODO: Collapsed state // TODO: Group invites @@ -135,6 +136,7 @@ export default class RoomSublist2 extends React.Component { room={room} key={`room-${room.roomId}`} showMessagePreview={this.props.layout.showPreviews} + isMinimized={this.props.isMinimized} /> ); } @@ -264,6 +266,7 @@ export default class RoomSublist2 extends React.Component { 'mx_RoomSublist2': true, 'mx_RoomSublist2_collapsed': false, // len && isCollapsed 'mx_RoomSublist2_hasMenuOpen': this.state.menuDisplayed, + 'mx_RoomSublist2_minimized': this.props.isMinimized, }); let content = null; @@ -282,14 +285,18 @@ export default class RoomSublist2 extends React.Component { if (tiles.length > nVisible) { // we have a cutoff condition - add the button to show all const numMissing = tiles.length - visibleTiles.length; + let showMoreText = ( + + {_t("Show %(count)s more", {count: numMissing})} + + ); + if (this.props.isMinimized) showMoreText = null; showMoreButton = (
{/* set by CSS masking */} - - {_t("Show %(count)s more", {count: numMissing})} - + {showMoreText}
); } diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index 2b1c418294..10845d3840 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -43,6 +43,7 @@ import { MessagePreviewStore } from "../../../stores/MessagePreviewStore"; interface IProps { room: Room; showMessagePreview: boolean; + isMinimized: boolean; // TODO: Allow falsifying counts (for invites and stuff) // TODO: Transparency? Was this ever used? @@ -158,6 +159,8 @@ export default class RoomTile2 extends React.Component { }; private renderGeneralMenu(): React.ReactElement { + if (this.props.isMinimized) return null; // no menu when minimized + let contextMenu = null; if (this.state.generalMenuDisplayed) { // The context menu appears within the list, so use the room tile as a reference point @@ -240,6 +243,7 @@ export default class RoomTile2 extends React.Component { 'mx_RoomTile2': true, 'mx_RoomTile2_selected': this.state.selected, 'mx_RoomTile2_hasMenuOpen': this.state.generalMenuDisplayed, + 'mx_RoomTile2_minimized': this.props.isMinimized, }); const badge = ; @@ -253,7 +257,7 @@ export default class RoomTile2 extends React.Component { // TODO: Tooltip? let messagePreview = null; - if (this.props.showMessagePreview) { + if (this.props.showMessagePreview && !this.props.isMinimized) { // The preview store heavily caches this info, so should be safe to hammer. const text = MessagePreviewStore.instance.getPreviewForRoom(this.props.room); @@ -273,6 +277,16 @@ export default class RoomTile2 extends React.Component { "mx_RoomTile2_nameHasUnreadEvents": this.state.notificationState.color >= NotificationColor.Bold, }); + let nameContainer = ( +
+
+ {name} +
+ {messagePreview} +
+ ); + if (this.props.isMinimized) nameContainer = null; + const avatarSize = 32; return ( @@ -291,12 +305,7 @@ export default class RoomTile2 extends React.Component {
-
-
- {name} -
- {messagePreview} -
+ {nameContainer}
{badge}