Menus!
Holds information about whether the menu is open or closed. Should be kept in your Model.
Internal.Msg
Passed into the #update function.
Create with #config.
init : Model
Initialise a closed menu
update : Msg -> { model : Model, config : Config option, options : List option } -> ( Model, Platform.Cmd.Cmd Msg )
Handles updating menu and focus state. Call this in your update function.
config : String -> Config option
Pass in an id to uniquely identity this dropdown. It must be unique to the page or weird things will happen.
The view is highly customisable. You decide how to structure the menu & how it is styled. There a few caveats here to note:
ul
node, it must take a list of only #link and other li
nodes to be valid HTMLPreset.Menu.view model
menuConfig
(\token ->
Html.div
[ Attr.class "..."
]
[ Preset.Menu.button token
[ Attr.class "..."
]
[ Html.text "Examples"
]
, Html.button
[ Attr.class "..."
, Key.tabbable False
]
[ if Preset.Menu.isOpen token then
Examples.Svg.chevronUp
else
Examples.Svg.chevronDown
]
, Preset.Menu.options token
[ Attr.class "..."
, Attr.classList
[ ( "...", Preset.Menu.isOpen token )
, ( "...", not (Preset.Menu.isOpen token) )
]
]
(List.indexedMap
(\idx option ->
Preset.Menu.link token
{ idx = idx }
[]
(\link ->
link
[ Attr.href "/"
, Attr.class "..."
]
[ Html.text option.label ]
)
)
Examples.MenuItem.list
)
]
)
Passed into your view function
isOpen : Token option -> Basics.Bool
Returns whether the menu state is open or closed You can use this in your view to only show the options list when the menu state is open, or switch chevron direction
Html.button
[ Attr.class "..."
, Key.tabbable False
]
[ if Preset.Menu.isOpen token then
Examples.Svg.chevronUp
else
Examples.Svg.chevronDown
]
focussedOption : Token option -> Maybe Basics.Int
Returns the currently focussed option. This provides consistency with other dropdowns like Preset.Combobox or Preset.Listbox, but menu items are different from the other dropdowns in that they are focussed directly, so for styling you just need to use a CSS focus selector.
link
[ Attr.href "/"
, Attr.class "bg-transparent focus:bg-purple-100"
]
[ Html.text option.label ]
view : { model : Model, config : Config option } -> (Token option -> Html Msg) -> Html Msg
This takes the menu model & config and gives you a token you can pass into the functions that make up a complete menu. Use #button, #options and #link to form a complete menu
Preset.Menu.view model menuConfig
(\token ->
... view code here
)
button : Token option -> List (Html.Attribute Msg) -> List (Html Msg) -> Html Msg
The button used to open and close the menu
Preset.Menu.button token
[ Attr.class "..."
]
[ Html.text "Examples"
]
options : Token option -> List (Html.Attribute Msg) -> List (Html Msg) -> Html Msg
A wrapper for the list of options in the menu. This should be a direct parent of your list of option nodes.
Preset.Menu.options token
[ Attr.class "..."
, Attr.classList
[ ( "...", Preset.Menu.isOpen token )
, ( "...", not (Preset.Menu.isOpen token) )
]
]
(List.indexedMap
(\idx option ->
Preset.Menu.link token
{ idx = idx }
[]
(\link ->
link
[ Attr.href "/"
, Attr.class "bg-transparent focus:bg-purple-100"
]
[ Html.text option.label ]
)
)
Examples.MenuItem.list
)
link : Token options -> { idx : Basics.Int } -> List (Html.Attribute Msg) -> ((List (Html.Attribute Msg) -> List (Html Basics.Never) -> Menus.Menu.Link Msg) -> Menus.Menu.Link Msg) -> Html Msg
Currently menus only support link children Takes the token returned from #view, the current option index, a list of HTML attributes, and a function. The function argument is in effect an Html.a node and can be used like a normal HTML node, although cannot be nested.
Preset.Menu.link token
{ idx = idx }
[]
(\link ->
link
[ Attr.href "/"
, Attr.class "bg-transparent focus:bg-purple-100"
]
[ Html.text option.label ]
)