thomasin / elm-menus / Preset.Menu

Menus!


type Model

Holds information about whether the menu is open or closed. Should be kept in your Model.


type alias Msg =
Internal.Msg

Passed into the #update function.


type Config option

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.

Views

The view is highly customisable. You decide how to structure the menu & how it is styled. There a few caveats here to note:

Preset.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
                )
            ]
    )


type Token option

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 ]
    )