Skip to content

Commit ff85571

Browse files
committed
Add active class for menu item in navigation.
1 parent a817cd6 commit ff85571

File tree

8 files changed

+101
-45
lines changed

8 files changed

+101
-45
lines changed

docs/content/fsdocs-default.css

+5-1
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,13 @@ main {
348348
}
349349
}
350350

351-
&:hover {
351+
&:hover, &.active {
352352
border-color: var(--text-color);
353353
}
354+
355+
&.active {
356+
font-weight: 600;
357+
}
354358
}
355359

356360
@media screen and (min-width: 768px) {

src/FSharp.Formatting.ApiDocs/GenerateHtml.fs

+6-4
Original file line numberDiff line numberDiff line change
@@ -617,9 +617,10 @@ type HtmlRender(model: ApiDocModel, ?menuTemplateFolder: string) =
617617
let link = model.IndexFileUrl(root, collectionName, qualify, model.FileExtensions.InUrl)
618618

619619
[ { Menu.MenuItem.Link = link
620-
Menu.MenuItem.Content = title } ]
620+
Menu.MenuItem.Content = title
621+
Menu.MenuItem.IsActive = true } ]
621622

622-
Menu.createMenu menuTemplateFolder.Value "API Reference" menuItems
623+
Menu.createMenu menuTemplateFolder.Value false "API Reference" menuItems
623624

624625
else
625626
let categorise = Categorise.model model
@@ -634,9 +635,10 @@ type HtmlRender(model: ApiDocModel, ?menuTemplateFolder: string) =
634635
let name = ns.Name
635636

636637
{ Menu.MenuItem.Link = link
637-
Menu.MenuItem.Content = name })
638+
Menu.MenuItem.Content = name
639+
Menu.MenuItem.IsActive = false })
638640

639-
Menu.createMenu menuTemplateFolder.Value "Namespaces" menuItems
641+
Menu.createMenu menuTemplateFolder.Value false "Namespaces" menuItems
640642
else
641643
listOfNamespacesNavAux otherDocs nsOpt
642644
|> List.map (fun html -> html.ToString())

src/FSharp.Formatting.ApiDocs/GenerateMarkdown.fs

+7-5
Original file line numberDiff line numberDiff line change
@@ -367,9 +367,10 @@ type MarkdownRender(model: ApiDocModel, ?menuTemplateFolder: string) =
367367
let link = model.IndexFileUrl(root, collectionName, qualify, model.FileExtensions.InUrl)
368368

369369
[ { Menu.MenuItem.Link = link
370-
Menu.MenuItem.Content = title } ]
370+
Menu.MenuItem.Content = title
371+
Menu.MenuItem.IsActive = false } ]
371372

372-
Menu.createMenu menuTemplateFolder.Value "API Reference" menuItems
373+
Menu.createMenu menuTemplateFolder.Value false "API Reference" menuItems
373374

374375
else
375376
let categorise = Categorise.model model
@@ -384,9 +385,10 @@ type MarkdownRender(model: ApiDocModel, ?menuTemplateFolder: string) =
384385
let name = ns.Name
385386

386387
{ Menu.MenuItem.Link = link
387-
Menu.MenuItem.Content = name })
388+
Menu.MenuItem.Content = name
389+
Menu.MenuItem.IsActive = false })
388390

389-
Menu.createMenu menuTemplateFolder.Value "Namespaces" menuItems
391+
Menu.createMenu menuTemplateFolder.Value false "Namespaces" menuItems
390392
else
391393
listOfNamespacesAux otherDocs nav nsOpt
392394
|> List.map (fun html -> html.ToString())
@@ -432,7 +434,7 @@ type MarkdownRender(model: ApiDocModel, ?menuTemplateFolder: string) =
432434

433435
()
434436

435-
for (nsIndex, ns) in Seq.indexed collection.Namespaces do
437+
for nsIndex, ns in Seq.indexed collection.Namespaces do
436438

437439
let content = MarkdownDocument(namespaceContent (nsIndex, ns), Map.empty)
438440

