@* Les champs d'un numéro de revue, partagés par l'ajout et la modification. Un seul exemplaire, et pas deux blocs jumeaux : le formulaire d'ajout et celui de modification doivent proposer exactement les mêmes champs, sinon une couverture ou une une saisies à la création ne seraient plus modifiables ensuite — ce qui est précisément le cas d'usage (on ajoute un numéro le jour où on l'achète, on en recopie la une plus tard). ⚠️ Aucun champ ne se lie DIRECTEMENT à « Saisie » : chacun passe par une propriété qui prévient le parent (OnChangement). Blazor ne redessine que le composant qui a traité l'événement — une frappe ici ne re-rendait donc pas l'écran qui porte le bouton « Ajouter ce numéro », et celui-ci restait grisé jusqu'à ce qu'autre chose redessine le parent (enregistrer la revue, par exemple). Voir CLAUDE.md. *@
@* ⚠️ Un titre à la fois, et surtout PAS une ligne de titres séparés par des points-virgules : voir SaisieListe, qui porte le pourquoi. Une virgule dans un titre (« Ukraine, deux ans après ») n'a plus rien à couper, puisque plus rien ne découpe. *@ @* ⚠️ Une URL, pas une photo. Aucune source ne donne la couverture d'un NUMÉRO : l'ISSN désigne la revue, pas la parution. Et le projet ne stocke aucun fichier — « ebooks : fiches uniquement » vaut ici aussi. *@ @code { [Parameter, EditorRequired] public AjoutNumeroRevue Saisie { get; set; } = default!; /// /// Signale au parent que la saisie a changé, pour qu'il se redessine. /// /// /// ⚠️ Sans cela, un bouton du parent qui dépend de la saisie reste figé. Blazor ne /// redessine que le composant ayant traité l'événement : « Ajouter ce numéro » vit dans /// l'écran de la revue, le champ « Numéro » vit ici, et le bouton restait donc désactivé /// tant que rien d'autre ne re-rendait le parent — constaté en usage, où il fallait passer /// par « Modifier la revue » puis « Enregistrer » pour le débloquer. /// /// Tous les champs préviennent, pas seulement le numéro : aucun autre bouton n'en dépend /// aujourd'hui, mais faire dépendre la correction de « lequel commande quoi » la referait /// oublier au premier champ ajouté. /// /// [Parameter] public EventCallback OnChangement { get; set; } /// /// Les champs du numéro, servis par des propriétés plutôt que liés à : /// c'est le seul endroit d'où prévenir le parent à coup sûr. /// private string Numero { get => Saisie.Numero; set { Saisie.Numero = value; Notifier(); } } private string? Note { get => Saisie.Note; set { Saisie.Note = value; Notifier(); } } private string? CoverUrl { get => Saisie.CoverUrl; set { Saisie.CoverUrl = value; Notifier(); } } /// /// Le parent est prévenu sans être attendu : InvokeAsync ne fait que demander un /// nouveau rendu, et un setter de propriété ne peut de toute façon rien attendre. /// private void Notifier() => _ = OnChangement.InvokeAsync(); /// /// Date affichée par le champ, en local. /// /// /// ⚠️ Le <input type="date"> produit une date sans fuseau. Elle est déclarée /// locale ici et convertie en UTC, comme toutes les dates du projet : sans cela la parution /// se décale d'un jour pour la moitié du globe. La conversion se fait dans le composant /// pour que les deux écrans qui s'en servent ne puissent pas l'oublier chacun de leur côté. /// private DateTime? Parution { get => Saisie.DateParution?.ToLocalTime().Date; set { Saisie.DateParution = value is { } date ? DateTime.SpecifyKind(date, DateTimeKind.Local).ToUniversalTime() : null; Notifier(); } } [Parameter] public EventCallback OnEntree { get; set; } private async Task SurTouche(KeyboardEventArgs e) { if (e.Key == "Enter") { await OnEntree.InvokeAsync(e); } } }