@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); } }