Rask
Rask
v0.23.1-alpha.0.494

Components

Phone

An international phone number, with the country picked from a searchable list of flags, formatted as it is typed and normalised to E.164.

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

Phone

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

public sealed partial class PhoneExample : Component
{
    protected override Component? Render() =>
        Div.Class("max-w-sm")[
            Ui.Phone.Value("").Country("US").Label("Phone number")
        ];
}
Live result
No results found
United States+1

Basic usage

Type 2015550123 and save: the field shows (201) 555-0123 and the member holds +12015550123. A number not yet complete is kept as typed.

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

public sealed partial class PhoneBoundExample : Component
{
    private readonly Contact _contact = new();
    private string? _saved;

    protected override Component? Render() =>
        Div.Class("max-w-sm space-y-3")[
            Ui.Phone.Bind(() => _contact.Phone).Country("US").Placeholder("(201) 555-0123").Id("ui-phone-bound"),
            Div.Class("flex items-center gap-3")[
                Ui.Button.Sm.Data("testid", "ui-phone-save").OnClick(() => { _saved = _contact.Phone ?? ""; })["Save"],
                Ui.Text.Data("testid", "ui-phone-saved")[Saved()]
            ]
        ];

    private string Saved() => _saved switch
    {
        null => "Nothing saved yet.",
        "" => "Saved: (empty)",
        var number => "Saved: " + number,
    };

    public sealed class Contact
    {
        public string? Phone { get; set; }
    }
}
Live result
No results found
United States+1

Nothing saved yet.

Default country

The country of an empty number. A number typed with its calling code picks its own.

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

public sealed partial class PhoneCountryExample : Component
{
    protected override Component? Render() =>
        Div.Class("max-w-sm")[
            Ui.Phone.Value("").Country("GB")
        ];
}
Live result
No results found
United Kingdom+44

Country list

Only these in the list. It does not limit what can be typed: check that on the server.

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

public sealed partial class PhoneCountriesExample : Component
{
    protected override Component? Render() =>
        Div.Class("max-w-sm")[
            Ui.Phone.Value("").Countries(["US", "CA", "GB", "AU"])
        ];
}
Live result
No results found

Country order

These first; the rest follow alphabetically in the page's language.

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

public sealed partial class PhoneCountryOrderExample : Component
{
    protected override Component? Render() =>
        Div.Class("max-w-sm")[
            Ui.Phone.Value("").CountryOrder(["US", "CA", "GB", "AU"])
        ];
}
Live result
No results found

Filled

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

public sealed partial class PhoneFilledExample : Component
{
    protected override Component? Render() =>
        Div.Class("max-w-sm")[
            Ui.Phone.Value("").Filled
        ];
}
Live result
No results found

Sizes

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

public sealed partial class PhoneSizesExample : Component
{
    protected override Component? Render() =>
        Div.Class("max-w-sm space-y-4")[
            Ui.Phone.Value("").Sm,
            Ui.Phone.Value("").Xs
        ];
}
Live result
No results found
No results found

Disabled and readonly

A read-only field keeps its number and takes the focus; its list does not open.

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

public sealed partial class PhoneDisabledExample : Component
{
    protected override Component? Render() =>
        Div.Class("max-w-sm space-y-4")[
            Ui.Phone.Value("+12015550123").Country("US").Disabled(),
            Ui.Phone.Value("+12015550123").Country("US").ReadOnly()
        ];
}
Live result
No results found
United States+1
No results found
United States+1

Invalid

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

public sealed partial class PhoneInvalidExample : Component
{
    protected override Component? Render() =>
        Div.Class("max-w-sm")[
            Ui.Phone.Value("").Invalid()
        ];
}
Live result
No results found

Behaviour

The value is what Flux's is

The bound string is the number in E.164 (+12015550123) once it is a possible one, and the text as typed until then (201), so it is not always E.164, and a possible number is not a checked one: validate it on the server. An extension typed after the number is dropped from the value. Picking a country keeps the number and reads it again for that country.

Everything is in the browser

The field runs intl-tel-input 29.2.3, the release fluxui.dev runs, so a formatting quirk of Flux's is this field's too, with Google's libphonenumber compiled. Typing, formatting, opening, searching and walking the list send the server nothing; the number goes with the next action, as Flux's wire:model does, or as it changes with .Live() or .Debounce(…). The list names the countries in the page's language (Intl.DisplayNames).

Two files, fetched when needed

The runtime fetches wwwroot/js/rask-ui-phone.js (21 KB gzipped) the first time a page carries a phone field, and that fetches rask-ui-phone-utils.js (61 KB gzipped) when the field mounts. Every app's build writes both, with their notices, and the flags the list shows. An app that will never draw one keeps its wwwroot clean with <RaskUiPhoneEngine>false</RaskUiPhoneEngine>.

Without script the field is a plain tel input beside the starting country, and a form posts the bound value.

Reference

Ui.Phone

flux:phone

An international phone number with a country list, bound as E.164 once it is a possible one.

Step
Takes
What it does
.Bind(…)() => a stringThe member it reads and writes: () => m.Mobile. Or Value with OnChange.
.Value(…)stringThe number as E.164, for a page that holds it itself.
.OnChange(…)a handler given the new valueRuns with the number when it is committed.
.AfterBind(…)a handler given the new valueRuns after Bind has written the model.
.Validate(…)a validatorA rule the number is checked by when it is written.
.Live()boolWrites the model as the number changes, after a 150 ms pause.
.Debounce(…)a TimeSpan: 300.MillisecondsWrites the model once typing has paused this long.
.Country(…)stringThe country of an empty number, by its code: "HU". None when unset.
.Countries(…)two-letter codes: ["US", "CA"]The countries the list offers. Every country when unset; what can be typed is not limited.
.CountryOrder(…)two-letter codes: ["US", "CA"]The countries the list shows first; the rest follow alphabetically.
.Placeholder(…)stringShown while the number is empty.
.Size(…)
Ui.PhoneSize
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.PhoneVariant
Outline
Filled

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

Outlined, or filled. Outlined when unset.
.Label(…)stringDraws a field around it, with this label and the error.
.Description(…)stringHelp text between the label and the field.
.DescriptionTrailing(…)stringHelp text under the field.
.Name(…)stringWhat the number is posted under from a plain form.
.Disabled()boolTakes no input, and the country cannot be changed.
.ReadOnly()boolShows its number and takes the focus; the number cannot be edited.
.Invalid()boolError styling the form did not ask for.
.ShowValidation()boolFalse leaves the member's message to a Ui.Error placed elsewhere.
Confirm