@using MaBibli.Shared.Dtos @* L'arbre des séries : rien d'autre que la descendance. ⚠️ Il dépliait auparavant la liste complète des tomes de chaque série, carte de livre comprise. Un cycle de quatre séries occupait alors plusieurs écrans, et l'on ne voyait justement plus ce qu'un arbre sert à voir — qui contient quoi. Les tomes ont leur écran, celui de la série ; ici on ne montre que le titre, l'avancement, et le lien qui y mène. Corollaire : tout est DÉPLIÉ par défaut, et l'on replie ce dont on ne veut pas. L'inverse (tout replié) obligeait à ouvrir chaque nœud pour découvrir s'il contenait quelque chose, c'est-à-dire à faire à la main le travail que l'arbre est censé faire. *@
  • @if (Enfants.Count > 0) { } else { @* Une puce à la place de la bascule : sans elle, les titres sans descendance ne s'aligneraient pas sur ceux qui en ont. *@ } @Serie.Titre @Avancement() @if (ActionsActives) { }
    @* Le chemin déjà rendu coupe la récursion : une boucle résiduelle en base ferait sinon boucler le rendu Razor à l'infini. Le garde-fou serveur, lui, reste borné par le nombre de séries. *@ @if (Deplie && Enfants.Count > 0) { }
  • @code { [Parameter, EditorRequired] public SerieDto Serie { get; set; } = default!; [Parameter, EditorRequired] public IReadOnlyList Toutes { get; set; } = []; [Parameter, EditorRequired] public IReadOnlyDictionary Totaux { get; set; } = new Dictionary(); [Parameter] public IReadOnlySet Chemin { get; set; } = new HashSet(); /// /// Nœuds REPLIÉS, et non dépliés : le défaut est de tout montrer. /// /// /// État local de l'écran, volontairement non persistant : c'est un confort de lecture, /// pas une propriété de la série. /// [Parameter] public ISet Replies { get; set; } = new HashSet(); [Parameter] public bool ActionsActives { get; set; } [Parameter] public string? MotifBlocage { get; set; } [Parameter] public EventCallback OnDeplacer { get; set; } private List Soeurs => Toutes .Where(s => s.SerieParenteId == Serie.SerieParenteId) .OrderBy(s => s.Position) .ThenBy(s => s.Id) .ToList(); private List Enfants => Toutes .Where(s => s.SerieParenteId == Serie.Id) .OrderBy(s => s.Position) .ThenBy(s => s.Id) .ToList(); private int _index => Soeurs.FindIndex(s => s.Id == Serie.Id); private bool Deplie => !Replies.Contains(Serie.Id); private IReadOnlySet CheminAvecSerie => new HashSet(Chemin) { Serie.Id }; /// /// « 3 sur 7 tomes », en comptant les descendants — c'est ce qu'on vient chercher. /// private string Avancement() { var total = Totaux.TryGetValue(Serie.Id, out var valeur) ? valeur : (Possedes: Serie.NombrePossedes, Total: Serie.Elements.Count); return total.Total == 0 ? "aucun tome recensé" : $"{total.Possedes} sur {total.Total} tome{(total.Total > 1 ? "s" : "")}"; } private void Basculer() { if (!Replies.Add(Serie.Id)) { Replies.Remove(Serie.Id); } } private Task Monter() => Deplacer(-1); private Task Descendre() => Deplacer(1); private Task Deplacer(int delta) => Serie.SerieParenteId is { } parent ? OnDeplacer.InvokeAsync(new DeplacementSerie(Serie.Id, parent, delta)) : Task.CompletedTask; }