phollyer/elm-ui-dropdown/1.2.1

NOTE: the latest version is 2.2.2

Dropdown

A dropdown component for Elm-UI.

In order to provide built-in keyboard navigation, and option filtering, it is necessary for the Dropdown to manage it's own internal state. Therefore any Dropdowns you require need to be stored on your Model, with events being handled in your update function.

There are a few gotchas to watch out for with functions that operate on the internal state of the Dropdown. Because of the effect they have on the internal state, using them in your view code will have no effect. They should therefore be used when you init the Dropdown, or in your update function where model changes can be captured.

The affected functions are, id, filterType, removeSelected & setSelected, along with all the functions for setting the menu options. Each function or section has a warning documenting this restriction where it's applicable.

All other functions can be used safely within view code.

Build

Setting Up

init : Dropdown option

Initialize a dropdown on your model.

import Dropdown


initialModel : Model
initialModel =
    { myDropdown = Dropdown.init }
id : String -> Dropdown option -> Dropdown option

Provide an ID for the dropdown.

This will become the element id in the DOM, and is required in order for keyboard navigation to work - it should therefore be unique.

import Dropdown


initialModel : Model
initialModel =
    { myDropdown =
        Dropdown.init
            |> Dropdown.id "my-drodown"
    }

Warning

The id needs to be stored on the dropdown model, and so should be set when you init the dropdown, or in your update function where the changes to the model can be captured.

If you set this in your view code it will have no effect and keyboard navigation won't work.

Input Type

= Button
| TextField

The type of input the user uses to access the dropdown.

The default is Button.

inputType : InputType -> Dropdown option -> Dropdown option

Set the InputType.

Setting Options

Warning

Options need to be stored on the dropdown model, and so should be set when you init the dropdown, or in your update function where the changes to the model can be captured.

If you set these in your view code they will have no effect and so no menu will appear.

optionsBy : (option -> String) -> List option -> Dropdown option -> Dropdown option

This is the easiest way to set the options for custom types.

Simply provide a function that takes an option and returns the String to be used for the label in the menu.

import Dropdown exposing (Dropdown)


type alias Model =
    { nameDropdown : Dropdown Person
    , ageDropdown : Dropdown Person
    }


initialModel : Model
initialModel =
    { nameDropdown =
        Dropdown.init
            |> Dropdown.optionsBy .name people
    , ageDropdown =
        Dropdown.init
            |> Dropdown.optionsBy (.age >> String.fromInt) people
    }


type alias Person =
    { name : String
    , age : Int
    }


people : List Person
people =
    [ { name = "John Doe", age = 99 }
    , { name = "Jane Doe", age = 98 }
    ]
options : List ( String, option ) -> Dropdown option -> Dropdown option

The options to set for your dropdown.

The first element in the list of tuples is always a String, and is used for the option's label in the menu that is displayed to the user.

import Dropdown exposing (Dropdown)


type alias Model =
    { customTypeDropdown : Dropdown CustomType }


initialModel : Model
initialModel =
    { customTypeDropdown =
        Dropdown.init
            |> Dropdown.options customTypeOptions
    }


type CustomType
    = A
    | B


customTypeOptions : List ( String, CustomType )
customTypeOptions =
    [ ( "A", A )
    , ( "B", B )
    ]
stringOptions : List String -> Dropdown String -> Dropdown String

The options to set for your dropdown if they are all Strings.

import Dropdown exposing (Dropdown)


type alias Model =
    { stringDropdown : Dropdown String }


initialModel : Model
initialModel =
    { stringDropdown =
        Dropdown.init
            |> Dropdown.stringOptions
                [ "A"
                , "B"
                , "C"
                ]
    }
intOptions : List Int -> Dropdown Int -> Dropdown Int

The options to set for your dropdown if they are all Ints.

import Dropdown exposing (Dropdown)


type alias Model =
    { stringDropdown : Dropdown Int }


initialModel : Model
initialModel =
    { stringDropdown =
        Dropdown.init
            |> Dropdown.intOptions
                [ 1
                , 2
                , 3
                ]
    }
floatOptions : List Float -> Dropdown Float -> Dropdown Float

The options to set for your dropdown if they are all Floats.

import Dropdown exposing (Dropdown)


type alias Model =
    { stringDropdown : Dropdown Float }


initialModel : Model
initialModel =
    { stringDropdown =
        Dropdown.init
            |> Dropdown.floatOptions
                [ 0.1
                , 0.2
                , 0.3
                ]
    }

Label

label : Element (Msg option) -> Dropdown option -> Dropdown option

Provide the label element for the InputType.

labelHidden : ( Bool, String ) -> Dropdown option -> Dropdown option

Hide the label.

buttonLabel : Element (Msg option) -> Dropdown option -> Dropdown option

The text element for the Button if nothing is selected.

