Rask
Rask
v0.23.1-alpha.0.745

Components

Form tabs

A long form split into tabs, each saying in a red badge how many of its fields need correcting, and a refused save opening the first tab that holds one. Not one of Flux UI's components: the kit's own, made of Flux's tabs and badge, reading the form it stands in — the page hands it nothing.

A long form's tabs

Press Save with Email, Street and City empty: Details says 1 and Address 2, and Details opens. Fill Email and press Save again: its count goes and Address opens — a correction counts once it reaches the form, with the next action, or as it is typed in a .Live() field. Holds(() => m.Address) holds every field on the address; a tab that holds nothing, like Notes, never shows a count. Every panel stays in the form, so what was typed on a hidden tab is saved with the rest.

FormTabsExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

// Press Save with a field empty: each tab counts its own in red, and the first such tab opens.
public sealed partial class FormTabsExample : Component
{
    private readonly Supplier _supplier = new();
    private string _said = "Nothing saved yet.";

    protected override Component? Render() =>
        Div.Class("space-y-3")[
            Form.Model(_supplier).OnSubmit(Save).Id("uft-form").Class("max-w-md space-y-6")[
                Ui.FormTabs[
                    Ui.FormTab.Name("details").Label("Details").Holds(() => _supplier.Name, () => _supplier.Email).Class("space-y-6")[
                        Ui.Input.Bind(() => _supplier.Name).Label("Name").Id("uft-name").Validate(Filled),
                        Ui.Input.Bind(() => _supplier.Email).Label("Email").Id("uft-email").Validate(Filled)
                    ],
                    Ui.FormTab.Name("address").Label("Address").Holds(() => _supplier.Address).Class("space-y-6")[
                        Ui.Input.Bind(() => _supplier.Address.Street).Label("Street").Id("uft-street").Validate(Filled),
                        Ui.Input.Bind(() => _supplier.Address.City).Label("City").Id("uft-city").Validate(Filled)
                    ],
                    Ui.FormTab.Name("notes").Label("Notes")[
                        Ui.Textarea.Bind(() => _supplier.Notes).Label("Notes").Id("uft-notes")
                    ]
                ],
                Ui.Button.Submit.Primary.Id("uft-save")["Save"]
            ],
            Ui.Text.Id("uft-said")[_said]
        ];

    private void Save(Supplier supplier) => _said = $"Saved {supplier.Name}, {supplier.Address.City}.";

    private static IEnumerable<string> Filled(string? value) => string.IsNullOrWhiteSpace(value) ? ["Fill this in."] : [];

    public sealed class Supplier
    {
        public string Name { get; set; } = "Aminoff Freight";

        public string Email { get; set; } = "";

        public Place Address { get; set; } = new();

        public string Notes { get; set; } = "";
    }

    public sealed class Place
    {
        public string Street { get; set; } = "";

        public string City { get; set; } = "";
    }
}
Live result

Nothing saved yet.

Reference

Ui.FormTabs

not in Flux UI

A long form's tabs, inside its Form: each counts its invalid fields in a red badge, and a refused save opens the first that holds one.

No steps of its own: what goes inside it is its content.

Ui.FormTab

not in Flux UI

One tab: its words, the fields it holds, and its children as the panel.

Step
Takes
What it does
.Name(…)stringTies the tab to its panel; unique in the row. Required, and taken first.
.Label(…)stringThe words on the tab. Required, and taken first.
.Holds(…)fields: () => m.Name, () => m.AddressThe fields whose messages the tab counts, as the member expressions a control binds; an object or a collection holds every field it reaches.

Confirm