162 lines
4.8 KiB
Plaintext
162 lines
4.8 KiB
Plaintext
---
|
||
import SiteLayout from "../../layouts/SiteLayout.astro";
|
||
---
|
||
|
||
<SiteLayout title="Recherche">
|
||
<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>
|
||
|
||
<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 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 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>
|
||
|
||
<p id="status" class="search-status" aria-live="polite"></p>
|
||
<ol id="results" class="search-results"></ol>
|
||
</section>
|
||
</section>
|
||
|
||
<script is:inline>
|
||
(() => {
|
||
const q = document.getElementById("q");
|
||
const ed = document.getElementById("edition");
|
||
const lv = document.getElementById("level");
|
||
const status = document.getElementById("status");
|
||
const results = document.getElementById("results");
|
||
|
||
let pfPromise = null;
|
||
|
||
async function getPagefind() {
|
||
if (pfPromise) return pfPromise;
|
||
pfPromise = (async () => {
|
||
try {
|
||
const pagefind = await import("/pagefind/pagefind.js");
|
||
await pagefind.init?.();
|
||
return pagefind;
|
||
} catch (e) {
|
||
console.error("[Recherche] Pagefind init failed:", e);
|
||
return null;
|
||
}
|
||
})();
|
||
return pfPromise;
|
||
}
|
||
|
||
function currentFilters() {
|
||
const filters = {};
|
||
if (ed.value) filters.edition = ed.value;
|
||
if (lv.value) filters.level = lv.value;
|
||
return Object.keys(filters).length ? filters : null;
|
||
}
|
||
|
||
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 lancer une recherche.");
|
||
clearResults();
|
||
return;
|
||
}
|
||
|
||
const pagefind = await getPagefind();
|
||
if (!pagefind) {
|
||
setStatus("Index de recherche indisponible. Lance `npm run build` puis `npm run preview`.");
|
||
clearResults();
|
||
return;
|
||
}
|
||
|
||
const opts = {};
|
||
const filters = currentFilters();
|
||
if (filters) opts.filters = filters;
|
||
|
||
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, 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}.`);
|
||
|
||
for (const item of items) {
|
||
const li = document.createElement("li");
|
||
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.className = "search-result__meta";
|
||
|
||
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(excerpt);
|
||
results.appendChild(li);
|
||
}
|
||
}
|
||
|
||
q.addEventListener("focus", () => { getPagefind(); });
|
||
q.addEventListener("input", runSearch);
|
||
ed.addEventListener("change", runSearch);
|
||
lv.addEventListener("change", runSearch);
|
||
|
||
setStatus("Tape un terme pour lancer une recherche.");
|
||
})();
|
||
</script>
|
||
</SiteLayout> |