The default is "-- Select --".

Placeholder

Maybe (Placeholder (Msg option))
-> Dropdown option
-> Dropdown option

Provide the Placeholder for the text field if TextField is the InputType.

The default is Nothing.

Positioning

= Above
| Below
| Left
| Right

The position of the label or the dropdown menu in relation to the InputType.

Set the position of the label in relation to the InputType.

The default is Above.

labelSpacing : Int -> Dropdown option -> Dropdown option

Set the spacing between the InputType and its label.

The default is 10.

Size & Style

maxHeight : Int -> Dropdown option -> Dropdown option

The max height for the dropdown, the default is 150.

(Vertical scrolling kicks in automatically.)

inputAttributes : List (Attribute (Msg option)) -> Dropdown option -> Dropdown option

The Attributes to set on the InputType.

optionAttributes : List (Attribute (Msg option)) -> Dropdown option -> Dropdown option

The Attributes to set on each option.

optionHoverAttributes : List (Attribute (Msg option)) -> Dropdown option -> Dropdown option

The Attributes to set on an option when hovered over with the mouse, or navigated to with the keyboard.

optionSelectedAttributes : List (Attribute (Msg option)) -> Dropdown option -> Dropdown option

The Attributes to set on an option when it has been selected by a user.

Filtering

Filtering is currently case insensitive.

= NoFilter
| StartsWith
| Contains
| StartsWithThenContains

The type of filter to apply when TextField is used as the InputType.

  • NoFilter: No filter will be applied.
  • StartsWith: Filter the list of options down to only those whose label starts with the entered text.
  • Contains: Filter the list of options down to only those whose label contains the entered text.
  • StartsWithThenContains: Filter the list of options down to only those whose label starts with the entered text or contains the entered text. The list of options will be sorted with StartsWith taking priority over Contains, with duplicates removed.

The default is NoFilter.

filterType : FilterType -> Dropdown option -> Dropdown option

Set the FiterType.

import Dropdown exposing (FilterType(..))


initialModel : Model
initialModel =
    { dropdown =
        Dropdown.init
            |> Dropdown.filterType StartsWith
    }

Warning

The FilterType needs to be stored on the dropdown model, and so should be set when you init the dropdown, or in your update function where the changes to the model can be captured.

If you set this in your view code it will have no effect, and filtering won't work.

Selected Option

setSelected : Maybe option -> Dropdown option -> Dropdown option

Set the selected option - it must exist in the list of options originally provided.

Warning

This function changes the internal state, and so needs to be used where the state change can be captured. This is likely to be your update function.

If you use this in your view code it will have no effect.

removeSelected : Dropdown option -> Dropdown option -> Dropdown option

Remove the selected option of one dropdown from the list of options of another dropdown.

This is useful if you have two dropdowns that show the same list of options, but each selection must be unique, therefore you don't want to show the selected option again.

For example, selecting a home team and away team from the same list of teams. In this case, once the home team has been selected, you may wish to remove that option from the list of away teams.

Dropdown.removeSelected homeTeamDropdown awayTeamDropdown

Warning

This function changes the internal state, and so needs to be used where the state change can be captured. This is likely to be your update function.

If you use this in your view code it will have no effect.

Query

selected : Dropdown option -> Maybe ( Int, String, option )

Determine if an option has been selected by the user.

If a Just is returned, it consists of the following:

  • Int: The index of the selected option.
  • String: The label of the selected option.
  • option: The option value itself.
selectedOption : Dropdown option -> Maybe option

Maybe retrieve the selected option.

Update

type OutMsg option
= NoOp
| Selected ( Int, String, option )
| TextChanged String
| FocusIn
| FocusOut

Pattern match on this type in your update function to determine the event that occured.

type Msg option

This is an opaque type, pattern match on OutMsg.

Msg option
-> Dropdown option
-> ( Dropdown option, Cmd (Msg option), OutMsg option )
import Dropdown exposing (OutMsg(..))


type Msg
    = DropdownMsg Dropdown.Msg


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        DropdownMsg subMsg ->
            let
                ( dropdown, cmd, outMsg ) =
                    Dropdown.update subMsg model.dropdown
            in
            case outMsg of
                Selected ( index, label, option ) ->
                    ( { model | dropdown = dropdown }
                    , Cmd.map DropdownMsg cmd
                    )


                TextChanged text ->
                    ( { model | dropdown = dropdown }
                    , Cmd.map DropdownMsg cmd
                    )


                ...

View

view : (Msg option -> msg) -> Dropdown option -> Element msg

Render the dropdown.

import Dropdown


type alias Model =
    { dropdown : Dropdown String }


type Msg
    = DropdownMsg Dropdown.Msg


view : Model -> Element Msg
view model =
    Dropdown.view DropdownMsg model.dropdown