blob: 3bccb7614b1affc3fc16c22746ed3ecd801b4c20 [file] [log] [blame]
/**
* @license
* Copyright 2021 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
const headerHeight = 3.5;
/**
* MainLayoutController calculates dynamic height values for header elements
* to support variable size sticky positioned elements in the header so that
* banners and breadcumbs may overflow to multiple lines.
*/
export class MainLayoutController {
private headerObserver: IntersectionObserver;
private navObserver: IntersectionObserver;
private asideObserver: IntersectionObserver;
constructor(
private mainHeader?: Element | null,
private mainNav?: Element | null,
private mainAside?: Element | null
) {
this.headerObserver = new IntersectionObserver(
([e]) => {
if (e.intersectionRatio < 1) {
for (const x of document.querySelectorAll('[class^="go-Main-header"')) {
x.setAttribute('data-fixed', 'true');
}
} else {
for (const x of document.querySelectorAll('[class^="go-Main-header"')) {
x.removeAttribute('data-fixed');
}
this.handleResize();
}
},
{ threshold: 1, rootMargin: `${headerHeight * 16}px` }
);
this.navObserver = new IntersectionObserver(
([e]) => {
if (e.intersectionRatio < 1) {
this.mainNav?.classList.add('go-Main-nav--fixed');
this.mainNav?.setAttribute('data-fixed', 'true');
} else {
this.mainNav?.classList.remove('go-Main-nav--fixed');
this.mainNav?.removeAttribute('data-fixed');
}
},
{ threshold: 1, rootMargin: `-${headerHeight * 16 + 10}px` }
);
this.asideObserver = new IntersectionObserver(
([e]) => {
if (e.intersectionRatio < 1) {
this.mainHeader?.setAttribute('data-raised', 'true');
} else {
this.mainHeader?.removeAttribute('data-raised');
}
},
{ threshold: 1, rootMargin: `-${headerHeight * 16 + 20}px 0px 0px 0px` }
);
this.init();
}
private init() {
this.handleResize();
window.addEventListener('resize', this.handleResize);
this.mainHeader?.addEventListener('dblclick', this.handleDoubleClick);
const siteHeader = document.querySelector('.js-siteHeader');
if (this.mainHeader?.hasChildNodes() && siteHeader) {
const headerSentinel = document.createElement('div');
siteHeader.prepend(headerSentinel);
this.headerObserver.observe(headerSentinel);
}
if (this.mainNav?.hasChildNodes()) {
const navSentinel = document.createElement('div');
this.mainNav.prepend(navSentinel);
this.navObserver.observe(navSentinel);
}
if (this.mainAside) {
const asideSentinel = document.createElement('div');
this.mainAside.prepend(asideSentinel);
this.asideObserver.observe(asideSentinel);
}
}
private handleDoubleClick: EventListener = e => {
const target = e.target;
if (target === this.mainHeader?.lastElementChild) {
window.getSelection()?.removeAllRanges();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
};
private handleResize = () => {
const setProp = (name: string, value: string) =>
document.documentElement.style.setProperty(name, value);
setProp('--js-unit-header-height', '0');
setTimeout(() => {
const mainHeaderHeight = (this.mainHeader?.getBoundingClientRect().height ?? 0) / 16;
setProp('--js-unit-header-height', `${mainHeaderHeight}rem`);
setProp('--js-sticky-header-height', `${headerHeight}rem`);
setProp('--js-unit-header-top', `${(mainHeaderHeight - headerHeight) * -1}rem`);
});
};
}
const el = <T extends HTMLElement>(selector: string) => document.querySelector<T>(selector);
new MainLayoutController(el('.js-mainHeader'), el('.js-mainNav'), el('.js-mainAside'));