src/FSharp.Formatting.Common/Menu.fs

+8-3
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@ open System
44
open System.IO
55
open FSharp.Formatting.Templating
66

7-
type MenuItem = { Link: string; Content: string }
7+
type MenuItem =
8+
{ Link: string
9+
Content: string
10+
IsActive: bool }
811

912
let private snakeCase (v: string) =
1013
System.Text.RegularExpressions.Regex
1114
.Replace(v, "[A-Z]", "$0")
1215
.Replace(" ", "_")
1316
.ToLower()
1417

15-
let createMenu (input: string) (header: string) (items: MenuItem list) : string =
18+
let createMenu (input: string) (isCategoryActive: bool) (header: string) (items: MenuItem list) : string =
1619
let pwd = Directory.GetCurrentDirectory()
1720
let menuTemplate = File.ReadAllText(Path.Combine(pwd, input, "_menu_template.html"))
1821
let menuItemTemplate = File.ReadAllText(Path.Combine(pwd, input, "_menu-item_template.html"))
@@ -27,13 +30,15 @@ let createMenu (input: string) (header: string) (items: MenuItem list) : string
2730
SimpleTemplating.ApplySubstitutionsInText
2831
[| ParamKeys.``fsdocs-menu-item-link``, link
2932
ParamKeys.``fsdocs-menu-item-content``, title
30-
ParamKeys.``fsdocs-menu-item-id``, id |]
33+
ParamKeys.``fsdocs-menu-item-id``, id
34+
ParamKeys.``fsdocs-menu-item-active-class``, (if isCategoryActive then "active" else "") |]
3135
menuItemTemplate)
3236
|> String.concat "\n"
3337

3438
SimpleTemplating.ApplySubstitutionsInText
3539
[| ParamKeys.``fsdocs-menu-header-content``, header
3640
ParamKeys.``fsdocs-menu-header-id``, snakeCase header
41+
ParamKeys.``fsdocs-menu-header-active-class``, (if isCategoryActive then "active" else "")
3742
ParamKeys.``fsdocs-menu-items``, menuItems |]
3843
menuTemplate
3944

src/FSharp.Formatting.Common/Templating.fs

+8
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ module ParamKeys =
164164
/// A parameter key known to FSharp.Formatting, available in _menu_template.html
165165
let ``fsdocs-menu-header-id`` = ParamKey "fsdocs-menu-header-id"
166166

167+
/// A parameter key known to FSharp.Formatting, available in _menu_template.html
168+
/// This will be an empty string if the category is not active.
169+
let ``fsdocs-menu-header-active-class`` = ParamKey "fsdocs-menu-header-active-class"
170+
167171
/// A parameter key known to FSharp.Formatting, available in _menu_template.html
168172
let ``fsdocs-menu-items`` = ParamKey "fsdocs-menu-items"
169173

@@ -176,6 +180,10 @@ module ParamKeys =
176180
/// A parameter key known to FSharp.Formatting, available in _menu-item_template.html
177181
let ``fsdocs-menu-item-id`` = ParamKey "fsdocs-menu-item-id"
178182

183+
/// A parameter key known to FSharp.Formatting, available in _menu-item_template.html
184+
/// /// This will be an empty string if the item is not active.
185+
let ``fsdocs-menu-item-active-class`` = ParamKey "fsdocs-menu-item-active-class"
186+
179187
/// A parameter key known to FSharp.Formatting, available when frontmatter is used correctly
180188
let ``fsdocs-previous-page-link`` = ParamKey "fsdocs-previous-page-link"
181189

src/FSharp.Formatting.Literate/Contexts.fs

+3
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ type internal LiterateDocModel =
7373

7474
/// The kind of output generated
7575
OutputKind: OutputKind
76+
77+
/// Used for the navigation section, to indicate the list item as active
78+
IsActive: bool
7679
}
7780

7881
// Get the URI for the resource when it is part of an overall site

src/FSharp.Formatting.Literate/Formatting.fs

