Skip to content

Commit 8e59337

Browse files
authored
Merge pull request #21 from JuliaComputing/sp/nested-nav
feat: Enable nested top-level navigation
2 parents 629080a + 424cd75 commit 8e59337

File tree

6 files changed

+225
-62
lines changed

6 files changed

+225
-62
lines changed

README.md

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,35 @@ using MultiDocumenter
99
clonedir = mktempdir()
1010

1111
docs = [
12-
("JuliaDocs/Documenter.jl.git", "gh-pages", false) => MultiDocumenter.MultiDocRef(
13-
upstream = joinpath(clonedir, "Documenter"),
14-
path = "doc",
15-
name = "Documenter"
16-
),
17-
# using SSH for cloning is suggested when you're dealing with private repos, because
18-
# an ssh-agent will handle your keys for you
19-
# ("JuliaDebug/Infiltrator.jl.git", "gh-pages", true) => MultiDocumenter.MultiDocRef(
20-
("JuliaDebug/Infiltrator.jl.git", "gh-pages", false) => MultiDocumenter.MultiDocRef(
21-
upstream = joinpath(clonedir, "Infiltrator"),
22-
path = "inf",
23-
name = "Infiltrator"
12+
MultiDocumenter.DropdownNav("Debugging", [
13+
MultiDocumenter.MultiDocRef(
14+
upstream = joinpath(clonedir, "Infiltrator"),
15+
path = "inf",
16+
name = "Infiltrator",
17+
giturl = "https://github.com/JuliaDebug/Infiltrator.jl.git",
18+
),
19+
MultiDocumenter.MultiDocRef(
20+
upstream = joinpath(clonedir, "JuliaInterpreter"),
21+
path = "debug",
22+
name = "JuliaInterpreter",
23+
giturl = "https://github.com/JuliaDebug/JuliaInterpreter.jl.git",
24+
),
25+
]),
26+
MultiDocumenter.MultiDocRef(
27+
upstream = joinpath(clonedir, "DataSets"),
28+
path = "data",
29+
name = "DataSets",
30+
giturl = "https://github.com/JuliaComputing/DataSets.jl.git",
31+
# or use ssh instead for private repos:
32+
# giturl = "[email protected]:JuliaComputing/DataSets.jl.git",
2433
),
2534
]
2635

27-
for ((remote, branch, use_ssh), docref) in docs
28-
prefix = use_ssh ? "[email protected]:" : "https://github.com/"
29-
run(`git clone --depth 1 $prefix$remote --branch $branch --single-branch $(docref.upstream)`)
30-
end
31-
3236
outpath = joinpath(@__DIR__, "out")
3337

3438
MultiDocumenter.make(
3539
outpath,
36-
collect(last.(docs));
40+
docs;
3741
search_engine = MultiDocumenter.SearchConfig(
3842
index_versions = ["stable"],
3943
engine = MultiDocumenter.FlexSearch

assets/default/multidoc.css

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,66 @@ html {
5353
}
5454

5555
#multi-page-nav .nav-item {
56-
margin: 0 0.75rem;
56+
padding: 0 0.75rem;
5757
line-height: var(--navbar-height);
5858
font-size: 14px;
5959
display: inline-block;
60-
padding: 0;
60+
margin: 0;
61+
}
62+
63+
#multi-page-nav .dropdown-label {
64+
cursor: pointer;
65+
color: #ccc;
66+
text-transform: uppercase;
67+
}
68+
69+
#multi-page-nav .nav-dropdown .dropdown-label::after {
70+
content: "";
71+
margin-left: 5px;
72+
73+
/* caret */
74+
display: inline-block;
75+
vertical-align: 0.2em;
76+
border-top: .3em solid;
77+
border-right: .3em solid transparent;
78+
border-bottom: 0;
79+
border-left: .3em solid transparent;
80+
}
81+
82+
#multi-page-nav .dropdown-label:hover {
83+
color: #fff;
84+
}
85+
86+
#multi-page-nav .nav-dropdown-container {
87+
background-color: #485353;
88+
border: 1px solid #5e6d6f;
89+
border-radius: 5px;
90+
margin-top: -5px;
91+
line-height: 30px;
92+
padding: 5px 0;
93+
94+
position: absolute;
95+
display: none;
96+
}
97+
98+
#multi-page-nav .nav-dropdown-container .nav-item:hover {
99+
background-color: #282f2f;
100+
}
101+
102+
#multi-page-nav .nav-dropdown.nav-expanded .nav-dropdown-container {
103+
display: block;
104+
}
105+
106+
#multi-page-nav .nav-dropdown-container a.nav-link {
107+
color: #ccc;
108+
line-height: 30px;
109+
padding: 0 1em;
110+
text-transform: none;
111+
width: 100%;
112+
}
113+
114+
#multi-page-nav .nav-dropdown-container a.nav-link:hover {
115+
color: #fff;
61116
}
62117

