Skip to content

Commit 1a5b165

Browse files
authored
Merge pull request #14607 from dotty-staging/scaladoc/rhs-table-of-content
Add table of content to static sites
2 parents 21a5401 + cd39061 commit 1a5b165

File tree

21 files changed

+314
-60
lines changed

21 files changed

+314
-60
lines changed

docs/_layouts/doc-page.html

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,5 @@ <h1>{{ page.title }}</h1>
1414
{% endif %}
1515
</header>
1616
{{ content }}
17-
<div class="content-contributors hidden">
18-
<h5>Contributors to this page:</h5>
19-
<div id="documentation-contributors" class="contributors-container"></div>
20-
</div>
2117
</main>
2218

docs/_layouts/static-site-main.html

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,20 @@
3636
{{ content }}
3737
<nav class="arrows-wrapper" aria-label="Page navigation">
3838
{% if page.previous %}
39-
<a rel="prev" href="{{ page.previous }}" class="arrows previous" aria-keyshortcuts="Left">
39+
<a rel="prev" href="{{ page.previous.url }}" class="arrows previous" aria-keyshortcuts="Left">
40+
<span>{{ page.previous.title }}</span>
4041
<i class="fa fa-angle-left"></i>
4142
</a>
4243
{% endif %}
4344
{% if page.next %}
44-
<a rel="next" href="{{ page.next }}" class="arrows next" aria-keyshortcuts="Right">
45+
<a rel="next" href="{{ page.next.url }}" class="arrows next" aria-keyshortcuts="Right">
46+
<span>{{ page.next.title }}</span>
4547
<i class="fa fa-angle-right"></i>
4648
</a>
4749
{% endif %}
4850
</nav>
51+
<div class="content-contributors hidden">
52+
<span><b>Contributors to this page</b></span>
53+
<div id="documentation-contributors" class="contributors-container"></div>
54+
</div>
4955
</div>

project/resources/referenceReplacements/_layouts/static-site-main.html

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,20 @@
3131
{{ content }}
3232
<nav class="arrows-wrapper" aria-label="Page navigation">
3333
{% if page.previous %}
34-
<a rel="prev" href="{{ page.previous }}" class="arrows previous" aria-keyshortcuts="Left">
34+
<a rel="prev" href="{{ page.previous.url }}" class="arrows previous" aria-keyshortcuts="Left">
35+
<span>{{ page.previous.title }}</span>
3536
<i class="fa fa-angle-left"></i>
3637
</a>
3738
{% endif %}
3839
{% if page.next %}
39-
<a rel="next" href="{{ page.next }}" class="arrows next" aria-keyshortcuts="Right">
40+
<a rel="next" href="{{ page.next.url }}" class="arrows next" aria-keyshortcuts="Right">
41+
<span>{{ page.next.title }}</span>
4042
<i class="fa fa-angle-right"></i>
4143
</a>
4244
{% endif %}
4345
</nav>
46+
<div class="content-contributors hidden">
47+
<span><b>Contributors to this page</b></span>
48+
<div id="documentation-contributors" class="contributors-container"></div>
49+
</div>
4450
</div>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<h1>Header 1</h1>
2+
<h2>Header 2</h2>
3+
<h3>Header 3</h3>

scaladoc-testcases/docs/_layouts/static-site-main.html

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,20 @@
99
{{ content }}
1010
<nav class="arrows-wrapper" aria-label="Page navigation">
1111
{% if page.previous %}
12-
<a rel="prev" href="{{ page.previous }}" class="arrows previous" aria-keyshortcuts="Left">
12+
<a rel="prev" href="{{ page.previous.url }}" class="arrows previous" aria-keyshortcuts="Left">
13+
<span>{{ page.previous.title }}</span>
1314
<i class="fa fa-angle-left"></i>
1415
</a>
1516
{% endif %}
1617
{% if page.next %}
17-
<a rel="next" href="{{ page.next }}" class="arrows next" aria-keyshortcuts="Right">
18+
<a rel="next" href="{{ page.next.url }}" class="arrows next" aria-keyshortcuts="Right">
19+
<span>{{ page.next.title }}</span>
1820
<i class="fa fa-angle-right"></i>
1921
</a>
2022
{% endif %}
2123
</nav>
24+
<div class="content-contributors hidden">
25+
<span><b>Contributors to this page</b></span>
26+
<div id="documentation-contributors" class="contributors-container"></div>
27+
</div>
2228
</div>

scaladoc-testcases/docs/sidebar.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ subsection:
44
- page: docs/f2.md
55
- page: docs/f3.md
66
- page: docs/f4.md
7+
- page: docs/f5.html

scaladoc/resources/dotty_res/scripts/ux.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,22 @@ window.addEventListener("DOMContentLoaded", () => {
2020
$(this).parent().toggleClass("expanded")
2121
});
2222

