FlexBox

Layout

Lays out children using a CSS-like flexbox model with configurable direction, justification, alignment, wrapping, and gap.

@using Spectre.Console
@using RazorConsole.Components
@using RazorConsole.Core.Renderables

<FlexBox Direction="FlexDirection.Column" Gap="1">
    <FlexBox Direction="FlexDirection.Row" Justify="FlexJustify.Center">
        <Markup Content="Create Your Account" Decoration="@(Decoration.Bold | Decoration.Underline)"
                Foreground="@Color.DeepSkyBlue1" />
    </FlexBox>

    @* ── Name fields with SpaceBetween ── *@
    <FlexBox Direction="FlexDirection.Row" Wrap="FlexWrap.Wrap" Justify="FlexJustify.SpaceBetween">
        <TextInput Label="First Name" Value="@_firstName" ValueChanged="OnFirstNameChanged"
                   Placeholder="Ada" FocusedBorderColor="@Color.DeepSkyBlue1"
                   BorderPadding="@_inputPadding" />
        <TextInput Label="Middle Name" Value="@_middleName" ValueChanged="OnMiddleNameChanged"
                   Placeholder="Augusta" FocusedBorderColor="@Color.DeepSkyBlue1"
                   BorderPadding="@_inputPadding" />
        <TextInput Label="Last Name" Value="@_lastName" ValueChanged="OnLastNameChanged"
                   Placeholder="Lovelace" FocusedBorderColor="@Color.DeepSkyBlue1"
                   BorderPadding="@_inputPadding" />
    </FlexBox>

    @* ── Email field full width ── *@
    <TextInput Label="Email" Value="@_email" ValueChanged="OnEmailChanged"
               Placeholder="ada@example.com" FocusedBorderColor="@Color.DeepSkyBlue1"
               BorderPadding="@_inputPadding" Expand="true" />

    @* ── Password field full width ── *@
    <TextInput Label="Password" Value="@_password" ValueChanged="OnPasswordChanged"
               Placeholder="Enter password" MaskInput="true"
               FocusedBorderColor="@Color.DeepSkyBlue1"
               BorderPadding="@_inputPadding" Expand="true" />

    @* ── Role selector with description ── *@
    <Markup Content="Select your role to determine access permissions:" Foreground="@Color.Grey70" />
    <Select TItem="string" Options="@_roles" Value="@_selectedRole"
            FocusedValue="@_focusedRole"
            FocusedValueChanged="@((v) => _focusedRole = v)"
            ValueChanged="@((v) => _selectedRole = v)"
            Placeholder="Choose a role" />

    @* ── Interests (wrapping tags) ── *@
    <Markup Content="Pick your interests:" Foreground="@Color.Grey70" />
    <FlexBox Direction="FlexDirection.Row" Wrap="FlexWrap.Wrap" Justify="FlexJustify.SpaceBetween">
        @foreach (var interest in _interests)
        {
            <TextButton Content="@interest" BackgroundColor="@(IsSelected(interest) ? Color.DeepSkyBlue1 : Color.Default)"
                        FocusedColor="Color.Cyan1" OnClick="@(() => ToggleInterest(interest))" />
        }
    </FlexBox>

    @* ── Action buttons ── *@
    <FlexBox Direction="FlexDirection.Row" Justify="FlexJustify.End" Gap="3">
        <TextButton Content="Cancel" BackgroundColor="Color.Grey"
                    FocusedColor="Color.Red" OnClick="HandleCancel" />
        <TextButton Content="Submit" BackgroundColor="Color.Grey"
                    FocusedColor="Color.Green" OnClick="HandleSubmit" />
    </FlexBox>
</FlexBox>

@* ── Status line ── *@
<Markup Content="@_statusText" Foreground="@_statusColor"
        Decoration="@Decoration.Italic" />
        
@code {
    private static readonly Padding _inputPadding = new(1, 0, 1, 0);
    private static readonly string[] _roles = ["Viewer", "Editor", "Admin"];
    private static readonly string[] _interests =
        ["C#", "RazorConsole", "Razor", "Blazor", "Spectre.Console", "TUI", "CLI", "DevOps", "AI"];

    private string _firstName = string.Empty;
    private string _middleName = string.Empty;
    private string _lastName = string.Empty;
    private string _email = string.Empty;
    private string _password = string.Empty;
    private string? _selectedRole;
    private string? _focusedRole;
    private readonly HashSet<string> _selectedInterests = new();
    private string _statusText = "Fill out the form and press Submit.";
    private Color _statusColor = Color.Grey70;

    private bool IsSelected(string interest) => _selectedInterests.Contains(interest);

    private void ToggleInterest(string interest)
    {
        if (!_selectedInterests.Remove(interest))
        {
            _selectedInterests.Add(interest);
        }

        StateHasChanged();
    }

    private Task OnFirstNameChanged(string? v)
    {
        _firstName = v ?? string.Empty;
        StateHasChanged();
        return Task.CompletedTask;
    }

    private Task OnMiddleNameChanged(string? v)
    {
        _middleName = v ?? string.Empty;
        StateHasChanged();
        return Task.CompletedTask;
    }

    private Task OnLastNameChanged(string? v)
    {
        _lastName = v ?? string.Empty;
        StateHasChanged();
        return Task.CompletedTask;
    }

    private Task OnEmailChanged(string? v)
    {
        _email = v ?? string.Empty;
        StateHasChanged();
        return Task.CompletedTask;
    }

    private Task OnPasswordChanged(string? v)
    {
        _password = v ?? string.Empty;
        StateHasChanged();
        return Task.CompletedTask;
    }

    private void HandleCancel()
    {
        _firstName = string.Empty;
        _middleName = string.Empty;
        _lastName = string.Empty;
        _email = string.Empty;
        _password = string.Empty;
        _selectedRole = null;
        _selectedInterests.Clear();
        _statusText = "Form cleared.";
        _statusColor = Color.Yellow;
        StateHasChanged();
    }

    private void HandleSubmit()
    {
        if (string.IsNullOrWhiteSpace(_firstName) || string.IsNullOrWhiteSpace(_email))
        {
            _statusText = "⚠ First name and email are required.";
            _statusColor = Color.Red;
        }
        else
        {
            var interests = _selectedInterests.Count > 0
                ? string.Join(", ", _selectedInterests)
                : "none";
            var fullName = string.IsNullOrWhiteSpace(_middleName)
                ? $"{_firstName} {_lastName}"
                : $"{_firstName} {_middleName} {_lastName}";
            _statusText = $"✔ Registered {fullName} as {_selectedRole ?? "Viewer"} with interests: {interests}.";
            _statusColor = Color.Green;
        }

        StateHasChanged();
    }
}

Parameters

8
NameTypeDefaultDescription
Appearance
Heightint?Explicit height constraint in lines. When null, uses the natural content height.
Widthint?Explicit width constraint in characters. When null, uses all available width.
Common
ChildContentRenderFragmentThe child content to lay out within the flex container.
Other
AlignFlexAlignHow items are aligned along the cross axis. Default is Start.
DirectionFlexDirectionLays out child content using a CSS-like flexbox model with configurable direction, justification, alignment, wrapping, and gap.
GapintSpacing between items along the main axis in characters (Row) or lines (Column). Default is 0.
JustifyFlexJustifyHow free space is distributed along the main axis. Default is Start.
WrapFlexWrapWhether items wrap to new lines when they exceed the available space. Default is NoWrap.

API