Rend l'attente visible, et met le champ de création sur une ligne

Une recherche BnF coûte environ une seconde par page, et jusqu'à dix pages pour
les nouveautés : sans rien à l'écran, l'application se lit comme plantée. Le lot
H l'avait montré — un écran qui ne dit rien pousse à recliquer, donc à relancer
l'appel.

Un indicateur ne remplace aucun message d'échec : une source muette garde ses
`EtatSourceBibliographie` et ses motifs. Ici on dit « ça travaille », jamais
« ça a marché ». L'animation se neutralise sous `prefers-reduced-motion`, la
rondelle restant visible : c'est elle qui porte l'information, pas sa rotation.

Le champ « nom d'une nouvelle série » et son bouton tiennent maintenant sur une
ligne. `min-width: 0` est ce qui la fait tenir à 320 px : sans lui, un élément de
formulaire refuse de rétrécir sous sa largeur intrinsèque et pousse le bouton
hors de l'écran.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mathieu
2026-08-21 11:25:09 +02:00
co-authored by Claude Opus 5
parent ad00fd5116
commit b8a5122b6f
4 changed files with 96 additions and 7 deletions
+25
View File
@@ -0,0 +1,25 @@
@*
Indicateur d'attente pour les appels lents (lot L1).
⚠️ Le besoin n'est pas décoratif. Une recherche BnF coûte environ une seconde par page, et
jusqu'à dix pages pour les nouveautés : sans rien à l'écran, l'application se lit comme
plantée, et l'utilisateur reclique — donc relance l'appel. C'est exactement ce que le lot H
avait constaté sur la bibliographie.
⚠️ Un indicateur NE REMPLACE PAS un message d'échec. Une source muette garde ses états
EtatSourceBibliographie et son motif : ici on dit « ça travaille », jamais « ça a marché ».
role="status" et non role="alert" : l'attente s'annonce sans interrompre le lecteur d'écran.
L'animation est neutralisée sous prefers-reduced-motion (voir app.css).
*@
<p class="attente @Classe" role="status">
<span class="attente-rondelle" aria-hidden="true"></span>
<span>@Message</span>
</p>
@code {
/// <summary>Ce qu'on attend, dit à l'utilisateur. Jamais un libellé générique si l'on sait mieux.</summary>
[Parameter, EditorRequired] public string Message { get; set; } = default!;
[Parameter] public string? Classe { get; set; }
}
+4 -3
View File
@@ -82,9 +82,10 @@
*@
@if (_etape == Etape.Recherche)
{
<p class="message-discret" role="status">
Recherche de <span class="code-isbn">@FormatageIsbn.Afficher(_isbn)</span> à la BnF, puis chez OpenLibrary…
</p>
@* L1 : l'étape existait déjà pour que l'enchaînement du scan SE VOIE ; la rondelle dit en
plus que ça travaille encore, ce qu'un texte figé ne distingue pas d'un écran bloqué. *@
<Patience Classe="attente-recherche"
Message="@($"Recherche de {FormatageIsbn.Afficher(_isbn)} à la BnF, puis chez OpenLibrary…")" />
}
@if (_etape == Etape.Scan)
+4 -4
View File
@@ -19,18 +19,18 @@
@* Créer une série depuis la liste : c'est le seul endroit d'où l'on part, et un écran de plus
pour un unique champ ne se justifierait pas. *@
<div class="barre-recherche">
@* M1 : le champ et son bouton sur UNE ligne. Le champ prend la place restante, le bouton
garde la sienne et son libellé ne se tronque pas — vérifié jusqu'à 320 px. *@
<div class="ligne-creation">
<input class="champ-saisie" type="text" placeholder="Nom d'une nouvelle série…"
@bind="_nouveau" @bind:event="oninput" @onkeydown="SurTouche"
aria-label="Nom de la nouvelle série" autocomplete="off" />
</div>
<div class="actions-formulaire">
<button type="button" class="bouton bouton-principal"
disabled="@(_enCours || !Reseau.EnLigne || string.IsNullOrWhiteSpace(_nouveau))"
title="@MotifBlocage"
@onclick="CreerAsync">
Créer la série
Créer
</button>
</div>
+63
View File
@@ -1383,3 +1383,66 @@ body {
border-bottom-color: #f5d76e;
}
}
/* --- Attente (lot L1) -------------------------------------------------
Un appel lent doit se voir : une recherche BnF coûte ~1 s par page, et
jusqu'à dix pages pour les nouveautés. Sans indicateur, l'écran se lit
comme planté et l'on reclique, ce qui relance l'appel.
------------------------------------------------------------------- */
.attente {
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--mb-texte-doux);
font-size: 0.9rem;
margin: 0.75rem 0;
}
.attente-rondelle {
flex: 0 0 auto;
width: 1rem;
height: 1rem;
border: 2px solid var(--mb-bord);
border-top-color: var(--mb-accent);
border-radius: 50%;
animation: attente-tourne 0.8s linear infinite;
}
@keyframes attente-tourne {
to { transform: rotate(360deg); }
}
/* Une animation perpétuelle est précisément ce que ce réglage demande d'éviter.
L'indicateur reste visible — c'est lui qui porte l'information, pas sa rotation. */
@media (prefers-reduced-motion: reduce) {
.attente-rondelle {
animation: none;
border-top-color: var(--mb-accent);
border-right-color: var(--mb-accent);
}
}
/* --- Créer depuis une liste : champ + bouton sur une ligne (lot M1) ----
⚠️ min-width: 0 sur le champ est ce qui fait tenir la ligne à 320 px : sans
lui, un élément de formulaire refuse de rétrécir sous sa largeur intrinsèque
et pousse le bouton hors de l'écran. Le bouton, lui, ne rétrécit pas et son
libellé ne se coupe pas — un bouton tronqué ne se lit plus.
------------------------------------------------------------------- */
.ligne-creation {
display: flex;
gap: 0.5rem;
align-items: stretch;
margin-bottom: 0.75rem;
}
.ligne-creation .champ-saisie {
flex: 1 1 auto;
min-width: 0;
}
.ligne-creation .bouton {
flex: 0 0 auto;
white-space: nowrap;
}