Select items from a menu list.
{ item : item, label : String }
A menu item that will be represented in the menu list.
The item
property is the type representation of the menu item that will be used in an Action.
The label
is the text representation that will be shown in the menu.
type Tool
= Screwdriver
| Hammer
| Drill
toolItems : MenuItem Tool
toolItems =
[ basicMenuItem { item = Screwdriver, label = "Screwdriver" }
, basicMenuItem { item = Hammer, label = "Hammer" }
, basicMenuItem { item = Drill, label = "Drill" }
]
yourView model =
Html.map SelectMsg <|
view
(single Nothing
|> menuItems toolItems
|> state model.selectState
)
Combine this with basicMenuItem to create a MenuItem
basicMenuItem : BasicMenuItem item -> MenuItem item
Create a basic type of MenuItem.
Use customMenuItem if you want more flexibility on how a menu item will look in the menu.
type Tool
= Screwdriver
| Hammer
| Drill
menuItems : List (MenuItem Tool)
menuItems =
[ basicMenuItem
{ item = Screwdriver, label = "Screwdriver" }
, basicMenuItem
{ item = Hammer, label = "Hammer" }
, basicMenuItem
{ item = Drill, label = "Drill" }
]
{ item : item
, label : String
, view : Html.Styled.Html Basics.Never
}
A menu item that will be represented in the menu list by a view you supply.
The item
property is the type representation of the menu item that will be used in an Action.
The label
is the text representation of the item.
The view is a Html
view that you supply.
type Tool
= Screwdriver
| Hammer
| Drill
toolItems : MenuItem Tool
toolItems =
[ customMenuItem { item = Screwdriver, label = "Screwdriver", view = text "Screwdriver" }
, customMenuItem { item = Hammer, label = "Hammer", view = text "Hammer" }
, customMenuItem { item = Drill, label = "Drill", view = text "Drill" }
]
yourView model =
Html.map SelectMsg <|
view
(single Nothing
|> menuItems toolItems
|> state model.selectState
)
The view you provide will be rendered in a li
element that is styled according to the value set by setStyles.
customMenuItem { item = Hammer, label = "Hammer", view = text "Hammer" }
-- => <li>Hammer</>
Combine this with customMenuItem to create a MenuItem.
customMenuItem : CustomMenuItem item -> MenuItem item
Create a custom type of MenuItem.
type Tool
= Screwdriver
| Hammer
| Drill
menuItems : List (MenuItem Tool)
menuItems =
[ customMenuItem
{ item = Screwdriver, label = "Screwdriver", view = text "Screwdriver" }
, customMenuItem
{ item = Hammer, label = "Hammer", view = text "Hammer" }
, customMenuItem
{ item = Drill, label = "Drill", view = text "Drill" }
]
group : String -> Group
Create a MenuItem group to provide visual organisation for your menu items.
Use with groupedMenuItem to add a MenuItem to a group.
type Tool
= Screwdriver
| Hammer
| Drill
toolGroup : Group
toolGroup =
group "tool"
menuItems : List (MenuItem Tool)
menuItems =
[ groupedMenuItem toolGroup
( basicMenuItem { item = Screwdriver, label = "Screwdriver" } )
, groupedMenuItem toolGroup
( basicMenuItem { item = Hammer, label = "Hammer" } )
, groupedMenuItem toolGroup
( basicMenuItem { item = Drill, label = "Drill" } )
]
groupedMenuItem : Group -> MenuItem item -> MenuItem item
Create a grouped MenuItem.
type Tool
= Screwdriver
| Hammer
| Drill
toolGroup : Group
toolGroup =
group "tool"
menuItems : List (MenuItem Tool)
menuItems =
[ groupedMenuItem toolGroup
( customMenuItem
{ item = Screwdriver
, label = "Screwdriver"
, view = text "Screwdriver"
}
)
, customMenuItem
{ item = Hammer, label = "Hammer", view = text "Hammer" }
, customMenuItem
{ item = Drill, label = "Drill", view = text "Drill" }
]
groupStyles : Styles.GroupConfig -> Group -> Group
Create custom styling for a Group.
This will override global styles for this group when using setGroupStyles
groupStyles : GroupConfig
groupStyles =
getGroupConfig default
|> setGroupColor (Css.hex "#EEEEEE")
toolGroup : Group
toolGroup =
group "tool"
|> groupStyles groupStyles
menuItems : List (MenuItem Tool)
menuItems =
[ groupedMenuItem toolGroup
( basicMenuItem { item = Screwdriver, label = "Screwdriver" } )
, groupedMenuItem toolGroup
( basicMenuItem { item = Hammer, label = "Hammer" } )
, groupedMenuItem toolGroup
( basicMenuItem { item = Drill, label = "Drill" } )
]
groupView : Html.Styled.Html Basics.Never -> Group -> Group
Create a custom view for a Group.
customView : Html Never
customView =
text "My custom group"
customGroup : Group
customGroup =
group "tool"
|> groupView customView
menuItems : List (MenuItem Tool)
menuItems =
[ groupedMenuItem customGroup
( basicMenuItem { item = Screwdriver, label = "Screwdriver" } )
, groupedMenuItem customGroup
( basicMenuItem { item = Hammer, label = "Hammer" } )
, groupedMenuItem customGroup
( basicMenuItem { item = Drill, label = "Drill" } )
]
filterableMenuItem : Basics.Bool -> MenuItem item -> MenuItem item
Choose whether a menu item is filterable.
Useful for when you always want to have a selectable option in the menu.
Menu items are filterable by default.
type Tool
= Screwdriver
| Hammer
| Drill
menuItems : List (MenuItem Tool)
menuItems =
[ customMenuItem
{ item = Screwdriver, label = "Screwdriver", view = text "Screwdriver" }
, customMenuItem
{ item = Hammer, label = "Hammer", view = text "Hammer" }
, customMenuItem
{ item = Drill, label = "Drill", view = text "Drill" }
|> filterableMenuItem False
]
NOTE: This only takes effect when searchable is True
.
dismissibleMenuItemTag : Basics.Bool -> MenuItem item -> MenuItem item
Choose whether a selected menu item tag can produce a Deselect
action.
This affects the multi Variant and is useful for when you want a selected tag to not be individually dismissible.
The tag will not render a dismiss button if False
.
default: True
type Tool
= Screwdriver
| Hammer
| Drill
menuItems : List (MenuItem Tool)
menuItems =
[ customMenuItem
{ item = Screwdriver, label = "Screwdriver", view = text "Screwdriver" }
, customMenuItem
{ item = Hammer, label = "Hammer", view = text "Hammer" }
, customMenuItem
{ item = Drill, label = "Drill", view = text "Drill" }
|> dismissibleMenuItemTag False
]
stylesMenuItem : Styles.MenuItemConfig -> MenuItem item -> MenuItem item
Set individual styles for a menu item.
These styles will override any global menu item styles set via setStyles.
To set a global style for all menu items use setStyles.
import Styles exposing (MenuItemConfig, default, getMenuItemConfig)
import Css
type Tool
= Screwdriver
| Hammer
| Drill
drillStyles : MenuItemConfig
drillStyles =
getMenuItemConfig default
|> setMenuItemColorHoverSelected (Css.hex "#EEEEEE")
menuItems : List (MenuItem Tool)
menuItems =
[ customMenuItem
{ item = Screwdriver, label = "Screwdriver", view = text "Screwdriver" }
, customMenuItem
{ item = Drill, label = "Drill", view = text "Drill" }
|> stylesMenuItem drillStyles
]
valueMenuItem : String -> MenuItem item -> MenuItem item
Explicitly set the value attribute for the input form control.
This is handy for when you are submitting a form and your server is expecting a value
that is different
from the label, like a database id.
Take the following selection
<option value="2" selected>Pagani BC</option>
When a form is submitted the server will see:
something: "2"
instead of:
something: "Pagani BC"
By default, the value attribute will be populated with the MenuItem
label.
type Tool
= Screwdriver
| Hammer
| Drill
menuItems : List (MenuItem Tool)
menuItems =
[ customMenuItem
{ item = Hammer, label = "Hammer", view = text "Hammer" }
|> valueMenuItem "2"
, customMenuItem
{ item = Drill, label = "Drill", view = text "Drill" }
|> valueMenuItem "3"
]
Specific events happen in the Select that you can react to from your update.
Maybe you want to find out what country someone is from?
When they select a country from the menu, it will be reflected in the Select action.
import Select exposing ( Action(..) )
type Msg
= SelectMsg (Select.Msg Country)
-- your other Msg's
type Country
= Australia
| Japan
| Taiwan
-- other countries
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
SelectMsg selectMsg ->
let
(maybeAction, selectState, selectCmds) =
Select.update selectMsg model.selectState
selectedCountry : Maybe Country
selectedCountry =
case maybeAction of
Just (Select.Select someCountry) ->
Just someCountry
Nothing ->
Nothing
in
-- (model, cmd)
NOTE: Multi native variants use the SelectBatch
action to determine selections.
initState : SelectId -> State
Set up an initial state in your init function.
type Country
= Australia
| Japan
| Taiwan
type alias Model =
{ selectState : State
, items : List (MenuItem Country)
, selectedCountry : Maybe Country
}
init : Model
init =
{ selectState = initState (selectIdentifier "country-select")
, items =
[ basicMenuItem
{ item = Australia, label = "Australia" }
, basicMenuItem
{ item = Japan, label = "Japan" }
, basicMenuItem
{ item = Taiwan, label = "Taiwan" }
]
, selectedCountry = Nothing
}
keepMenuOpen : Basics.Bool -> State -> State
Keeps the menu open at all times.
Use this with care as all actions that normally close the menu like selections, or escape, or clicking away will not close it.
focus : Msg item
Opens the menu and sets focus on the Variant.
Handy when using a menu Variant as dropdown.
yourUpdate : (model, Cmd msg )
yourUpdate msg model =
case msg of
FocusTheSelect ->
let
( actions, updatedState, cmds ) =
update focus model.selectState
in
({ model | selectState = updatedState }, Cmd.map SelectMsg cmds)
NOTE: Successfull focus will dispatch the FocusSet
Action
isFocused : State -> Basics.Bool
Check to see that the variant has focus.
This will return true if any focusable element inside the control has focus i.e. If the clear button is visible and has focus.
yourUpdate : (State, Cmd msg )
yourUpdate msg state =
case msg of
SelectMsg msg ->
let
( actions, updatedState, cmds ) =
update msg state
in
if isFocused updatedState then
(updatedState, makeSomeRequest)
else
(updatedState, Cmd.none)
isMenuOpen : State -> Basics.Bool
Check that the menu is open and visible.
yourUpdate : (State, Cmd msg )
yourUpdate msg state =
case msg of
SelectMsg msg ->
let
( actions, updatedState, cmds ) =
update msg state
in
if isFocused updatedState && isMenuOpen updatedState then
(updatedState, makeSomeRequest)
else
(updatedState, Cmd.none)
menuItems : List (MenuItem item) -> Config item -> Config item
The items that will appear in the menu list.
NOTE: When using the (multi) select, selected items will be reflected as a tags and visually removed from the menu list.
items =
[ basicMenuItem
{ item = SomeValue, label = "Some label" }
]
yourView =
view
(Single Nothing |> menuItems items)
clearable : Basics.Bool -> Config item -> Config item
Allows a single variant selected menu item to be cleared.
To handle a cleared item refer to the ClearedSingleSelect action.
items =
[ basicMenuItem
{ item = SomeValue, label = "Some label" }
]
yourView model =
Html.map SelectMsg <|
view
( single Nothing
|> clearable True
|> menuItems items
)
placeholder : String -> Config item -> Config item
The text that will appear as an input placeholder.
yourView model =
Html.map SelectMsg <|
view
(single Nothing |> placeholder "some placeholder")
selectIdentifier : String -> SelectId
The ID for the rendered Select input
NOTE: It is important that the ID's of all selects that exist on a page remain unique so I add some extra stuff at the end of the String you provide to help out.
If you don't want this see staticSelectIdentifier.
Illegal id characters will be replaced with "_".
init : State
init =
initState (selectIdentifier "someUniqueId")
staticSelectIdentifier : String -> SelectId
A static ID for the rendered Select input
The exact string you pass will be the ID used internally.
This is handy when you want a label tags for
attribute to match the variant id
without needing to remember to add the extra stuff.
See also selectIdentifier.
Illegal id characters will be replaced with "_".
init : State
init =
initState (staticSelectIdentifier "someUniqueStaticId")
state : State -> Config item -> Config item
The select state.
This is usually persisted in your model.
model : Model
model =
{ selectState = initState }
yourView : Model
yourView model =
Html.map SelectMsg <|
view
(single Nothing
|> state model.selectState
)
update : Msg item -> State -> ( Maybe (Action item), State, Platform.Cmd.Cmd (Msg item) )
Add a branch in your update to handle the view Msg's.
yourUpdate msg model =
case msg of
SelectMsg selectMsg ->
update selectMsg model.selectState
view : Config item -> Html.Styled.Html (Msg item)
Render the select
yourView model =
Html.map SelectMsg <|
view (single Nothing)
searchable : Basics.Bool -> Config item -> Config item
Renders an input that let's you input text to search for menu items.
yourView model =
Html.map SelectMsg <|
view
(single Nothing |> searchable True)
NOTE: This doesn't affect the Native single select variant.
setStyles : Styles.Config -> Config item -> Config item
Change some of the visual styles of the select.
Useful for styling the select using your color branding.
import Select.Styles as Styles
baseStyles : Styles.Config
baseStyles =
Styles.default
controlBranding : Styles.ControlConfig
controlBranding =
Styles.getControlConfig baseStyles
|> Styles.setControlBorderColor (Css.hex "#FFFFFF")
|> Styles.setControlBorderColorFocus (Css.hex "#0168B3")
selectBranding : Styles.Config
selectBranding =
baseStyles
|> Styles.setControlStyles controlBranding
yourView model =
Html.map SelectMsg <|
view
(single Nothing |> setStyles selectBranding)
name : String -> Config item -> Config item
The name attribute of a native select variant
A form will need this attribute to know how to label the data.
yourView model =
label
[ id "selectLabelId" ]
[ text "Select your country"
, Html.map SelectMsg <|
view
(singleNative Nothing
|> name "country"
)
]
single : Maybe (MenuItem item) -> Config item
Select a single item.
countries : List (MenuItem Country)
countries =
[ basicMenuItem
{ item = Australia, label = "Australia" }
, basicMenuitem
{ item = Taiwan, label = "Taiwan"
-- other countries
]
yourView =
Html.map SelectMsg <|
view
(single Nothing |> menuItems countries)
singleMenu : Maybe (MenuItem item) -> Config item
Menu only single select.
countries : List (MenuItem Country)
countries =
[ basicMenuItem
{ item = Australia, label = "Australia" }
, basicMenuitem
{ item = Taiwan, label = "Taiwan"
-- other countries
]
yourView =
Html.map SelectMsg <|
view
(singleMenu Nothing |> menuItems countries)
NOTE: By default the menu will not render until it is focused and interacted with. This is for accessibility reasons.
You can use focus to open and focus the menu if you are using this variant as a dropdown.
menu : Config item
Menu only select.
Unlike a singleMenu this variant does not accept or display options as selected.
Useful when you want to know what someone has selected like a list of settings or options.
actions : List (MenuItem Actions)
actions =
[ basicMenuItem
{ item = Update, label = "Update" }
, basicMenuitem
{ item = Delete, label = "Delete"
-- other actions
]
yourView =
Html.map SelectMsg <|
view
(menu |> menuItems actions)
NOTE: By default the menu will not render until it is focused and interacted with. This is for accessibility reasons.
You can use focus to open and focus the menu if you are using this variant as a dropdown.
multi : List (MenuItem item) -> Config item
Select multiple items.
Selected items will render as tags and be visually removed from the menu list.
yourView model =
Html.map SelectMsg <|
view
(multi model.selectedCountries
|> menuItems model.countries
)
singleNative : Maybe (MenuItem item) -> Config item
Select a single item with a native html select element.
Useful for when you want to give a native select experience such as on touch devices.
countries : List (MenuItem Country)
countries =
[ basicMenuItem
{ item = Australia, label = "Australia" }
, basicMenuItem
{ item = Taiwan, label = "Taiwan"
-- other countries
]
yourView =
Html.map SelectMsg <|
view
(singleNative Nothing |> menuItems countries)
Note
The only Action event that will be fired from the native single select is
the Select
Action. The other actions are not currently supported.
Some Config values will not take effect when using the single native variant
multiNative : List (MenuItem item) -> Config item
Select multiple items with a native html select element.
Useful for when you want to give a native select experience such as on touch devices.
countries : List (MenuItem Country)
countries =
[ basicMenuItem
{ item = Australia, label = "Australia" }
, basicMenuItem
{ item = Taiwan, label = "Taiwan"
-- other countries
]
yourView =
Html.map SelectMsg <|
view
(multiNative [] |> menuItems countries)
Note
The only Action event that will be fired from the native multi select is
the SelectBatch
Action. The other actions are not currently supported.
Some Config values will not take effect when using the multi native variant
disabled : Basics.Bool -> Config item -> Config item
Disables the select input so that it cannot be interacted with.
yourView model =
Html.map SelectMsg <|
view
(single Nothing |> disabled True)
labelledBy : String -> Config item -> Config item
The element ID of the label for the select.
It is best practice to render the select with a label.
yourView model =
label
[ id "selectLabelId" ]
[ text "Select your country"
, Html.map SelectMsg <|
view
(single Nothing |> labelledBy "selectLabelId")
]
ariaDescribedBy : String -> Config item -> Config item
The ID of an element that describes the select.
yourView model =
label
[ id "selectLabelId" ]
[ text "Select your country"
, Html.map SelectMsg <|
view
(single Nothing
|> labelledBy "selectLabelId"
|> ariaDescribedBy "selectDescriptionId"
)
, div [ id "selectDescriptionId" ] [ text "This text describes the select" ]
]
loading : Basics.Bool -> Config item -> Config item
Displays an animated loading icon to visually represent that menu items are being loaded.
This would be useful if you are loading menu options asynchronously, like from a server.
yourView model =
Html.map SelectMsg <|
view
(single Nothing |> loading True)
loadingMessage : String -> Config item -> Config item
Displays when there are no matched menu items and loading is True.
yourView model =
Html.map SelectMsg <|
view
(single Nothing |> loadingMessage "Fetching items...")
jsOptimize : Basics.Bool -> State -> State
Opt in to a Javascript optimization.
Read the Advanced section of the README for a good explanation on why you might like to opt in.
model : Model model =
{ selectState =
initState (selectIdentifier "some-unique-id")
|> jsOptimize True
}
Install the Javascript package:
npm
npm install @confidenceman02/elm-select
Import script
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Viewer</title>
<script src="/node_modules/@confidenceman02/elm-select/dist/dynamic.min.js"></script>
</head>
<body>
<main></main>
<script src="index.js"></script>
</body>
</html>
Alternatively you can import the script wherever you are initialising your program.
import { Elm } from "./src/Main";
import "@confidenceman02/elm-select"
Elm.Main.init({node, flags})