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 = []; let _menuPanelClassesToForceDropdown = [];
const PANEL_WIDTH = 340; const PANEL_WIDTH = 340;
const DEBOUNCE_HEADER_DELAY = 10;
export default class GlimmerSiteHeader extends Component { export default class GlimmerSiteHeader extends Component {
@service appEvents; @service appEvents;
@ -36,6 +37,7 @@ export default class GlimmerSiteHeader extends Component {
@tracked _dockedHeader = false; @tracked _dockedHeader = false;
_animate = false; _animate = false;
_headerWrap; _headerWrap;
_mainOutletWrapper;
_swipeMenuOrigin; _swipeMenuOrigin;
_applicationElement; _applicationElement;
_resizeObserver; _resizeObserver;
@ -69,42 +71,51 @@ export default class GlimmerSiteHeader extends Component {
} }
@bind @bind
updateHeaderOffset() { updateOffsets() {
// Safari likes overscolling the page (on both iOS and macOS). // clamping to 0 to prevent negative values (hello, Safari)
// This shows up as a negative value in window.scrollY. const headerWrapBottom = Math.max(
// 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(
0, 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()) { if (DEBUG && isTesting()) {
offsetTop -= document mainOutletOffsetTop -= document
.getElementById("ember-testing-container") .getElementById("ember-testing-container")
.getBoundingClientRect().top; .getBoundingClientRect().top;
offsetTop -= 1; // For 1px border on testing container mainOutletOffsetTop -= 1; // For 1px border on testing container
} }
const documentStyle = document.documentElement.style; const docStyle = document.documentElement.style;
const currentValue = const currentHeaderOffset =
parseInt(documentStyle.getPropertyValue("--header-offset"), 10) || 0; parseInt(docStyle.getPropertyValue("--header-offset"), 10) || 0;
const newValue = this._headerWrap.offsetHeight + offsetTop; const newHeaderOffset = headerWrapBottom;
if (currentValue !== newValue) { if (currentHeaderOffset !== newHeaderOffset) {
documentStyle.setProperty("--header-offset", `${newValue}px`); 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 @bind
_onScroll() { _scheduleUpdateOffsets() {
schedule("afterRender", this.updateHeaderOffset); schedule("afterRender", this.updateOffsets);
} }
@action @action
@ -119,17 +130,13 @@ export default class GlimmerSiteHeader extends Component {
} }
this._headerWrap = document.querySelector(".d-header-wrap"); this._headerWrap = document.querySelector(".d-header-wrap");
this._mainOutletWrapper = document.querySelector("#main-outlet-wrapper");
if (this._headerWrap) { if (this._headerWrap) {
schedule("afterRender", () => { schedule("afterRender", () => {
this.headerElement = this._headerWrap.querySelector("header.d-header"); 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, passive: true,
}); });
@ -137,20 +144,11 @@ export default class GlimmerSiteHeader extends Component {
const dirs = ["up", "down"]; const dirs = ["up", "down"];
this._itsatrap.bind(dirs, (e) => this._handleArrowKeysNav(e)); this._itsatrap.bind(dirs, (e) => this._handleArrowKeysNav(e));
this._resizeObserver = new ResizeObserver((entries) => { this._resizeObserver = new ResizeObserver(() => {
for (let entry of entries) { this._recalculateHeaderOffset();
if (entry.contentRect) {
const headerTop = this.headerElement?.offsetTop;
document.documentElement.style.setProperty(
"--header-top",
`${headerTop}px`
);
this.updateHeaderOffset();
}
}
}); });
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?.destroy();
this._itsatrap = null; this._itsatrap = null;
window.removeEventListener("scroll", this._onScroll); window.removeEventListener("scroll", this._recalculateHeaderOffset);
this._resizeObserver?.disconnect(); this._resizeObserver?.disconnect();
} }

View File

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

View File

@ -45,7 +45,7 @@
display: flex; display: flex;
grid-area: sidebar; grid-area: sidebar;
position: sticky; position: sticky;
top: var(--header-offset); top: var(--main-outlet-offset);
background: var(--d-sidebar-background); background: var(--d-sidebar-background);
@include unselectable; @include unselectable;
@ -57,7 +57,7 @@
} }
height: calc( 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; align-self: start;

View File

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

View File

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

View File

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

View File

@ -2,7 +2,8 @@
// desktop and mobile // desktop and mobile
// -1px is for the bottom border of the chat navbar // -1px is for the bottom border of the chat navbar
$base-height: calc( $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)); height: calc($base-height - var(--composer-height, 0px));

View File

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