DEV: Refactor header offset calculations (#29398)
This commit is contained in:
parent
6c91148db8
commit
041ac3d8b7
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue