Fait saisir un par un ce qui se saisissait sur une ligne à séparateurs
Les thèmes et les auteurs souffraient du défaut corrigé hier sur les unes : un champ relié à chaque frappe sur une propriété qui découpait puis recomposait la liste, donc un séparateur intapable. Ils passent au même geste — écrire, Entrée, puis modifier ou retirer chaque ligne — et le composant est partagé plutôt que recopié une troisième fois. Le bloc des rôles se fond dans la liste des auteurs : il listait les mêmes noms une seconde fois. Renommer une ligne conserve son rôle, parce qu'on corrige un nom au lieu de reconstruire la liste. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,77 +32,15 @@
|
||||
</div>
|
||||
|
||||
@*
|
||||
⚠️ Un titre à la fois, et surtout PAS une ligne de titres séparés par des points-virgules.
|
||||
Le champ unique se reliait à chaque frappe sur une propriété qui découpait puis recomposait
|
||||
la liste : la saisie était donc réécrite sous les doigts — un point-virgule tapé disparaissait
|
||||
(il ouvrait une valeur vide, aussitôt élaguée), et l'espace qui le suit était rogné par le
|
||||
même élagage. Autrement dit, le séparateur du champ était le seul caractère qu'on ne pouvait
|
||||
pas y taper.
|
||||
|
||||
Ici la liste est la donnée : on écrit un titre, Entrée l'y range, et chaque titre se modifie
|
||||
ou se retire ensuite. Une virgule (« Ukraine, deux ans après ») n'a plus rien à couper,
|
||||
puisque plus rien ne découpe.
|
||||
⚠️ 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.
|
||||
*@
|
||||
<div class="champ">
|
||||
<label class="champ-libelle" for="@_idChamp">
|
||||
À la une <span class="champ-aide">(un titre à la fois : tapez-le, puis Entrée)</span>
|
||||
</label>
|
||||
|
||||
<div class="ligne-creation">
|
||||
<input id="@_idChamp" class="champ-saisie" type="text" @bind="_titreSaisi"
|
||||
@bind:event="oninput" @onkeydown="SurToucheArticle"
|
||||
placeholder="Ukraine, deux ans après" autocomplete="off" />
|
||||
|
||||
<button type="button" class="bouton bouton-compact"
|
||||
disabled="@(string.IsNullOrWhiteSpace(_titreSaisi))" @onclick="ValiderArticle">
|
||||
@(_indexModifie is null ? "Ajouter" : "Enregistrer")
|
||||
</button>
|
||||
|
||||
@if (_indexModifie is not null)
|
||||
{
|
||||
<button type="button" class="bouton bouton-compact bouton-discret"
|
||||
@onclick="AnnulerModification">
|
||||
Annuler
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (_messageArticle is not null)
|
||||
{
|
||||
<p class="message-discret" role="status">@_messageArticle</p>
|
||||
}
|
||||
|
||||
@if (Saisie.Articles.Count > 0)
|
||||
{
|
||||
<ul class="liste-unes">
|
||||
@for (var rang = 0; rang < Saisie.Articles.Count; rang++)
|
||||
{
|
||||
@* La copie locale est indispensable : sans elle, les fermetures des deux
|
||||
boutons captureraient la variable de boucle, donc son état final. *@
|
||||
var index = rang;
|
||||
var titre = Saisie.Articles[rang];
|
||||
|
||||
<li class="ligne-une" @key="index">
|
||||
<span class="ligne-une-titre">@titre</span>
|
||||
|
||||
<div class="actions-tome">
|
||||
<button type="button" class="bouton bouton-compact"
|
||||
aria-label="@($"Modifier « {titre} »")"
|
||||
@onclick="() => ModifierArticle(index)">
|
||||
Modifier
|
||||
</button>
|
||||
|
||||
<button type="button" class="bouton bouton-compact bouton-danger"
|
||||
aria-label="@($"Supprimer « {titre} »")"
|
||||
@onclick="() => SupprimerArticle(index)">
|
||||
Supprimer
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
<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
|
||||
@@ -165,108 +103,6 @@
|
||||
/// </remarks>
|
||||
private void Notifier() => _ = OnChangement.InvokeAsync();
|
||||
|
||||
/// <summary>Identifiant du champ « à la une », pour que son libellé le désigne vraiment.</summary>
|
||||
private readonly string _idChamp = $"une-{Guid.NewGuid():N}";
|
||||
|
||||
/// <summary>Titre en cours de frappe. <b>Il n'est relié à rien d'autre</b> : voir plus haut.</summary>
|
||||
private string? _titreSaisi;
|
||||
|
||||
/// <summary>Rang du titre qu'on modifie — <c>null</c> = on en ajoute un nouveau.</summary>
|
||||
private int? _indexModifie;
|
||||
|
||||
private string? _messageArticle;
|
||||
|
||||
/// <summary>Saisie servie au rendu précédent, pour repérer que le parent en a changé.</summary>
|
||||
private AjoutNumeroRevue? _saisiePrecedente;
|
||||
|
||||
/// <summary>
|
||||
/// Le parent remplace la saisie entière (numéro enregistré, autre numéro ouvert en
|
||||
/// modification) : ce qui restait dans le champ appartenait au numéro d'avant.
|
||||
/// </summary>
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (!ReferenceEquals(_saisiePrecedente, Saisie))
|
||||
{
|
||||
_saisiePrecedente = Saisie;
|
||||
RemettreAZero();
|
||||
}
|
||||
}
|
||||
|
||||
private void RemettreAZero()
|
||||
{
|
||||
_titreSaisi = null;
|
||||
_indexModifie = null;
|
||||
_messageArticle = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entrée range le titre, comme le bouton.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ⚠️ Cette touche ne remonte <b>pas</b> à <c>OnEntree</c> : celui-ci enregistre le numéro
|
||||
/// entier, et l'on serait alors bien en peine de saisir deux titres à la une.
|
||||
/// </remarks>
|
||||
private void SurToucheArticle(KeyboardEventArgs e)
|
||||
{
|
||||
if (e.Key == "Enter")
|
||||
{
|
||||
ValiderArticle();
|
||||
}
|
||||
}
|
||||
|
||||
private void ValiderArticle()
|
||||
{
|
||||
var titre = _titreSaisi?.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(titre))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Même règle de doublon que partout ailleurs : au sens de la normalisation, et le
|
||||
// libellé déjà rangé est celui qui reste.
|
||||
var cle = NormalisationTexte.Normaliser(titre);
|
||||
var deja = Saisie.Articles.FindIndex(a => NormalisationTexte.Normaliser(a) == cle);
|
||||
|
||||
if (deja >= 0 && deja != _indexModifie)
|
||||
{
|
||||
_messageArticle = "Ce titre est déjà à la une de ce numéro.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (_indexModifie is { } index)
|
||||
{
|
||||
Saisie.Articles[index] = titre;
|
||||
}
|
||||
else
|
||||
{
|
||||
Saisie.Articles.Add(titre);
|
||||
}
|
||||
|
||||
RemettreAZero();
|
||||
Notifier();
|
||||
}
|
||||
|
||||
/// <summary>Reprend un titre dans le champ : on le corrige là où on l'a écrit.</summary>
|
||||
private void ModifierArticle(int index)
|
||||
{
|
||||
_messageArticle = null;
|
||||
_indexModifie = index;
|
||||
_titreSaisi = Saisie.Articles[index];
|
||||
}
|
||||
|
||||
private void AnnulerModification() => RemettreAZero();
|
||||
|
||||
private void SupprimerArticle(int index)
|
||||
{
|
||||
Saisie.Articles.RemoveAt(index);
|
||||
|
||||
// Le titre qu'on modifiait vient peut-être de disparaître, et les rangs suivants ont
|
||||
// glissé : reprendre la modification écrirait sur le mauvais titre.
|
||||
RemettreAZero();
|
||||
Notifier();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Date affichée par le champ, en <b>local</b>.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user