63118
#multi-page-nav a.nav-link {
@@ -121,7 +176,7 @@ html {
121176

122177
#multi-page-nav .nav-item {
123178
line-height: unset;
124-
margin: 0 0 1em 0;
179+
padding: 0 0 1em 0;
125180
width: 100%;
126181
}
127182

@@ -141,6 +196,11 @@ html {
141196
-webkit-transition-duration: 0.7s;
142197
}
143198

199+
#multi-page-nav .nav-dropdown-container {
200+
position: relative;
201+
margin-bottom: 1em;
202+
}
203+
144204
#documenter .docs-main header.docs-navbar.headroom--unpinned.headroom--not-top.headroom--not-bottom {
145205
top: -100vh;
146206
}

assets/multidoc_injector.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,10 @@ require(["jquery"], function ($) {
1515
.getElementById("nav-items")
1616
.classList.toggle("hidden-on-mobile");
1717
});
18+
Array.prototype.map.call(document.getElementsByClassName("dropdown-label"), function (el) {
19+
el.addEventListener("click", function () {
20+
el.parentElement.classList.toggle("nav-expanded")
21+
})
22+
})
1823
});
1924
});

sample.png

26.3 KB
Loading

src/MultiDocumenter.jl

Lines changed: 112 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,26 @@ struct MultiDocRef
2020

2121
path::String
2222
name::String
23+
24+
# these are not actually used internally
25+
giturl::String
26+
branch::String
27+
end
28+
29+
function MultiDocRef(; upstream, name, path, giturl = "", branch = "gh-pages")
30+
MultiDocRef(upstream, path, name, giturl, branch)
31+
end
32+
33+
struct DropdownNav
34+
name::String
35+
children::Vector{MultiDocRef}
2336
end
2437

2538
struct BrandImage
2639
path::String
2740
imagepath::String
2841
end
2942

