Elm-pages: "highlighting" internal links

This may really be a beginner question in terms of elm-pages as well as very specific to the project, but I haven’t found it answered anywhere else so please excuse me posting here.

I’ve checked out the elm-pages starter repo and stripped it from the things I don’t want. Now for my navbar, I’d like to stay consistent and highlight the link of the currently selected sub page (e.g. accentuate the link to the contact page if the user is currently viewing it). For the preexisting blog, there is the function highlightableLink:

highlightableLink :
    PagePath Pages.PathKey
    -> Directory Pages.PathKey Directory.WithIndex
    -> String
    -> Element msg
highlightableLink currentPath linkDirectory displayName =
    let
        isHighlighted =
            currentPath |> Directory.includes linkDirectory
    in
    Element.link
        (if isHighlighted then
            [ Font.underline
            , Font.color Palette.color.primary
            ]

         else
            []
        )
        { url = linkDirectory |> Directory.indexPath |> PagePath.toString
        , label = Element.text displayName
        }

With this in place, you can highlight or not the link to the blog like so:

 Element.row [ Element.spacing 15 ]
               [  highlightableLink currentPath Pages.pages.blog.directory "Blog"
               ]

This works because the generated Pages file contains this:

pages =
    { blog =
        { draft = (buildPage [ "blog", "draft" ])
        , hello = (buildPage [ "blog", "hello" ])
        , index = (buildPage [ "blog" ])
        , directory = directoryWithIndex ["blog"]
        }
    , contact = (buildPage [ "contact" ])
    , index = (buildPage [  ])
    , directory = directoryWithIndex []
    }

To me it seems though that “flat” pages (self contained vs. being a category for sub pages) don’t get a directory, therefore I can not call the highlightableLink with e.g. Pages.pages.contact instead of Pages.pages.blog.directory. So is there a canonical way of highlighting a link to a flat page? So far everything presented is part of the aforementioned repository except for my contact page.

Please be kind, I’m not only new to elm-pages, but to the elm language itself, too. :innocent:

1 Like

Hello! Good question.

I’m trying to think through what the logic should be at a high-level. My first thought is something like:

  • If current path == a link's path, then it’s highlighted
  • If a link's path == current path with the last segment dropped AND it’s not /
Current Path Link Highlight?
/ / Yes
/about / No
/blog/my-post /blog Yes

I think this covers the cases, but I may be missing something.

Using PagePath.toPath, you could implement that logic with something like this:

isHighlighted currentPath linkPath =
  currentPath == linkPath || (List.drop 1 currentPath == linkPath && currentPath /= [])

I didn’t test it out, but let me know how that goes or if you have any questions!

In elm-pages 2.0, which is getting close to the final release, the Pages.pages record is no longer generated to reduce the bundle size. Instead, there is a Route type. But you can get the path as a List String from any page as well, so this technique translates well to the upcoming version, too. Hope that helps!

1 Like

Thanks for tuning in, Dillon! :slight_smile:

I did what every good developer would do and copy-pasted your code into mine, and after some confusion and finally reading up on what PagePath.toPath actually does, I came up with an amalgam of the starter code, your ad hoc hack and some reinterpretation by me that actually works:

-- ############### snippy snappy ######################

            , Element.row [ Element.spacing 15 ]
                [ internalLink Pages.pages.contact currentPath "Kontakt" ]
            ]
        ]


internalLink : PagePath Pages.PathKey -> PagePath Pages.PathKey -> String -> Element msg
internalLink target currentPath label =
    let
        isHighlighted =
            isLinkHighlighted currentPath target
    in
    Element.link
        (if isHighlighted then
            [ Font.underline
            , Font.color Palette.color.primary
            ]

         else
            []
        )
        { url = PagePath.toString target
        , label = Element.text label
        }


isLinkHighlighted : PagePath Pages.PathKey -> PagePath Pages.PathKey -> Bool
isLinkHighlighted currentPath linkPath =
    let
        current =
            PagePath.toPath currentPath

        target =
            PagePath.toPath linkPath
    in
    current == target || (List.drop 1 current == target && current /= [])

I moved the conversion into the isLinkHighlighted function so it can be called with PathKey instead of a List, but that’s basically it. Thank you! :v:

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.