8.9 KiB
8.9 KiB
#114 — Configuration — refonte mobile-responsive de la section Settings
Statut : 🟢 · Impact : 🟡 · Zone : frontend (mobile, ≤ 768 px) Fichiers :
frontend/index.html,frontend/js/config.js,frontend/style.css,frontend/locales/{fr,en}.json,tests/frontend/config-mobile.test.mjs,tests/e2e/config-mobile.spec.js.
Contexte
La page Configurations (#config-modal) était utilisable en mobile seulement
partiellement (correctifs BUG-071) : le sommaire s'ouvrait en bloc haut, la modale
n'était pas plein écran, les cibles tactiles étaient sous 44 px, le clavier virtuel
iOS zoomait les champs, la rangée « Sauvegarder » disparaissait au scroll et plusieurs
dettes HTML/i18n étaient restées en place.
Ce qui a été livré
A. Sommaire en drawer coulissant
#config-navdevient un panneau coulissant gauche (position: fixed,width: min(320px, 88vw),z-index: 40) sous un fond assombri (#config-modal.config-toc-open::before,z-index: 35).- Ouverture/fermeture pilotée par la classe
.config-toc-opensur#config-modal(JS_setConfigNav) + undisplayinline conservé pour le contrat de reset. - Bouton
#config-toc-close(classe.help-toc-close,aria-labelconfig.toc_close) dans l'en-tête du drawer ; visible uniquement dans le drawer de la config (masqué sur desktop où la nav est toujours visible). - Backdrop : un tap hors du drawer ferme d'abord le drawer, pas la modale
(
e.target === modal→config-toc-openprésent →_setConfigNav(false)). - Échap : ferme le drawer d'abord, puis la modale.
- Fermeture de la modale (
closeConfigModal) nettoie toujours la classeconfig-toc-openet ledisplayinline (aucun fond ne subsiste). - Animation
config-toc-slide-in(translateX) à l'ouverture.
B. Modale plein écran
#config-modal:padding: 0,.editor-containeren100vw × 100dvh(100dvh= hauteur du viewport dynamique, tient compte de la barre du navigateur mobile),border-radius: 0,border: none.- Le contenu (
#config-scroll) garde son scroll propre ; le sous-bloc.config-contentreçoit unpadding-bottom: 80 pxpour ne jamais passer sous la rangée sticky.
C. Cibles tactiles ≥ 44 px & anti-zoom iOS
- Champs :
.config-input,.config-select,.help-nav-search,.profile-field .config-input/.config-select→min-height: 44px+font-size: 16px(anti-zoom iOS : unfont-size < 16pxdéclenche le zoom automatique au focus). La règle est scoppée#config-modalpour ne pas écraser.mfa-code-input(qui a sa propre typographie). - Boutons :
.config-btn-save,.config-btn-secondary,.config-btn-primary,.config-btn-danger,.config-btn-add,.config-btn-sm,.mfa-link-btn,.theme-action-btn,.profile-avatar-actions .config-btn-secondary,.editor-btn(fermer),#config-hamburger,#config-toc-close,.help-search-clear→min-height/min-width: 44px. - Liens du sommaire :
.help-nav-link→min-height: 44px(ligne tactile confortable). .help-hamburgerpasse de 36 px à 44 px en mobile (règle partagée avec le modal d'aide).
D. Rangée « Sauvegarder » sticky
.config-actions-row(dans#cfg-backend-settings) →position: sticky; bottom: 0, empilée verticalement (flex-direction: column), boutons pleine largeur 44 px, fond opaque + bordure,padding-bottomavecenv(safe-area-inset-bottom)(barre home iOS).- La rangée reste visible pendant le scroll de la section backend ; le
padding-bottom: 80pxde.config-contentgarantit qu'elle ne masque jamais les derniers contrôles.
E. Formulaires 1 colonne & grilles
.config-row→ 1 colonne (grid-template-columns: 1fr),.config-input--numpleine largeur,text-align: left..ai-default-grid/.ai-provider-fields→ 1 colonne.- Add-rows (
.config-add-row,.config-add-pattern) → wrap + largeurs inline (180/140/100px) neutralisées (width: auto !important). - Items webhook/token/share → wrap ; URLs/méta sur leur propre ligne
(
overflow-wrap: anywhere) ; boutons de suppression 44 px. .hidden-files-add-row→ wrap, input pleine largeur..config-diag-row→ wrap..profile-avatar-row→ wrap ;.profile-form→max-width: 100%..webauthn-key-item→ wrap ;.webauthn-key-label→ pleine largeur..theme-gridreste enauto-fill minmax(160px, 1fr)(déjà responsive).
F. MFA & sécurité
.mfa-recovery-list→ 1 colonne en mobile (2 colonnes illisibles à 360 px)..mfa-verify-section,.mfa-recovery-actions,.mfa-disable-actions→ wrap ; champs/boutons enfants en pleine largeur..mfa-code-input→width: 100%,max-width: 320px,letter-spacing: 6px(au lieu de 12 px qui débordait),min-height: 52px..mfa-secret-code→word-break: break-all(secret TOTP long)..mfa-code-input-group→ wrap.- Règle morte
.mfa-recovery-inputsupprimée (auth.js utilisemfa-code-input recovery-input).
G. Dettes HTML corrigées
.config-actions-row(Sauvegarder / Réindexer / Réinitialiser) déplacée dans#cfg-backend-settings(elle était hors de toute section → le sticky n'avait pas de conteneur de scroll fiable) ;</section>orphelin supprimé.- Id dupliqué
cfg-partages-publicsretiré du<h2>(l'id reste sur la<section>, cf. #113). - Conteneur mort
#plugins-settings-containersupprimé (le rendu réel est#cfg-pluginsviaplugins.js). - Sections plugins/about ré-indentées.
#mt-explorer: libellé brutsettings.searchremplacé par<span data-i18n="settings.explorer">(i18n correct, FR « Explorateur » / EN « Files »).- Doublons CSS
.config-btn-add(3 définitions) réduits à la définition de référence.
H. i18n FR/EN
- Purge des clés mortes (aucune référence HTML/JS/tests/backend) :
settings.backend,settings.backend_hint,settings.restart_badge,settings.save,settings.plugins. - Ajouts :
settings.explorer(FR « Explorateur » / EN « Files »),config.toc_close(FR « Fermer le sommaire » / EN « Close contents »). - Correctif :
settings.tabsen FR était le mot anglais « Tabs » → « Onglets » (EN reste « Tabs »). - Clés vivantes conservées :
settings.reindex,settings.no_restart_badge,settings.backend_section,settings.security,settings.search,settings.tabs,settings.search_placeholder.
Tests
Statics — tests/frontend/config-mobile.test.mjs (27, au CI)
- BUG-071a–e (hamburger, toggle JS, grilles/wrap, ancres mortes,
data-i18n-attrmulti-paires) — conservés et adaptés au drawer. - #114a :
#config-toc-closeprésent + i18n ;_setConfigNavbascule.config-toc-open; backdrop::before(z-index 35) ;.help-toc-closemasqué sur desktop / visible dans le drawer ; Échap et backdrop ferment le drawer d'abord ;closeConfigModalnettoie la classe. - #114b : modale
100dvh+padding: 0; inputs/selects16px+44px; boutons44px; rangée sticky (position: sticky+flex-direction: column+ safe-area) ; MFA 1 colonne + wrap + code full-width ;.config-actions-rowbien dans#cfg-backend-settings. - #114i18n : clés mortes purgées ;
settings.explorerFR/EN ;settings.tabsFR = « Onglets » ;#mt-explorerportedata-i18n="settings.explorer";#plugins-settings-containerabsent.
E2E — tests/e2e/config-mobile.spec.js (5, projet chromium-mobile)
- Hamburger → drawer
position: fixed+ classeconfig-toc-opensur la modale. - Bouton
#config-toc-closeet tap backdrop ferment le drawer sans fermer la modale. - Sélection d'une section → scroll doux + lien actif + repli du drawer.
- Modale plein écran (largeur/hauteur ≈ viewport) +
#config-hamburger,#config-closeet#cfg-save-backend≥ 44 px. - Aucun débordement horizontal à 393 px (sections IA / tokens / webhooks / partages).
Détails d'implémentation notables
- Spécificité CSS : les règles
#config-modal #config-nav(2 ids) priment sur les règles génériques.help-nav/body .help-navqui masquent la nav en mobile — pas besoin de!important. - Backdrop = pseudo-élément : les clics sur
::beforesont attribués à l'élément or (#config-modal), donc le handlere.target === modalexistant fonctionne sans node supplémentaire. - Contrat de reset conservé :
configNavOnOpen.style.display = ''à l'ouverture (test statique BUG-071b) — le CSS reprend la main (drawer masqué par défaut sur mobile). 100dvhavec repli100vh: les navigateurs sans supportdvhgardent le comportement précédent.- La règle
.help-hamburger { display: inline-flex }du bloc mobile du modal d'aide est partagée (44 px) ; le drawer de la config double avec#config-modal .help-hamburgerpour rester robuste à un réordonnancement des règles.