23+
const observer = new IntersectionObserver(entries => {
24+
entries.forEach(entry => {
25+
const id = entry.target.getAttribute('id');
26+
if (entry.intersectionRatio > 0) {
27+
document.querySelector(`#toc li a[href="#${id}"]`).parentElement.classList.add('active');
28+
} else {
29+
document.querySelector(`#toc li a[href="#${id}"]`).parentElement.classList.remove('active');
30+
}
31+
});
32+
});
33+
34+
35+
document.querySelectorAll('#content section[id]').forEach((section) => {
36+
observer.observe(section);
37+
});
38+
2339
document.querySelectorAll("#sideMenu2 a").forEach(elem => elem.addEventListener('click', e => e.stopPropagation()))
2440

2541
$('.names .tab').on('click', function() {

scaladoc/resources/dotty_res/styles/scalastyle.css

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
/* Layout Settings (changes on small screens) */
1212
--side-width: 300px;
13+
--toc-width: 300px;
1314
--content-padding: 24px 42px;
1415
--footer-height: 42px;
1516
}
@@ -43,6 +44,7 @@ body, button, input {
4344
#main-content {
4445
min-height: calc(100vh - var(--footer-height) - 24px);
4546
margin-left: var(--side-width);
47+
margin-right: var(--toc-width);
4648
padding: var(--content-padding);
4749
padding-bottom: calc(24px + var(--footer-height));
4850

@@ -905,6 +907,46 @@ footer .socials {
905907
height: 8px;
906908
}
907909

910+
#toc {
911+
position: fixed;
912+
right: 0px;
913+
top: 0px;
914+
width: var(--toc-width);
915+
height: 100%;
916+
917+
padding-top: 15vh;
918+
padding-right: 10px;
919+
920+
display: flex;
921+
flex-direction: column;
922+
}
923+
924+
#toc .toc-title {
925+
font-weight: bold;
926+
padding-left: 16px;
927+
}
928+
929+
#toc ul {
930+
list-style-type: none;
931+
padding-left: 16px;
932+
}
933+
934+
#toc a {
935+
display: block;
936+
padding-top: 0.5em;
937+
padding-bottom: 0.5em;
938+
color: var(--leftbar-fg);
939+
transition-duration: 0.2s;
940+
}
941+
942+
#toc li:hover > a {
943+
color: var(--link-hover-fg);
944+
}
945+
946+
#toc li.active > a {
947+
color: var(--link-hover-fg);
948+
}
949+
908950
/* Signature coloring */
909951

