Fix: pattern regex v-flag, help mobile: TOC accordion not slide-over, search always in TOC
CI / lint (push) Failing after 4s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 9s

This commit is contained in:
2026-06-03 11:13:37 -04:00
parent baba4e9fc7
commit 227e157793
4 changed files with 33 additions and 79 deletions
+1 -5
View File
@@ -762,7 +762,7 @@
<div class="editor-modal" id="help-modal"> <div class="editor-modal" id="help-modal">
<div class="editor-container help-container"> <div class="editor-container help-container">
<div class="editor-header"> <div class="editor-header">
<button class="help-hamburger" id="help-hamburger" title="Sommaire" aria-label="Afficher le sommaire" onclick="var n=document.getElementById('help-nav');var o=document.getElementById('help-nav-overlay');if(n){n.classList.toggle('open');if(o)o.classList.toggle('visible');var s=document.getElementById('help-nav-search');if(n.classList.contains('open')&&s)setTimeout(function(){s.focus()},150);document.body.style.overflow=n.classList.contains('open')?'hidden':''}"> <button class="help-hamburger" id="help-hamburger" title="Sommaire" aria-label="Afficher le sommaire" onclick="var n=document.getElementById('help-nav');if(n){n.style.display=n.style.display==='none'?'flex':'none'}">
<i data-lucide="menu" style="width:18px;height:18px"></i> <i data-lucide="menu" style="width:18px;height:18px"></i>
</button> </button>
<div class="editor-title" id="help-title">Guide d'utilisation ObsiGate</div> <div class="editor-title" id="help-title">Guide d'utilisation ObsiGate</div>
@@ -772,14 +772,10 @@
</button> </button>
</div> </div>
</div> </div>
<div class="help-nav-overlay" id="help-nav-overlay" onclick="var n=document.getElementById('help-nav');var o=document.getElementById('help-nav-overlay');if(n)n.classList.remove('open');if(o)o.classList.remove('visible');document.body.style.overflow=''"></div>
<div class="editor-body help-body" id="help-body"> <div class="editor-body help-body" id="help-body">
<nav class="help-nav" id="help-nav"> <nav class="help-nav" id="help-nav">
<div class="help-nav-header"> <div class="help-nav-header">
<div class="help-nav-title">Sommaire</div> <div class="help-nav-title">Sommaire</div>
<button class="help-nav-close" id="help-nav-close" title="Fermer" aria-label="Fermer le sommaire" onclick="var n=document.getElementById('help-nav');var o=document.getElementById('help-nav-overlay');if(n)n.classList.remove('open');if(o)o.classList.remove('visible');document.body.style.overflow=''">
<i data-lucide="x" style="width:16px;height:16px"></i>
</button>
</div> </div>
<div class="help-nav-search-wrap"> <div class="help-nav-search-wrap">
<input type="text" class="help-nav-search" id="help-nav-search" placeholder="Rechercher dans l'aide..." autocomplete="off" spellcheck="false"> <input type="text" class="help-nav-search" id="help-nav-search" placeholder="Rechercher dans l'aide..." autocomplete="off" spellcheck="false">
+1 -1
View File
@@ -465,7 +465,7 @@ const AdminPanel = {
<div class="admin-form-card"> <div class="admin-form-card">
<h3>${title}</h3> <h3>${title}</h3>
<form id="admin-user-form"> <form id="admin-user-form">
${!isEdit ? '<div class="form-group"><label>Nom d\'utilisateur</label><input type="text" name="username" required pattern="[a-zA-Z0-9_-]{2,32}" placeholder="username"></div>' : ""} ${!isEdit ? '<div class="form-group"><label>Nom d\'utilisateur</label><input type="text" name="username" required pattern="[a-zA-Z0-9_\-]{2,32}" placeholder="username"></div>' : ""}
<div class="form-group"><label>Nom affiché</label><input type="text" name="display_name" value="${isEdit ? user.display_name || "" : ""}"></div> <div class="form-group"><label>Nom affiché</label><input type="text" name="display_name" value="${isEdit ? user.display_name || "" : ""}"></div>
<div class="form-group"><label>${isEdit ? "Nouveau mot de passe (vide = inchangé)" : "Mot de passe"}</label><input type="password" name="password" ${!isEdit ? 'required minlength="8"' : ""} placeholder="${isEdit ? "Laisser vide pour ne pas changer" : "Min. 8 caractères"}"></div> <div class="form-group"><label>${isEdit ? "Nouveau mot de passe (vide = inchangé)" : "Mot de passe"}</label><input type="password" name="password" ${!isEdit ? 'required minlength="8"' : ""} placeholder="${isEdit ? "Laisser vide pour ne pas changer" : "Min. 8 caractères"}"></div>
<div class="form-group"><label>Rôle</label><select name="role"><option value="user" ${isEdit && user.role === "user" ? "selected" : ""}>Utilisateur</option><option value="admin" ${isEdit && user.role === "admin" ? "selected" : ""}>Admin</option></select></div> <div class="form-group"><label>Rôle</label><select name="role"><option value="user" ${isEdit && user.role === "user" ? "selected" : ""}>Utilisateur</option><option value="admin" ${isEdit && user.role === "admin" ? "selected" : ""}>Admin</option></select></div>
+16 -21
View File
@@ -195,6 +195,9 @@ function initHelpModal() {
modal.classList.add("active"); modal.classList.add("active");
closeHeaderMenu(); closeHeaderMenu();
safeCreateIcons(); safeCreateIcons();
// Reset TOC to CSS default (mobile: hidden, desktop: visible)
const nav = document.getElementById("help-nav");
if (nav) nav.style.display = '';
if (!helpNavInitialized) { if (!helpNavInitialized) {
initHelpNavigation(); initHelpNavigation();
helpNavInitialized = true; helpNavInitialized = true;
@@ -220,9 +223,6 @@ function initHelpNavigation() {
const navList = document.getElementById("help-nav-list"); const navList = document.getElementById("help-nav-list");
const nav = document.getElementById("help-nav"); const nav = document.getElementById("help-nav");
const searchInput = document.getElementById("help-nav-search"); const searchInput = document.getElementById("help-nav-search");
const hamburger = document.getElementById("help-hamburger");
const navClose = document.getElementById("help-nav-close");
const overlay = document.getElementById("help-nav-overlay");
const helpContent = document.querySelector(".help-content"); const helpContent = document.querySelector(".help-content");
if (!helpBody || !navList || !nav) return; if (!helpBody || !navList || !nav) return;
@@ -238,23 +238,16 @@ function initHelpNavigation() {
sectionTextCache[s.id] = (s.textContent || '').toLowerCase(); sectionTextCache[s.id] = (s.textContent || '').toLowerCase();
}); });
// ── Mobile: open/close TOC slide-over ── // ── Mobile: simple display toggle ──
function openTOC() {
nav.classList.add("open");
if (overlay) overlay.classList.add("visible");
if (searchInput) setTimeout(() => searchInput.focus(), 150);
document.body.style.overflow = "hidden";
}
function closeTOC() {
nav.classList.remove("open");
if (overlay) overlay.classList.remove("visible");
document.body.style.overflow = "";
}
function isMobile() { return window.innerWidth <= 768; } function isMobile() { return window.innerWidth <= 768; }
function toggleTOC() {
if (hamburger) hamburger.addEventListener("click", openTOC); if (nav.style.display === 'none' || nav.style.display === '') {
if (navClose) navClose.addEventListener("click", closeTOC); nav.style.display = 'flex';
if (overlay) overlay.addEventListener("click", closeTOC); if (searchInput) setTimeout(() => searchInput.focus(), 150);
} else {
nav.style.display = 'none';
}
}
// ── Scroll to section ── // ── Scroll to section ──
function scrollToSection(targetId) { function scrollToSection(targetId) {
@@ -262,7 +255,7 @@ function initHelpNavigation() {
if (!section) return; if (!section) return;
section.scrollIntoView({ behavior: "smooth", block: "start" }); section.scrollIntoView({ behavior: "smooth", block: "start" });
section.setAttribute("tabindex", "-1"); section.setAttribute("tabindex", "-1");
if (isMobile()) closeTOC(); if (isMobile()) nav.style.display = 'none';
} }
// ── Focus the content pane ── // ── Focus the content pane ──
@@ -281,7 +274,9 @@ function initHelpNavigation() {
// ── Focus TOC active link ── // ── Focus TOC active link ──
function focusTOC() { function focusTOC() {
if (isMobile() && !nav.classList.contains("open")) openTOC(); if (isMobile() && (nav.style.display === 'none' || nav.style.display === '')) {
nav.style.display = 'flex';
}
const activeLink = navList.querySelector(".help-nav-link.active:not(.hidden)"); const activeLink = navList.querySelector(".help-nav-link.active:not(.hidden)");
if (activeLink) { activeLink.focus(); return; } if (activeLink) { activeLink.focus(); return; }
const firstLink = navList.querySelector(".help-nav-link:not(.hidden)"); const firstLink = navList.querySelector(".help-nav-link:not(.hidden)");
+15 -52
View File
@@ -2193,9 +2193,6 @@ select {
margin-right: 8px; margin-right: 8px;
} }
.help-hamburger:hover { color: var(--accent); border-color: var(--accent); } .help-hamburger:hover { color: var(--accent); border-color: var(--accent); }
.help-nav-overlay {
display: none;
}
.help-nav { .help-nav {
width: 260px; width: 260px;
min-width: 260px; min-width: 260px;
@@ -2227,19 +2224,6 @@ select {
letter-spacing: 0.05em; letter-spacing: 0.05em;
color: var(--text-muted); color: var(--text-muted);
} }
.help-nav-close {
display: none;
width: 32px; height: 32px;
padding: 0;
border: 1px solid var(--border);
border-radius: 6px;
background: transparent;
color: var(--text-secondary);
cursor: pointer;
align-items: center;
justify-content: center;
}
.help-nav-close:hover { color: var(--accent); border-color: var(--accent); }
.help-nav-search-wrap { .help-nav-search-wrap {
padding: 0 12px 8px; padding: 0 12px 8px;
flex-shrink: 0; flex-shrink: 0;
@@ -6449,49 +6433,28 @@ body.popup-mode .content-area {
border-radius: 0; border-radius: 0;
} }
/* Hamburger visible on mobile */
.help-hamburger { .help-hamburger {
display: inline-flex; display: inline-flex;
} }
/* TOC: slide-over from left */ /* TOC: collapsible block at top (not slide-over) */
.help-nav-overlay {
display: block;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 1100;
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease;
}
.help-nav-overlay.visible {
opacity: 1;
pointer-events: auto;
}
.help-nav { .help-nav {
position: fixed; position: static;
top: 0; width: 100%;
left: 0; min-width: 100%;
bottom: 0; max-height: none;
width: 280px; height: auto;
min-width: 280px; z-index: auto;
max-height: 100vh; transform: none;
height: 100vh; transition: none;
z-index: 1200; border-right: none;
transform: translateX(-100%); border-bottom: 1px solid var(--border);
transition: transform 0.25s ease; box-shadow: none;
border-right: 1px solid var(--border);
box-shadow: 2px 0 16px rgba(0,0,0,0.3);
flex-shrink: 0; flex-shrink: 0;
display: none; /* hidden by default on mobile */
} }
.help-nav.open { .help-nav-header {
transform: translateX(0); padding: 10px 16px 6px;
}
.help-nav-close {
display: inline-flex;
} }
/* Content full-width */ /* Content full-width */