Skip to content

Commit 7effca9

Browse files
committed
feat: mega dropdown nav
and switch to htl for internals
1 parent 21a3098 commit 7effca9

File tree

8 files changed

+191
-142
lines changed

8 files changed

+191
-142
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ version = "0.2.0"
66
[deps]
77
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
88
Gumbo = "708ec375-b3d6-5a57-a7ce-8257bf98657a"
9+
HypertextLiteral = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
910
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
1011
NodeJS = "2bd173c7-0d6d-553b-b6af-13a54713934c"
1112
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"

assets/default/multidoc.css

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ html {
6666
text-transform: uppercase;
6767
}
6868

69-
#multi-page-nav .nav-dropdown .dropdown-label::after {
69+
#multi-page-nav .dropdown-label::after {
7070
content: "";
7171
margin-left: 5px;
7272

@@ -99,10 +99,32 @@ html {
9999
background-color: #282f2f;
100100
}
101101

102-
#multi-page-nav .nav-dropdown.nav-expanded .nav-dropdown-container {
102+
#multi-page-nav .nav-expanded .nav-dropdown-container {
103103
display: block;
104104
}
105105

106+
#multi-page-nav .nav-mega-dropdown-container {
107+
width: 100vw;
108+
left: 0;
109+
padding: 1em 2em;
110+
border-radius: 0;
111+
border: none;
112+
flex-wrap: wrap;
113+
}
114+
115+
#multi-page-nav .nav-mega-dropdown-container .nav-mega-column {
116+
margin: 0 2em;
117+
}
118+
119+
#multi-page-nav .nav-mega-dropdown-container .column-header {
120+
color: #fff;
121+
text-transform: uppercase;
122+
}
123+
124+
#multi-page-nav .nav-expanded .nav-mega-dropdown-container {
125+
display: flex;
126+
}
127+
106128
#multi-page-nav .nav-dropdown-container a.nav-link {
107129
color: #ccc;
108130
line-height: 30px;
@@ -129,8 +151,6 @@ html {
129151
font-weight: bold;
130152
}
131153

132-
/* Search */
133-
134154
/* Documenter css tweaks */
135155

136156
#documenter .docs-main header.docs-navbar {
@@ -201,6 +221,15 @@ html {
201221
margin-bottom: 1em;
202222
}
203223

224+
#multi-page-nav .nav-mega-dropdown-container {
225+
width: unset;
226+
padding: 1em;
227+
}
228+
229+
#multi-page-nav .nav-mega-dropdown-container .nav-mega-column {
230+
margin: 0 1em;
231+
}
232+
204233
#documenter .docs-main header.docs-navbar.headroom--unpinned.headroom--not-top.headroom--not-bottom {
205234
top: -100vh;
206235
}

assets/multidoc_injector.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,18 @@ 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")
18+
document.body.addEventListener("click", function (ev) {
19+
console.log(ev)
20+
if (!ev.target.matches(".nav-dropdown-container")) {
21+
console.log("closing")
22+
Array.prototype.forEach.call(document.getElementsByClassName("dropdown-label"), function (el) {
23+
el.parentElement.classList.remove("nav-expanded")
24+
});
25+
}
26+
if (ev.target.matches(".dropdown-label")) {
27+
console.log("opening")
28+
ev.target.parentElement.classList.add("nav-expanded")
29+
}
2130
})
22-
})
2331
});
2432
});

src/MultiDocumenter.jl

Lines changed: 31 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module MultiDocumenter
22

33
import Gumbo, AbstractTrees
4+
using HypertextLiteral
45

