@* Couverture d'un livre, avec substitut. Les URL de couverture OpenLibrary renvoient des 502 intermittents (mesuré en phase 2 : 200, 200, puis 502 sur le même ISBN). On ne valide donc JAMAIS l'URL par un appel préalable : on l'affiche, et si l'image casse, l'attribut onerror la masque et laisse apparaître le substitut qui se trouve déjà dessous. Aucun aller-retour serveur, aucun état C# à gérer. @key sur l'img : si l'URL change, Blazor recrée l'élément au lieu de réutiliser celui que le onerror a marqué comme cassé. Agrandissement (IDEES.md) : seulement quand il y a une URL. Sans couverture, il n'y a rien à agrandir — le substitut à initiale reste un simple décor, jamais un bouton. *@
@if (ADesPixels) { @if (Agrandissable) { @* Un vrai } else { Couverture de @Titre } }
@if (_agrandie) { @* Fermeture au clic extérieur (le clic sur l'image ne remonte pas) et à Échap. Le tabindex rend le calque focusable : sans focus, aucun keydown ne lui parviendrait. *@ } @code { [Parameter] public string? Url { get; set; } [Parameter] public string? Titre { get; set; } [Parameter] public string? Classe { get; set; } /// Le clic sur l'image l'affiche en grand. Sans effet s'il n'y a pas de couverture. [Parameter] public bool Agrandissable { get; set; } private bool _agrandie; private bool _aFocaliser; private ElementReference _calque; private bool ADesPixels => !string.IsNullOrWhiteSpace(Url); private string Initiale => string.IsNullOrWhiteSpace(Titre) ? "?" : Titre.Trim()[..1].ToUpperInvariant(); private void Ouvrir() { _agrandie = true; _aFocaliser = true; } private void Fermer() => _agrandie = false; private void SurToucheAsync(KeyboardEventArgs e) { if (e.Key is "Escape" or "Esc") { Fermer(); } } protected override async Task OnAfterRenderAsync(bool premierRendu) { if (_aFocaliser) { _aFocaliser = false; await _calque.FocusAsync(); } } }