Polish glossary complete index compact sticky behavior
This commit is contained in:
@@ -5,6 +5,7 @@ interface Props {
|
||||
sectionHeadSelector?: string;
|
||||
mobileBreakpoint?: number;
|
||||
autoCollapseDelta?: number;
|
||||
compactSticky?: boolean;
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -13,12 +14,13 @@ const {
|
||||
sectionHeadSelector = ".glossary-portal-section__head",
|
||||
mobileBreakpoint = 860,
|
||||
autoCollapseDelta = 160,
|
||||
compactSticky = false,
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<script
|
||||
is:inline
|
||||
define:vars={{ heroMoreId, heroToggleId, sectionHeadSelector, mobileBreakpoint, autoCollapseDelta }}
|
||||
define:vars={{ heroMoreId, heroToggleId, sectionHeadSelector, mobileBreakpoint, autoCollapseDelta, compactSticky }}
|
||||
>
|
||||
(() => {
|
||||
const boot = () => {
|
||||
@@ -53,6 +55,14 @@ const {
|
||||
const isCompactViewport = () =>
|
||||
mqMobile.matches || mqSmallLandscape.matches;
|
||||
|
||||
const keepPortalPremiumOnCompact = () =>
|
||||
body.classList.contains("is-index-complet-page");
|
||||
|
||||
const compactShouldDisablePortal = () =>
|
||||
isCompactViewport() && !keepPortalPremiumOnCompact();
|
||||
|
||||
const compactStickyEnabled = () => Boolean(compactSticky);
|
||||
|
||||
const stripLocalSticky = () => {
|
||||
document.querySelectorAll(sectionHeadSelector).forEach((el) => {
|
||||
el.classList.remove("is-sticky");
|
||||
@@ -69,13 +79,13 @@ const {
|
||||
};
|
||||
|
||||
const computeFollowOn = () =>
|
||||
!isCompactViewport() &&
|
||||
(!isCompactViewport() || compactStickyEnabled()) &&
|
||||
follow.classList.contains("is-on") &&
|
||||
follow.style.display !== "none" &&
|
||||
follow.getAttribute("aria-hidden") !== "true";
|
||||
|
||||
const computeCondensed = () => {
|
||||
if (isCompactViewport()) return false;
|
||||
if (isCompactViewport() && !compactStickyEnabled()) return false;
|
||||
|
||||
const heroRect = hero.getBoundingClientRect();
|
||||
const stickyTop = readStickyTop();
|
||||
@@ -89,7 +99,7 @@ const {
|
||||
const PIN_EPS = 3;
|
||||
|
||||
const isHeroPinned = () => {
|
||||
if (isCompactViewport()) return false;
|
||||
if (isCompactViewport() && !compactStickyEnabled()) return false;
|
||||
|
||||
const rect = hero.getBoundingClientRect();
|
||||
const stickyTop = readStickyTop();
|
||||
@@ -104,7 +114,16 @@ const {
|
||||
};
|
||||
|
||||
const applyLocalStickyHeight = () => {
|
||||
const h = isHeroPinned() ? measureHeroHeight() : 0;
|
||||
/*
|
||||
Stabilisation premium :
|
||||
une fois le hero condensé, on réserve sa hauteur réelle.
|
||||
Cela évite les oscillations d’un frame où isHeroPinned()
|
||||
peut repasser brièvement à false pendant le scroll.
|
||||
*/
|
||||
const shouldReserveHero =
|
||||
body.classList.contains(CONDENSED_CLASS) || isHeroPinned();
|
||||
|
||||
const h = shouldReserveHero ? measureHeroHeight() : 0;
|
||||
if (h === lastHeroHeight) return;
|
||||
lastHeroHeight = h;
|
||||
|
||||
@@ -183,7 +202,7 @@ const {
|
||||
const expanded = body.classList.contains(EXPANDED_CLASS);
|
||||
const collapsed = condensed && !expanded;
|
||||
|
||||
if (isCompactViewport() || !condensed) {
|
||||
if ((isCompactViewport() && !compactStickyEnabled()) || !condensed) {
|
||||
body.classList.remove(EXPANDED_CLASS);
|
||||
expandedAtY = null;
|
||||
|
||||
@@ -210,7 +229,7 @@ const {
|
||||
};
|
||||
|
||||
const maybeAutoCollapseOnScroll = () => {
|
||||
if (isCompactViewport()) {
|
||||
if (isCompactViewport() && !compactStickyEnabled()) {
|
||||
lastScrollY = window.scrollY || 0;
|
||||
return;
|
||||
}
|
||||
@@ -239,7 +258,7 @@ const {
|
||||
const syncAll = () => {
|
||||
stripLocalSticky();
|
||||
|
||||
if (isCompactViewport()) {
|
||||
if (isCompactViewport() && !compactStickyEnabled()) {
|
||||
body.classList.remove(FOLLOW_ON_CLASS);
|
||||
body.classList.remove(CONDENSED_CLASS);
|
||||
body.classList.remove(EXPANDED_CLASS);
|
||||
@@ -422,66 +441,134 @@ const {
|
||||
}
|
||||
|
||||
@media (max-width: 860px){
|
||||
:global(body.is-glossary-portal-page #reading-follow),
|
||||
:global(body.is-glossary-portal-page #reading-follow .reading-follow__inner){
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page) #reading-follow),
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page) #reading-follow .reading-follow__inner){
|
||||
display: none !important;
|
||||
opacity: 0 !important;
|
||||
visibility: hidden !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-portal-page){
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page)){
|
||||
--followbar-h: 0px !important;
|
||||
--sticky-offset-px: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px)) !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-portal-page .glossary-portal-hero){
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero){
|
||||
margin-bottom: var(--portal-hero-margin-bottom, 18px);
|
||||
border-radius: 20px !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-portal-page .glossary-portal-hero__more){
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero__more){
|
||||
max-height: none !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-portal-page .glossary-portal-hero__toggle){
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero__toggle){
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
:global(body.is-glossary-portal-page #reading-follow),
|
||||
:global(body.is-glossary-portal-page #reading-follow .reading-follow__inner){
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page) #reading-follow),
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page) #reading-follow .reading-follow__inner){
|
||||
display: none !important;
|
||||
opacity: 0 !important;
|
||||
visibility: hidden !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-portal-page){
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page)){
|
||||
--followbar-h: 0px !important;
|
||||
--sticky-offset-px: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px)) !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-portal-page .glossary-portal-hero){
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero){
|
||||
margin-bottom: var(--portal-hero-margin-bottom, 12px);
|
||||
border-radius: 16px !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-portal-page .glossary-portal-hero__more){
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero__more){
|
||||
max-height: none !important;
|
||||
opacity: 1 !important;
|
||||
overflow: visible !important;
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-portal-page .glossary-portal-hero__toggle){
|
||||
:global(body.is-glossary-portal-page:not(.is-index-complet-page) .glossary-portal-hero__toggle){
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* =========================================================
|
||||
Index complet — exception premium compacte portal
|
||||
---------------------------------------------------------
|
||||
Les portails ordinaires restent neutralisés en compact.
|
||||
L’index complet, lui, conserve la pile :
|
||||
hero sticky → reading-follow → lettres flottantes.
|
||||
========================================================= */
|
||||
|
||||
@media (max-width: 980px){
|
||||
:global(body.is-index-complet-page[data-edition-key="glossaire"][data-sticky-mode="glossary-portal"]) .glossary-portal-hero,
|
||||
:global(body.is-index-complet-page[data-edition-key="glossaire"][data-sticky-mode="glossary-portal"]) .glossary-page-hero{
|
||||
position: sticky !important;
|
||||
top: var(--glossary-sticky-top, calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px))) !important;
|
||||
z-index: 72 !important;
|
||||
margin-bottom: 0 !important;
|
||||
overflow: hidden !important;
|
||||
transform: translateZ(0) !important;
|
||||
backface-visibility: hidden !important;
|
||||
border-bottom-left-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
}
|
||||
|
||||
:global(body.is-index-complet-page #reading-follow){
|
||||
display: block !important;
|
||||
position: fixed !important;
|
||||
left: var(--reading-left) !important;
|
||||
width: var(--reading-width) !important;
|
||||
top: calc(var(--glossary-sticky-top, var(--sticky-header-h, 0px)) + var(--glossary-local-sticky-h, 0px) - var(--followbar-h, 0px) - 1px) !important;
|
||||
z-index: 71 !important;
|
||||
visibility: hidden !important;
|
||||
opacity: 0 !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
:global(body.is-index-complet-page.glossary-portal-follow-on #reading-follow){
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
:global(body.is-index-complet-page #reading-follow .reading-follow__inner){
|
||||
display: block !important;
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
max-height: none !important;
|
||||
margin-top: -1px !important;
|
||||
border-top-left-radius: 0 !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
}
|
||||
|
||||
:global(body.is-index-complet-page #gic-follow-letters){
|
||||
position: fixed !important;
|
||||
left: var(--reading-left) !important;
|
||||
width: var(--reading-width) !important;
|
||||
top: calc(var(--glossary-sticky-top, var(--sticky-header-h, 0px)) + var(--gic-premium-hero-h, 0px) + var(--gic-premium-follow-h, 0px) - 1px) !important;
|
||||
z-index: 70 !important;
|
||||
}
|
||||
|
||||
:global(body.is-index-complet-page.gic-letters-docked #gic-follow-letters){
|
||||
display: flex !important;
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -931,14 +931,14 @@ const WHOAMI_FORCE_LOCALHOST = (import.meta.env.PUBLIC_WHOAMI_FORCE_LOCALHOST ??
|
||||
min-width: 0 !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"]:not([data-sticky-mode="glossary-home"])) .glossary-hero,
|
||||
:global(body[data-edition-key="glossaire"]:not([data-sticky-mode="glossary-home"]):not([data-sticky-mode="glossary-portal"])) .glossary-hero,
|
||||
:global(body[data-edition-key="glossaire"]) .glossary-home,
|
||||
:global(body[data-edition-key="glossaire"]) .glossary-map,
|
||||
:global(body[data-edition-key="glossaire"]) .glossary-map-block,
|
||||
:global(body[data-edition-key="glossaire"]) .glossary-map-section,
|
||||
:global(body[data-edition-key="glossaire"]) .glossary-page-hero,
|
||||
:global(body[data-edition-key="glossaire"]) .glossary-portal-hero,
|
||||
:global(body[data-edition-key="glossaire"]) .scene-hero{
|
||||
:global(body[data-edition-key="glossaire"]:not([data-sticky-mode="glossary-portal"])) .glossary-page-hero,
|
||||
:global(body[data-edition-key="glossaire"]:not([data-sticky-mode="glossary-portal"])) .glossary-portal-hero,
|
||||
:global(body[data-edition-key="glossaire"]:not([data-sticky-mode="glossary-portal"])) .scene-hero{
|
||||
position: static !important;
|
||||
top: auto !important;
|
||||
left: auto !important;
|
||||
@@ -953,7 +953,7 @@ const WHOAMI_FORCE_LOCALHOST = (import.meta.env.PUBLIC_WHOAMI_FORCE_LOCALHOST ??
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"]) #reading-follow{
|
||||
:global(body[data-edition-key="glossaire"]:not([data-sticky-mode="glossary-portal"])) #reading-follow{
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,12 +126,6 @@ const prolongerLinks = [
|
||||
<span class="gic-aside__pill">
|
||||
{doctrinesCount} doctrine{doctrinesCount > 1 ? "s" : ""}
|
||||
</span>
|
||||
<span class="gic-aside__pill">
|
||||
{verbesCount} verbe{verbesCount > 1 ? "s" : ""}
|
||||
</span>
|
||||
<span class="gic-aside__pill">
|
||||
{casIaCount} entrée{casIaCount > 1 ? "s" : ""} cas IA
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="gic-aside__note">
|
||||
@@ -232,6 +226,8 @@ const prolongerLinks = [
|
||||
<GlossaryPortalStickySync
|
||||
heroMoreId="gic-hero-more"
|
||||
heroToggleId="gic-hero-toggle"
|
||||
mobileBreakpoint={980}
|
||||
compactSticky={true}
|
||||
/>
|
||||
|
||||
<script is:inline>
|
||||
@@ -265,30 +261,39 @@ const prolongerLinks = [
|
||||
|
||||
body.classList.add(BODY_CLASS);
|
||||
|
||||
const syncPremiumStickyMetrics = () => {
|
||||
const hero = document.querySelector("[data-glossary-portal-hero]");
|
||||
const followInner = follow.querySelector(".reading-follow__inner");
|
||||
|
||||
const heroH = hero ? Math.round(hero.getBoundingClientRect().height || 0) : 0;
|
||||
const followOn =
|
||||
follow.classList.contains("is-on") &&
|
||||
follow.style.display !== "none" &&
|
||||
follow.getAttribute("aria-hidden") !== "true";
|
||||
|
||||
const followH = followOn && followInner
|
||||
? Math.round(followInner.getBoundingClientRect().height || 0)
|
||||
: 0;
|
||||
|
||||
const lettersH =
|
||||
!lettersFollow.hidden && lettersFollow.getAttribute("aria-hidden") === "false"
|
||||
? Math.round(lettersFollow.getBoundingClientRect().height || 0)
|
||||
: 0;
|
||||
|
||||
root.style.setProperty("--gic-premium-hero-h", `${heroH}px`);
|
||||
root.style.setProperty("--gic-premium-follow-h", `${followH}px`);
|
||||
root.style.setProperty("--gic-premium-letters-h", `${lettersH}px`);
|
||||
};
|
||||
|
||||
const forceHideGlobalFollowOnCompactViewport = () => {
|
||||
if (!isCompactViewport()) {
|
||||
follow.style.display = "";
|
||||
return;
|
||||
}
|
||||
|
||||
follow.classList.remove("is-on");
|
||||
follow.setAttribute("aria-hidden", "true");
|
||||
follow.style.display = "none";
|
||||
follow.innerHTML = "";
|
||||
|
||||
root.style.setProperty("--followbar-h", "0px");
|
||||
root.style.setProperty(
|
||||
"--sticky-offset-px",
|
||||
"calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px))"
|
||||
);
|
||||
setAnchorOffset(0);
|
||||
follow.style.display = "";
|
||||
syncPremiumStickyMetrics();
|
||||
};
|
||||
|
||||
const isCompactViewport = () =>
|
||||
mqMobile.matches || mqSmallLandscape.matches;
|
||||
|
||||
const computeFollowOn = () =>
|
||||
!isCompactViewport() &&
|
||||
follow.classList.contains("is-on") &&
|
||||
follow.style.display !== "none" &&
|
||||
follow.getAttribute("aria-hidden") !== "true";
|
||||
@@ -320,7 +325,7 @@ const prolongerLinks = [
|
||||
};
|
||||
|
||||
const syncLettersDockState = () => {
|
||||
if (mqMobile.matches || !computeFollowOn()) {
|
||||
if (!computeFollowOn()) {
|
||||
body.classList.remove(LETTERS_DOCKED_CLASS);
|
||||
lettersFollow.hidden = true;
|
||||
lettersFollow.setAttribute("aria-hidden", "true");
|
||||
@@ -503,6 +508,7 @@ const prolongerLinks = [
|
||||
forceHideGlobalFollowOnCompactViewport();
|
||||
syncFollowLettersTop();
|
||||
syncLettersDockState();
|
||||
syncPremiumStickyMetrics();
|
||||
syncActiveLetter();
|
||||
});
|
||||
};
|
||||
@@ -565,6 +571,777 @@ const prolongerLinks = [
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script is:inline>
|
||||
/*
|
||||
INDEX COMPLET — STACK PREMIUM MOBILE/TABLETTE V2
|
||||
|
||||
Principe :
|
||||
- uniquement sur /glossaire/index-complet/ ;
|
||||
- rail vertical calculé en pixels réels ;
|
||||
- cockpit compact :
|
||||
header → hero condensé → follow H2 compact → lettres horizontales.
|
||||
*/
|
||||
(() => {
|
||||
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 inner = follow?.querySelector(".reading-follow__inner");
|
||||
const lettersSource = document.getElementById("gic-letters-source");
|
||||
const lettersFollow = document.getElementById("gic-follow-letters");
|
||||
|
||||
if (!body || !root || !hero || !follow || !inner || !lettersSource || !lettersFollow) return;
|
||||
|
||||
const mqCompact = window.matchMedia("(max-width: 980px)");
|
||||
const mqSmallLandscape = window.matchMedia(
|
||||
"(orientation: landscape) and (max-width: 920px) and (max-height: 520px)"
|
||||
);
|
||||
|
||||
const isCompact = () => mqCompact.matches || mqSmallLandscape.matches;
|
||||
|
||||
const pxVar = (name, fallback = 0) => {
|
||||
const raw = getComputedStyle(root).getPropertyValue(name).trim();
|
||||
const n = Number.parseFloat(raw);
|
||||
return Number.isFinite(n) ? n : fallback;
|
||||
};
|
||||
|
||||
const stackTop = () => {
|
||||
const glossaryTop = pxVar("--glossary-sticky-top", NaN);
|
||||
if (Number.isFinite(glossaryTop) && glossaryTop > 0) return glossaryTop;
|
||||
|
||||
const headerH = pxVar("--sticky-header-h", 0);
|
||||
const gap = pxVar("--page-gap", 12);
|
||||
return Math.max(0, Math.round(headerH + gap));
|
||||
};
|
||||
|
||||
const heads = Array.from(
|
||||
document.querySelectorAll(".glossary-portal-section__head, .gic-section__head")
|
||||
);
|
||||
|
||||
const titleOf = (head) => {
|
||||
const h = head?.querySelector("h2, h3");
|
||||
return (h?.textContent || head?.textContent || "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
};
|
||||
|
||||
const writeFollow = (title) => {
|
||||
const h1 = inner.querySelector(".rf-h1");
|
||||
const h2 = inner.querySelector(".rf-h2");
|
||||
const h3 = inner.querySelector(".rf-h3");
|
||||
|
||||
if (h1) h1.textContent = "Index complet";
|
||||
if (h2) h2.textContent = title || "Index alphabétique";
|
||||
if (h3) h3.textContent = "";
|
||||
|
||||
follow.classList.add("is-on");
|
||||
follow.setAttribute("aria-hidden", "false");
|
||||
follow.style.setProperty("display", "block", "important");
|
||||
|
||||
inner.style.setProperty("display", "block", "important");
|
||||
inner.style.setProperty("visibility", "visible", "important");
|
||||
inner.style.setProperty("opacity", "1", "important");
|
||||
|
||||
body.classList.add("glossary-portal-follow-on", "gic-premium-follow-on");
|
||||
};
|
||||
|
||||
const clearFollow = () => {
|
||||
follow.classList.remove("is-on");
|
||||
follow.setAttribute("aria-hidden", "true");
|
||||
|
||||
body.classList.remove(
|
||||
"glossary-portal-follow-on",
|
||||
"gic-premium-follow-on",
|
||||
"gic-letters-docked",
|
||||
"gic-premium-letters-on"
|
||||
);
|
||||
|
||||
lettersFollow.hidden = true;
|
||||
lettersFollow.setAttribute("aria-hidden", "true");
|
||||
|
||||
root.style.setProperty("--gic-premium-follow-h", "0px");
|
||||
root.style.setProperty("--gic-premium-letters-h", "0px");
|
||||
root.style.setProperty("--gic-stack-h", "0px");
|
||||
root.style.setProperty("--gic-follow-letters-offset", "0px");
|
||||
};
|
||||
|
||||
const currentHead = () => {
|
||||
const top = stackTop();
|
||||
const heroH = pxVar("--gic-premium-hero-h", 0);
|
||||
const followH = pxVar("--gic-premium-follow-h", 0);
|
||||
const lettersH = pxVar("--gic-premium-letters-h", 0);
|
||||
const threshold = top + heroH + followH + lettersH + 18;
|
||||
|
||||
let current = null;
|
||||
for (const head of heads) {
|
||||
const r = head.getBoundingClientRect();
|
||||
if (r.top <= threshold) current = head;
|
||||
else break;
|
||||
}
|
||||
return current;
|
||||
};
|
||||
|
||||
const measure = () => {
|
||||
const heroH = body.classList.contains("glossary-portal-hero-condensed")
|
||||
? Math.max(0, Math.round(hero.getBoundingClientRect().height || 0))
|
||||
: 0;
|
||||
|
||||
const followH = body.classList.contains("gic-premium-follow-on")
|
||||
? Math.max(0, Math.round(inner.getBoundingClientRect().height || 0))
|
||||
: 0;
|
||||
|
||||
const lettersH = body.classList.contains("gic-premium-letters-on")
|
||||
? Math.max(0, Math.round(lettersFollow.getBoundingClientRect().height || 0))
|
||||
: 0;
|
||||
|
||||
const stackH = heroH + followH + lettersH;
|
||||
|
||||
root.style.setProperty("--gic-premium-top", `${stackTop()}px`);
|
||||
root.style.setProperty("--gic-premium-hero-h", `${heroH}px`);
|
||||
root.style.setProperty("--gic-premium-follow-h", `${followH}px`);
|
||||
root.style.setProperty("--gic-premium-letters-h", `${lettersH}px`);
|
||||
root.style.setProperty("--gic-stack-h", `${stackH}px`);
|
||||
root.style.setProperty("--glossary-local-sticky-h", `${stackH}px`);
|
||||
root.style.setProperty("--followbar-h", `${followH}px`);
|
||||
root.style.setProperty("--sticky-offset-px", `${stackTop() + stackH + 12}px`);
|
||||
};
|
||||
|
||||
const syncLetters = () => {
|
||||
if (!body.classList.contains("gic-premium-follow-on")) {
|
||||
body.classList.remove("gic-letters-docked", "gic-premium-letters-on");
|
||||
lettersFollow.hidden = true;
|
||||
lettersFollow.setAttribute("aria-hidden", "true");
|
||||
root.style.setProperty("--gic-follow-letters-offset", "0px");
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceRect = lettersSource.getBoundingClientRect();
|
||||
const followBottom = stackTop() + pxVar("--gic-premium-hero-h", 0) + pxVar("--gic-premium-follow-h", 0);
|
||||
const shouldDock = sourceRect.top <= followBottom + 8;
|
||||
|
||||
body.classList.toggle("gic-letters-docked", shouldDock);
|
||||
body.classList.toggle("gic-premium-letters-on", shouldDock);
|
||||
|
||||
lettersFollow.hidden = !shouldDock;
|
||||
lettersFollow.setAttribute("aria-hidden", shouldDock ? "false" : "true");
|
||||
|
||||
if (shouldDock) {
|
||||
lettersFollow.style.setProperty("display", "flex", "important");
|
||||
root.style.setProperty(
|
||||
"--gic-follow-letters-offset",
|
||||
`${Math.max(0, Math.round(lettersFollow.getBoundingClientRect().height || 0)) + 12}px`
|
||||
);
|
||||
} else {
|
||||
root.style.setProperty("--gic-follow-letters-offset", "0px");
|
||||
}
|
||||
};
|
||||
|
||||
let raf = 0;
|
||||
|
||||
const sync = () => {
|
||||
raf = 0;
|
||||
|
||||
body.classList.add("is-index-complet-page", "is-glossary-portal-page");
|
||||
|
||||
if (!isCompact()) {
|
||||
body.classList.remove(
|
||||
"gic-premium-compact",
|
||||
"gic-premium-follow-on",
|
||||
"gic-premium-letters-on"
|
||||
);
|
||||
|
||||
hero.style.removeProperty("position");
|
||||
hero.style.removeProperty("top");
|
||||
hero.style.removeProperty("z-index");
|
||||
|
||||
follow.style.removeProperty("display");
|
||||
follow.style.removeProperty("top");
|
||||
follow.style.removeProperty("left");
|
||||
follow.style.removeProperty("width");
|
||||
|
||||
lettersFollow.style.removeProperty("display");
|
||||
lettersFollow.style.removeProperty("top");
|
||||
lettersFollow.style.removeProperty("left");
|
||||
lettersFollow.style.removeProperty("width");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
body.classList.add("gic-premium-compact");
|
||||
|
||||
const top = stackTop();
|
||||
const scrollY = window.scrollY || window.pageYOffset || 0;
|
||||
const heroRect = hero.getBoundingClientRect();
|
||||
|
||||
const condensed = scrollY > 8 && heroRect.top <= top + 8;
|
||||
body.classList.toggle("glossary-portal-hero-condensed", condensed);
|
||||
|
||||
hero.style.setProperty("position", "sticky", "important");
|
||||
hero.style.setProperty("top", `${top}px`, "important");
|
||||
hero.style.setProperty("z-index", "72", "important");
|
||||
|
||||
if (!condensed) {
|
||||
clearFollow();
|
||||
measure();
|
||||
return;
|
||||
}
|
||||
|
||||
measure();
|
||||
|
||||
const head = currentHead();
|
||||
const rawTitle = titleOf(head) || "Index alphabétique";
|
||||
const title = /^[A-ZÀÂÄÇÉÈÊËÎÏÔÖÙÛÜŸ]$/i.test(rawTitle)
|
||||
? "Index alphabétique"
|
||||
: rawTitle;
|
||||
|
||||
writeFollow(title);
|
||||
|
||||
measure();
|
||||
syncLetters();
|
||||
measure();
|
||||
};
|
||||
|
||||
const schedule = () => {
|
||||
if (raf) return;
|
||||
raf = requestAnimationFrame(sync);
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", schedule, { passive: true });
|
||||
window.addEventListener("resize", schedule);
|
||||
window.addEventListener("pageshow", schedule);
|
||||
|
||||
if (document.fonts?.ready) {
|
||||
document.fonts.ready.then(schedule).catch(() => {});
|
||||
}
|
||||
|
||||
if (mqCompact.addEventListener) mqCompact.addEventListener("change", schedule);
|
||||
else if (mqCompact.addListener) mqCompact.addListener(schedule);
|
||||
|
||||
if (mqSmallLandscape.addEventListener) mqSmallLandscape.addEventListener("change", schedule);
|
||||
else if (mqSmallLandscape.addListener) mqSmallLandscape.addListener(schedule);
|
||||
|
||||
schedule();
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", boot, { once: true });
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style is:global>
|
||||
/* INDEX COMPLET — STACK PREMIUM MOBILE/TABLETTE V2 */
|
||||
|
||||
@media (max-width: 980px){
|
||||
body.is-index-complet-page.gic-premium-compact{
|
||||
scroll-padding-top: calc(var(--gic-premium-top, 0px) + var(--gic-stack-h, 0px) + 16px);
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact .glossary-portal-hero,
|
||||
body.is-index-complet-page.gic-premium-compact .glossary-page-hero{
|
||||
position: sticky !important;
|
||||
top: var(--gic-premium-top, var(--glossary-sticky-top, 0px)) !important;
|
||||
z-index: 72 !important;
|
||||
overflow: hidden !important;
|
||||
margin-bottom: 12px !important;
|
||||
transform: translateZ(0) !important;
|
||||
backface-visibility: hidden !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero,
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-page-hero{
|
||||
padding: 7px 10px 7px !important;
|
||||
row-gap: 3px !important;
|
||||
border-radius: 14px 14px 0 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
box-shadow: 0 12px 30px rgba(0,0,0,.24) !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero__kicker{
|
||||
font-size: 8px !important;
|
||||
line-height: 1 !important;
|
||||
letter-spacing: .105em !important;
|
||||
opacity: .66 !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero h1{
|
||||
font-size: clamp(1.18rem, 4.8vw, 1.48rem) !important;
|
||||
line-height: 1 !important;
|
||||
letter-spacing: -.028em !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__intro{
|
||||
display: -webkit-box !important;
|
||||
-webkit-box-orient: vertical !important;
|
||||
-webkit-line-clamp: 1 !important;
|
||||
line-clamp: 1 !important;
|
||||
max-height: 1.12em !important;
|
||||
overflow: hidden !important;
|
||||
font-size: .66rem !important;
|
||||
line-height: 1.12 !important;
|
||||
opacity: .72 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__more{
|
||||
max-height: 0 !important;
|
||||
opacity: 0 !important;
|
||||
overflow: hidden !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero__toggle{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact #reading-follow{
|
||||
display: block !important;
|
||||
position: fixed !important;
|
||||
left: var(--reading-left) !important;
|
||||
width: var(--reading-width) !important;
|
||||
top: calc(var(--gic-premium-top, 0px) + var(--gic-premium-hero-h, 0px) - 1px) !important;
|
||||
z-index: 71 !important;
|
||||
opacity: 0 !important;
|
||||
visibility: hidden !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.gic-premium-follow-on #reading-follow{
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact #reading-follow .reading-follow__inner{
|
||||
display: block !important;
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
max-height: none !important;
|
||||
min-height: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
padding: 5px 56px 5px 9px !important;
|
||||
border-top-left-radius: 0 !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
border-bottom-left-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
box-shadow: 0 8px 18px rgba(0,0,0,.16) !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact #reading-follow .rf-h1,
|
||||
body.is-index-complet-page.gic-premium-compact #reading-follow .rf-h3,
|
||||
body.is-index-complet-page.gic-premium-compact #reading-follow .rf-actions{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact #reading-follow .rf-h2{
|
||||
display: block !important;
|
||||
font-size: .78rem !important;
|
||||
line-height: 1.08 !important;
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
letter-spacing: -.012em !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact #gic-follow-letters{
|
||||
position: fixed !important;
|
||||
left: var(--reading-left) !important;
|
||||
width: var(--reading-width) !important;
|
||||
top: calc(
|
||||
var(--gic-premium-top, 0px)
|
||||
+ var(--gic-premium-hero-h, 0px)
|
||||
+ var(--gic-premium-follow-h, 0px)
|
||||
- 1px
|
||||
) !important;
|
||||
z-index: 70 !important;
|
||||
display: none !important;
|
||||
flex-wrap: nowrap !important;
|
||||
gap: 6px !important;
|
||||
overflow-x: auto !important;
|
||||
overflow-y: hidden !important;
|
||||
scrollbar-width: none !important;
|
||||
-webkit-overflow-scrolling: touch !important;
|
||||
margin: 0 !important;
|
||||
padding: 6px 8px 7px !important;
|
||||
border-radius: 0 0 12px 12px !important;
|
||||
opacity: 0 !important;
|
||||
visibility: hidden !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact #gic-follow-letters::-webkit-scrollbar{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact #gic-follow-letters a{
|
||||
flex: 0 0 auto !important;
|
||||
min-width: 28px !important;
|
||||
height: 28px !important;
|
||||
padding: 0 9px !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.gic-premium-letters-on #gic-follow-letters{
|
||||
display: flex !important;
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact .gic-section,
|
||||
body.is-index-complet-page.gic-premium-compact .gic-group,
|
||||
body.is-index-complet-page.gic-premium-compact .glossary-portal-section{
|
||||
scroll-margin-top: calc(var(--gic-premium-top, 0px) + var(--gic-stack-h, 0px) + 18px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 761px) and (max-width: 980px){
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero,
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-page-hero{
|
||||
padding: 8px 12px 8px !important;
|
||||
border-radius: 16px 16px 0 0 !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero h1{
|
||||
font-size: clamp(1.48rem, 3vw, 1.82rem) !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__intro{
|
||||
font-size: .76rem !important;
|
||||
line-height: 1.14 !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact #reading-follow .rf-h2{
|
||||
font-size: .86rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero,
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-page-hero{
|
||||
padding: 5px 8px 5px !important;
|
||||
row-gap: 2px !important;
|
||||
border-radius: 12px 12px 0 0 !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed .glossary-portal-hero h1{
|
||||
font-size: clamp(1rem, 3vw, 1.22rem) !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact.glossary-portal-hero-condensed:not(.glossary-portal-hero-expanded) .glossary-portal-hero__intro{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact #reading-follow .reading-follow__inner{
|
||||
padding-top: 4px !important;
|
||||
padding-bottom: 4px !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact #gic-follow-letters{
|
||||
padding-top: 4px !important;
|
||||
padding-bottom: 5px !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-premium-compact #gic-follow-letters a{
|
||||
min-width: 24px !important;
|
||||
height: 24px !important;
|
||||
padding: 0 7px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script is:inline>
|
||||
/*
|
||||
INDEX COMPLET — FOLLOW LABEL ET STABILITÉ DESKTOP
|
||||
|
||||
Sur cette page, les sections alphabétiques A/B/C… ne doivent pas
|
||||
devenir le titre du reading-follow : la lettre active est déjà
|
||||
portée par la barre alphabétique.
|
||||
*/
|
||||
(() => {
|
||||
const boot = () => {
|
||||
const body = document.body;
|
||||
const follow = document.getElementById("reading-follow");
|
||||
const inner = follow?.querySelector(".reading-follow__inner");
|
||||
|
||||
if (!body || !follow || !inner) return;
|
||||
|
||||
const LETTER_RE = /^[A-ZÀÂÄÇÉÈÊËÎÏÔÖÙÛÜŸ]$/i;
|
||||
|
||||
const normalize = () => {
|
||||
if (!body.classList.contains("is-index-complet-page")) return;
|
||||
|
||||
const h1 = inner.querySelector(".rf-h1");
|
||||
const h2 = inner.querySelector(".rf-h2");
|
||||
const h3 = inner.querySelector(".rf-h3");
|
||||
|
||||
const txt = (h2?.textContent || "").replace(/\s+/g, " ").trim();
|
||||
|
||||
if (
|
||||
h2 &&
|
||||
(
|
||||
LETTER_RE.test(txt) ||
|
||||
body.classList.contains("gic-letters-docked") ||
|
||||
body.classList.contains("gic-premium-letters-on")
|
||||
)
|
||||
) {
|
||||
if (h1) h1.textContent = "Index complet du glossaire";
|
||||
h2.textContent = "Index alphabétique";
|
||||
if (h3) h3.textContent = "";
|
||||
}
|
||||
};
|
||||
|
||||
let raf = 0;
|
||||
const schedule = () => {
|
||||
if (raf) return;
|
||||
raf = requestAnimationFrame(() => {
|
||||
raf = 0;
|
||||
normalize();
|
||||
});
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(schedule);
|
||||
|
||||
observer.observe(body, {
|
||||
attributes: true,
|
||||
attributeFilter: ["class"],
|
||||
});
|
||||
|
||||
observer.observe(inner, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true,
|
||||
});
|
||||
|
||||
window.addEventListener("scroll", schedule, { passive: true });
|
||||
window.addEventListener("resize", schedule);
|
||||
window.addEventListener("pageshow", schedule);
|
||||
|
||||
schedule();
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", boot, { once: true });
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style is:global>
|
||||
/* INDEX COMPLET — FOLLOW LABEL ET STABILITÉ DESKTOP */
|
||||
|
||||
@media (min-width: 981px){
|
||||
body.is-index-complet-page.glossary-portal-follow-on #reading-follow{
|
||||
top: calc(
|
||||
var(--glossary-sticky-top, var(--sticky-header-h, 0px))
|
||||
+ var(--glossary-local-sticky-h, 0px)
|
||||
- 1px
|
||||
) !important;
|
||||
z-index: 71 !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-letters-docked #gic-follow-letters{
|
||||
top: calc(
|
||||
var(--glossary-sticky-top, var(--sticky-header-h, 0px))
|
||||
+ var(--glossary-local-sticky-h, 0px)
|
||||
+ var(--followbar-h, 0px)
|
||||
- 1px
|
||||
) !important;
|
||||
z-index: 70 !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-letters-docked #reading-follow .rf-h2{
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script is:inline>
|
||||
/*
|
||||
INDEX COMPLET — DESKTOP FOLLOW GATE
|
||||
|
||||
Le reading-follow ne doit pas apparaître en haut de page.
|
||||
Il devient visible seulement lorsque le hero est effectivement condensé.
|
||||
*/
|
||||
(() => {
|
||||
const boot = () => {
|
||||
const body = document.body;
|
||||
const hero = document.querySelector("[data-glossary-portal-hero]");
|
||||
const follow = document.getElementById("reading-follow");
|
||||
|
||||
if (!body || !hero || !follow) return;
|
||||
|
||||
const mqDesktop = window.matchMedia("(min-width: 981px)");
|
||||
|
||||
const sync = () => {
|
||||
if (!mqDesktop.matches) {
|
||||
body.classList.remove("gic-desktop-follow-gated");
|
||||
return;
|
||||
}
|
||||
|
||||
const condensed = body.classList.contains("glossary-portal-hero-condensed");
|
||||
body.classList.toggle("gic-desktop-follow-gated", !condensed);
|
||||
|
||||
if (!condensed) {
|
||||
follow.classList.remove("is-on");
|
||||
follow.setAttribute("aria-hidden", "true");
|
||||
}
|
||||
};
|
||||
|
||||
let raf = 0;
|
||||
const schedule = () => {
|
||||
if (raf) return;
|
||||
raf = requestAnimationFrame(() => {
|
||||
raf = 0;
|
||||
sync();
|
||||
});
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(schedule);
|
||||
observer.observe(body, {
|
||||
attributes: true,
|
||||
attributeFilter: ["class"],
|
||||
});
|
||||
|
||||
window.addEventListener("scroll", schedule, { passive: true });
|
||||
window.addEventListener("resize", schedule);
|
||||
window.addEventListener("pageshow", schedule);
|
||||
|
||||
if (mqDesktop.addEventListener) mqDesktop.addEventListener("change", schedule);
|
||||
else if (mqDesktop.addListener) mqDesktop.addListener(schedule);
|
||||
|
||||
schedule();
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", boot, { once: true });
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style is:global>
|
||||
/* INDEX COMPLET — DESKTOP FOLLOW GATE */
|
||||
|
||||
@media (min-width: 981px){
|
||||
body.is-index-complet-page.gic-desktop-follow-gated #reading-follow,
|
||||
body.is-index-complet-page.gic-desktop-follow-gated #reading-follow .reading-follow__inner{
|
||||
opacity: 0 !important;
|
||||
visibility: hidden !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
body.is-index-complet-page.gic-desktop-follow-gated #gic-follow-letters{
|
||||
opacity: 0 !important;
|
||||
visibility: hidden !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script is:inline>
|
||||
/*
|
||||
INDEX COMPLET — AUTO-CENTER ACTIVE LETTER
|
||||
|
||||
Sur mobile, la barre alphabétique horizontale doit suivre le lecteur :
|
||||
la lettre active est automatiquement recentrée dans gic-follow-letters.
|
||||
*/
|
||||
(() => {
|
||||
const boot = () => {
|
||||
const body = document.body;
|
||||
const lettersFollow = document.getElementById("gic-follow-letters");
|
||||
|
||||
if (!body || !lettersFollow) return;
|
||||
|
||||
const mqCompact = window.matchMedia("(max-width: 760px)");
|
||||
|
||||
let lastHref = "";
|
||||
let raf = 0;
|
||||
|
||||
const centerActiveLetter = () => {
|
||||
raf = 0;
|
||||
|
||||
if (!mqCompact.matches) return;
|
||||
if (!body.classList.contains("is-index-complet-page")) return;
|
||||
if (!body.classList.contains("gic-premium-letters-on")) return;
|
||||
|
||||
const active =
|
||||
lettersFollow.querySelector('a[aria-current="true"]') ||
|
||||
lettersFollow.querySelector("a.is-active");
|
||||
|
||||
if (!active) return;
|
||||
|
||||
const href = active.getAttribute("href") || "";
|
||||
const force = href !== lastHref;
|
||||
lastHref = href;
|
||||
|
||||
const containerRect = lettersFollow.getBoundingClientRect();
|
||||
const activeRect = active.getBoundingClientRect();
|
||||
|
||||
const activeCenter =
|
||||
activeRect.left - containerRect.left + lettersFollow.scrollLeft + activeRect.width / 2;
|
||||
|
||||
const target =
|
||||
activeCenter - lettersFollow.clientWidth / 2;
|
||||
|
||||
const max =
|
||||
lettersFollow.scrollWidth - lettersFollow.clientWidth;
|
||||
|
||||
const next = Math.max(0, Math.min(max, Math.round(target)));
|
||||
|
||||
if (force || Math.abs(lettersFollow.scrollLeft - next) > 6) {
|
||||
lettersFollow.scrollTo({
|
||||
left: next,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const schedule = () => {
|
||||
if (raf) return;
|
||||
raf = requestAnimationFrame(centerActiveLetter);
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(schedule);
|
||||
|
||||
observer.observe(lettersFollow, {
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ["class", "aria-current"],
|
||||
});
|
||||
|
||||
observer.observe(body, {
|
||||
attributes: true,
|
||||
attributeFilter: ["class"],
|
||||
});
|
||||
|
||||
window.addEventListener("scroll", schedule, { passive: true });
|
||||
window.addEventListener("resize", schedule);
|
||||
window.addEventListener("pageshow", schedule);
|
||||
|
||||
if (mqCompact.addEventListener) mqCompact.addEventListener("change", schedule);
|
||||
else if (mqCompact.addListener) mqCompact.addListener(schedule);
|
||||
|
||||
schedule();
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", boot, { once: true });
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
</GlossaryLayout>
|
||||
|
||||
<style>
|
||||
@@ -1041,4 +1818,5 @@ const prolongerLinks = [
|
||||
--sticky-offset-px: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user