+4-1
Original file line numberDiff line numberDiff line change
@@ -262,4 +262,7 @@ module internal Formatting =
262262
CategoryIndex = categoryIndex
263263
Index = index
264264
IndexText = indexText
265-
Substitutions = substitutions }
265+
Substitutions = substitutions
266+
// No don't know this until later.
267+
// See DocContent.GetNavigationEntries
268+
IsActive = false }

src/fsdocs-tool/BuildCommand.fs

+60-31
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,12 @@ type internal DocContent
578578
uri = model.Uri(root) }
579579
| _ -> () |]
580580

581-
member _.GetNavigationEntries(input, docModels: (string * bool * LiterateDocModel) list) =
581+
member _.GetNavigationEntries
582+
(
583+
input,
584+
docModels: (string * bool * LiterateDocModel) list,
585+
currentPagePath: string option
586+
) =
582587
let modelsForList =
583588
[ for thing in docModels do
584589
match thing with
@@ -587,12 +592,16 @@ type internal DocContent
587592
&& model.OutputKind = OutputKind.Html
588593
&& not (Path.GetFileNameWithoutExtension(inputFileFullPath) = "index")
589594
->
590-
model
595+
{ model with
596+
IsActive =
597+
match currentPagePath with
598+
| None -> false
599+
| Some currentPagePath -> currentPagePath = inputFileFullPath }
591600
| _ -> () ]
592601

593602
let modelsByCategory =
594603
modelsForList
595-
|> List.groupBy (fun model -> model.Category)
604+
|> List.groupBy (fun (model) -> model.Category)
596605
|> List.sortBy (fun (_, ms) ->
597606
match ms.[0].CategoryIndex with
598607
| Some s ->
@@ -602,19 +611,12 @@ type internal DocContent
602611
Int32.MaxValue)
603612
| None -> Int32.MaxValue)
604613

605-
let orderList (list: LiterateDocModel list) =
614+
let orderList (list: (LiterateDocModel) list) =
606615
list
607-
|> List.sortBy (fun model ->
608-
match model.Index with
609-
| Some s ->
610-
(try
611-
int32 s
612-
with _ ->
613-
Int32.MaxValue)
614-
| None -> Int32.MaxValue)
616+
|> List.sortBy (fun model -> Option.defaultValue Int32.MaxValue model.Index)
615617

616618
if Menu.isTemplatingAvailable input then
617-
let createGroup (header: string) (items: LiterateDocModel list) : string =
619+
let createGroup (isCategoryActive: bool) (header: string) (items: LiterateDocModel list) : string =
618620
//convert items into menuitem list
619621
let menuItems =
620622
orderList items
@@ -623,18 +625,20 @@ type internal DocContent
623625
let title = System.Web.HttpUtility.HtmlEncode model.Title
624626

625627
{ Menu.MenuItem.Link = link
626-
Menu.MenuItem.Content = title })
628+
Menu.MenuItem.Content = title
629+
Menu.MenuItem.IsActive = model.IsActive })
627630

