thomasin / elm-menus / Preset.Combobox

A custom combobox, with support for keyboard navigation This Preset can be used for basic comboboxes, without complex selection models or option/value relationships.


type Model option

Stores whether the menu is open or not, and which option is currently focussed.
It does not store which option is selected.
Is updated and returned in the #update function.


type Context

Context is passed around internally, and is just used to track if the menu has been recently opened

init : Model option

Initialise a closed menu, you can use this in your own init function


type alias Msg option =
Internal.Msg option

Passed in to #update

menuOpened : Msg option

A #Msg that is sent when the menu is opened

menuClosed : Msg option

A #Msg that is sent when the menu is closed

update : Msg option -> { model : Model option, config : Context -> Config option selection, options : List option, selected : selection } -> ( selection, Model option, Platform.Cmd.Cmd (Msg option) )

Call this in your own update function, it returns the latest selection, the dropdown model, and Cmds to pass on. You will need to save the selection and menu in your model.

Configuration


type Config option selection

Created in #config
This is passed into #update and #view and is used to control how the menu reacts to user input


type alias OptionConfig option selection =
{ id : String
, optionToLabel : option -> String
, optionToId : option -> String
, optionToSelection : option -> selection
, selectionToOption : selection -> List option -> Maybe option
, selectionToLabel : selection -> String
, selectionCleared : Menus.Select.Change selection
, matchesInput : String -> option -> Basics.Bool
, containsInput : String -> option -> Basics.Bool
, visibleOptions : VisibleOptionConfig option selection -> String -> selection -> Maybe ( Menus.Active.Active option
, List option ) -> Maybe ( Menus.Active.Active option
, List option ) 
}

Passed in to #config
- id: Must be unique, and is displayed in the HTML, used for accessibility
- optionToLabel: Allows users to use keyboard input to search for options
- optionToId: Uniquely identify each option - optionToSelection: Turn any option into your selection, for instance you might want the selection to be Maybe option, so you would have optionToSelection = Just - selectionToOption: Given the current selection and a list of options, find the option that corresponds to the selection. - selectionToLabel: What to set as the menu input's value - selectionCleared: When the user presses backspace, while the menu is closed or the input is empty, what should happen to the selection? Returning NotChanged will make the combobox non-clearable. - matchesInput: Given a string and an option, does the option exactly match the string? - containsInput: Given a string and an option, does the option "contain" the string? - visibleOptions: The Preset does some work under the scenes to already filter down visible options using data passed in from this config object, but there are a couple of "plugins", #autoSelect and #creatable you can use here to change how the combobox functions.

config : OptionConfig option selection -> Context -> Config option selection

Create a #Config type. You don't need to explicitly pass in the Context yourself, the #update and #view functions expect Context -> Config option selection.


type alias VisibleOptionConfig option selection =
{ optionToLabel : option -> String
, optionToId : option -> String
, optionToSelection : option -> selection
, selectionToOption : selection -> List option -> Maybe option
, selectionToLabel : selection -> String
, matchesInput : String -> option -> Basics.Bool
, containsInput : String -> option -> Basics.Bool 
}

This is passed down to the #autoSelect and #creatable plugins from the main config

autoSelect : VisibleOptionConfig option selection -> String -> selection -> Maybe ( Menus.Active.Active option, List option ) -> Maybe ( Menus.Active.Active option, List option )

This plugin will automatically select the closest match to the input. If you are combining this with other plugins, use it last or any changes within the latter plugins won't be autoselected.

Preset.Combobox.config
    { ...
    , visibleOptions =
        Preset.Combobox.autoSelect
    }


type alias CreatableConfig option =
{ newOption : String -> option }

Passed into #creatable Define how to create a new option, given a string

creatable : CreatableConfig option -> VisibleOptionConfig option selection -> String -> selection -> Maybe ( Menus.Active.Active option, List option ) -> Maybe ( Menus.Active.Active option, List option )

This plugin allows people to create new options when they enter input that doesn't exactly match any of the options.

Preset.Combobox.config
    { ...
    , visibleOptions =
        Preset.Combobox.creatable
            { newOption = \label -> NewOption label }
    }

Views

view : { model : Model option, config : Context -> Config option selection, options : List option, selected : selection } -> (Token option selection -> List option -> Html (Msg option)) -> Html (Msg option)

This needs to wrap your menu. It does not render any HTML, but provides a #Token that you pass into view functions, and also the currently visible options.

isOpen : Token option selection -> Basics.Bool

Check whether the menu is open or closed

focussedOption : Token option selection -> Maybe option

Returns the currently focussed option

options : Token option selection -> List (Html.Attribute (Msg option)) -> List (Html (Msg option)) -> Html (Msg option)

A wrapper for the list of options in the menu. This should be a direct parent of your list of option nodes.

option : Token option selection -> { value : option, isSelected : Basics.Bool } -> List (Html.Attribute (Msg option)) -> List (Html Basics.Never) -> Html (Msg option)

A focus and selectable option

input : Token option selection -> { placeholder : String } -> List (Html.Attribute (Msg option)) -> Html (Msg option)

The input used to open and close the menu

optionsDiv : Token option selection -> List (Html.Attribute (Msg option)) -> List (Html (Msg option)) -> Html (Msg option)

A wrapper for the list of options in the menu. This should be a direct parent of your list of option nodes.


type Token option selection

Created automatically when you call #view, pass it in to view functions

token : { model : Model option, config : Context -> Config option selection, options : List option, selected : selection } -> Token option selection

Create a token manually for use in view and helper functions