diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss
index 746f373e64..c725b02f84 100644
--- a/res/css/views/rooms/_RoomSublist2.scss
+++ b/res/css/views/rooms/_RoomSublist2.scss
@@ -138,6 +138,34 @@ limitations under the License.
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
+
+ .mx_RoomSublist2_collapseBtn {
+ display: inline-block;
+ position: relative;
+
+ // Default hidden
+ visibility: hidden;
+ width: 0;
+ height: 0;
+
+ &::before {
+ content: '';
+ width: 12px;
+ height: 12px;
+ position: absolute;
+ top: 1px;
+ left: 1px;
+ mask-position: center;
+ mask-size: contain;
+ mask-repeat: no-repeat;
+ background: $primary-fg-color;
+ mask-image: url('$(res)/img/feather-customised/chevron-down.svg');
+ }
+
+ &.mx_RoomSublist2_collapseBtn_collapsed::before {
+ mask-image: url('$(res)/img/feather-customised/chevron-right.svg');
+ }
+ }
}
}
@@ -251,6 +279,17 @@ limitations under the License.
background-color: $roomlist2-button-bg-color;
}
}
+
+ .mx_RoomSublist2_headerContainer {
+ .mx_RoomSublist2_headerText {
+ .mx_RoomSublist2_collapseBtn {
+ visibility: visible;
+ width: 12px;
+ height: 12px;
+ margin-right: 4px;
+ }
+ }
+ }
}
&.mx_RoomSublist2_minimized {
diff --git a/res/img/feather-customised/chevron-right.svg b/res/img/feather-customised/chevron-right.svg
new file mode 100644
index 0000000000..258de414a1
--- /dev/null
+++ b/res/img/feather-customised/chevron-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx
index 650828e9b0..ba0ba211b7 100644
--- a/src/components/structures/LeftPanel2.tsx
+++ b/src/components/structures/LeftPanel2.tsx
@@ -105,13 +105,11 @@ export default class LeftPanel2 extends React.Component {
const header = sublist.querySelector(".mx_RoomSublist2_stickable");
if (slRect.top + headerHeight > bottom && !gotBottom) {
- console.log(`${header.textContent} is off the bottom`);
header.classList.add("mx_RoomSublist2_headerContainer_sticky");
header.classList.add("mx_RoomSublist2_headerContainer_stickyBottom");
header.style.width = `${headerStickyWidth}px`;
gotBottom = true;
} else if (slRect.top < top) {
- console.log(`${header.textContent} is off the top`);
header.classList.add("mx_RoomSublist2_headerContainer_sticky");
header.classList.add("mx_RoomSublist2_headerContainer_stickyTop");
header.style.width = `${headerStickyWidth}px`;
diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx
index 5c23004a4f..2b0c549bd5 100644
--- a/src/components/views/rooms/RoomSublist2.tsx
+++ b/src/components/views/rooms/RoomSublist2.tsx
@@ -134,7 +134,28 @@ export default class RoomSublist2 extends React.Component {
this.forceUpdate(); // because the layout doesn't trigger a re-render
};
+ private onHeaderClick = (ev: React.MouseEvent) => {
+ let target = ev.target as HTMLDivElement;
+ if (!target.classList.contains('mx_RoomSublist2_headerText')) {
+ // If we don't have the headerText class, the user clicked the span in the headerText.
+ target = target.parentElement as HTMLDivElement;
+ }
+
+ const possibleSticky = target.parentElement;
+ const sublist = possibleSticky.parentElement.parentElement;
+ if (possibleSticky.classList.contains('mx_RoomSublist2_headerContainer_sticky')) {
+ // is sticky - jump to list
+ sublist.scrollIntoView({behavior: 'smooth'});
+ } else {
+ // on screen - toggle collapse
+ this.props.layout.isCollapsed = !this.props.layout.isCollapsed;
+ this.forceUpdate(); // because the layout doesn't trigger an update
+ }
+ };
+
private renderTiles(): React.ReactElement[] {
+ if (this.props.layout && this.props.layout.isCollapsed) return []; // don't waste time on rendering
+
const tiles: React.ReactElement[] = [];
if (this.props.rooms) {
@@ -249,6 +270,11 @@ export default class RoomSublist2 extends React.Component {
);
}
+ const collapseClasses = classNames({
+ 'mx_RoomSublist2_collapseBtn': true,
+ 'mx_RoomSublist2_collapseBtn_collapsed': this.props.layout && this.props.layout.isCollapsed,
+ });
+
const classes = classNames({
'mx_RoomSublist2_headerContainer': true,
'mx_RoomSublist2_headerContainer_withAux': !!addRoomButton,
@@ -264,7 +290,9 @@ export default class RoomSublist2 extends React.Component {
className={"mx_RoomSublist2_headerText"}
role="treeitem"
aria-level={1}
+ onClick={this.onHeaderClick}
>
+
{this.props.label}
{this.renderMenu()}
diff --git a/src/stores/room-list/ListLayout.ts b/src/stores/room-list/ListLayout.ts
index af9d6801a3..f17001f64e 100644
--- a/src/stores/room-list/ListLayout.ts
+++ b/src/stores/room-list/ListLayout.ts
@@ -21,11 +21,13 @@ const TILE_HEIGHT_PX = 44;
interface ISerializedListLayout {
numTiles: number;
showPreviews: boolean;
+ collapsed: boolean;
}
export class ListLayout {
private _n = 0;
private _previews = false;
+ private _collapsed = false;
constructor(public readonly tagId: TagID) {
const serialized = localStorage.getItem(this.key);
@@ -34,9 +36,19 @@ export class ListLayout {
const parsed = JSON.parse(serialized);
this._n = parsed.numTiles;
this._previews = parsed.showPreviews;
+ this._collapsed = parsed.collapsed;
}
}
+ public get isCollapsed(): boolean {
+ return this._collapsed;
+ }
+
+ public set isCollapsed(v: boolean) {
+ this._collapsed = v;
+ this.save();
+ }
+
public get showPreviews(): boolean {
return this._previews;
}
@@ -100,6 +112,7 @@ export class ListLayout {
return {
numTiles: this.visibleTiles,
showPreviews: this.showPreviews,
+ collapsed: this.isCollapsed,
};
}
}