This library helps you create a menu. Your data is stored separately; keep it in whatever shape makes the most sense for your application. A menu has a lot of uses: form input, mentions, search, etc.
I have (hopefully!) given the users of this library a large amount of customizability.
I recommend looking at the examples
before diving into the API or source code.
view : ViewConfig data -> Basics.Int -> State -> List data -> Html Msg
Take a list of data
and turn it into a menu.
The ViewConfig
argument is the configuration for the menu view.
ViewConfig
describes the HTML we want to show for each item and the list.
The Int
argument is how many results you would like to show.
The State
argument describes what is selected via mouse and keyboard.
Note: The State
and List data
should live in your Model.
The ViewConfig
for the menu belongs in your view code.
ViewConfig
should never exist in your model.
Describe any potential menu configurations statically.
This pattern has been inspired by Elm Sortable Table.
update : UpdateConfig msg data -> Msg -> Basics.Int -> State -> List data -> ( State, Maybe msg )
Use this function to update the menu's State
.
Provide the same data as your view.
The Int
argument is how many results you would like to show.
subscription : Platform.Sub.Sub Msg
Add this to your program
's subscriptions so the menu will respond to keyboard input.
viewConfig : { toId : data -> String, ul : List (Html.Attribute Basics.Never), li : KeySelected -> MouseSelected -> data -> HtmlDetails Basics.Never } -> ViewConfig data
Create the configuration for your view
function (ViewConfig
).
Say we have a List Person
that we want to show as a series of options.
We would create a ViewConfig
like so:
import Menu
viewConfig : Menu.ViewConfig Person
viewConfig =
let
customizedLi keySelected mouseSelected person =
{ attributes =
[ classList
[ ( "menu-item", True )
, ( "key-selected", keySelected )
, ( "mouse-selected", mouseSelected )
]
]
, children = [ Html.text person.name ]
}
in
Menu.viewConfig
{ toId = .name
, ul = [ class "menu-list" ]
, li = customizedLi
}
You provide the following information in your menu configuration:
toId
— turn a Person
into a unique ID. This lets us use
Html.Keyed
under the hood to make sorting faster.ul
— specify any non-behavioral attributes you'd like for the list menu.li
— specify any non-behavioral attributes and children for a list item: both selection states are provided.
See the examples to get a better understanding!updateConfig : { toId : data -> String, onKeyDown : Basics.Int -> Maybe String -> Maybe msg, onTooLow : Maybe msg, onTooHigh : Maybe msg, onMouseEnter : String -> Maybe msg, onMouseLeave : String -> Maybe msg, onMouseClick : String -> Maybe msg, separateSelections : Basics.Bool } -> UpdateConfig msg data
Create the configuration for your update
function (UpdateConfig
).
Say we have a List Person
that we want to show as a series of options.
We would create an UpdateConfig
like so:
import Menu
updateConfig : Menu.UpdateConfig Msg Person
updateConfig =
Menu.updateConfig
{ toId = .name
, onKeyDown =
\code maybeId ->
if code == 38 || code == 40 then
Nothing
else if code == 13 then
Maybe.map SelectPerson maybeId
else
Just Reset
, onTooLow = Nothing
, onTooHigh = Nothing
, onMouseEnter = \_ -> Nothing
, onMouseLeave = \_ -> Nothing
, onMouseClick = \id -> Just (SelectPerson id)
, separateSelections = False
}
You provide the following information in your menu configuration:
toId
— turn a Person
into a unique ID.ul
— specify any non-behavioral attributes you'd like for the list menu.li
— specify any non-behavioral attributes and children for a list item: both selection states are provided.Tracks keyboard and mouse selection within the menu.
current : State -> ( Maybe String, Maybe String )
Current State.
empty : State
A State with nothing selected.
reset : UpdateConfig msg data -> State -> State
Reset the keyboard navigation but leave the mouse state alone. Convenient when the two selections are represented separately.
resetToFirstItem : UpdateConfig msg data -> List data -> Basics.Int -> State -> State
Like reset
but defaults to a keyboard selection of the first item.
resetToLastItem : UpdateConfig msg data -> List data -> Basics.Int -> State -> State
Like reset
but defaults to a keyboard selection of the last item.
Basics.Bool
True if the element has been selected via keyboard navigation.
Basics.Bool
True if the element has been selected via mouse hover.
A message type for the menu to update.
Configuration for your menu, describing your menu and its items.
Note: Your ViewConfig
should never be held in your model. It should only appear in view code.
Configuration for updates
{ attributes : List (Html.Attribute msg)
, children : List (Html msg)
}
HTML lists require li
tags as children, so we allow you to specify everything about li
HTML node except the nodeType.
Sections require a separate view and configuration since another type of data must be provided: sections.
Note: Section data can have any shape: your static configuration will just tell the menu how to grab an ID for a section and its related data.
viewWithSections : ViewWithSectionsConfig data sectionData -> Basics.Int -> State -> List sectionData -> Html Msg
Presents a menu with sections.
You can follow the same instructions as described for view
, providing a more advanced configuration and different data shape.
ViewWithSectionsConfig
sets up your menu to handle sectioned data.
The sectioned data becomes the new data argument for viewWithSections
.
sectionConfig : { toId : sectionData -> String, getData : sectionData -> List data, ul : List (Html.Attribute Basics.Never), li : sectionData -> SectionNode Basics.Never } -> SectionConfig data sectionData
Create the SectionConfig
for your view
function.
Say we have a List Century
that we want to show as a series of sections.
We would create a SectionConfig
like so:
type alias Century =
{ title : String
, people : List Person
}
import Menu
sectionConfig : Menu.SectionConfig Person Century
sectionConfig =
Menu.sectionConfig
{ toId = .title
, getData = .people
, ul = [ class "menu-section-list" ]
, li =
\section ->
{ nodeType = "div"
, attributes = [ class "menu-section-item" ]
, children =
[ div [ class "menu-section-box" ]
[ strong [ class "menu-section-text" ] [ text section.title ]
]
]
}
}
You provide the following information in your menu configuration:
toId
— turn a Century
into a unique ID.getData
— extract the data from Century
, in this case: List Person
.ul
— specify any non-behavioral attributes you'd like for the section list.li
— specify any non-behavioral attributes and children for a section.viewWithSectionsConfig : { toId : data -> String, ul : List (Html.Attribute Basics.Never), li : KeySelected -> MouseSelected -> data -> HtmlDetails Basics.Never, section : SectionConfig data sectionData } -> ViewWithSectionsConfig data sectionData
The same configuration as viewConfig, but provide a section configuration as well.
{ nodeType : String
, attributes : List (Html.Attribute msg)
, children : List (Html msg)
}
Describe everything about a Section HTML node.
The configuration for a section of the menu.
Note: This should never live in your model.
Configuration for your menu, describing your menu, its sections, and its items.
Note: This should never live in your model.