Components
Autocomplete
An input that suggests what to type and writes the picked suggestion into itself. What it holds is the text, so anything may be typed; to show a name and store an id, use a select's combobox instead.
Drawn and behaving as Flux UI's autocomplete does, measured against its live docs.
Autocomplete
Type "ne": the list narrows to the states that hold it, and Enter writes the lit one into the input.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class AutocompleteExample : Component
{
private static readonly string[] States =
[
"Alabama", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho",
"Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan",
"Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico",
"New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island",
"South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia",
"Wisconsin", "Wyoming",
];
private string _state = "";
protected override Component? Render() =>
Div.Class("max-w-sm space-y-3")[
Ui.Autocomplete.Value(_state).OnChange(text => { _state = text; }).Label("State of residence").Id("ui-autocomplete-state")[
States.Select(state => Ui.AutocompleteItem.Key(state)[state])
],
Ui.Text.Data("testid", "ui-autocomplete-value")[_state.Length == 0 ? "Nothing typed." : $"State: {_state}."]
];
}Nothing typed.
Suggestions from the server
Debounce hands the page what was typed once typing has paused, and the page answers with the items: one query for a word, not one for a letter.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class AutocompleteSearchExample : Component
{
// What a table would hold: more rows than a list shows, so the page answers a search with the first few.
private static readonly string[] Cities =
[
"Amsterdam", "Athens", "Berlin", "Bern", "Brussels", "Budapest", "Copenhagen", "Dublin", "Helsinki", "Lisbon",
"Ljubljana", "Madrid", "Oslo", "Paris", "Prague", "Riga", "Rome", "Stockholm", "Vienna", "Warsaw",
];
private string _city = "";
private string[] _found = [.. Cities.Take(6)];
private int _searches;
protected override Component? Render() =>
Div.Class("max-w-sm space-y-3")[
Ui.Autocomplete.Value(_city).OnChange(Search).Debounce(250.Milliseconds).Label("City").Id("ui-autocomplete-city")[
_found.Select(city => Ui.AutocompleteItem.Key(city)[city])
],
Ui.Text.Data("testid", "ui-autocomplete-city-state")[
$"City: {(_city.Length == 0 ? "none" : _city)}. Searches: {_searches}."
]
];
private void Search(string text)
{
_city = text;
_searches++;
_found = [.. Cities.Where(name => name.Contains(text, StringComparison.CurrentCultureIgnoreCase)).Take(6)];
}
}City: none. Searches: 0.
Behaviour
It holds the text
An item has no value of its own, and what is typed need not be an item. The text reaches the page as an input's does: a bound member with the next action (.Live(): as it is typed), and OnChange when an item is picked, when Escape empties the input, and when what was typed is left.
It takes the input's own steps, which is what Flux documents for it: Type, Size, Variant, Icon, Kbd, Clearable(), Copyable(), Mask and the rest, and ContainerClass for the open list (a height, such as max-h-80).
Suggestions from the server
Debounce(250.Milliseconds) hands the page what was typed once typing has paused as well (Flux's wire:model.live.debounce.250ms), and the page answers it with the items. The autocomplete has no search callback apart from its value: what is typed is what it holds, so the pause is when the value arrives.
The list still narrows at every keystroke, since Flux's autocomplete has no filter to turn off, and only the page waits. An item pressed during the pause is the text, and what was typed before it is never handed over. A bound autocomplete writes its member at the pause: Debounce is a way of being Live(), so AfterBind is where the search goes.
The keyboard
Recorded on fluxui.dev and matched. The cursor, the filter and the pick are the browser's, and nothing is sent to the page until the input's text is.
Key | What happens |
|---|---|
| A click, ArrowDown, ArrowUp | Open the list; the cursor starts on the item last picked, else the first. |
| Typing | Opens the list on the items that hold the text anywhere, whatever the case or the accents. When nothing matches, nothing is drawn. |
| ArrowDown, ArrowUp | Move the cursor and stop at either end. Home and End are the text's own. |
| Enter | Writes the active item into the input and closes. With no active item it does nothing. |
| Escape | Closes the list and empties the input, open or not. |
| Tab, a click elsewhere | Close, and keep whatever was typed. |
| The pointer | Lights the row it enters, which goes dark when the pointer leaves the list; a click picks it and the focus stays in the input. |
No script of the kit's
The list is a native popover placed by CSS anchor positioning, and the cursor, the filter, the open state and the pick are the runtime's (data-rask-list). The page behind the open list is locked (data-rask-lock).
The input is role="combobox" with aria-autocomplete="list", aria-haspopup="listbox", aria-expanded, aria-controls and, while open, aria-activedescendant. The list is role="listbox" and, as Flux writes it, aria-multiselectable="true"; the item last picked stays aria-selected whatever is typed afterwards.
Reference
Ui.Autocomplete
A text input that suggests what to type. Its items are its children.
Step | Takes | What it does |
|---|---|---|
.Bind(…) | () => a string | The member it reads and writes: () => m.State. Or Value with OnChange. |
.Value(…) | string | The text, for a page that holds it itself. |
.OnChange(…) | a handler given the new value | Runs with the text when it is picked, cleared, or typed and left, and with Debounce once typing has paused. |
.AfterBind(…) | a handler given the new value | Runs after Bind has written the model. |
.Validate(…) | a validator | A rule the text is checked by when it is written. |
.Live() | bool | Writes the model as soon as an item is picked, not with the next action. |
.Debounce(…) | a TimeSpan: 250.Milliseconds | Hands the page what was typed once typing has paused this long: suggestions from a database. |
.Type(…) | InputTypeText 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. Text when unset. |
.Label(…) | string | Draws a field around it, with this label and the error. |
.Description(…) | string | Help text between the label and the input. |
.Placeholder(…) | string | Shown while it is empty. |
.Size(…) | Ui.AutocompleteSizeBase 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.AutocompleteVariantOutline Filled Each is a step of its own too, unless an HTML tag has its name. | Outlined, or filled with no border. Outlined when unset. |
.Disabled() | bool | An input that cannot be typed into or opened. |
.ReadOnly() | bool | Shows its text and takes none. |
.Invalid() | bool | Error styling the form did not ask for. |
.Multiple() | bool | For a file input: several files. |
.Mask(…) | string | A pattern the text is laid into: 99/99/9999. |
.Icon(…) | Ui.IconName, or a component | An icon, or content, at the start of the input. |
.IconTrailing(…) | Ui.IconName, or a component | An icon, or content, at the end of the input. |
.Kbd(…) | string | A keyboard shortcut shown at the end of the input. |
.Clearable() | bool | A button that empties the input, while it has text. |
.Copyable() | bool | A button that copies what it holds. |
.Viewable() | bool | For a password input: a button that shows what was typed. |
.As(…) | Ui.AutocompleteAsInput Button Each is a step of its own too, unless an HTML tag has its name. | What it is drawn as. The input when unset. |
.ContainerClass(…) | string | Classes for the open list: a height, such as max-h-80. |
.InputClass(…) | string | Classes for the <input> itself rather than the box around it. |
.ShowValidation() | bool | False leaves the member's message to a Ui.Error placed elsewhere. |
Ui.AutocompleteItem
One suggestion. Its children are its words.
Step | Takes | What it does |
|---|---|---|
.Disabled() | bool | Shown but not pickable. |