Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Table of Contents element #30

Merged
merged 23 commits into from
Dec 29, 2020
Merged
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions src/Builtins.jl
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,160 @@ function show(io::IO, ::MIME"text/html", radio::Radio)
end

get(radio::Radio) = radio.default


"""Generate Table of Contents using Markdown cells. Headers h1-h6 are used.

`title` header to this element, defaults to "Table of Contents"

`depth` value to limit the header elements, in range 1 to 6, defaults to 3

`aside` fix the element to right, defaults to true

`indent` flag indicating whether to vertically align elements as per heirarchy


# Examples
`@bind TableOfContents()`

`@bind TableOfContents(title="Experiments", depth=1)`

`@bind TableOfContents(title="సూ చి క", indent=true)`

`@bind TableOfContents(aside=false)`

"""
struct TableOfContents
title::AbstractString
depth::Int
aside::Bool
indent::Bool
end
TableOfContents(;title::AbstractString="Table of Contents", depth::Int=3, aside::Bool=true, indent::Bool=true) = TableOfContents(title, depth, aside, indent)

function show(io::IO, ::MIME"text/html", toc::TableOfContents)

if length(toc.title) > 0
print(io, """<div class="title">$(toc.title)</div>""")
end

withtag(io, :script) do
print(io, """
const getParentCellId = el => {
// Traverse up the DOM tree until you reach a pluto-cell
while (el.nodeName != 'PLUTO-CELL') {
el = el.parentNode;
if (!el) return null;
}
return el.id;
}

const getElementsByNodename = nodeName => Array.from(
document.querySelectorAll(
"pluto-notebook pluto-output " + nodeName
)
).map(el => {
return {
"nodeName" : el.nodeName,
"parentCellId": getParentCellId(el),
"innerText": el.innerText
}
})

const plutoCellIds = Array.from(
document.querySelectorAll(
"pluto-notebook pluto-cell"
)
).map(el => el.id)

const depth = Math.max(1, Math.min(6, $(toc.depth))) // should be in range 1:6
const range = Array.from({length: depth}, (x, i) => i+1) // [1, ... depth]
var headers = [].concat.apply([], range.map(i => getElementsByNodename("h"+i))); // flatten [[h1s...], [h2s...], ...]
headers.sort((a,b) => plutoCellIds.indexOf(a.parentCellId) - plutoCellIds.indexOf(b.parentCellId)); // sort in the order of appearance

return html`<div class="toc">
<div class="markdown">
<div class="admonition">
<p class="admonition-title">\$(toc.title)</p>
<p class="toc-content">
\${headers.map(h => html`
<div>
<a class="\${h.nodeName}"
href="#\${h.parentCellId}"
onmouseover="(()=>{document.getElementById('\${h.parentCellId}').firstElementChild.classList.add('pluto-shoulder-hover')})()"
onmouseout="(()=>{document.getElementById('\${h.parentCellId}').firstElementChild.classList.remove('pluto-shoulder-hover')})()">
\${h.innerText}
</a>
</div>`
)}
</p>
</div>
</div>
</div>`
""")
end

withtag(io, :style) do
print(io, """
a {
text-decoration: none;
font-weight: normal;
color: gray;
}

a:hover {
color: black;
}

.title{
display: block;
font-size: 2em;
margin-top: 0.67em;
margin-bottom: 0.67em;
margin-left: 0;
margin-right: 0;
font-weight: bold;
}
""")

if toc.aside
print(io, """
@media screen and (max-width: 1081px) {
.toc {
}
}

@media screen and (min-width: 1081px) {
.toc {
position:fixed; right:20px; top:60px; width:25%;
}
}
""")
end

if toc.indent
print(io, """
a.H1 {
padding: 0px 0px;
}
a.H2 {
padding: 0px 10px;
}
a.H3 {
padding: 0px 20px;
}
a.H4 {
padding: 0px 30px;
}
a.H5 {
padding: 0px 40px;
}
a.H6 {
padding: 0px 50px;
}"""
)
end
end
end

get(toc::TableOfContents) = toc.default