Rask
Rask
v0.23.1-alpha.0.478

Components

Input

A line of text, of any input type the browser has, bound to the model or held by the page. Everything inside the box (an icon, a shortcut, the clear, copy and reveal buttons) is a step, and a group joins a prefix, a suffix or a button to it.

Drawn and behaving as Flux UI's input does, measured against its live docs.

Input

In a field of its own making: a label, a description and the error, around the input.

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

public sealed partial class InputExample : Component
{
    private string Username { get; set; } = "";

    protected override Component? Render() =>
        Ui.Field.Class("max-w-sm")[
            Ui.Label["Username"],
            Ui.Description["This will be publicly displayed."],
            Ui.Input.Bind(() => Username),
            Ui.Error
        ];
}
Live result
This will be publicly displayed.

Shorthand

Label and Description on the input draw the same field, with its error.

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

public sealed partial class InputShorthandExample : Component
{
    protected override Component? Render() =>
        Ui.Input.Of<string>().Label("Username").Description("This will be publicly displayed.").Class("max-w-sm");
}
Live result
This will be publicly displayed.

Class targeting

The input is a wrapper around an <input>: Class goes on the wrapper, InputClass on the <input> itself.

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

public sealed partial class InputClassTargetingExample : Component
{
    protected override Component? Render() =>
        Ui.Input.Value("BA7K-7QZ5").Class("max-w-xs").InputClass("font-mono");
}
Live result

Types

The browser's input types, email, password, date and the rest, from Type.

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

public sealed partial class InputTypesExample : Component
{
    protected override Component? Render() =>
        Div.Class("grid max-w-sm gap-6")[
            Ui.Input.Of<string>().Type(InputType.Email).Label("Email"),
            Ui.Input.Of<string>().Type(InputType.Password).Label("Password"),
            Ui.Input.Of<string>().Type(InputType.Date).Max("2999-12-31").Label("Date")
        ];
}
Live result

File

A file input with a "Choose files" button and the chosen name; OnFiles hands the page what was chosen.

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

public sealed partial class InputFileExample : Component
{
    private int _chosen;

    protected override Component? Render() =>
        Div.Class("grid max-w-sm gap-6")[
            Ui.Input.Of<string>().Type(InputType.File).Label("Logo").OnFiles(files => { _chosen = files.Count; }),
            Ui.Input.Of<string>().Type(InputType.File).Label("Attachments").Multiple().OnFiles(files => { _chosen = files.Count; }),
            Ui.Text[$"Files chosen last: {_chosen}."]
        ];
}
Live result

Files chosen last: 0.

Smaller

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

public sealed partial class InputSmallerExample : Component
{
    protected override Component? Render() =>
        Ui.Input.Of<string>().Sm.Placeholder("Filter by...").Class("max-w-xs");
}
Live result

Disabled

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

public sealed partial class InputDisabledExample : Component
{
    protected override Component? Render() =>
        Ui.Input.Of<string>().Disabled().Label("Email").Class("max-w-sm");
}
Live result

Readonly

For a value that may be read and copied but not changed, here drawn filled.

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

public sealed partial class InputReadonlyExample : Component
{
    protected override Component? Render() =>
        Ui.Input.Value("BA7K7QZ511S8Z2K").ReadOnly().Filled.Class("max-w-sm");
}
Live result

Invalid

Error styling the form did not ask for. A bound input is marked invalid on its own.

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

public sealed partial class InputInvalidExample : Component
{
    protected override Component? Render() =>
        Ui.Input.Of<string>().Invalid().Class("max-w-sm");
}
Live result

Input masking

Each keystroke is held to the pattern: 9 is a digit, a a letter, * either.

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

public sealed partial class InputMaskExample : Component
{
    protected override Component? Render() =>
        Ui.Input.Value("7161234567").Mask("(999) 999-9999").Attributes(("aria-label", "Phone")).Class("max-w-sm");
}
Live result

Icons

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

public sealed partial class InputIconsExample : Component
{
    protected override Component? Render() =>
        Div.Class("grid max-w-sm gap-4")[
            Ui.Input.Of<string>().Icon(Ui.IconName.MagnifyingGlass).Placeholder("Search orders"),
            Ui.Input.Of<string>().IconTrailing(Ui.IconName.CreditCard).Placeholder("4444-4444-4444-4444"),
            Ui.Input.Of<string>().IconTrailing(Ui.IconName.Loading).Placeholder("Search transactions")
        ];
}
Live result

Icon buttons

IconTrailing also takes content of your own, such as a button.

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

public sealed partial class InputIconButtonsExample : Component
{
    protected override Component? Render() =>
        Div.Class("grid max-w-sm gap-4")[
            Ui.Input.Of<string>().Placeholder("Search orders").IconTrailing(
                Ui.Button.Sm.Subtle.Icon(Ui.IconName.XMark).Class("-mr-1").Aria("label", "Clear")),
            Ui.Input.Value("password").Type(InputType.Password).IconTrailing(
                Ui.Button.Sm.Subtle.Icon(Ui.IconName.Eye).Class("-mr-1").Aria("label", "Show password"))
        ];
}
Live result

