Experimental. A combobox: a text input that suggests values from a list as you type, for picking one or several.
Mirrors EXPERIMENTAL_Suggestion from @digdir/designsystemet-react. As there,
the API and markup may change in any release.
Field.view []
[ Label.view [] [ Html.text "Kommune" ]
, Suggestion.single
{ options = Municipality.all
, selected = model.municipality
, toValue = Municipality.toCode
, toLabel = Municipality.toName
, onSelect = PickedMunicipality
}
|> Suggestion.withClear { label = "Tøm" }
|> Suggestion.withEmptyMessage [ Html.text "Ingen treff" ]
|> Suggestion.view []
]
Suggestion.multiple
{ options = Tag.all
, selected = model.tags
, toValue = Tag.toId
, toLabel = Tag.toName
, onChange = ChangedTags
}
|> Suggestion.withToggle { label = "Vis alle" }
|> Suggestion.view [ Attr.attribute "aria-label" "Stikkord" ]The attributes you give view go on the text <input />. Add Events.onInput
there to get the typed text, e.g. to fetch options from a server.
Elm owns the selection. When the user picks or removes a value, the web
component fires comboboxbeforeselect. The module cancels it and sends
onSelect or onChange with the new selection, so nothing changes on screen
until your model does.
React also has an uncontrolled mode (defaultSelected) where the web component
adds and removes selected items itself. That would insert nodes among Elm's, so
it isn't ported.
If a value isn't in options (or in selected, when removing), you get your
current selection back unchanged and the event is still cancelled.
<Suggestion selected onSelectedChange> → Suggestion.single { … }<Suggestion multiple selected onSelectedChange> → Suggestion.multiple { … }<Suggestion.Input> → the attributes of Suggestion.view<Suggestion.Option value> → options, toValue, toLabel<Suggestion.Toggle aria-label> → Suggestion.withToggle { label = "…" }<Suggestion.Clear> + data-sr-clear → Suggestion.withClear { label = "…" }<Suggestion.Empty> → Suggestion.withEmptyMessage [ … ]<Suggestion.Option label> with markup in it → Suggestion.withOptionContent (\a -> [ … ])filter={false} → Suggestion.withoutFilteringfilter={fn} → filter options yourself + withoutFilteringcreatable + <Suggestion.Empty data-empty="…"> → Suggestion.withCreatable { … }name → Suggestion.withName "…"renderSelected → toLabeldefaultSelected → not ported: controlled onlyonBeforeMatch → not portedBy default the web component shows the options whose label contains the typed
text, ignoring case, like React's default filter. With
withoutFiltering every option you pass is shown, so you
can filter options yourself or show what a server sent back.
<ds-suggestion> (built on u-combobox and u-datalist from u-elements)
writes these, so the module never renders them:
role, list, popovertarget, autocomplete,
enterkeyhint, aria-autocomplete, aria-controls, aria-expanded,
aria-activedescendant, aria-description, and its value in single
mode (it shows the selected label)<data> items: aria-label, role, slot, tabindex,
hiddenselected, aria-selected, aria-disabled,
aria-hidden, role, idhidden, tabindex, aria-label, data-sr-*,
data-is-floatinghidden, label, value, disabled,
data-createhidden, aria-hidden, tabindex,
and aria-expanded on the toggle (Elm renders the initial hidden and
aria-expanded="false", like React, and never changes them)<option>s of the hidden <select> from withName,
and its multiple (set from data-multiple)data-sr-*) other than the clear label, from
the CSS custom properties --dsc-suggestion-sr-*Pick at most one value. The input shows the selected label.
toValue must give every option a different string.toLabel is what the user sees and types to find it.onSelect gets Nothing when the user clears the field.Pick any number of values. They are shown as removable chips before the
input, in the order of selected.
A button, shown as an arrow, that opens and closes the list. label is its
accessible name.
A button, shown as an ×, that empties the input (and in single mode the
selection). label is its accessible name.
What the list shows when nothing matches, like
[ Html.text "Ingen treff" ]. While fetching options from a server, show
[ Spinner.decorative [], Html.text "Laster..." ] instead.
Show more than the label in each option, like a second line of detail.
toLabel is still what the user types to find it, what the input shows once
picked, and what a chip says.
|> Suggestion.withOptionContent
(\place -> [ Html.text place.name, Html.div [] [ Html.text place.county ] ])Show every option you pass, instead of only those matching the typed text.
Let the user add a value that isn't among the options.
template is the text of the "add" option in the list; %s is replaced by
what the user typed: "Legg til «%s»".onCreate gets the typed text when the user picks it. Turn it into one of
your values, add it to options and to the selection.The "add" option replaces the withEmptyMessage message.
Submit the selected values with a surrounding <form>, under this name. A
hidden <select> holds them.
A suggestion field being configured. Start with
singleormultiple, end withview.