910952
:root {
@@ -941,6 +983,12 @@ footer .socials {
941983
}
942984
}
943985
/* Landscape phones, portait tablets */
986+
@media(max-width: 1200px) {
987+
:root {
988+
--toc-width: 0px;
989+
}
990+
}
991+
944992
@media(max-width: 768px) {
945993
:root {
946994
--content-padding: 12px 12px;
@@ -1070,43 +1118,51 @@ footer .socials {
10701118

10711119
/* Nav Icons */
10721120

1121+
.arrows-wrapper {
1122+
display: flex;
1123+
justify-content: space-between;
1124+
padding-top: 16px;
1125+
padding-bottom: 16px;
1126+
}
1127+
10731128
.arrows {
1074-
font-size: 3em;
1129+
color: var(--link-fg);
1130+
font-size: 1em;
10751131
text-align: center;
10761132

1077-
position: fixed;
1078-
top: 0;
1079-
bottom: 0;
1080-
margin: 0;
1081-
max-width: 150px;
1082-
min-width: 90px;
1083-
10841133
display: flex;
1085-
justify-content: center;
1086-
flex-direction: column;
1134+
align-items: center;
10871135

10881136
transition: color 0.5s, background-color 0.5s;
10891137
}
10901138

1139+
.arrows span {
1140+
max-width: 10vw;
1141+
overflow: hidden;
1142+
white-space: nowrap;
1143+
text-overflow: ellipsis;
1144+
}
1145+
1146+
.arrows i {
1147+
margin: 8px;
1148+
}
1149+
10911150
.arrows.previous, .arrows.next {
10921151
color: var(--grey400);
10931152
}
10941153

10951154
.arrows:hover {
10961155
text-decoration: none;
1097-
color: var(--grey300);
1098-
background-color: var(--grey900);
1156+
color: var(--link-hover-fg);
10991157
transition: background-color 0.15s, color 0.15s;
11001158
}
11011159

11021160
.previous {
1103-
left: var(--side-width);
1104-
float: left;
1161+
flex-direction: row-reverse;
11051162
}
11061163

11071164
.next {
1108-
right: 0;
1109-
float: right;
1165+
flex-direction: row;
11101166
}
11111167

11121168
@media screen and (max-width: 1000px) {

scaladoc/src/dotty/tools/scaladoc/api.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dotty.tools.scaladoc
22

33
import dotty.tools.scaladoc.tasty.comments.Comment
4+
import util.HTML.AppliedTag
45

56
enum Visibility(val name: String):
67
case Unrestricted extends Visibility("")
@@ -258,3 +259,18 @@ case class SnippetCompilerData(
258259
imports: List[String],
259260
position: SnippetCompilerData.Position
260261
)
262+
263+
case class PageContent(content: AppliedTag, toc: Seq[TocEntry])
264+
265+
case class TocEntry(level: Int, content: String, anchor: String)
266+
267+
object TocEntry:
268+
val tagLevels: Map[String, Int] = Map(
269+
("h1" -> 1),
270+
("h2" -> 2),
271+
("h3" -> 3),
272+
("h4" -> 4),
273+
("h5" -> 5),
274+
("h6" -> 6)
275+
)
276+
def apply(tag: String, content: String, anchor: String): TocEntry = TocEntry(tagLevels(tag), content, anchor)

scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class HtmlRenderer(rootPackage: Member, members: Map[DRI, Member])(using ctx: Do
2222
html(
2323
mkHead(page),
2424
body(
25-
if !page.hasFrame then renderContent(page)
25+
if !page.hasFrame then renderContent(page).content
2626
else mkFrame(page.link, parents, renderContent(page))
2727
)
2828
)
@@ -148,7 +148,23 @@ class HtmlRenderer(rootPackage: Member, members: Map[DRI, Member])(using ctx: Do
148148
)
149149
}
150150

151-
private def mkFrame(link: Link, parents: Vector[Link], content: => AppliedTag): AppliedTag =
151+
private def renderTableOfContents(toc: Seq[TocEntry]): Option[AppliedTag] =
152+
def renderTocRec(level: Int, rest: Seq[TocEntry]): Seq[AppliedTag] =
153+
rest match {
154+
case Nil => Nil
155+
case head :: tail if head.level == level =>
156+
val (nested, rest) = tail.span(_.level > level)
157+
val nestedList = if nested.nonEmpty then Seq(ul(renderTocRec(level + 1, nested))) else Nil
158+
li(a(href := head.anchor)(head.content), nestedList) +: renderTocRec(level, rest)
159+
case rest @ (head :: tail) if head.level > level =>
160+
val (prefix, suffix) = rest.span(_.level > level)
161+
li(ul(renderTocRec(level + 1, prefix))) +: renderTocRec(level, suffix)
162+
}
163+
164+
renderTocRec(1, toc).headOption.map(toc => nav(cls := "toc-nav")(ul(cls := "toc-list")(toc)))
165+
166+
167+
private def mkFrame(link: Link, parents: Vector[Link], content: => PageContent): AppliedTag =
152168
val projectLogo =
153169
args.projectLogo.map { path =>
154170
val fileName = Paths.get(path).getFileName()
@@ -202,7 +218,7 @@ class HtmlRenderer(rootPackage: Member, members: Map[DRI, Member])(using ctx: Do
202218
div(id := "scaladoc-searchBar"),
203219
main(id := "main-content")(
204220
parentsHtml,
205-
div(id := "content")(content),
221+
div(id := "content")(content.content),
206222
),
207223
footer(
208224
div(id := "generated-by")(
@@ -239,5 +255,11 @@ class HtmlRenderer(rootPackage: Member, members: Map[DRI, Member])(using ctx: Do
239255
)
240256
)
241257
)
242-
)
258+
),
259+
renderTableOfContents(content.toc).fold(Nil) { toc =>
260+
div(id := "toc")(
261+
span(cls := "toc-title")("In this article"),
262+
toc
263+
)
264+
}
243265
)

scaladoc/src/dotty/tools/scaladoc/renderers/MarkdownRenderer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class MarkdownRenderer(rootPackage: Member, members: Map[DRI, Member])(using ctx
2222
super.render()
2323

2424
override def pageContent(page: Page, parents: Vector[Link]): AppliedTag =
25-
renderContent(page)
25+
renderContent(page).content
2626

2727
private def renderResources(): Seq[String] =
2828
allResources(Nil).flatMap(renderResource)

scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext
387387
div(cls := "filterLowerContainer")()
388388
)
389389

390-
def fullMember(m: Member): AppliedTag =
390+
def fullMember(m: Member): PageContent =
391391
val intro = m.kind match
392392
case Kind.RootPackage =>Seq(h1(summon[DocContext].args.name))
393393
case _ =>
@@ -401,11 +401,13 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext
401401
memberSignature(m)
402402
)
403403
)
404-
405-
div(
406-
intro,
407-
memberInfo(m, withBrief = false),
408-
classLikeParts(m),
409-
buildDocumentableFilter, // TODO Need to make it work in JS :(
410-
buildMembers(m)
404+
PageContent(
405+
div(
406+
intro,
407+
memberInfo(m, withBrief = false),
408+
classLikeParts(m),
409+
buildDocumentableFilter, // TODO Need to make it work in JS :(
410+
buildMembers(m)
411+
),
412+
Seq.empty // For now, we don't support table of contents in members
411413
)

0 commit comments

Comments
 (0)