Support list collapsing and jumping

Fixes https://github.com/vector-im/riot-web/issues/14036
This commit is contained in:
Travis Ralston 2020-06-15 19:47:25 -06:00
parent 8596905cee
commit 4186070489
5 changed files with 81 additions and 2 deletions

View file

@ -138,6 +138,34 @@ limitations under the License.
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
white-space: nowrap; 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; 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 { &.mx_RoomSublist2_minimized {

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>

After

Width:  |  Height:  |  Size: 270 B

View file

@ -105,13 +105,11 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
const header = sublist.querySelector<HTMLDivElement>(".mx_RoomSublist2_stickable"); const header = sublist.querySelector<HTMLDivElement>(".mx_RoomSublist2_stickable");
if (slRect.top + headerHeight > bottom && !gotBottom) { 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_sticky");
header.classList.add("mx_RoomSublist2_headerContainer_stickyBottom"); header.classList.add("mx_RoomSublist2_headerContainer_stickyBottom");
header.style.width = `${headerStickyWidth}px`; header.style.width = `${headerStickyWidth}px`;
gotBottom = true; gotBottom = true;
} else if (slRect.top < top) { } 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_sticky");
header.classList.add("mx_RoomSublist2_headerContainer_stickyTop"); header.classList.add("mx_RoomSublist2_headerContainer_stickyTop");
header.style.width = `${headerStickyWidth}px`; header.style.width = `${headerStickyWidth}px`;

View file

@ -134,7 +134,28 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
this.forceUpdate(); // because the layout doesn't trigger a re-render this.forceUpdate(); // because the layout doesn't trigger a re-render
}; };
private onHeaderClick = (ev: React.MouseEvent<HTMLDivElement>) => {
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[] { private renderTiles(): React.ReactElement[] {
if (this.props.layout && this.props.layout.isCollapsed) return []; // don't waste time on rendering
const tiles: React.ReactElement[] = []; const tiles: React.ReactElement[] = [];
if (this.props.rooms) { if (this.props.rooms) {
@ -249,6 +270,11 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
); );
} }
const collapseClasses = classNames({
'mx_RoomSublist2_collapseBtn': true,
'mx_RoomSublist2_collapseBtn_collapsed': this.props.layout && this.props.layout.isCollapsed,
});
const classes = classNames({ const classes = classNames({
'mx_RoomSublist2_headerContainer': true, 'mx_RoomSublist2_headerContainer': true,
'mx_RoomSublist2_headerContainer_withAux': !!addRoomButton, 'mx_RoomSublist2_headerContainer_withAux': !!addRoomButton,
@ -264,7 +290,9 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
className={"mx_RoomSublist2_headerText"} className={"mx_RoomSublist2_headerText"}
role="treeitem" role="treeitem"
aria-level={1} aria-level={1}
onClick={this.onHeaderClick}
> >
<span className={collapseClasses} />
<span>{this.props.label}</span> <span>{this.props.label}</span>
</AccessibleButton> </AccessibleButton>
{this.renderMenu()} {this.renderMenu()}

View file

@ -21,11 +21,13 @@ const TILE_HEIGHT_PX = 44;
interface ISerializedListLayout { interface ISerializedListLayout {
numTiles: number; numTiles: number;
showPreviews: boolean; showPreviews: boolean;
collapsed: boolean;
} }
export class ListLayout { export class ListLayout {
private _n = 0; private _n = 0;
private _previews = false; private _previews = false;
private _collapsed = false;
constructor(public readonly tagId: TagID) { constructor(public readonly tagId: TagID) {
const serialized = localStorage.getItem(this.key); const serialized = localStorage.getItem(this.key);
@ -34,9 +36,19 @@ export class ListLayout {
const parsed = <ISerializedListLayout>JSON.parse(serialized); const parsed = <ISerializedListLayout>JSON.parse(serialized);
this._n = parsed.numTiles; this._n = parsed.numTiles;
this._previews = parsed.showPreviews; 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 { public get showPreviews(): boolean {
return this._previews; return this._previews;
} }
@ -100,6 +112,7 @@ export class ListLayout {
return { return {
numTiles: this.visibleTiles, numTiles: this.visibleTiles,
showPreviews: this.showPreviews, showPreviews: this.showPreviews,
collapsed: this.isCollapsed,
}; };
} }
} }