@typeparam TValue
@using SteUp.Shared.Components.Layout.Sheet
@*
Scelta a valore singolo. Al posto della tendina: una riga tappabile che
apre un bottom sheet con le opzioni a tutta larghezza.
La tendina di serie e' un residuo del web — galleggia bianca su bianco
sopra le card, si porta dietro la barra di scorrimento del desktop e offre
bersagli alti quanto una riga di testo. Qui l'elenco arriva dal basso, ha
un titolo, righe da 56 px e la scelta corrente segnata: si usa in piedi,
con una mano, anche con i guanti.
*@
@foreach (var item in Items)
{
var selected = IsSelected(item);
}
@code {
/// Titolo dello sheet: e' il nome del campo ("Reparto", "Motivo").
[Parameter, EditorRequired] public string Title { get; set; } = "";
[Parameter] public string? Subtitle { get; set; }
[Parameter, EditorRequired] public IEnumerable Items { get; set; } = [];
[Parameter] public TValue? Value { get; set; }
[Parameter] public EventCallback ValueChanged { get; set; }
/// Come si scrive un'opzione. Deve essere fornita: il ToString() di un DTO
/// non e' un'etichetta.
[Parameter, EditorRequired] public Func ItemLabel { get; set; } = _ => null;
[Parameter] public string Placeholder { get; set; } = "Scegli";
[Parameter] public bool ReadOnly { get; set; }
private bool _open;
private bool HasValue => Value is not null;
private string? SelectedLabel => Value is null ? null : ItemLabel(Value);
// Il confronto passa per l'etichetta, non per l'oggetto: il valore corrente
// arriva ricostruito dall'entita' salvata (Scheda.Reparto lo crea al volo da
// codice e descrizione), quindi non e' mai lo stesso oggetto di quelli in
// elenco e il confronto di riferimento non segnerebbe mai nulla.
private bool IsSelected(TValue item) =>
Value is not null &&
string.Equals(ItemLabel(item), SelectedLabel, StringComparison.Ordinal);
private void Open()
{
if (!ReadOnly) _open = true;
}
private async Task Choose(TValue item)
{
_open = false;
Value = item;
await ValueChanged.InvokeAsync(item);
}
}