DEV: Refactor header offset calculations (#29398)

This commit is contained in:
Penar Musaraj 2024-10-31 09:50:01 -04:00 committed by GitHub
parent 6c91148db8
commit 041ac3d8b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 51 additions and 59 deletions

View File

@ -22,6 +22,7 @@ import Header from "./header";
let _menuPanelClassesToForceDropdown = [];
const PANEL_WIDTH = 340;
const DEBOUNCE_HEADER_DELAY = 10;
export default class GlimmerSiteHeader extends Component {
@service appEvents;
@ -36,6 +37,7 @@ export default class GlimmerSiteHeader extends Component {
@tracked _dockedHeader = false;
_animate = false;
_headerWrap;
_mainOutletWrapper;
_swipeMenuOrigin;
_applicationElement;
_resizeObserver;
@ -69,42 +71,51 @@ export default class GlimmerSiteHeader extends Component {
}
@bind
updateHeaderOffset() {
// Safari likes overscolling the page (on both iOS and macOS).
// This shows up as a negative value in window.scrollY.
// We can use this to offset the headerWrap's top offset to avoid
// jitteriness and bad positioning.
const windowOverscroll = Math.min(0, window.scrollY);
// The headerWrap's top offset can also be a negative value on Safari,
// because of the changing height of the viewport (due to the URL bar).
// For our use case, it's best to ensure this is clamped to 0.
const headerWrapTop = Math.max(
updateOffsets() {
// clamping to 0 to prevent negative values (hello, Safari)
const headerWrapBottom = Math.max(
0,
Math.floor(this._headerWrap.getBoundingClientRect().top)
Math.floor(this._headerWrap.getBoundingClientRect().bottom)
);
let mainOutletOffsetTop = Math.max(
0,
Math.floor(this._mainOutletWrapper.getBoundingClientRect().top) -
headerWrapBottom
);
let offsetTop = headerWrapTop + windowOverscroll;
if (DEBUG && isTesting()) {
offsetTop -= document
mainOutletOffsetTop -= document
.getElementById("ember-testing-container")
.getBoundingClientRect().top;
offsetTop -= 1; // For 1px border on testing container
mainOutletOffsetTop -= 1; // For 1px border on testing container
}
const documentStyle = document.documentElement.style;
const currentValue =
parseInt(documentStyle.getPropertyValue("--header-offset"), 10) || 0;
const newValue = this._headerWrap.offsetHeight + offsetTop;
if (currentValue !== newValue) {
documentStyle.setProperty("--header-offset", `${newValue}px`);
const docStyle = document.documentElement.style;
const currentHeaderOffset =
parseInt(docStyle.getPropertyValue("--header-offset"), 10) || 0;
const newHeaderOffset = headerWrapBottom;
if (currentHeaderOffset !== newHeaderOffset) {
docStyle.setProperty("--header-offset", `${newHeaderOffset}px`);
}
const currentMainOutletOffset =
parseInt(docStyle.getPropertyValue("--main-outlet-offset"), 10) || 0;
const newMainOutletOffset = headerWrapBottom + mainOutletOffsetTop;
if (currentMainOutletOffset !== newMainOutletOffset) {
docStyle.setProperty("--main-outlet-offset", `${newMainOutletOffset}px`);
}
}
@debounce(DEBOUNCE_HEADER_DELAY)
_recalculateHeaderOffset() {
this._scheduleUpdateOffsets();
}
@bind
_onScroll() {
schedule("afterRender", this.updateHeaderOffset);
_scheduleUpdateOffsets() {
schedule("afterRender", this.updateOffsets);
}
@action
@ -119,17 +130,13 @@ export default class GlimmerSiteHeader extends Component {
}
this._headerWrap = document.querySelector(".d-header-wrap");
this._mainOutletWrapper = document.querySelector("#main-outlet-wrapper");
if (this._headerWrap) {
schedule("afterRender", () => {
this.headerElement = this._headerWrap.querySelector("header.d-header");
this.updateHeaderOffset();
document.documentElement.style.setProperty(
"--header-top",
`${this.headerElement.offsetTop}px`
);
});
window.addEventListener("scroll", this._onScroll, {
window.addEventListener("scroll", this._recalculateHeaderOffset, {
passive: true,
});
@ -137,20 +144,11 @@ export default class GlimmerSiteHeader extends Component {
const dirs = ["up", "down"];
this._itsatrap.bind(dirs, (e) => this._handleArrowKeysNav(e));
this._resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
if (entry.contentRect) {
const headerTop = this.headerElement?.offsetTop;
document.documentElement.style.setProperty(
"--header-top",
`${headerTop}px`
);
this.updateHeaderOffset();
}
}
this._resizeObserver = new ResizeObserver(() => {
this._recalculateHeaderOffset();
});
this._resizeObserver.observe(this._headerWrap);
this._resizeObserver.observe(document.querySelector(".discourse-root"));
}
}
@ -410,7 +408,7 @@ export default class GlimmerSiteHeader extends Component {
this._itsatrap?.destroy();
this._itsatrap = null;
window.removeEventListener("scroll", this._onScroll);
window.removeEventListener("scroll", this._recalculateHeaderOffset);
this._resizeObserver?.disconnect();
}

View File

@ -16,7 +16,7 @@
// positions are relative to the .d-header .panel div
top: 100%; // directly underneath .panel
right: -10px; // 10px to the right of .panel - adjust as needed
max-height: 80vh;
max-height: calc(100dvh - var(--header-offset) - 1em);
border-radius: var(--d-border-radius-large);
}
@ -660,14 +660,14 @@
width: 100%;
position: fixed;
background-color: rgba(0, 0, 0, 0.3);
top: var(--header-top);
top: 0;
left: 0;
display: none;
touch-action: pan-y pinch-zoom;
}
.menu-panel.slide-in {
top: var(--header-top);
top: 0;
box-sizing: border-box;
// ensure there's always space to click outside on tiny devices
max-width: 90vw;
@ -678,11 +678,5 @@
}
box-shadow: 0px 0 30px -2px rgba(0, 0, 0, 0.5);
--base-height: calc(var(--100dvh) - var(--header-top));
height: var(--base-height);
html.footer-nav-ipad & {
height: calc(var(--base-height) - var(--footer-nav-height));
}
height: var(--100dvh);
}

View File

@ -45,7 +45,7 @@
display: flex;
grid-area: sidebar;
position: sticky;
top: var(--header-offset);
top: var(--main-outlet-offset);
background: var(--d-sidebar-background);
@include unselectable;
@ -57,7 +57,7 @@
}
height: calc(
var(--composer-vh, var(--1dvh)) * 100 - var(--header-offset, 0px)
var(--composer-vh, var(--1dvh)) * 100 - var(--main-outlet-offset, 0px)
);
align-self: start;

View File

@ -5,7 +5,6 @@
&.drop-down {
.panel-body {
max-height: calc(100vh - var(--header-offset));
max-width: calc(100vw - 2em);
}
}

View File

@ -191,7 +191,7 @@ body.has-full-page-chat {
.c-navbar-container {
position: sticky;
top: var(--header-offset);
top: var(--main-outlet-offset);
}
.chat-messages-scroller {

View File

@ -87,7 +87,7 @@
resize: none;
max-height: calc(
(
var(--100dvh) - var(--header-offset, 0px) -
var(--100dvh) - var(--main-outlet-offset, 0px) -
var(--chat-header-offset, 0px)
) / 100 * 25
);

View File

@ -2,7 +2,8 @@
// desktop and mobile
// -1px is for the bottom border of the chat navbar
$base-height: calc(
var(--composer-vh, 1vh) * 100 - var(--header-offset, 0px) - 1px - $inset
var(--composer-vh, 1vh) * 100 - var(--main-outlet-offset, 0px) - 1px -
$inset
);
height: calc($base-height - var(--composer-height, 0px));

View File

@ -1,7 +1,7 @@
.has-full-page-chat:not(.discourse-sidebar) {
.full-page-chat {
.channels-list {
height: calc(100vh - var(--header-offset));
height: calc(100vh - var(--main-outlet-offset));
border-right: 1px solid var(--primary-low);
background: var(--secondary);
overflow-y: auto;