56
"""
67
SearchConfig(index_versions = ["stable"], engine = MultiDocumenter.FlexSearch)
@@ -35,6 +36,16 @@ struct DropdownNav
3536
children::Vector{MultiDocRef}
3637
end
3738

39+
struct Column
40+
name
41+
children::Vector{MultiDocRef}
42+
end
43+
44+
struct MegaDropdownNav
45+
name
46+
columns::Vector{Column}
47+
end
48+
3849
struct BrandImage
3950
path::String
4051
imagepath::String
@@ -58,6 +69,7 @@ function walk_outputs(f, root, docs::Vector{MultiDocRef}, dirs::Vector{String})
5869
end
5970
end
6071

72+
include("renderers.jl")
6173
include("search/flexsearch.jl")
6274
include("search/stork.jl")
6375

@@ -137,6 +149,12 @@ function flatten_multidocrefs(docs::Vector)
137149
for doc in docs
138150
if doc isa MultiDocRef
139151
push!(out, doc)
152+
elseif doc isa MegaDropdownNav
153+
for col in doc.columns
154+
for doc in col.children
155+
push!(out, doc)
156+
end
157+
end
140158
else
141159
for doc in doc.children
142160
push!(out, doc)
@@ -160,7 +178,7 @@ function make_output_structure(docs::Vector{MultiDocRef}, prettyurls)
160178

161179
for doc in docs
162180
outpath = joinpath(dir, doc.path)
163-
cp(doc.upstream, outpath)
181+
cp(doc.upstream, outpath; force = true)
164182

165183
gitpath = joinpath(outpath, ".git")
166184
if isdir(gitpath)
@@ -181,32 +199,6 @@ function make_output_structure(docs::Vector{MultiDocRef}, prettyurls)
181199
return dir
182200
end
183201

184-
function insert_multidocref_html!(navitems, doc, dir, thispagepath, prettyurls)
185-
path = joinpath(dir, doc.path)
186-
if !isfile(joinpath(path, "index.html"))
187-
stable = joinpath(path, "stable")
188-
dev = joinpath(path, "dev")
189-
if isfile(joinpath(stable, "index.html"))
190-
path = stable
191-
elseif isfile(joinpath(dev, "index.html"))
192-
path = dev
193-
end
194-
end
195-
rp = relpath(path, thispagepath)
196-
a = Gumbo.HTMLElement{:a}(
197-
[],
198-
navitems,
199-
Dict(
200-
"href" => string(rp, prettyurls ? "/" : "/index.html"),
201-
"class" =>
202-
startswith(thispagepath, joinpath(dir, doc.path, "")) ? # need to force a trailing pathsep here
203-
"nav-link active nav-item" : "nav-link nav-item",
204-
),
205-
)
206-
push!(a.children, Gumbo.HTMLText(a, doc.name))
207-
push!(navitems.children, a)
208-
end
209-
210202
function make_global_nav(
211203
dir,
212204
docs::Vector,
@@ -215,67 +207,18 @@ function make_global_nav(
215207
search_engine,
216208
prettyurls,
217209
)
218-
nav = Gumbo.HTMLElement{:nav}([], Gumbo.NullNode(), Dict("id" => "multi-page-nav"))
219-
220-
if brand_image !== nothing
221-
a = Gumbo.HTMLElement{:a}(
222-
[],
223-
nav,
224-
Dict(
225-
"class" => "brand",
226-
"href" => relpath(joinpath(dir, brand_image.path), thispagepath),
227-
),
228-
)
229-
img = Gumbo.HTMLElement{:img}(
230-
[],
231-
a,
232-
Dict(
233-
"src" => relpath(joinpath(dir, brand_image.imagepath), thispagepath),
234-
"alt" => "Home",
235-
),
236-
)
237-
push!(a.children, img)
238-
push!(nav.children, a)
239-
end
240-
241-
navitems = Gumbo.HTMLElement{:div}(
242-
[],
243-
nav,
244-
Dict("id" => "nav-items", "class" => "hidden-on-mobile"),
245-
)
246-
push!(nav.children, navitems)
247-
248-
for doc in docs
249-
if doc isa MultiDocRef
250-
insert_multidocref_html!(navitems, doc, dir, thispagepath, prettyurls)
251-
else # doc isa DropdownNav
252-
div = Gumbo.HTMLElement{:div}(
253-
[],
254-
navitems,
255-
Dict("class" => "nav-dropdown"),
256-
)
257-
span = Gumbo.HTMLElement{:span}([], div, Dict("class" => "nav-item dropdown-label"))
258-
push!(div.children, span)
259-
push!(span.children, Gumbo.HTMLText(div, doc.name))
260-
ul = Gumbo.HTMLElement{:ul}([], div, Dict("class" => "nav-dropdown-container"))
261-
push!(div.children, ul)
262-
push!(navitems.children, div)
263-
264-
for doc in doc.children
265-
li = Gumbo.HTMLElement{:li}([], ul, Dict())
266-
insert_multidocref_html!(li, doc, dir, thispagepath, prettyurls)
267-
push!(ul.children, li)
268-
end
269-
end
270-
end
271-
if search_engine != false
272-
search_engine.engine.inject_html!(navitems)
273-
end
274-
275-
toggler = Gumbo.HTMLElement{:a}([], nav, Dict("id" => "multidoc-toggler"))
276-
push!(nav.children, toggler)
277-
278-
return nav
210+
nav = @htl """
211+
<nav id="multi-page-nav">
212+
$(render(brand_image, dir, thispagepath))
213+
<div id="nav-items" class="hidden-on-mobile">
214+
$([render(doc, dir, thispagepath, prettyurls) for doc in docs])
215+
$(search_engine.engine.render())
216+
</div>
217+
<a id="multidoc-toggler"></a>
218+
</nav>
219+
"""
220+
221+
return htl_to_gumbo(nav)
279222
end
280223

281224
function make_global_stylesheet(custom_stylesheets, path)

src/renderers.jl

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
render(::Nothing, args...) = nothing
2+
3+
function render(brand_image::BrandImage, dir, thispagepath)
4+
return @htl """
5+
<a class="brand" href="$(relpath(joinpath(dir, brand_image.path), thispagepath))">
6+
<img src="$(relpath(joinpath(dir, brand_image.imagepath), thispagepath))" alt="home">
7+
</a>"""
8+
end
9+
10+
function render(doc::MultiDocRef, dir, thispagepath, prettyurls)
11+
path = joinpath(dir, doc.path)
12+
if !isfile(joinpath(path, "index.html"))
13+
stable = joinpath(path, "stable")
14+
dev = joinpath(path, "dev")
15+
if isfile(joinpath(stable, "index.html"))
16+
path = stable
17+
elseif isfile(joinpath(dev, "index.html"))
18+
path = dev
19+
end
20+
end
21+
rp = relpath(path, thispagepath)
22+
href = string(rp, prettyurls ? "/" : "/index.html")
23+
# need to force a trailing pathsep here
24+
class = startswith(thispagepath, joinpath(dir, doc.path, "")) ?
25+
"nav-link active nav-item" : "nav-link nav-item"
26+
27+
return @htl """
28+
<a href="$href" class="$class">$(doc.name)</a>
29+
"""
30+
end
31+
32+
function render(doc::DropdownNav, dir, thispagepath, prettyurls)
33+
return @htl """
34+
<div class="nav-dropown">
35+
<span class="nav-item dropdown-label">$(doc.name)</span>
36+
<ul class="nav-dropdown-container">
37+
$([render(doc, dir, thispagepath, prettyurls) for doc in doc.children])
38+
</ul>
39+
</div>
40+
"""
41+
end
42+
43+
function render(doc::MegaDropdownNav, dir, thispagepath, prettyurls)
44+
return @htl """
45+
<div class="nav-dropown">
46+
<span class="nav-item dropdown-label">$(doc.name)</span>
47+
<div class="nav-dropdown-container nav-mega-dropdown-container">
48+
$([render(doc, dir, thispagepath, prettyurls) for doc in doc.columns])
49+
</div>
50+
</div>
51+
"""
52+
end
53+
54+
function render(doc::Column, dir, thispagepath, prettyurls)
55+
return @htl """
56+
<div class="nav-mega-column">
57+
<h5 class="column-header">$(doc.name)</h5>
58+
<ul class="column-content">
59+
$([render(doc, dir, thispagepath, prettyurls) for doc in doc.children])
60+
</ul>
61+
</div>
62+
"""
63+
end
64+
65+
htl_to_gumbo(htl) = Gumbo.parsehtml(sprint(show, htl)).root.children[2].children[1]

src/search/flexsearch.jl

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module FlexSearch
22
import Gumbo, JSON, AbstractTrees, NodeJS
3+
using HypertextLiteral
34
import ..walk_outputs
45

56
const ID = Ref(0)
@@ -101,28 +102,15 @@ function inject_styles!(custom_styles)
101102
pushfirst!(custom_styles, joinpath("assets", "default", "flexsearch.css"))
102103
end
103104

104-
function inject_html!(parent)
105-
div = Gumbo.HTMLElement{:div}([], parent, Dict("class" => "search nav-item"))
106-
push!(parent.children, div)
107-
input = Gumbo.HTMLElement{:input}(
108-
[],
109-
div,
110-
Dict("id" => "search-input", "placeholder" => "Search..."),
111-
)
112-
push!(div.children, input)
113-
suggestions = Gumbo.HTMLElement{:ul}(
114-
[],
115-
div,
116-
Dict("id" => "search-result-container", "class" => "suggestions hidden"),
117-
)
118-
push!(div.children, suggestions)
119-
keybinding = Gumbo.HTMLElement{:div}(
120-
[],
121-
div,
122-
Dict("class" => "search-keybinding"),
123-
)
124-
push!(keybinding.children, Gumbo.HTMLText(keybinding, "/"))
125-
push!(div.children, keybinding)
105+
function render()
106+
return @htl """
107+
<div class="search nav-item">
108+
<input id="search-input" placeholder="Search...">
109+
<ul id="search-result-container" class="suggestions hidden">
110+
</ul>
111+
<div class="search-keybinding"></div>
112+
</div>
113+
"""
126114
end
127115

128116
function to_json_index(index::SearchIndex, file)

0 commit comments

Comments
 (0)