100 lines
2.5 KiB
JavaScript
100 lines
2.5 KiB
JavaScript
/**
|
|
* Site Header Component
|
|
* Sticky header container that holds top-bar, navigation, and search
|
|
* Collapses on scroll down, expands on scroll up
|
|
* Based on: https://stackoverflow.com/questions/63902512/
|
|
*/
|
|
class SiteHeader extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.attachShadow({ mode: "open" });
|
|
this.prevScrollPos = 0;
|
|
this.headerOffset = 0;
|
|
this.collapsibleHeight = 0;
|
|
this.handleScroll = this.handleScroll.bind(this);
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.render();
|
|
this.collapsible = this.shadowRoot.querySelector(".collapsible");
|
|
window.addEventListener("scroll", this.handleScroll, { passive: true });
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
window.removeEventListener("scroll", this.handleScroll);
|
|
}
|
|
|
|
handleScroll() {
|
|
const currentScrollPos = window.scrollY;
|
|
const isScrollingDown = currentScrollPos > this.prevScrollPos;
|
|
const delta = currentScrollPos - this.prevScrollPos;
|
|
|
|
if (!this.collapsibleHeight && this.collapsible) {
|
|
this.collapsibleHeight = this.collapsible.scrollHeight;
|
|
}
|
|
|
|
if (isScrollingDown) {
|
|
this.headerOffset = Math.min(
|
|
this.collapsibleHeight,
|
|
Math.max(0, this.headerOffset + delta)
|
|
);
|
|
this.classList.remove("reveal");
|
|
} else if (delta < 0) {
|
|
this.headerOffset = 0;
|
|
this.classList.add("reveal");
|
|
}
|
|
|
|
this.collapsible.style.transform = `translateY(-${this.headerOffset}px)`;
|
|
|
|
this.prevScrollPos = currentScrollPos;
|
|
}
|
|
|
|
render() {
|
|
this.shadowRoot.innerHTML = `
|
|
<style>
|
|
:host {
|
|
display: block;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
background-color: transparent;
|
|
}
|
|
|
|
.header {
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.collapsible {
|
|
position: relative;
|
|
z-index: 1;
|
|
overflow: hidden;
|
|
transform: translateY(0);
|
|
transition: none;
|
|
will-change: transform;
|
|
}
|
|
|
|
:host(.reveal) .collapsible {
|
|
transition: transform 220ms ease;
|
|
}
|
|
|
|
::slotted(top-bar) {
|
|
position: relative;
|
|
z-index: 2;
|
|
border-bottom: 1px solid var(--color-border, #e2e8f0);
|
|
}
|
|
</style>
|
|
<header class="header">
|
|
<slot name="top-bar"></slot>
|
|
<div class="collapsible">
|
|
<slot name="nav"></slot>
|
|
<slot name="search"></slot>
|
|
</div>
|
|
</header>
|
|
`;
|
|
}
|
|
}
|
|
|
|
customElements.define("site-header", SiteHeader);
|