feat(ui): harmoniser navigation pages d’entrée et recherche
All checks were successful
SMOKE / smoke (push) Successful in 6s
CI / build-and-anchors (push) Successful in 44s
CI / build-and-anchors (pull_request) Successful in 36s

This commit is contained in:
2026-04-25 01:31:14 +02:00
parent 8605b7198f
commit 64e56e8abc
10 changed files with 835 additions and 254 deletions

View File

@@ -1,41 +1,51 @@
---
import SiteLayout from "../../layouts/SiteLayout.astro";
---
<SiteLayout title="Recherche">
<h1>Recherche</h1>
<p>Recherche plein texte (statique) dans les pages “édition-livre”.</p>
<section class="search-page" aria-labelledby="search-title">
<header class="landing-hero search-hero">
<p class="landing-kicker">Exploration transversale</p>
<h1 id="search-title">Recherche</h1>
<p class="landing-lead">
Rechercher un terme, une notion ou un passage dans les textes de lédition web.
</p>
</header>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:12px 0 18px;">
<label>
<span style="display:block;font-size:12px;opacity:0.85;">Terme</span>
<input id="q" type="search" placeholder="Ex: archicratie, régulation, inertie…" style="padding:8px 10px;min-width:280px;">
</label>
<section class="landing-section search-panel" aria-label="Formulaire de recherche">
<div class="search-controls">
<label class="search-field search-field--main">
<span>Terme</span>
<input id="q" type="search" autocomplete="off" placeholder="Ex. archicratie, régulation, inertie…" />
</label>
<label>
<span style="display:block;font-size:12px;opacity:0.85;">Édition</span>
<select id="edition" style="padding:8px 10px;">
<option value="">Toutes</option>
<option value="archicratie">Archicratie</option>
<option value="traite">Traité</option>
<option value="ia">Cas IA</option>
<option value="glossaire">Glossaire</option>
<option value="atlas">Atlas</option>
</select>
</label>
<label class="search-field">
<span>Édition</span>
<select id="edition">
<option value="">Toutes</option>
<option value="archicrat-ia">Essai-thèse</option>
<option value="cas-ia">Cas IA</option>
<option value="glossaire">Glossaire</option>
<option value="commencer">Commencer</option>
</select>
</label>
<label>
<span style="display:block;font-size:12px;opacity:0.85;">Niveau</span>
<select id="level" style="padding:8px 10px;">
<option value="">Tous</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</label>
</div>
<label class="search-field">
<span>Niveau</span>
<select id="level">
<option value="">Tous</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
</label>
</div>
<div id="status" style="margin:10px 0;opacity:0.9;"></div>
<ol id="results" style="padding-left:18px;"></ol>
<p id="status" class="search-status" aria-live="polite"></p>
<ol id="results" class="search-results"></ol>
</section>
</section>
<script is:inline>
(() => {
@@ -52,10 +62,10 @@ import SiteLayout from "../../layouts/SiteLayout.astro";
pfPromise = (async () => {
try {
const pagefind = await import("/pagefind/pagefind.js");
// init facultatif, mais accélère si on clique dans linput
await pagefind.init?.();
return pagefind;
} catch {
} catch (e) {
console.error("[Recherche] Pagefind init failed:", e);
return null;
}
})();
@@ -69,25 +79,26 @@ import SiteLayout from "../../layouts/SiteLayout.astro";
return Object.keys(filters).length ? filters : null;
}
function clearResults() {
results.innerHTML = "";
}
function setStatus(msg) {
status.textContent = msg;
}
function clearResults() {
results.innerHTML = "";
}
async function runSearch() {
const term = q.value.trim();
if (!term) {
setStatus("Tape un terme pour chercher.");
setStatus("Tape un terme pour lancer une recherche.");
clearResults();
return;
}
const pagefind = await getPagefind();
if (!pagefind) {
setStatus("Index de recherche indisponible. Fais `npm run build` puis `npm run preview`.");
setStatus("Index de recherche indisponible. Lance `npm run build` puis `npm run preview`.");
clearResults();
return;
}
@@ -96,49 +107,56 @@ import SiteLayout from "../../layouts/SiteLayout.astro";
const filters = currentFilters();
if (filters) opts.filters = filters;
setStatus("Recherche…");
setStatus("Recherche en cours…");
const search = await pagefind.debouncedSearch(term, opts, 250);
if (search === null) return;
const items = await Promise.all(search.results.slice(0, 20).map(r => r.data()));
setStatus(`${items.length} résultat(s) (sur ${search.results.length}).`);
const items = await Promise.all(search.results.slice(0, 24).map((r) => r.data()));
clearResults();
if (!items.length) {
setStatus("Aucun résultat pour cette recherche.");
return;
}
setStatus(`${items.length} résultat(s) affiché(s) sur ${search.results.length}.`);
results.innerHTML = "";
for (const item of items) {
const li = document.createElement("li");
li.style.marginBottom = "12px";
li.className = "search-result";
const a = document.createElement("a");
a.className = "search-result__title";
a.href = item.url;
a.textContent = item.meta?.title || item.url;
const meta = document.createElement("div");
meta.style.fontSize = "12px";
meta.style.opacity = "0.85";
const edv = item.meta?.edition ? `édition: ${item.meta.edition}` : "";
const lv = item.meta?.level ? `niveau: ${item.meta.level}` : "";
const v = item.meta?.version ? `v${item.meta.version}` : "";
meta.textContent = [edv, lv, v].filter(Boolean).join(" · ");
meta.className = "search-result__meta";
const ex = document.createElement("div");
ex.style.marginTop = "6px";
ex.innerHTML = item.excerpt || "";
const edition = item.meta?.edition ? `édition : ${item.meta.edition}` : "";
const level = item.meta?.level ? `niveau : ${item.meta.level}` : "";
const version = item.meta?.version ? `v${item.meta.version}` : "";
meta.textContent = [edition, level, version].filter(Boolean).join(" · ");
const excerpt = document.createElement("div");
excerpt.className = "search-result__excerpt";
excerpt.innerHTML = item.excerpt || "";
li.appendChild(a);
if (meta.textContent) li.appendChild(meta);
li.appendChild(ex);
li.appendChild(excerpt);
results.appendChild(li);
}
}
// Précharge quand focus => UX meilleure
q.addEventListener("focus", () => { getPagefind(); });
q.addEventListener("input", runSearch);
ed.addEventListener("change", runSearch);
lv.addEventListener("change", runSearch);
setStatus("Tape un terme pour chercher.");
setStatus("Tape un terme pour lancer une recherche.");
})();
</script>
</SiteLayout>
</SiteLayout>