628-
Menu.createMenu input header menuItems
631+
Menu.createMenu input isCategoryActive header menuItems
629632
// No categories specified
630633
if modelsByCategory.Length = 1 && (fst modelsByCategory.[0]) = None then
631634
let _, items = modelsByCategory.[0]
632-
createGroup "Documentation" items
635+
createGroup false "Documentation" items
633636
else
634637
modelsByCategory
635638
|> List.map (fun (header, items) ->
636639
let header = Option.defaultValue "Other" header
637-
createGroup header items)
640+
let isActive = items |> List.exists (fun m -> m.IsActive)
641+
createGroup isActive header items)
638642
|> String.concat "\n"
639643
else
640644
[
@@ -644,22 +648,34 @@ type internal DocContent
644648

645649
for model in snd modelsByCategory.[0] do
646650
let link = model.Uri(root)
651+
let activeClass = if model.IsActive then "active" else ""
647652

648-
li [ Class "nav-item" ] [ a [ Class "nav-link"; (Href link) ] [ encode model.Title ] ]
653+
li
654+
[ Class $"nav-item %s{activeClass}" ]
655+
[ a [ Class "nav-link"; (Href link) ] [ encode model.Title ] ]
649656
else
650657
// At least one category has been specified. Sort each category by index and emit
651658
// Use 'Other' as a header for uncategorised things
652659
for (cat, modelsInCategory) in modelsByCategory do
653660
let modelsInCategory = orderList modelsInCategory
654661

662+
let categoryActiveClass =
663+
if modelsInCategory |> List.exists (fun m -> m.IsActive) then
664+
"active"
665+
else
666+
""
667+
655668
match cat with
656-
| Some c -> li [ Class "nav-header" ] [ !!c ]
657-
| None -> li [ Class "nav-header" ] [ !! "Other" ]
669+
| Some c -> li [ Class $"nav-header %s{categoryActiveClass}" ] [ !!c ]
670+
| None -> li [ Class $"nav-header %s{categoryActiveClass}" ] [ !! "Other" ]
658671

659672
for model in modelsInCategory do
660673
let link = model.Uri(root)
674+
let activeClass = if model.IsActive then "active" else ""
661675

662-
li [ Class "nav-item" ] [ a [ Class "nav-link"; (Href link) ] [ encode model.Title ] ] ]
676+
li
677+
[ Class $"nav-item %s{activeClass}" ]
678+
[ a [ Class "nav-link"; (Href link) ] [ encode model.Title ] ] ]
663679
|> List.map (fun html -> html.ToString())
664680
|> String.concat " \n"
665681

@@ -1675,12 +1691,9 @@ type CoreBuildOptions(watch) =
16751691
)
16761692

16771693
let docModels = docContent.Convert(this.input, defaultTemplate, extraInputs)
1678-
16791694
let actualDocModels = docModels |> List.map fst |> List.choose id
1680-
16811695
let extrasForSearchIndex = docContent.GetSearchIndexEntries(actualDocModels)
1682-
1683-
let navEntries = docContent.GetNavigationEntries(this.input, actualDocModels)
1696+
let navEntriesWithoutActivePage = docContent.GetNavigationEntries(this.input, actualDocModels, None)
16841697

16851698
let headTemplateContent =
16861699
let headTemplatePath = Path.Combine(this.input, "_head.html")
@@ -1709,20 +1722,36 @@ type CoreBuildOptions(watch) =
17091722
latestDocContentSearchIndexEntries <- extrasForSearchIndex
17101723

17111724
latestDocContentGlobalParameters <-
1712-
[ ParamKeys.``fsdocs-list-of-documents``, navEntries
1725+
[ ParamKeys.``fsdocs-list-of-documents``, navEntriesWithoutActivePage
17131726
ParamKeys.``fsdocs-head-extra``, headTemplateContent
17141727
ParamKeys.``fsdocs-body-extra``, bodyTemplateContent ]
17151728

17161729
latestDocContentPhase2 <-
17171730
(fun globals ->
1718-
17191731
printfn ""
17201732
printfn "Write Content:"
17211733

1722-
for (_thing, action) in docModels do
1723-
action globals
1724-
1725-
))
1734+
for (optDocModel, action) in docModels do
1735+
let globals =
1736+
match optDocModel with
1737+
| None -> globals
1738+
| Some(currentPagePath, _, _) ->
1739+
// Update the nav entries with the current page doc model
1740+
let navEntries =
1741+
docContent.GetNavigationEntries(
1742+
this.input,
1743+
actualDocModels,
1744+
Some currentPagePath
1745+
)
1746+
1747+
globals
1748+
|> List.map (fun (pk, v) ->
1749+
if pk <> ParamKeys.``fsdocs-list-of-documents`` then
1750+
pk, v
1751+
else
1752+
ParamKeys.``fsdocs-list-of-documents``, navEntries)
1753+
1754+
action globals))
17261755

17271756
let runDocContentPhase2 () =
17281757
protect "Content generation (phase 2)" (fun () ->

0 commit comments

Comments
 (0)