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
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")
];
}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.
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; }
}
}Nothing saved yet.
Default country
The country of an empty number. A number typed with its calling code picks its own.
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")
];
}Country list
Only these in the list. It does not limit what can be typed: check that on the server.
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"])
];
}Country order
These first; the rest follow alphabetically in the page's language.
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"])
];
}Filled
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
];
}Sizes
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
];
}Disabled and readonly
A read-only field keeps its number and takes the focus; its list does not open.
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()
];
}Invalid
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()
];
}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
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 string | The member it reads and writes: () => m.Mobile. Or Value with OnChange. |
.Value(…) | string | The number as E.164, for a page that holds it itself. |
.OnChange(…) | a handler given the new value | Runs with the number when it is committed. |
.AfterBind(…) | a handler given the new value | Runs after Bind has written the model. |
.Validate(…) | a validator | A rule the number is checked by when it is written. |
.Live() | bool | Writes the model as the number changes, after a 150 ms pause. |
.Debounce(…) | a TimeSpan: 300.Milliseconds | Writes the model once typing has paused this long. |
.Country(…) | string | The 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(…) | string | Shown while the number is empty. |
.Size(…) | Ui.PhoneSizeBase 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.PhoneVariantOutline Filled Each is a step of its own too, unless an HTML tag has its name. | Outlined, or filled. Outlined when unset. |
.Label(…) | string | Draws a field around it, with this label and the error. |
.Description(…) | string | Help text between the label and the field. |
.DescriptionTrailing(…) | string | Help text under the field. |
.Name(…) | string | What the number is posted under from a plain form. |
.Disabled() | bool | Takes no input, and the country cannot be changed. |
.ReadOnly() | bool | Shows its number and takes the focus; the number cannot be edited. |
.Invalid() | bool | Error styling the form did not ask for. |
.ShowValidation() | bool | False leaves the member's message to a Ui.Error placed elsewhere. |