Gestion de bibliothèque personnelle auto-hébergée : catalogue, prêts, scan de code-barres, consultation hors-ligne. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
138 lines
5.6 KiB
Plaintext
138 lines
5.6 KiB
Plaintext
@*
|
|
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.
|
|
*@
|
|
<div class="champs-numero">
|
|
<label class="champ">
|
|
<span class="champ-libelle">Numéro</span>
|
|
<input class="champ-saisie" type="text" placeholder="43" @bind="Numero"
|
|
@bind:event="oninput" @onkeydown="SurTouche" autocomplete="off" />
|
|
</label>
|
|
|
|
<label class="champ">
|
|
<span class="champ-libelle">Parution <span class="champ-aide">(facultatif)</span></span>
|
|
<input class="champ-saisie" type="date" @bind="Parution" />
|
|
</label>
|
|
|
|
<label class="champ">
|
|
<span class="champ-libelle">Note <span class="champ-aide">(facultatif)</span></span>
|
|
<input class="champ-saisie" type="text" @bind="Note" @bind:event="oninput"
|
|
placeholder="dossier sur l'eau" autocomplete="off" />
|
|
</label>
|
|
</div>
|
|
|
|
@*
|
|
⚠️ 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.
|
|
*@
|
|
<SaisieListe Valeurs="Saisie.Articles" Libelle="À la une"
|
|
Aide=" (un titre à la fois : tapez-le, puis Entrée)"
|
|
Placeholder="Ukraine, deux ans après"
|
|
MessageDoublon="Ce titre est déjà à la une de ce numéro."
|
|
OnChangement="Notifier" />
|
|
|
|
@*
|
|
⚠️ 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.
|
|
*@
|
|
<label class="champ">
|
|
<span class="champ-libelle">
|
|
Couverture <span class="champ-aide">(adresse d'une image, facultatif)</span>
|
|
</span>
|
|
<input class="champ-saisie" type="url" @bind="CoverUrl" @bind:event="oninput"
|
|
placeholder="https://…" autocomplete="off" />
|
|
</label>
|
|
|
|
@code {
|
|
[Parameter, EditorRequired] public AjoutNumeroRevue Saisie { get; set; } = default!;
|
|
|
|
/// <summary>
|
|
/// Signale au parent que la saisie a changé, pour qu'il se redessine.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// ⚠️ <b>Sans cela, un bouton du parent qui dépend de la saisie reste figé.</b> 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.
|
|
/// <para>
|
|
/// 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é.
|
|
/// </para>
|
|
/// </remarks>
|
|
[Parameter] public EventCallback OnChangement { get; set; }
|
|
|
|
/// <summary>
|
|
/// Les champs du numéro, servis par des propriétés plutôt que liés à <see cref="Saisie"/> :
|
|
/// c'est le seul endroit d'où prévenir le parent à coup sûr.
|
|
/// </summary>
|
|
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(); }
|
|
}
|
|
|
|
/// <remarks>
|
|
/// Le parent est prévenu sans être attendu : <c>InvokeAsync</c> ne fait que demander un
|
|
/// nouveau rendu, et un setter de propriété ne peut de toute façon rien attendre.
|
|
/// </remarks>
|
|
private void Notifier() => _ = OnChangement.InvokeAsync();
|
|
|
|
/// <summary>
|
|
/// Date affichée par le champ, en <b>local</b>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// ⚠️ Le <c><input type="date"></c> 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é.
|
|
/// </remarks>
|
|
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<KeyboardEventArgs> OnEntree { get; set; }
|
|
|
|
private async Task SurTouche(KeyboardEventArgs e)
|
|
{
|
|
if (e.Key == "Enter")
|
|
{
|
|
await OnEntree.InvokeAsync(e);
|
|
}
|
|
}
|
|
}
|