129 lines
3.2 KiB
Plaintext
129 lines
3.2 KiB
Plaintext
---
|
||
// src/components/LevelToggle.astro
|
||
const { initialLevel = 1 } = Astro.props;
|
||
---
|
||
|
||
<div class="level-toggle" role="group" aria-label="Mode d’édition">
|
||
<button type="button" class="level-btn" data-level="1">Propos</button>
|
||
<button type="button" class="level-btn" data-level="2">Références</button>
|
||
<button type="button" class="level-btn" data-level="3">Illustrations</button>
|
||
<button type="button" class="level-btn" data-level="4">Commentaires</button>
|
||
</div>
|
||
|
||
<script is:inline define:vars={{ initialLevel }}>
|
||
(() => {
|
||
const BODY = document.body;
|
||
|
||
const wrap = document.querySelector(".level-toggle");
|
||
if (!wrap) return;
|
||
|
||
const buttons = Array.from(wrap.querySelectorAll("button[data-level]"));
|
||
if (!buttons.length) return;
|
||
|
||
const KEY = "archicratie:readingLevel";
|
||
|
||
function clampLevel(n) {
|
||
const x = Number.parseInt(String(n), 10);
|
||
if (!Number.isFinite(x)) return 1;
|
||
return Math.min(4, Math.max(1, x));
|
||
}
|
||
|
||
function setActiveUI(lvl) {
|
||
for (const b of buttons) {
|
||
const on = String(b.dataset.level) === String(lvl);
|
||
b.classList.toggle("is-active", on);
|
||
b.setAttribute("aria-pressed", on ? "true" : "false");
|
||
}
|
||
}
|
||
|
||
function captureBeforeLevelSwitch() {
|
||
const paraId =
|
||
window.__archiCurrentParaId ||
|
||
window.__archiLastParaId ||
|
||
String(location.hash || "").replace(/^#/, "") ||
|
||
"";
|
||
|
||
window.__archiLevelSwitchCtx = {
|
||
paraId,
|
||
hash: location.hash || "",
|
||
scrollY: window.scrollY || 0,
|
||
t: Date.now(),
|
||
};
|
||
}
|
||
|
||
function applyLevel(lvl, { persist = true } = {}) {
|
||
const v = clampLevel(lvl);
|
||
|
||
if (BODY) BODY.dataset.readingLevel = String(v);
|
||
setActiveUI(v);
|
||
|
||
if (persist) {
|
||
try { localStorage.setItem(KEY, String(v)); } catch {}
|
||
}
|
||
|
||
try {
|
||
window.dispatchEvent(
|
||
new CustomEvent("archicratie:readingLevel", { detail: { level: v } })
|
||
);
|
||
} catch {}
|
||
}
|
||
|
||
// init : storage > initialLevel
|
||
let start = clampLevel(initialLevel);
|
||
try {
|
||
const stored = localStorage.getItem(KEY);
|
||
if (stored) start = clampLevel(stored);
|
||
} catch {}
|
||
|
||
applyLevel(start, { persist: false });
|
||
|
||
// clicks
|
||
wrap.addEventListener("click", (ev) => {
|
||
const btn = ev.target?.closest?.("button[data-level]");
|
||
if (!btn) return;
|
||
ev.preventDefault();
|
||
|
||
// ✅ crucial : on capture la position AVANT le reflow lié au changement de niveau
|
||
captureBeforeLevelSwitch();
|
||
applyLevel(btn.dataset.level);
|
||
});
|
||
})();
|
||
</script>
|
||
|
||
<style>
|
||
.level-toggle{
|
||
display: inline-flex;
|
||
gap: 8px;
|
||
align-items: center;
|
||
}
|
||
|
||
.level-btn{
|
||
border: 1px solid rgba(127,127,127,0.40);
|
||
background: rgba(127,127,127,0.08);
|
||
border-radius: 999px;
|
||
padding: 6px 10px;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
transition: filter .12s ease, transform .12s ease, background .12s ease, border-color .12s ease;
|
||
}
|
||
|
||
.level-btn:hover{
|
||
filter: brightness(1.08);
|
||
}
|
||
|
||
.level-btn.is-active{
|
||
border-color: rgba(160,160,255,0.95);
|
||
background: rgba(140,140,255,0.18);
|
||
font-weight: 900;
|
||
}
|
||
|
||
.level-btn.is-active:hover{
|
||
filter: brightness(1.12);
|
||
}
|
||
|
||
.level-btn:active{
|
||
transform: translateY(1px);
|
||
}
|
||
</style>
|