From f21aedc6cf38d741de183fa1d158d7f7de673699 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 26 Feb 2021 10:23:09 +0000 Subject: [PATCH] Add Space Panel with Room List filtering --- res/css/_components.scss | 1 + res/css/structures/_LeftPanel.scss | 22 +- res/css/structures/_SpacePanel.scss | 237 ++++++++++++++++++ res/img/element-icons/expand-space-panel.svg | 4 + res/themes/light/css/_mods.scss | 4 + src/components/structures/LeftPanel.tsx | 23 +- src/components/views/avatars/RoomAvatar.tsx | 2 +- src/components/views/spaces/SpacePanel.tsx | 212 ++++++++++++++++ .../views/spaces/SpaceTreeLevel.tsx | 184 ++++++++++++++ src/i18n/strings/en_EN.json | 4 +- src/stores/room-list/RoomListStore.ts | 23 +- src/stores/room-list/SpaceWatcher.ts | 39 +++ src/stores/room-list/algorithms/Algorithm.ts | 3 + .../room-list/filters/SpaceFilterCondition.ts | 69 +++++ 14 files changed, 796 insertions(+), 31 deletions(-) create mode 100644 res/css/structures/_SpacePanel.scss create mode 100644 res/img/element-icons/expand-space-panel.svg create mode 100644 src/components/views/spaces/SpacePanel.tsx create mode 100644 src/components/views/spaces/SpaceTreeLevel.tsx create mode 100644 src/stores/room-list/SpaceWatcher.ts create mode 100644 src/stores/room-list/filters/SpaceFilterCondition.ts diff --git a/res/css/_components.scss b/res/css/_components.scss index 006bac09c9..29b5262826 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -27,6 +27,7 @@ @import "./structures/_RoomView.scss"; @import "./structures/_ScrollPanel.scss"; @import "./structures/_SearchBox.scss"; +@import "./structures/_SpacePanel.scss"; @import "./structures/_TabbedView.scss"; @import "./structures/_ToastContainer.scss"; @import "./structures/_UploadBar.scss"; diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index 168590502d..f1f27014ee 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -15,6 +15,7 @@ limitations under the License. */ $groupFilterPanelWidth: 56px; // only applies in this file, used for calculations +$roomListCollapsedWidth: 68px; .mx_LeftPanel { background-color: $roomlist-bg-color; @@ -37,18 +38,12 @@ $groupFilterPanelWidth: 56px; // only applies in this file, used for calculation // GroupFilterPanel handles its own CSS } - &:not(.mx_LeftPanel_hasGroupFilterPanel) { - .mx_LeftPanel_roomListContainer { - width: 100%; - } - } - // 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_LeftPanel_roomListContainer { - width: calc(100% - $groupFilterPanelWidth); background-color: $roomlist-bg-color; - + flex: 1 0 0; + min-width: 0; // Create another flexbox (this time a column) for the room list components display: flex; flex-direction: column; @@ -168,17 +163,10 @@ $groupFilterPanelWidth: 56px; // only applies in this file, used for calculation // These styles override the defaults for the minimized (66px) layout &.mx_LeftPanel_minimized { min-width: unset; - - // We have to forcefully set the width to override the resizer's style attribute. - &.mx_LeftPanel_hasGroupFilterPanel { - width: calc(68px + $groupFilterPanelWidth) !important; - } - &:not(.mx_LeftPanel_hasGroupFilterPanel) { - width: 68px !important; - } + width: unset !important; .mx_LeftPanel_roomListContainer { - width: 68px; + width: $roomListCollapsedWidth; .mx_LeftPanel_userHeader { flex-direction: row; diff --git a/res/css/structures/_SpacePanel.scss b/res/css/structures/_SpacePanel.scss new file mode 100644 index 0000000000..8de85f95ef --- /dev/null +++ b/res/css/structures/_SpacePanel.scss @@ -0,0 +1,237 @@ +/* +Copyright 2021 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. +*/ + +$topLevelHeight: 32px; +$nestedHeight: 24px; +$gutterSize: 21px; +$activeStripeSize: 4px; + +.mx_SpacePanel { + flex: 0 0 auto; + background-color: $groupFilterPanel-bg-color; + padding: 0; + margin: 0; + + // Create another flexbox so the Panel fills the container + display: flex; + flex-direction: column; + overflow-y: auto; + + .mx_SpacePanel_spaceTreeWrapper { + flex: 1; + } + + .mx_SpacePanel_toggleCollapse { + flex: 0 0 auto; + width: 40px; + height: 40px; + mask-position: center; + mask-size: 32px; + mask-repeat: no-repeat; + margin-left: $gutterSize; + margin-bottom: 12px; + background-color: $roomlist-header-color; + mask-image: url('$(res)/img/element-icons/expand-space-panel.svg'); + + &.expanded { + transform: scaleX(-1); + } + } + + ul { + margin: 0; + list-style: none; + padding: 0; + padding-left: 16px; + } + + .mx_AutoHideScrollbar { + padding: 16px 12px 16px 0; + } + + .mx_SpaceButton_toggleCollapse { + cursor: pointer; + } + + .mx_SpaceItem { + position: relative; + } + + .mx_SpaceItem.collapsed { + & > .mx_SpaceButton > .mx_SpaceButton_toggleCollapse { + transform: rotate(-90deg); + } + + & > .mx_SpaceTreeLevel { + display: none; + } + } + + .mx_SpaceItem:not(.hasSubSpaces) > .mx_SpaceButton { + margin-left: $gutterSize; + + &.mx_SpaceButton_active { + &::before { + left: -$gutterSize; + } + } + } + + .mx_SpaceButton { + border-radius: 8px; + position: relative; + margin-bottom: 16px; + display: flex; + align-items: center; + + .mx_SpaceButton_name { + flex: 1; + margin-left: 8px; + white-space: nowrap; + display: block; + max-width: 150px; + text-overflow: ellipsis; + overflow: hidden; + + font-size: $font-14px; + line-height: $font-18px; + } + + .mx_SpaceButton_toggleCollapse { + width: calc($gutterSize - $activeStripeSize); + margin-left: 1px; + height: 20px; + mask-position: center; + mask-size: 20px; + mask-repeat: no-repeat; + background-color: $roomlist-header-color; + mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); + } + + &.mx_SpaceButton_active { + &::before { + position: absolute; + content: ''; + width: $activeStripeSize; + top: 0; + left: 0; + bottom: 0; + background-color: $accent-color; + border-radius: 0 4px 4px 0; + } + } + + .mx_SpaceButton_avatarPlaceholder { + width: $topLevelHeight; + min-width: $topLevelHeight; + height: $topLevelHeight; + border-radius: 8px; + + &::before { + position: absolute; + content: ''; + width: $topLevelHeight; + height: $topLevelHeight; + top: 0; + left: 0; + mask-position: center; + mask-repeat: no-repeat; + mask-size: 18px; + } + } + + &.mx_SpaceButton_home .mx_SpaceButton_avatarPlaceholder { + background-color: #ffffff; + + &::before { + background-color: #3f3d3d; + mask-image: url('$(res)/img/element-icons/home.svg'); + } + } + + &.mx_SpaceButton_newCancel .mx_SpaceButton_avatarPlaceholder { + background-color: $icon-button-color; + + &::before { + transform: rotate(45deg); + } + } + + .mx_BaseAvatar_image { + border-radius: 8px; + } + } + + .mx_SpacePanel_badgeContainer { + height: 16px; + // don't set width so that it takes no space when there is no badge to show + margin: auto 0; // vertically align + + // Create a flexbox to make aligning dot badges easier + display: flex; + align-items: center; + + .mx_NotificationBadge { + margin: 0 2px; // centering + } + + .mx_NotificationBadge_dot { + // make the smaller dot occupy the same width for centering + margin-left: 7px; + margin-right: 7px; + } + } + + &.collapsed { + .mx_SpaceButton { + .mx_SpacePanel_badgeContainer { + position: absolute; + right: -8px; + top: -4px; + } + } + } + + &:not(.collapsed) { + .mx_SpaceButton:hover, + .mx_SpaceButton:focus-within, + .mx_SpaceButton_hasMenuOpen { + // Hide the badge container on hover because it'll be a menu button + .mx_SpacePanel_badgeContainer { + width: 0; + height: 0; + display: none; + } + } + } + + /* root space buttons are bigger and not indented */ + & > .mx_AutoHideScrollbar { + & > .mx_SpaceButton { + height: $topLevelHeight; + + &.mx_SpaceButton_active::before { + height: $topLevelHeight; + } + } + + & > ul { + padding-left: 0; + } + } +} diff --git a/res/img/element-icons/expand-space-panel.svg b/res/img/element-icons/expand-space-panel.svg new file mode 100644 index 0000000000..11232acd58 --- /dev/null +++ b/res/img/element-icons/expand-space-panel.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/themes/light/css/_mods.scss b/res/themes/light/css/_mods.scss index 30aaeedf8f..fbca58dfb1 100644 --- a/res/themes/light/css/_mods.scss +++ b/res/themes/light/css/_mods.scss @@ -16,6 +16,10 @@ backdrop-filter: blur($groupFilterPanel-background-blur-amount); } + .mx_SpacePanel { + backdrop-filter: blur($groupFilterPanel-background-blur-amount); + } + .mx_LeftPanel .mx_LeftPanel_roomListContainer { backdrop-filter: blur($roomlist-background-blur-amount); } diff --git a/src/components/structures/LeftPanel.tsx b/src/components/structures/LeftPanel.tsx index 4445ff3ff8..82dd9443cc 100644 --- a/src/components/structures/LeftPanel.tsx +++ b/src/components/structures/LeftPanel.tsx @@ -39,6 +39,7 @@ import { OwnProfileStore } from "../../stores/OwnProfileStore"; import { MatrixClientPeg } from "../../MatrixClientPeg"; import RoomListNumResults from "../views/rooms/RoomListNumResults"; import LeftPanelWidget from "./LeftPanelWidget"; +import SpacePanel from "../views/spaces/SpacePanel"; interface IProps { isMinimized: boolean; @@ -388,12 +389,19 @@ export default class LeftPanel extends React.Component { } public render(): React.ReactNode { - const groupFilterPanel = !this.state.showGroupFilterPanel ? null : ( -
- - {SettingsStore.getValue("feature_custom_tags") ? : null} -
- ); + let leftLeftPanel; + // Currently TagPanel.enableTagPanel is disabled when Legacy Communities are disabled so for now + // ignore it and force the rendering of SpacePanel if that Labs flag is enabled. + if (SettingsStore.getValue("feature_spaces")) { + leftLeftPanel = ; + } else if (this.state.showGroupFilterPanel) { + leftLeftPanel = ( +
+ + {SettingsStore.getValue("feature_custom_tags") ? : null} +
+ ); + } const roomList = { const containerClasses = classNames({ "mx_LeftPanel": true, - "mx_LeftPanel_hasGroupFilterPanel": !!groupFilterPanel, "mx_LeftPanel_minimized": this.props.isMinimized, }); @@ -417,7 +424,7 @@ export default class LeftPanel extends React.Component { return (
- {groupFilterPanel} + {leftLeftPanel}