Files
milinda-pitch/js/components/site-header.js
2026-01-15 10:40:31 +01:00

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);