Clearable, copyable, and viewable inputs

Three buttons the input draws itself: one that empties it, one that reveals a password, one that copies the value.

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

public sealed partial class InputClearableExample : Component
{
    private string _search = "Jack Skellington";

    protected override Component? Render() =>
        Div.Class("grid max-w-sm gap-4")[
            Ui.Input.Value(_search).Placeholder("Search orders").Clearable()
                .OnInput(text => { _search = text; }).OnChange(text => { _search = text; }),
            Ui.Input.Value("password").Type(InputType.Password).Viewable(),
            Ui.Input.Value("FLUX-1234-5678-ABCD-EFGH").Icon(Ui.IconName.Key).ReadOnly().Copyable(),
            Ui.Text.Data("testid", "ui-input-clearable-state")[$"Searching for \"{_search}\"."]
        ];
}
Live result

Searching for "Jack Skellington".

Keyboard hint

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

public sealed partial class InputKbdExample : Component
{
    protected override Component? Render() =>
        Ui.Input.Of<string>().Kbd("⌘K").Icon(Ui.IconName.MagnifyingGlass).Placeholder("Search...").Class("max-w-sm");
}
Live result
⌘K

As a button

A <button> drawn as the input, for a search that opens a palette.

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

public sealed partial class InputAsButtonExample : Component
{
    private int _pressed;

    protected override Component? Render() =>
        Div.Class("grid max-w-sm gap-4")[
            Ui.Input.Of<string>().As(Ui.InputAs.Button).Placeholder("Search...").Icon(Ui.IconName.MagnifyingGlass).Kbd("⌘K")
                .OnClick(() => { _pressed++; }),
            Ui.Text[$"Pressed {_pressed} time{(_pressed == 1 ? "" : "s")}."]
        ];
}
Live result

Pressed 0 times.

With buttons

An input group joins its neighbours into one outline: the two ends keep their corners and a border is drawn once between them.

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

public sealed partial class InputWithButtonsExample : Component
{
    private string? _currency = "USD";

    protected override Component? Render() =>
        Div.Class("grid max-w-sm gap-4")[
            Ui.InputGroup[
                Ui.Input.Of<string>().Placeholder("Post title"),
                Ui.Button.Icon(Ui.IconName.Plus)["New post"]
            ],
            Ui.InputGroup[
                Ui.Select.Value(_currency).OnChange(currency => { _currency = currency; }).Class("max-w-fit")[
                    Ui.SelectOption["USD"],
                    Ui.SelectOption["EUR"],
                    Ui.SelectOption["GBP"]
                ],
                Ui.Input.Of<string>().Placeholder("$99.99")
            ]
        ];
}
Live result

Text prefixes and suffixes

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

public sealed partial class InputAffixesExample : Component
{
    protected override Component? Render() =>
        Div.Class("grid max-w-sm gap-4")[
            Ui.InputGroup[
                Ui.InputGroupPrefix["https://"],
                Ui.Input.Of<string>().Placeholder("example.com")
            ],
            Ui.InputGroup[
                Ui.Input.Of<string>().Placeholder("chunky-spaceship"),
                Ui.InputGroupSuffix[".brand.com"]
            ]
        ];
}
Live result
https://
.brand.com

Input group labels

For a label, the group goes in a field, which reaches the input inside it.

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

public sealed partial class InputGroupLabelExample : Component
{
    private string Website { get; set; } = "";

    protected override Component? Render() =>
        Ui.Field.Class("max-w-sm")[
            Ui.Label["Website"],
            Ui.InputGroup[
                Ui.InputGroupPrefix["https://"],
                Ui.Input.Bind(() => Website).Placeholder("example.com")
            ],
            Ui.Error
        ];
}
Live result
https://

Behaviour

When the value is written

Bind(() => m.Email) or Value(email).OnChange(…) stands where Flux has wire:model. A bound input is written with the reader's next action and travels with it, as wire:model does; .Live() writes as it is typed, after a 150 ms pause (wire:model.live), .Debounce(300.Milliseconds) after a pause of your own, and .Blur() when the reader leaves the field (wire:model.blur). OnInput hears every keystroke.

The type comes from the model: Ui.Input.Bind(() => m.Seats) over an int is a number field with nothing said at the call site. A field with nothing in it yet opens on its type alone: Ui.Input.Of<string>().Label("Search").

No script of the kit's

What Flux does in Alpine is the runtime's, by attribute. The clear button (data-rask-clear) empties the field in the click and leaves the focus in it, and CSS hides it while the field is empty. The copy button (data-rask-copy) writes the clipboard in the click and shows a tick for two seconds. Mask holds each keystroke to its pattern (data-rask-mask), and the value drawn and committed is shaped the same way. The reveal button is a handler. A file input is a <label> around the real input.

Attributes(("aria-label", "Search keys")) forwards an attribute to the <input>, as Flux forwards what it does not know: the way to name an input that has no label.

In a group, label the group

Ui.Field[Ui.Label["Website"], Ui.InputGroup[…], Ui.Error]: the group stands for its input, so the field's label and error reach it. A neighbour that is not an input (a button, a native select) joins the outline by carrying data-ui-group-target, which the kit's button and select already do.

