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, setSelected, removeSelected, [removeOption](#removeOption] & openOnMouseEnter, along with all the functions for setting the menu options. Each function or section has a warning documenting this restriction where it is applicable.
All other functions can be used safely within view
code.
An opaque type representing the internal model.
Use this to define the option
type on your model.
import Dropdown exposing (Dropdown)
type alias Model =
{ stringDropdown : Dropdown String
, intDropdown : Dropdown Int
, customTypeDropdown : Dropdown CustomType
}
type CustomType
= A
| B
...
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.
The type of input the user uses to access the dropdown.
The default is Button
.
inputType : InputType -> Dropdown option -> Dropdown option
Set the InputType
.
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 String
s.
import Dropdown exposing (Dropdown)
type alias Model =
{ stringDropdown : Dropdown String }
initialModel : Model
initialModel =
{ stringDropdown =
Dropdown.init
|> Dropdown.stringOptions
[ "A"
, "B"
, "C"
]
}
intOptions : List Basics.Int -> Dropdown Basics.Int -> Dropdown Basics.Int
The options to set for your dropdown if they are all Int
s.
import Dropdown exposing (Dropdown)
type alias Model =
{ stringDropdown : Dropdown Int }
initialModel : Model
initialModel =
{ stringDropdown =
Dropdown.init
|> Dropdown.intOptions
[ 1
, 2
, 3
]
}
floatOptions : List Basics.Float -> Dropdown Basics.Float -> Dropdown Basics.Float
The options to set for your dropdown if they are all Float
s.
import Dropdown exposing (Dropdown)
type alias Model =
{ stringDropdown : Dropdown Float }
initialModel : Model
initialModel =
{ stringDropdown =
Dropdown.init
|> Dropdown.floatOptions
[ 0.1
, 0.2
, 0.3
]
}
reset : Dropdown option -> Dropdown option
Reset the dropdown.
The selected option will be set to Nothing
, and any text in the TextField
InputType will be removed.
The list of options will be reset to the last full list of options supplied if any options have been programmatically removed.
label : Element (Msg option) -> Dropdown option -> Dropdown option
Provide the label element for the InputType.
labelHidden : ( Basics.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 (Element.Input.Placeholder (Msg option)) -> Dropdown option -> Dropdown option
Provide the Placeholder for the text field if TextField is the InputType.
The default is Nothing
.
The position of the label or the dropdown menu in relation to the InputType.
labelPlacement : Placement -> Dropdown option -> Dropdown option
Set the position of the label in relation to the InputType.
The default is Above.
labelSpacing : Basics.Int -> Dropdown option -> Dropdown option
Set the spacing between the InputType and its label.
The default is 10.
menuPlacement : Placement -> Dropdown option -> Dropdown option
Set the position of the dropdown menu in relation to the InputType.
The default is Below.
menuSpacing : Basics.Int -> Dropdown option -> Dropdown option
Set the spacing between the InputType and the menu.
The default is 0.
maxHeight : Basics.Int -> Dropdown option -> Dropdown option
The max height for the dropdown, the default is 150.
(Vertical scrolling kicks in automatically.)
inputAttributes : List (Element.Attribute (Msg option)) -> Dropdown option -> Dropdown option
The Attributes to set on the InputType.
menuAttributes : List (Element.Attribute (Msg option)) -> Dropdown option -> Dropdown option
The Attributes to set on the menu container.
optionAttributes : List (Element.Attribute (Msg option)) -> Dropdown option -> Dropdown option
The Attributes to set on each option.
optionHoverAttributes : List (Element.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 (Element.Attribute (Msg option)) -> Dropdown option -> Dropdown option
The Attributes to set on an option when it has been selected by a user.
Filtering is currently case insensitive.
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.
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.
awayTeamDropdown =
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.
removeOption : option -> Dropdown option -> Dropdown option
Remove an option
from the internal list.
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.
setSelectedLabel : Maybe String -> Dropdown option -> Dropdown option
Set the selected label - 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.
openOnMouseEnter : Basics.Bool -> Dropdown option -> Dropdown option
Choose whether the menu opens when the mouse enters.
If this is set to True
the menu will also close automatically when the mouse
leaves.
The default is False
.
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.
open : Dropdown option -> Dropdown option
close : Dropdown option -> Dropdown option
selected : Dropdown option -> Maybe ( Basics.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.
selectedLabel : Dropdown option -> Maybe String
Maybe retrieve the label for the selected option.
list : Dropdown option -> List ( Basics.Int, String, option )
List all the option
information. The tuples returned represent:
option
option
option
itselflistOptions : Dropdown option -> List option
List all the option
s.
listLabels : Dropdown option -> List String
List all the labels for each option.
text : Dropdown option -> String
Get the text entered in the TextField.
isOpen : Dropdown option -> Basics.Bool
Determine if the dropdown is open or not.
getId : Dropdown option -> String
Get the id
of the dropdown.
Pattern match on this type in your update
function to determine the event
that occured.
Selected (Int, String, option)
Int
is the index of the option in the menuString
is the label of the option displayed in the menuoption
is the option
.TextChanged String
String
is the text entered by the user.This is an opaque type, pattern match on OutMsg.
update : Msg option -> Dropdown option -> ( Dropdown option, Platform.Cmd.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
)
...
subscriptions : Dropdown option -> Platform.Sub.Sub (Msg option)
Subscribe to the browser onResize
event.
When the orientation changes on some mobile devices the dropdown can lose focus, resulting in it failing to close if the user taps outside the dropdown.
Subscribing to this subscription
results in the dropdown regaining focus
when the orientation changes so that the user experience doesn't change.
This subscription is only active when the dropdown is open.
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