30-
function MultiDocRef(; upstream, name, path)
31-
MultiDocRef(upstream, path, name)
32-
end
33-
3443
function walk_outputs(f, root, docs::Vector{MultiDocRef}, dirs::Vector{String})
3544
for ref in docs
3645
p = joinpath(root, ref.path)
@@ -78,16 +87,17 @@ Aggregates multiple Documenter.jl-based documentation pages `docs` into `outdir`
7887
"""
7988
function make(
8089
outdir,
81-
docs::Vector{MultiDocRef};
90+
docs::Vector;
8291
assets_dir = nothing,
8392
brand_image::Union{Nothing,BrandImage} = nothing,
8493
custom_stylesheets = [],
8594
custom_scripts = [],
8695
search_engine = DEFAULT_ENGINE,
87-
prettyurls = true
96+
prettyurls = true,
8897
)
98+
maybe_clone(flatten_multidocrefs(docs))
8999

90-
dir = make_output_structure(docs, prettyurls)
100+
dir = make_output_structure(flatten_multidocrefs(docs), prettyurls)
91101
out_assets = joinpath(dir, "assets")
92102
if assets_dir !== nothing && isdir(assets_dir)
93103
cp(assets_dir, out_assets)
@@ -109,11 +119,11 @@ function make(
109119
custom_stylesheets,
110120
custom_scripts,
111121
search_engine,
112-
prettyurls
122+
prettyurls,
113123
)
114124

115125
if search_engine != false
116-
search_engine.engine.build_search_index(dir, docs, search_engine)
126+
search_engine.engine.build_search_index(dir, flatten_multidocrefs(docs), search_engine)
117127
end
118128

119129
cp(dir, outdir; force = true)
@@ -122,7 +132,30 @@ function make(
122132
return outdir
123133
end
124134

125-
function make_output_structure(docs::Vector, prettyurls)
135+
function flatten_multidocrefs(docs::Vector)
136+
out = MultiDocRef[]
137+
for doc in docs
138+
if doc isa MultiDocRef
139+
push!(out, doc)
140+
else
141+
for doc in doc.children
142+
push!(out, doc)
143+
end
144+
end
145+
end
146+
out
147+
end
148+
149+
function maybe_clone(docs::Vector{MultiDocRef})
150+
for doc in docs
151+
if !isdir(doc.upstream)
152+
@info "Upstream at $(doc.upstream) does not exist. `git clone`ing `$(doc.giturl)#$(doc.branch)`"
153+
run(`git clone --depth 1 $(doc.giturl) --branch $(doc.branch) --single-branch $(doc.upstream)`)
154+
end
155+
end
156+
end
157+
158+
function make_output_structure(docs::Vector{MultiDocRef}, prettyurls)
126159
dir = mktempdir()
127160

128161
for doc in docs
@@ -131,7 +164,7 @@ function make_output_structure(docs::Vector, prettyurls)
131164

132165
gitpath = joinpath(outpath, ".git")
133166
if isdir(gitpath)
134-
rm(gitpath, recursive=true)
167+
rm(gitpath, recursive = true)
135168
end
136169
end
137170

@@ -148,7 +181,14 @@ function make_output_structure(docs::Vector, prettyurls)
148181
return dir
149182
end
150183

151-
function make_global_nav(dir, docs, thispagepath, brand_image, search_engine, prettyurls)
184+
function make_global_nav(
185+
dir,
186+
docs::Vector,
187+
thispagepath,
188+
brand_image,
189+
search_engine,
190+
prettyurls,
191+
)
152192
nav = Gumbo.HTMLElement{:nav}([], Gumbo.NullNode(), Dict("id" => "multi-page-nav"))
153193

154194
if brand_image !== nothing
@@ -180,19 +220,51 @@ function make_global_nav(dir, docs, thispagepath, brand_image, search_engine, pr
180220
push!(nav.children, navitems)
181221

182222
for doc in docs
183-
rp = relpath(joinpath(dir, doc.path), thispagepath)
184-
a = Gumbo.HTMLElement{:a}(
185-
[],
186-
navitems,
187-
Dict(
188-
"href" => string(rp, prettyurls ? "/" : "/index.html"),
189-
"class" =>
190-
startswith(thispagepath, joinpath(dir, doc.path, "")) ? # need to force a trailing pathsep here
191-
"nav-link active nav-item" : "nav-link nav-item",
192-
),
193-
)
194-
push!(a.children, Gumbo.HTMLText(a, doc.name))
195-
push!(navitems.children, a)
223+
if doc isa MultiDocRef
224+
rp = relpath(joinpath(dir, doc.path), thispagepath)
225+
a = Gumbo.HTMLElement{:a}(
226+
[],
227+
navitems,
228+
Dict(
229+
"href" => string(rp, prettyurls ? "/" : "/index.html"),
230+
"class" =>
231+
startswith(thispagepath, joinpath(dir, doc.path, "")) ? # need to force a trailing pathsep here
232+
"nav-link active nav-item" : "nav-link nav-item",
233+
),
234+
)
235+
push!(a.children, Gumbo.HTMLText(a, doc.name))
236+
push!(navitems.children, a)
237+
else # doc isa DropdownNav
238+
div = Gumbo.HTMLElement{:div}(
239+
[],
240+
navitems,
241+
Dict("class" => "nav-dropdown"),
242+
)
243+
span = Gumbo.HTMLElement{:span}([], div, Dict("class" => "nav-item dropdown-label"))
244+
push!(div.children, span)
245+
push!(span.children, Gumbo.HTMLText(div, doc.name))
246+
ul = Gumbo.HTMLElement{:ul}([], div, Dict("class" => "nav-dropdown-container"))
247+
push!(div.children, ul)
248+
push!(navitems.children, div)
249+
250+
for doc in doc.children
251+
rp = relpath(joinpath(dir, doc.path), thispagepath)
252+
li = Gumbo.HTMLElement{:li}([], ul, Dict())
253+
a = Gumbo.HTMLElement{:a}(
254+
[],
255+
li,
256+
Dict(
257+
"href" => string(rp, prettyurls ? "/" : "/index.html"),
258+
"class" =>
259+
startswith(thispagepath, joinpath(dir, doc.path, "")) ? # need to force a trailing pathsep here
260+
"nav-link active nav-item" : "nav-link nav-item",
261+
),
262+
)
263+
push!(a.children, Gumbo.HTMLText(a, doc.name))
264+
push!(li.children, a)
265+
push!(ul.children, li)
266+
end
267+
end
196268
end
197269
if search_engine != false
198270
search_engine.engine.inject_html!(navitems)
@@ -249,12 +321,12 @@ end
249321

250322
function inject_styles_and_global_navigation(
251323
dir,
252-
docs::Vector{MultiDocRef},
324+
docs::Vector,
253325
brand_image,
254326
custom_stylesheets,
255327
custom_scripts,
256328
search_engine,
257-
prettyurls
329+
prettyurls,
258330
)
259331

260332
if search_engine != false
@@ -283,7 +355,10 @@ function inject_styles_and_global_navigation(
283355

284356

285357
page = read(path, String)
286-
if startswith(page, "<!--This file is automatically generated by Documenter.jl-->")
358+
if startswith(
359+
page,
360+
"<!--This file is automatically generated by Documenter.jl-->",
361+
)
287362
continue
288363
end
289364
doc = Gumbo.parsehtml(page)
@@ -307,12 +382,18 @@ function inject_styles_and_global_navigation(
307382
documenter_div = first(el.children)
308383
if documenter_div isa Gumbo.HTMLElement &&
309384
Gumbo.getattr(documenter_div, "id", "") == "documenter"
310-
@debug "Could not detect Documenter page layout in $path. This may be due to an old version of Documenter."
385+
@debug "Could not detect Documenter page layout in $path. This may be due to an old version of Documenter."
311386
end
312387
# inject global navigation as first element in body
313388

314-
global_nav =
315-
make_global_nav(dir, docs, root, brand_image, search_engine, prettyurls)
389+
global_nav = make_global_nav(
390+
dir,
391+
docs,
392+
root,
393+
brand_image,
394+
search_engine,
395+
prettyurls,
396+
)
316397
global_nav.parent = el
317398
pushfirst!(el.children, global_nav)
318399
injected += 1

0 commit comments

Comments
 (0)