From Flux

Flux's attributes, as steps:

Flux
What happens
<flux:input wire:model="email">Ui.Input.Bind(() => m.Email)
wire:model.live, .live.debounce.300ms, wire:model.blur.Live(), .Debounce(300.Milliseconds), .Blur()
size="sm", variant="filled".Sm, .Filled
icon="key", icon:trailing="credit-card".Icon(Ui.IconName.Key), .IconTrailing(Ui.IconName.CreditCard)
<x-slot name="iconTrailing">.IconTrailing(Ui.Button.Sm.Subtle.Icon(Ui.IconName.XMark))
input:class="font-mono".InputClass("font-mono")
as="button".As(Ui.InputAs.Button)
mask:dynamic="$money($input)"Not built: it is an Alpine expression.

Reference

Ui.Input

flux:input

A line of text in a box that holds its icons and buttons. Its type is the model's.

Step
Takes
What it does
.Bind(…)() => a TThe model's member it reads and writes: () => m.Email. Or Value with OnChange.
.Value(…)T, the type it editsWhat it holds, for a page that holds it itself.
.OnChange(…)a handler given the new valueRuns with the new value when it is committed.
.AfterBind(…)a handler given the new valueRuns after Bind has written the model.
.Validate(…)a validatorA rule the value is checked by when it is written.
.Live()boolWrites the model as it is typed, after a 150 ms pause: Flux's wire:model.live.
.Blur()boolWrites the model when the reader leaves the field: Flux's wire:model.blur.
.Debounce(…)a TimeSpan: 300.MillisecondsWrites the model once typing has paused this long.
.OnInput(…)a handler given the new valueRuns at every keystroke with the text as typed.
.Label(…)stringDraws a field around it, with this label and the error.
.Description(…)stringHelp text between the label and the input.
.DescriptionTrailing(…)stringHelp text under the input.
.Placeholder(…)stringShown while it is empty.
.Type(…)
InputType
Text
Search
Tel
Url
Email
Password
Number
Checkbox
Radio
File
Range
Color
Date
DatetimeLocal
Time
Week
Month
Hidden
Button
Submit
Reset
Image

Each is a step of its own too, unless an HTML tag has its name.

The input's type: Email, Password, Date, File… The model's type decides when unset.
.Size(…)
Ui.InputSize
Base
Sm
Xs

Each is a step of its own too, unless an HTML tag has its name.

How tall it is. 40px when unset.
.Variant(…)
Ui.InputVariant
Outline
Filled

Each is a step of its own too, unless an HTML tag has its name.

How it is drawn. Outlined when unset.
.Disabled()boolDims it and takes it out of reach.
.ReadOnly()boolMay be read and copied, not changed.
.Invalid()boolError styling the form did not ask for. A bound input is invalid while its member holds a message.
.Mask(…)stringHolds each keystroke to a pattern: 9 a digit, a a letter, * either.
.Icon(…)a Ui.IconName, or a componentAn icon, or content of your own, at the start of the box.
.IconTrailing(…)a Ui.IconName, or a componentAn icon, or content of your own such as a button, at the end.
.Kbd(…)stringA keyboard shortcut shown at the end: "⌘K".
.Clearable()boolA button that empties it, shown while there is text.
.Copyable()boolA button that copies the value.
.Viewable()boolA button that shows a password and hides it again.
.As(…)
Ui.InputAs
Input
Button

Each is a step of its own too, unless an HTML tag has its name.

Button draws a <button> as the input, pressed with OnClick.
.OnClick(…)a handler, with no argumentRuns when an input drawn as a button is pressed.
.Multiple()boolLets a file input take several files.
.OnFiles(…)a handler given the filesRuns with the files chosen in a file input.
.InputClass(…)stringClasses for the <input> itself; Class goes on the box around it.
.Min(…)stringThe input's own min: the lowest number or the first day.
.Max(…)stringThe input's own max.
.Step(…)stringThe input's own step.
.MaxLength(…)intThe most characters it takes.
.Autofocus()boolTakes the focus when the page opens.
.Name(…)stringWhat it is posted under in a form.
.Ref(…)ElementRefA reference to the <input>, for a call through the browser's APIs.
.ShowValidation()boolFalse leaves the member's message to a Ui.Error placed elsewhere.
.Attributes(…)("aria-label", "Search") pairsAttributes for the <input> itself.

Ui.InputGroup

flux:input.group

An input and its neighbours in one outline. In a field, it stands for its input.

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

Ui.InputGroupPrefix

flux:input.group.prefix

Words before the input: "https://".

Step
Takes
What it does
.Size(…)
Ui.InputSize
Base
Sm
Xs

Each is a step of its own too, unless an HTML tag has its name.

How tall it is, to match a smaller input.

Ui.InputGroupSuffix

flux:input.group.suffix

Words after the input: ".brand.com".

Step
Takes
What it does
.Size(…)
Ui.InputSize
Base
Sm
Xs

Each is a step of its own too, unless an HTML tag has its name.

How tall it is, to match a smaller input.
Confirm