From fac91ae7960debef44dd9e3be07ce29759bcccf8 Mon Sep 17 00:00:00 2001 From: Sebastian Pfitzner Date: Tue, 11 Oct 2022 15:33:38 +0200 Subject: [PATCH] feat: mega dropdown nave and switch to htl for internals --- Project.toml | 1 + assets/default/multidoc.css | 37 +++++++++-- assets/multidoc_injector.js | 16 +++-- src/MultiDocumenter.jl | 119 ++++++++++-------------------------- src/renderers.jl | 65 ++++++++++++++++++++ src/search/flexsearch.jl | 32 +++------- src/search/stork.jl | 33 +++------- test/runtests.jl | 32 +++++++++- 8 files changed, 192 insertions(+), 143 deletions(-) create mode 100644 src/renderers.jl diff --git a/Project.toml b/Project.toml index f4f1e43c..8ef3fafb 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.2.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" Gumbo = "708ec375-b3d6-5a57-a7ce-8257bf98657a" +HypertextLiteral = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" NodeJS = "2bd173c7-0d6d-553b-b6af-13a54713934c" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" diff --git a/assets/default/multidoc.css b/assets/default/multidoc.css index 80779069..ec943d1e 100644 --- a/assets/default/multidoc.css +++ b/assets/default/multidoc.css @@ -66,7 +66,7 @@ html { text-transform: uppercase; } -#multi-page-nav .nav-dropdown .dropdown-label::after { +#multi-page-nav .dropdown-label::after { content: ""; margin-left: 5px; @@ -99,10 +99,32 @@ html { background-color: #282f2f; } -#multi-page-nav .nav-dropdown.nav-expanded .nav-dropdown-container { +#multi-page-nav .nav-expanded .nav-dropdown-container { display: block; } +#multi-page-nav .nav-mega-dropdown-container { + width: 100vw; + left: 0; + padding: 1em 2em; + border-radius: 0; + border: none; + flex-wrap: wrap; +} + +#multi-page-nav .nav-mega-dropdown-container .nav-mega-column { + margin: 0 2em; +} + +#multi-page-nav .nav-mega-dropdown-container .column-header { + color: #fff; + text-transform: uppercase; +} + +#multi-page-nav .nav-expanded .nav-mega-dropdown-container { + display: flex; +} + #multi-page-nav .nav-dropdown-container a.nav-link { color: #ccc; line-height: 30px; @@ -129,8 +151,6 @@ html { font-weight: bold; } -/* Search */ - /* Documenter css tweaks */ #documenter .docs-main header.docs-navbar { @@ -201,6 +221,15 @@ html { margin-bottom: 1em; } + #multi-page-nav .nav-mega-dropdown-container { + width: unset; + padding: 1em; + } + + #multi-page-nav .nav-mega-dropdown-container .nav-mega-column { + margin: 0 1em; + } + #documenter .docs-main header.docs-navbar.headroom--unpinned.headroom--not-top.headroom--not-bottom { top: -100vh; } diff --git a/assets/multidoc_injector.js b/assets/multidoc_injector.js index 7505bce1..09a0b872 100644 --- a/assets/multidoc_injector.js +++ b/assets/multidoc_injector.js @@ -15,10 +15,18 @@ require(["jquery"], function ($) { .getElementById("nav-items") .classList.toggle("hidden-on-mobile"); }); - Array.prototype.map.call(document.getElementsByClassName("dropdown-label"), function (el) { - el.addEventListener("click", function () { - el.parentElement.classList.toggle("nav-expanded") + document.body.addEventListener("click", function (ev) { + console.log(ev) + if (!ev.target.matches(".nav-dropdown-container")) { + console.log("closing") + Array.prototype.forEach.call(document.getElementsByClassName("dropdown-label"), function (el) { + el.parentElement.classList.remove("nav-expanded") + }); + } + if (ev.target.matches(".dropdown-label")) { + console.log("opening") + ev.target.parentElement.classList.add("nav-expanded") + } }) - }) }); }); diff --git a/src/MultiDocumenter.jl b/src/MultiDocumenter.jl index f3f08419..82652ee1 100644 --- a/src/MultiDocumenter.jl +++ b/src/MultiDocumenter.jl @@ -1,6 +1,7 @@ module MultiDocumenter import Gumbo, AbstractTrees +using HypertextLiteral """ SearchConfig(index_versions = ["stable"], engine = MultiDocumenter.FlexSearch) @@ -35,6 +36,16 @@ struct DropdownNav children::Vector{MultiDocRef} end +struct Column + name + children::Vector{MultiDocRef} +end + +struct MegaDropdownNav + name + columns::Vector{Column} +end + struct BrandImage path::String imagepath::String @@ -58,6 +69,7 @@ function walk_outputs(f, root, docs::Vector{MultiDocRef}, dirs::Vector{String}) end end +include("renderers.jl") include("search/flexsearch.jl") include("search/stork.jl") @@ -137,6 +149,12 @@ function flatten_multidocrefs(docs::Vector) for doc in docs if doc isa MultiDocRef push!(out, doc) + elseif doc isa MegaDropdownNav + for col in doc.columns + for doc in col.children + push!(out, doc) + end + end else for doc in doc.children push!(out, doc) @@ -160,7 +178,7 @@ function make_output_structure(docs::Vector{MultiDocRef}, prettyurls) for doc in docs outpath = joinpath(dir, doc.path) - cp(doc.upstream, outpath) + cp(doc.upstream, outpath; force = true) gitpath = joinpath(outpath, ".git") if isdir(gitpath) @@ -181,32 +199,6 @@ function make_output_structure(docs::Vector{MultiDocRef}, prettyurls) return dir end -function insert_multidocref_html!(navitems, doc, dir, thispagepath, prettyurls) - path = joinpath(dir, doc.path) - if !isfile(joinpath(path, "index.html")) - stable = joinpath(path, "stable") - dev = joinpath(path, "dev") - if isfile(joinpath(stable, "index.html")) - path = stable - elseif isfile(joinpath(dev, "index.html")) - path = dev - end - end - rp = relpath(path, thispagepath) - a = Gumbo.HTMLElement{:a}( - [], - navitems, - Dict( - "href" => string(rp, prettyurls ? "/" : "/index.html"), - "class" => - startswith(thispagepath, joinpath(dir, doc.path, "")) ? # need to force a trailing pathsep here - "nav-link active nav-item" : "nav-link nav-item", - ), - ) - push!(a.children, Gumbo.HTMLText(a, doc.name)) - push!(navitems.children, a) -end - function make_global_nav( dir, docs::Vector, @@ -215,67 +207,18 @@ function make_global_nav( search_engine, prettyurls, ) - nav = Gumbo.HTMLElement{:nav}([], Gumbo.NullNode(), Dict("id" => "multi-page-nav")) - - if brand_image !== nothing - a = Gumbo.HTMLElement{:a}( - [], - nav, - Dict( - "class" => "brand", - "href" => relpath(joinpath(dir, brand_image.path), thispagepath), - ), - ) - img = Gumbo.HTMLElement{:img}( - [], - a, - Dict( - "src" => relpath(joinpath(dir, brand_image.imagepath), thispagepath), - "alt" => "Home", - ), - ) - push!(a.children, img) - push!(nav.children, a) - end - - navitems = Gumbo.HTMLElement{:div}( - [], - nav, - Dict("id" => "nav-items", "class" => "hidden-on-mobile"), - ) - push!(nav.children, navitems) - - for doc in docs - if doc isa MultiDocRef - insert_multidocref_html!(navitems, doc, dir, thispagepath, prettyurls) - else # doc isa DropdownNav - div = Gumbo.HTMLElement{:div}( - [], - navitems, - Dict("class" => "nav-dropdown"), - ) - span = Gumbo.HTMLElement{:span}([], div, Dict("class" => "nav-item dropdown-label")) - push!(div.children, span) - push!(span.children, Gumbo.HTMLText(div, doc.name)) - ul = Gumbo.HTMLElement{:ul}([], div, Dict("class" => "nav-dropdown-container")) - push!(div.children, ul) - push!(navitems.children, div) - - for doc in doc.children - li = Gumbo.HTMLElement{:li}([], ul, Dict()) - insert_multidocref_html!(li, doc, dir, thispagepath, prettyurls) - push!(ul.children, li) - end - end - end - if search_engine != false - search_engine.engine.inject_html!(navitems) - end - - toggler = Gumbo.HTMLElement{:a}([], nav, Dict("id" => "multidoc-toggler")) - push!(nav.children, toggler) - - return nav + nav = @htl """ + + """ + + return htl_to_gumbo(nav) end function make_global_stylesheet(custom_stylesheets, path) diff --git a/src/renderers.jl b/src/renderers.jl new file mode 100644 index 00000000..f0c0cc91 --- /dev/null +++ b/src/renderers.jl @@ -0,0 +1,65 @@ +render(::Nothing, args...) = nothing + +function render(brand_image::BrandImage, dir, thispagepath) + return @htl """ + + home + """ +end + +function render(doc::MultiDocRef, dir, thispagepath, prettyurls) + path = joinpath(dir, doc.path) + if !isfile(joinpath(path, "index.html")) + stable = joinpath(path, "stable") + dev = joinpath(path, "dev") + if isfile(joinpath(stable, "index.html")) + path = stable + elseif isfile(joinpath(dev, "index.html")) + path = dev + end + end + rp = relpath(path, thispagepath) + href = string(rp, prettyurls ? "/" : "/index.html") + # need to force a trailing pathsep here + class = startswith(thispagepath, joinpath(dir, doc.path, "")) ? + "nav-link active nav-item" : "nav-link nav-item" + + return @htl """ + $(doc.name) + """ +end + +function render(doc::DropdownNav, dir, thispagepath, prettyurls) + return @htl """ + + """ +end + +function render(doc::MegaDropdownNav, dir, thispagepath, prettyurls) + return @htl """ + + """ +end + +function render(doc::Column, dir, thispagepath, prettyurls) + return @htl """ + + """ +end + +htl_to_gumbo(htl) = Gumbo.parsehtml(sprint(show, htl)).root.children[2].children[1] diff --git a/src/search/flexsearch.jl b/src/search/flexsearch.jl index 043feb81..2cfb3f0c 100644 --- a/src/search/flexsearch.jl +++ b/src/search/flexsearch.jl @@ -1,5 +1,6 @@ module FlexSearch import Gumbo, JSON, AbstractTrees, NodeJS +using HypertextLiteral import ..walk_outputs const ID = Ref(0) @@ -101,28 +102,15 @@ function inject_styles!(custom_styles) pushfirst!(custom_styles, joinpath("assets", "default", "flexsearch.css")) end -function inject_html!(parent) - div = Gumbo.HTMLElement{:div}([], parent, Dict("class" => "search nav-item")) - push!(parent.children, div) - input = Gumbo.HTMLElement{:input}( - [], - div, - Dict("id" => "search-input", "placeholder" => "Search..."), - ) - push!(div.children, input) - suggestions = Gumbo.HTMLElement{:ul}( - [], - div, - Dict("id" => "search-result-container", "class" => "suggestions hidden"), - ) - push!(div.children, suggestions) - keybinding = Gumbo.HTMLElement{:div}( - [], - div, - Dict("class" => "search-keybinding"), - ) - push!(keybinding.children, Gumbo.HTMLText(keybinding, "/")) - push!(div.children, keybinding) +function render() + return @htl """ + + """ end function to_json_index(index::SearchIndex, file) diff --git a/src/search/stork.jl b/src/search/stork.jl index 718fd37a..a362b565 100644 --- a/src/search/stork.jl +++ b/src/search/stork.jl @@ -1,6 +1,7 @@ module Stork import Pkg.TOML import Gumbo, AbstractTrees +using HypertextLiteral import ..walk_outputs function has_stork() @@ -69,29 +70,13 @@ function inject_styles!(custom_styles) pushfirst!(custom_styles, joinpath("assets", "default", "stork.css")) end -function inject_html!(parent) - div = Gumbo.HTMLElement{:div}( - [], - parent, - Dict("class" => "search stork-wrapper nav-item"), - ) - push!(parent.children, div) - input = Gumbo.HTMLElement{:input}( - [], - div, - Dict( - "id" => "search-input", - "class" => "stork-input", - "data-stork" => "multidocumenter", - "placeholder" => "Search...", - ), - ) - push!(div.children, input) - suggestions = Gumbo.HTMLElement{:div}( - [], - div, - Dict("class" => "stork-output", "data-stork" => "multidocumenter-output"), - ) - push!(div.children, suggestions) +function render() + return @htl """ + + """ end end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 35513794..094b3775 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,6 +19,36 @@ using Test giturl = "https://github.com/JuliaDebug/JuliaInterpreter.jl.git", ), ]), + MultiDocumenter.MegaDropdownNav("Mega Debugger", [ + MultiDocumenter.Column("Colum 1", [ + MultiDocumenter.MultiDocRef( + upstream = joinpath(clonedir, "Infiltrator"), + path = "inf", + name = "Infiltrator", + giturl = "https://github.com/JuliaDebug/Infiltrator.jl.git", + ), + MultiDocumenter.MultiDocRef( + upstream = joinpath(clonedir, "JuliaInterpreter"), + path = "debug", + name = "JuliaInterpreter", + giturl = "https://github.com/JuliaDebug/JuliaInterpreter.jl.git", + ), + ]), + MultiDocumenter.Column("Colum 2", [ + MultiDocumenter.MultiDocRef( + upstream = joinpath(clonedir, "Infiltrator"), + path = "inf", + name = "Infiltrator", + giturl = "https://github.com/JuliaDebug/Infiltrator.jl.git", + ), + MultiDocumenter.MultiDocRef( + upstream = joinpath(clonedir, "JuliaInterpreter"), + path = "debug", + name = "JuliaInterpreter", + giturl = "https://github.com/JuliaDebug/JuliaInterpreter.jl.git", + ), + ]), + ]), MultiDocumenter.MultiDocRef( upstream = joinpath(clonedir, "DataSets"), path = "data", @@ -59,6 +89,6 @@ using Test @test !occursin("/inf/dev/", store_content) end - rm(outpath, recursive=true, force=true) + # rm(outpath, recursive=true, force=true) rm(clonedir, recursive=true, force=true) end