diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index 57f690346b..eaa22a3efa 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -127,6 +127,13 @@ $tagPanelWidth: 70px; // only applies in this file, used for calculations overflow: hidden; min-height: 0; + &.stickyBottom { + padding-bottom: 32px; + } + + &.stickyTop { + padding-top: 32px; + } } .mx_LeftPanel2_actualRoomListContainer { diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index 037c3bd4ff..51dc4c0c4c 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -153,11 +153,16 @@ export default class LeftPanel2 extends React.Component { header.style.width = `${headerStickyWidth}px`; header.style.removeProperty("top"); gotBottom = true; - } else if ((slRect.top - (headerHeight / 3)) < top) { + } else if (((slRect.top - (headerHeight * 0.6) + headerHeight) < top) || sublist === sublists[0]) { + // the header should become sticky once it is 60% or less out of view at the top. + // We also add headerHeight because the sticky header is put above the scrollable area, + // into the padding of .mx_LeftPanel2_roomListWrapper, + // by subtracting headerHeight from the top below. + // We also always try to make the first sublist header sticky. 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`; + header.style.top = `${rlRect.top - headerHeight}px`; if (lastTopHeader) { lastTopHeader.style.display = "none"; } @@ -172,6 +177,26 @@ export default class LeftPanel2 extends React.Component { header.style.removeProperty("top"); } } + + // add appropriate sticky classes to wrapper so it has + // the necessary top/bottom padding to put the sticky header in + const listWrapper = list.parentElement; + if (gotBottom) { + listWrapper.classList.add("stickyBottom"); + } else { + listWrapper.classList.remove("stickyBottom"); + } + if (lastTopHeader) { + listWrapper.classList.add("stickyTop"); + } else { + listWrapper.classList.remove("stickyTop"); + } + + // ensure scroll doesn't go above the gap left by the header of + // the first sublist always being sticky if no other header is sticky + if (list.scrollTop < headerHeight) { + list.scrollTop = headerHeight; + } } // TODO: Improve header reliability: https://github.com/vector-im/riot-web/issues/14232