294 lines
8.0 KiB
Plaintext
294 lines
8.0 KiB
Plaintext
---
|
|
interface Props {
|
|
heroMoreId: string;
|
|
heroToggleId: string;
|
|
sectionHeadSelector?: string;
|
|
mobileBreakpoint?: number;
|
|
autoCollapseDelta?: number;
|
|
}
|
|
|
|
const {
|
|
heroMoreId,
|
|
heroToggleId,
|
|
sectionHeadSelector = ".glossary-portal-section__head",
|
|
mobileBreakpoint = 860,
|
|
autoCollapseDelta = 160,
|
|
} = Astro.props;
|
|
---
|
|
|
|
<script
|
|
is:inline
|
|
define:vars={{ heroMoreId, heroToggleId, sectionHeadSelector, mobileBreakpoint, autoCollapseDelta }}
|
|
>
|
|
(() => {
|
|
const boot = () => {
|
|
const body = document.body;
|
|
const root = document.documentElement;
|
|
const hero = document.querySelector("[data-glossary-portal-hero]");
|
|
const follow = document.getElementById("reading-follow");
|
|
const heroMore = document.getElementById(heroMoreId);
|
|
const heroToggle = document.getElementById(heroToggleId);
|
|
|
|
if (!body || !root || !hero || !follow) return;
|
|
|
|
const BODY_CLASS = "is-glossary-portal-page";
|
|
const FOLLOW_ON_CLASS = "glossary-portal-follow-on";
|
|
const EXPANDED_CLASS = "glossary-portal-hero-expanded";
|
|
const mqMobile = window.matchMedia(`(max-width: ${mobileBreakpoint}px)`);
|
|
|
|
let expandedAtY = null;
|
|
let lastScrollY = window.scrollY || 0;
|
|
let raf = 0;
|
|
|
|
body.classList.add(BODY_CLASS);
|
|
|
|
const heroHeight = () =>
|
|
Math.max(0, Math.round(hero.getBoundingClientRect().height || 0));
|
|
|
|
const stripLocalSticky = () => {
|
|
document.querySelectorAll(sectionHeadSelector).forEach((el) => {
|
|
el.classList.remove("is-sticky");
|
|
el.removeAttribute("data-sticky-active");
|
|
});
|
|
};
|
|
|
|
const computeFollowOn = () =>
|
|
!mqMobile.matches &&
|
|
follow.classList.contains("is-on") &&
|
|
follow.style.display !== "none" &&
|
|
follow.getAttribute("aria-hidden") !== "true";
|
|
|
|
const applyLocalStickyHeight = () => {
|
|
const h = mqMobile.matches ? 0 : heroHeight();
|
|
|
|
if (typeof window.__archiSetLocalStickyHeight === "function") {
|
|
window.__archiSetLocalStickyHeight(h);
|
|
} else {
|
|
root.style.setProperty("--glossary-local-sticky-h", `${h}px`);
|
|
}
|
|
};
|
|
|
|
const syncFollowState = () => {
|
|
const on = computeFollowOn();
|
|
body.classList.toggle(FOLLOW_ON_CLASS, on);
|
|
return on;
|
|
};
|
|
|
|
const collapseHero = () => {
|
|
if (!body.classList.contains(EXPANDED_CLASS)) return;
|
|
|
|
body.classList.remove(EXPANDED_CLASS);
|
|
expandedAtY = null;
|
|
|
|
if (heroMore) {
|
|
heroMore.setAttribute("aria-hidden", "true");
|
|
}
|
|
|
|
if (heroToggle) {
|
|
heroToggle.hidden = false;
|
|
heroToggle.setAttribute("aria-expanded", "false");
|
|
}
|
|
|
|
try {
|
|
window.__archiUpdateFollow?.();
|
|
} catch {}
|
|
|
|
schedule();
|
|
};
|
|
|
|
const expandHero = () => {
|
|
body.classList.add(EXPANDED_CLASS);
|
|
expandedAtY = window.scrollY || 0;
|
|
|
|
if (heroMore) {
|
|
heroMore.setAttribute("aria-hidden", "false");
|
|
}
|
|
|
|
if (heroToggle) {
|
|
heroToggle.hidden = true;
|
|
heroToggle.setAttribute("aria-expanded", "true");
|
|
}
|
|
|
|
try {
|
|
window.__archiUpdateFollow?.();
|
|
} catch {}
|
|
|
|
schedule();
|
|
};
|
|
|
|
const syncHeroState = () => {
|
|
const followOn = computeFollowOn();
|
|
const expanded = body.classList.contains(EXPANDED_CLASS);
|
|
const collapsed = followOn && !expanded;
|
|
|
|
if (!followOn || mqMobile.matches) {
|
|
body.classList.remove(EXPANDED_CLASS);
|
|
expandedAtY = null;
|
|
|
|
if (heroMore) {
|
|
heroMore.setAttribute("aria-hidden", "false");
|
|
}
|
|
|
|
if (heroToggle) {
|
|
heroToggle.hidden = true;
|
|
heroToggle.setAttribute("aria-expanded", "false");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (heroMore) {
|
|
heroMore.setAttribute("aria-hidden", collapsed ? "true" : "false");
|
|
}
|
|
|
|
if (heroToggle) {
|
|
heroToggle.hidden = !collapsed;
|
|
heroToggle.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
}
|
|
};
|
|
|
|
const maybeAutoCollapseOnScroll = () => {
|
|
if (mqMobile.matches) {
|
|
lastScrollY = window.scrollY || 0;
|
|
return;
|
|
}
|
|
|
|
if (!computeFollowOn()) {
|
|
lastScrollY = window.scrollY || 0;
|
|
return;
|
|
}
|
|
|
|
if (!body.classList.contains(EXPANDED_CLASS)) {
|
|
lastScrollY = window.scrollY || 0;
|
|
return;
|
|
}
|
|
|
|
if (expandedAtY == null) {
|
|
lastScrollY = window.scrollY || 0;
|
|
return;
|
|
}
|
|
|
|
const currentY = window.scrollY || 0;
|
|
const scrollingDown = currentY > lastScrollY;
|
|
const delta = currentY - expandedAtY;
|
|
|
|
if (scrollingDown && delta >= autoCollapseDelta) {
|
|
collapseHero();
|
|
}
|
|
|
|
lastScrollY = currentY;
|
|
};
|
|
|
|
const syncAll = () => {
|
|
stripLocalSticky();
|
|
syncFollowState();
|
|
syncHeroState();
|
|
applyLocalStickyHeight();
|
|
};
|
|
|
|
const schedule = () => {
|
|
if (raf) return;
|
|
raf = requestAnimationFrame(() => {
|
|
raf = 0;
|
|
requestAnimationFrame(syncAll);
|
|
});
|
|
};
|
|
|
|
heroToggle?.addEventListener("click", expandHero);
|
|
|
|
const onScroll = () => {
|
|
maybeAutoCollapseOnScroll();
|
|
schedule();
|
|
};
|
|
|
|
const followObserver = new MutationObserver(schedule);
|
|
followObserver.observe(follow, {
|
|
attributes: true,
|
|
attributeFilter: ["class", "style", "aria-hidden"],
|
|
subtree: false,
|
|
});
|
|
|
|
const heroResizeObserver =
|
|
typeof ResizeObserver !== "undefined"
|
|
? new ResizeObserver(schedule)
|
|
: null;
|
|
|
|
heroResizeObserver?.observe(hero);
|
|
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
window.addEventListener("resize", schedule);
|
|
window.addEventListener("pageshow", schedule);
|
|
|
|
if (document.fonts?.ready) {
|
|
document.fonts.ready.then(schedule).catch(() => {});
|
|
}
|
|
|
|
if (mqMobile.addEventListener) {
|
|
mqMobile.addEventListener("change", schedule);
|
|
} else if (mqMobile.addListener) {
|
|
mqMobile.addListener(schedule);
|
|
}
|
|
|
|
schedule();
|
|
};
|
|
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", boot, { once: true });
|
|
} else {
|
|
boot();
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
<style>
|
|
:global(body.is-glossary-portal-page #reading-follow){
|
|
z-index: 10;
|
|
}
|
|
|
|
:global(body.is-glossary-portal-page.glossary-portal-follow-on .glossary-portal-hero){
|
|
margin-bottom: 0;
|
|
padding: 12px 16px 14px;
|
|
row-gap: 10px;
|
|
border-bottom-left-radius: 0;
|
|
border-bottom-right-radius: 0;
|
|
}
|
|
|
|
:global(body.is-glossary-portal-page.glossary-portal-follow-on .glossary-portal-hero h1){
|
|
font-size: clamp(1.9rem, 3.2vw, 2.55rem);
|
|
}
|
|
|
|
:global(body.is-glossary-portal-page.glossary-portal-follow-on .glossary-portal-hero__intro){
|
|
max-width: var(--portal-hero-follow-intro-max-w, 68ch);
|
|
font-size: .98rem;
|
|
line-height: 1.48;
|
|
}
|
|
|
|
:global(body.is-glossary-portal-page.glossary-portal-follow-on:not(.glossary-portal-hero-expanded) .glossary-portal-hero__more){
|
|
max-height: 0;
|
|
opacity: 0;
|
|
overflow: hidden;
|
|
pointer-events: none;
|
|
}
|
|
|
|
:global(body.is-glossary-portal-page.glossary-portal-follow-on:not(.glossary-portal-hero-expanded) .glossary-portal-hero__toggle){
|
|
display: inline-flex;
|
|
}
|
|
|
|
:global(body.is-glossary-portal-page.glossary-portal-follow-on #reading-follow .reading-follow__inner){
|
|
border-top-left-radius: 0;
|
|
border-top-right-radius: 0;
|
|
}
|
|
|
|
:global(body.is-glossary-portal-page .glossary-portal-section__head.is-sticky),
|
|
:global(body.is-glossary-portal-page .glossary-portal-section__head[data-sticky-active="true"]){
|
|
position: static !important;
|
|
top: auto !important;
|
|
z-index: auto !important;
|
|
padding: 0 !important;
|
|
border: 0 !important;
|
|
background: transparent !important;
|
|
box-shadow: none !important;
|
|
backdrop-filter: none !important;
|
|
-webkit-backdrop-filter: none !important;
|
|
}
|
|
</style> |