Initial attempt at sticky headers
Docs enclosed in diff.
This commit is contained in:
@@ -86,6 +86,70 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Apply this on resize, init, etc for reliability
|
||||
private onScroll = (ev: React.MouseEvent<HTMLDivElement>) => {
|
||||
const list = ev.target as HTMLDivElement;
|
||||
const rlRect = list.getBoundingClientRect();
|
||||
const bottom = rlRect.bottom;
|
||||
const top = rlRect.top;
|
||||
const sublists = list.querySelectorAll<HTMLDivElement>(".mx_RoomSublist2");
|
||||
const headerHeight = 32; // Note: must match the CSS!
|
||||
const headerRightMargin = 24; // calculated from margins and widths to align with non-sticky tiles
|
||||
|
||||
const headerStickyWidth = rlRect.width - headerRightMargin;
|
||||
|
||||
let gotBottom = false;
|
||||
for (const sublist of sublists) {
|
||||
const slRect = sublist.getBoundingClientRect();
|
||||
|
||||
const header = sublist.querySelector<HTMLDivElement>(".mx_RoomSublist2_headerText");
|
||||
|
||||
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`;
|
||||
header.style.top = `${rlRect.top}px`;
|
||||
} else {
|
||||
header.classList.remove("mx_RoomSublist2_headerContainer_sticky");
|
||||
header.classList.remove("mx_RoomSublist2_headerContainer_stickyTop");
|
||||
header.classList.remove("mx_RoomSublist2_headerContainer_stickyBottom");
|
||||
header.style.width = `unset`;
|
||||
}
|
||||
|
||||
// const name = header.textContent;
|
||||
// if (hRect.bottom + headerHeight < top) {
|
||||
// // Before the content (top of list)
|
||||
// header.classList.add(
|
||||
// "mx_RoomSublist2_headerContainer_sticky",
|
||||
// "mx_RoomSublist2_headerContainer_stickyTop",
|
||||
// );
|
||||
// } else {
|
||||
// header.classList.remove(
|
||||
// "mx_RoomSublist2_headerContainer_sticky",
|
||||
// "mx_RoomSublist2_headerContainer_stickyTop",
|
||||
// "mx_RoomSublist2_headerContainer_stickyBottom",
|
||||
// );
|
||||
// }
|
||||
|
||||
// if (!hitMiddle && (headerHeight + hRect.top) >= bottom) {
|
||||
// // if we got here, the header is visible
|
||||
// hitMiddle = true;
|
||||
// header.style.backgroundColor = 'red';
|
||||
// } else {
|
||||
// header.style.top = "0px";
|
||||
// header.style.bottom = "unset";
|
||||
// header.style.backgroundColor = "unset";
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
private renderHeader(): React.ReactNode {
|
||||
// TODO: Update when profile info changes
|
||||
// TODO: Presence
|
||||
@@ -191,7 +255,7 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
|
||||
<aside className="mx_LeftPanel2_roomListContainer">
|
||||
{this.renderHeader()}
|
||||
{this.renderSearchExplore()}
|
||||
<div className="mx_LeftPanel2_actualRoomListContainer">
|
||||
<div className="mx_LeftPanel2_actualRoomListContainer" onScroll={this.onScroll}>
|
||||
{roomList}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
30
src/utils/css.ts
Normal file
30
src/utils/css.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
export function addClass(classes: string, clazz: string): string {
|
||||
if (!classes.includes(clazz)) return `${classes} ${clazz}`;
|
||||
return classes;
|
||||
}
|
||||
|
||||
export function removeClass(classes: string, clazz: string): string {
|
||||
const idx = classes.indexOf(clazz);
|
||||
if (idx >= 0) {
|
||||
const beforeStr = classes.substring(0, idx);
|
||||
const afterStr = classes.substring(idx + clazz.length);
|
||||
return `${beforeStr} ${afterStr}`.trim();
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
Reference in New Issue
Block a user