feat(glossaire): polish sticky entry flow and aside navigation
This commit is contained in:
@@ -65,13 +65,15 @@ const slugOf = (item) =>
|
||||
const hrefOf = (item) => `/glossaire/${slugOf(item)}/`;
|
||||
|
||||
const collator = new Intl.Collator("fr", { sensitivity: "base", numeric: true });
|
||||
const bySlug = new Map(allEntries.map((item) => [slugOf(item), item]));
|
||||
const bySlug = new Map(
|
||||
allEntries.map((item) => [slugOf(item).toLowerCase(), item])
|
||||
);
|
||||
|
||||
function resolveEntries(slugs = []) {
|
||||
const seen = new Set();
|
||||
|
||||
return slugs
|
||||
.map((slug) => bySlug.get(String(slug || "").trim()))
|
||||
.map((slug) => bySlug.get(String(slug || "").trim().toLowerCase()))
|
||||
.filter(Boolean)
|
||||
.filter((item) => {
|
||||
const slug = slugOf(item);
|
||||
@@ -125,6 +127,10 @@ const kindLabels = {
|
||||
verbe: "Verbe",
|
||||
paradigme: "Paradigme",
|
||||
doctrine: "Doctrine",
|
||||
dispositif: "Dispositif",
|
||||
figure: "Figure",
|
||||
qualification: "Qualification",
|
||||
epistemologie: "Épistémologie",
|
||||
};
|
||||
|
||||
const domainLabels = {
|
||||
@@ -145,13 +151,13 @@ const displayFamily =
|
||||
kindLabels[entry.data.kind] ??
|
||||
"Fiche";
|
||||
|
||||
const displayDomain =
|
||||
domainLabels[entry.data.domain] ??
|
||||
entry.data.domain;
|
||||
const displayDomain = entry.data.domain
|
||||
? (domainLabels[entry.data.domain] ?? entry.data.domain)
|
||||
: "";
|
||||
|
||||
const displayLevel =
|
||||
levelLabels[entry.data.level] ??
|
||||
entry.data.level;
|
||||
const displayLevel = entry.data.level
|
||||
? (levelLabels[entry.data.level] ?? entry.data.level)
|
||||
: "";
|
||||
|
||||
const hasScholarlyMeta =
|
||||
(entry.data.mobilizedAuthors?.length ?? 0) > 0 ||
|
||||
@@ -161,6 +167,7 @@ const hasScholarlyMeta =
|
||||
<GlossaryLayout
|
||||
title={entry.data.title}
|
||||
version={entry.data.version}
|
||||
stickyMode="glossary-entry"
|
||||
>
|
||||
<Fragment slot="aside">
|
||||
<GlossaryAside currentEntry={entry} allEntries={allEntries} />
|
||||
@@ -173,41 +180,52 @@ const hasScholarlyMeta =
|
||||
</p>
|
||||
)}
|
||||
|
||||
<header class="glossary-entry-head">
|
||||
<h1>{entry.data.term}</h1>
|
||||
<p class="glossary-entry-dek">
|
||||
<em>{entry.data.definitionShort}</em>
|
||||
</p>
|
||||
<header class="glossary-entry-head" data-ge-hero>
|
||||
<div class="glossary-entry-head__title">
|
||||
<h1>{entry.data.term}</h1>
|
||||
</div>
|
||||
|
||||
<div class="glossary-entry-signals" aria-label="Repères de lecture">
|
||||
<span class="glossary-pill glossary-pill--family">
|
||||
<strong>Famille :</strong> {displayFamily}
|
||||
</span>
|
||||
<span class="glossary-pill">
|
||||
<strong>Domaine :</strong> {displayDomain}
|
||||
</span>
|
||||
<span class="glossary-pill">
|
||||
<strong>Niveau :</strong> {displayLevel}
|
||||
</span>
|
||||
<div class="glossary-entry-summary">
|
||||
<p class="glossary-entry-dek">
|
||||
<em>{entry.data.definitionShort}</em>
|
||||
</p>
|
||||
|
||||
<div class="glossary-entry-signals" aria-label="Repères de lecture">
|
||||
<span class="glossary-pill glossary-pill--family">
|
||||
<strong>Famille :</strong> {displayFamily}
|
||||
</span>
|
||||
|
||||
{displayDomain && (
|
||||
<span class="glossary-pill">
|
||||
<strong>Domaine :</strong> {displayDomain}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{displayLevel && (
|
||||
<span class="glossary-pill">
|
||||
<strong>Niveau :</strong> {displayLevel}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{hasScholarlyMeta && (
|
||||
<div class="glossary-entry-meta">
|
||||
{(entry.data.mobilizedAuthors?.length ?? 0) > 0 && (
|
||||
<p>
|
||||
<strong>Auteurs mobilisés :</strong> {entry.data.mobilizedAuthors.join(" / ")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{(entry.data.comparisonTraditions?.length ?? 0) > 0 && (
|
||||
<p>
|
||||
<strong>Traditions de comparaison :</strong> {entry.data.comparisonTraditions.join(" / ")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{hasScholarlyMeta && (
|
||||
<div class="glossary-entry-meta">
|
||||
{(entry.data.mobilizedAuthors?.length ?? 0) > 0 && (
|
||||
<p>
|
||||
<strong>Auteurs mobilisés :</strong> {entry.data.mobilizedAuthors.join(" / ")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{(entry.data.comparisonTraditions?.length ?? 0) > 0 && (
|
||||
<p>
|
||||
<strong>Traditions de comparaison :</strong> {entry.data.comparisonTraditions.join(" / ")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="glossary-entry-body">
|
||||
<Content />
|
||||
</div>
|
||||
@@ -235,6 +253,117 @@ const hasScholarlyMeta =
|
||||
)}
|
||||
</GlossaryLayout>
|
||||
|
||||
<script is:inline>
|
||||
(() => {
|
||||
const boot = () => {
|
||||
const body = document.body;
|
||||
const root = document.documentElement;
|
||||
const hero = document.querySelector("[data-ge-hero]");
|
||||
const follow = document.getElementById("reading-follow");
|
||||
const mqMobile = window.matchMedia("(max-width: 860px)");
|
||||
|
||||
if (!body || !root || !hero || !follow) return;
|
||||
|
||||
const BODY_CLASS = "is-glossary-entry-page";
|
||||
const FOLLOW_ON_CLASS = "glossary-entry-follow-on";
|
||||
|
||||
let lastHeight = -1;
|
||||
let lastFollowOn = null;
|
||||
let raf = 0;
|
||||
|
||||
body.classList.add(BODY_CLASS);
|
||||
|
||||
const heroHeight = () =>
|
||||
Math.max(0, Math.round(hero.getBoundingClientRect().height || 0));
|
||||
|
||||
const computeFollowOn = () =>
|
||||
!mqMobile.matches &&
|
||||
follow.classList.contains("is-on") &&
|
||||
follow.style.display !== "none" &&
|
||||
follow.getAttribute("aria-hidden") !== "true";
|
||||
|
||||
const stripLocalSticky = () => {
|
||||
document
|
||||
.querySelectorAll(
|
||||
".glossary-entry-body h2, .glossary-entry-body h3, .glossary-relations h2, .glossary-relations h3"
|
||||
)
|
||||
.forEach((el) => {
|
||||
el.classList.remove("is-sticky");
|
||||
el.removeAttribute("data-sticky-active");
|
||||
});
|
||||
};
|
||||
|
||||
const applyLocalStickyHeight = () => {
|
||||
const h = mqMobile.matches ? 0 : heroHeight();
|
||||
if (h === lastHeight) return;
|
||||
lastHeight = h;
|
||||
|
||||
if (typeof window.__archiSetLocalStickyHeight === "function") {
|
||||
window.__archiSetLocalStickyHeight(h);
|
||||
} else {
|
||||
root.style.setProperty("--glossary-local-sticky-h", `${h}px`);
|
||||
}
|
||||
};
|
||||
|
||||
const syncFollowState = () => {
|
||||
const on = computeFollowOn();
|
||||
if (on === lastFollowOn) return;
|
||||
lastFollowOn = on;
|
||||
body.classList.toggle(FOLLOW_ON_CLASS, on);
|
||||
};
|
||||
|
||||
const syncAll = () => {
|
||||
stripLocalSticky();
|
||||
syncFollowState();
|
||||
applyLocalStickyHeight();
|
||||
};
|
||||
|
||||
const schedule = () => {
|
||||
if (raf) return;
|
||||
raf = requestAnimationFrame(() => {
|
||||
raf = 0;
|
||||
syncAll();
|
||||
});
|
||||
};
|
||||
|
||||
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("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>
|
||||
.glossary-legacy-note{
|
||||
padding: 10px 12px;
|
||||
@@ -247,25 +376,57 @@ const hasScholarlyMeta =
|
||||
}
|
||||
|
||||
.glossary-entry-head{
|
||||
margin-bottom: 18px;
|
||||
position: sticky;
|
||||
top: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px));
|
||||
z-index: 11;
|
||||
margin: 0 0 24px;
|
||||
border: 1px solid rgba(127,127,127,0.18);
|
||||
border-radius: 28px;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0,0,0,0.60), rgba(0,0,0,0.92)),
|
||||
radial-gradient(900px 240px at 20% 0%, rgba(0,217,255,0.08), transparent 60%);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
overflow: hidden;
|
||||
transition:
|
||||
border-radius 180ms ease,
|
||||
box-shadow 180ms ease,
|
||||
border-color 180ms ease;
|
||||
}
|
||||
|
||||
.glossary-entry-head__title{
|
||||
padding: 18px 18px 16px;
|
||||
}
|
||||
|
||||
.glossary-entry-head h1{
|
||||
margin-bottom: 10px;
|
||||
margin: 0;
|
||||
font-size: clamp(2.2rem, 4vw, 3.15rem);
|
||||
line-height: 1.02;
|
||||
letter-spacing: -.04em;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
.glossary-entry-summary{
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
padding: 16px 18px 18px;
|
||||
border-top: 1px solid rgba(127,127,127,0.14);
|
||||
background: rgba(255,255,255,0.02);
|
||||
}
|
||||
|
||||
.glossary-entry-dek{
|
||||
margin: 0 0 14px;
|
||||
font-size: 1.02rem;
|
||||
line-height: 1.6;
|
||||
opacity: .95;
|
||||
margin: 0;
|
||||
max-width: 76ch;
|
||||
font-size: 1.04rem;
|
||||
line-height: 1.55;
|
||||
opacity: .94;
|
||||
}
|
||||
|
||||
.glossary-entry-signals{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin: 0 0 6px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.glossary-pill{
|
||||
@@ -286,7 +447,7 @@ const hasScholarlyMeta =
|
||||
}
|
||||
|
||||
.glossary-entry-meta{
|
||||
margin: 0 0 18px;
|
||||
margin: 0;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(127,127,127,0.18);
|
||||
border-radius: 12px;
|
||||
@@ -307,6 +468,13 @@ const hasScholarlyMeta =
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.glossary-entry-body h2,
|
||||
.glossary-entry-body h3,
|
||||
.glossary-relations h2,
|
||||
.glossary-relations h3{
|
||||
scroll-margin-top: calc(var(--sticky-offset-px, 96px) + 18px);
|
||||
}
|
||||
|
||||
.glossary-relations{
|
||||
margin-top: 26px;
|
||||
padding-top: 18px;
|
||||
@@ -356,6 +524,59 @@ const hasScholarlyMeta =
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page #reading-follow){
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-head){
|
||||
margin-bottom: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-summary){
|
||||
gap: 10px;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-signals){
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-meta){
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on #reading-follow){
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on #reading-follow .reading-follow__inner){
|
||||
margin-top: -1px;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page .glossary-entry-body h2.is-sticky),
|
||||
:global(body.is-glossary-entry-page .glossary-entry-body h2[data-sticky-active="true"]),
|
||||
:global(body.is-glossary-entry-page .glossary-entry-body h3.is-sticky),
|
||||
:global(body.is-glossary-entry-page .glossary-entry-body h3[data-sticky-active="true"]),
|
||||
:global(body.is-glossary-entry-page .glossary-relations h2.is-sticky),
|
||||
:global(body.is-glossary-entry-page .glossary-relations h2[data-sticky-active="true"]),
|
||||
:global(body.is-glossary-entry-page .glossary-relations h3.is-sticky),
|
||||
:global(body.is-glossary-entry-page .glossary-relations h3[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;
|
||||
}
|
||||
|
||||
@media (max-width: 720px){
|
||||
.glossary-entry-signals{
|
||||
gap: 6px;
|
||||
@@ -366,6 +587,32 @@ const hasScholarlyMeta =
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 860px){
|
||||
.glossary-entry-head{
|
||||
position: static;
|
||||
border-radius: 22px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.glossary-entry-head__title{
|
||||
padding: 14px 14px 12px;
|
||||
}
|
||||
|
||||
.glossary-entry-summary{
|
||||
gap: 12px;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.glossary-entry-dek{
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-head){
|
||||
margin-bottom: 20px;
|
||||
border-radius: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-entry-meta{
|
||||
background: rgba(255,255,255,0.03);
|
||||
|
||||
Reference in New Issue
Block a user