Skip to content

Commit 8ef0bac

Browse files
committed
Make navigation much snappier
1 parent e8d748e commit 8ef0bac

File tree

5 files changed

+106
-49
lines changed

5 files changed

+106
-49
lines changed

scala3doc/resources/dotty_res/scripts/ux.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ window.addEventListener("DOMContentLoaded", () => {
1515
}
1616
}
1717

18+
$("#sideMenu2 span").on('click', function(){
19+
$(this).parent().toggleClass("expanded")
20+
});
21+
1822

1923
if (location.hash) {
2024
var selected = document.getElementById(location.hash.substring(1));

scala3doc/resources/dotty_res/styles/scalastyle.css

Lines changed: 57 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ th {
123123
}
124124

125125
/* Left bar */
126-
#sideMenu {
126+
#sideMenu2 {
127127
overflow-y: auto;
128128
scrollbar-width: thin;
129129
height: 100%;
@@ -159,63 +159,73 @@ th {
159159
width: 116px;
160160
}
161161

162-
.sideMenuPart {
163-
padding-left: 1em;
162+
#sideMenu2 div {
163+
padding-left: 0.7em;
164+
position: relative;
165+
display: none;
166+
}
167+
168+
#sideMenu2 div.expanded {
169+
display: block;
170+
}
171+
172+
#sideMenu2 div>div.expanded {
173+
display: none;
174+
}
175+
176+
#sideMenu2 div.expanded>div {
177+
display: block;
178+
}
179+
180+
#sideMenu2>div{
181+
display: block;
164182
}
165-
.sideMenuPart a {
183+
184+
#sideMenu2 a {
166185
align-items: center;
167186
flex: 1;
168187
overflow-x: hidden;
169188
overflow-wrap: anywhere;
170189
color: var(--leftbar-fg);
171190
margin-right: .5rem;
172-
padding: 7px 0;
173-
}
174-
.sideMenuPart > .overview {
175-
display: flex;
176-
align-items: center;
177-
position: relative;
178-
user-select: none;
179-
}
180-
.sideMenuPart > .overview::before {
181-
width: var(--side-width);
182-
box-sizing: border-box;
183-
content: '';
184-
top: 0;
185-
right: 0;
186-
bottom: 0;
187-
position: absolute;
188-
z-index: 1;
189-
}
190-
.sideMenuPart > .overview:hover::before {
191-
background: var(--leftbar-hover-bg);
191+
padding-top: 4px;
192+
padding-bottom: 4px;
193+
margin-top: 4px;
194+
margin-bottom: 4px;
195+
display: block;
196+
width: 100%;
192197
}
193-
.sideMenuPart > .overview:hover > a {
194-
color: var(--leftbar-hover-fg);
198+
199+
#sideMenu2 div.selected {
200+
font-weight: bold;
195201
}
196-
.sideMenuPart[data-active] > .overview::before {
202+
203+
#sideMenu2 a.selected {
197204
background: var(--leftbar-current-bg);
205+
margin-left: calc(0px - var(--side-width));
206+
padding-left: var(--side-width);
198207
}
199-
.sideMenuPart[data-active] > .overview > a {
200-
font-weight: bold;
201-
color: var(--leftbar-current-fg);
202-
}
203-
.sideMenuPart.hidden > .sideMenuPart {
204-
height: 0;
205-
visibility: hidden;
208+
209+
#sideMenu2 a.selected:before {
210+
background: var(--leftbar-current-bg);
206211
}
207-
.overview a, .overview .navButton {
208-
z-index: 3;
212+
213+
#sideMenu2 a:hover {
214+
color: var(--leftbar-hover-fg);
215+
background: var(--leftbar-hover-bg);
209216
}
210-
.sideMenuPart .navButton {
217+
218+
#sideMenu2 span {
211219
align-items: center;
212-
display: flex;
213-
justify-content: flex-end;
214-
padding: 7px 10px;
215220
cursor: pointer;
221+
position: absolute;
222+
right: 1em;
223+
top: 6px;
224+
padding: 4px;
216225
}
217-
.sideMenuPart .navButtonContent::before {
218-
content: "\e905"; /* arrow up */
226+
227+
#sideMenu2 span::before {
228+
content: "\e903"; /* arrow down */
219229
font-family: "dotty-icons" !important;
220230
font-size: 20px;
221231
line-height: var(--leftbar-font-size);
@@ -225,10 +235,11 @@ th {
225235
align-items: center;
226236
justify-content: center;
227237
}
228-
.sideMenuPart.hidden .navButtonContent::before {
229-
content: "\e903"; /* arrow down */
238+
#sideMenu2 .expanded>span::before {
239+
content: "\e905"; /* arrow up */
230240
}
231-
.sideMenuPart .navButton:hover .navButtonContent::before {
241+
242+
#sideMenu2 .div:hover>span::before {
232243
color: var(--leftbar-current-bg);
233244
}
234245

@@ -295,7 +306,7 @@ th {
295306
padding-top: 0;
296307
}
297308

298-
span[data-unresolved-link].strikethrough, a.strikethrough, div.strikethrough {
309+
span[data-unresolved-link].deprecated, a.deprecated, div.deprecated {
299310
text-decoration: line-through;
300311
}
301312
.brief {

scala3doc/src/dotty/dokka/DocContext.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ case class DocContext(args: Scala3doc.Args, compilerContext: CompilerContext)
8686

8787
lazy val displaySourceSets = getSourceSets.toDisplaySourceSet
8888

89+
import org.jetbrains.dokka.base.renderers.html.{NavigationNode, NavigationPage}
90+
var navigationNode: Option[NavigationNode] = None
91+
8992
val logger = new Scala3DocDokkaLogger(using compilerContext)
9093

9194
lazy val staticSiteContext = args.docsRoot.map(path => StaticSiteContext(

scala3doc/src/dotty/dokka/site/NavigationCreator.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,20 @@ class NavigationCreator(using ctx: DocContext) extends PageTransformer:
6262
def defaultApiPages = processApiPages(input.getChildren.asScala.toList)
6363
val nodes = ctx.staticSiteContext.fold(defaultApiPages)(processStaticPages(input))
6464

65-
val navigationPage = new NavigationPage(new NavigationNode(
65+
summon[DocContext].navigationNode = Some(new NavigationNode(
6666
ctx.args.name,
6767
ctx.staticSiteContext.fold(topLevelDri)(_ => docsRootDRI),
6868
ctx.displaySourceSets,
6969
nodes
7070
))
71+
72+
val emptyNode = new NavigationNode(
73+
ctx.args.name,
74+
ctx.staticSiteContext.fold(topLevelDri)(_ => docsRootDRI),
75+
ctx.displaySourceSets,
76+
JNil,
77+
)
78+
79+
val navigationPage = new NavigationPage(emptyNode)
7180
val newChildren = input.getChildren ++ JList(emptyNavigationJson, navigationPage)
7281
input.modified(input.getName, newChildren)

scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ import scala.util.Try
2727
import org.jetbrains.dokka.base.renderers.html.SearchbarDataInstaller
2828
import org.jsoup.Jsoup
2929
import java.nio.file.Paths
30+
import org.jetbrains.dokka.base.renderers.html.{NavigationNode, NavigationPage}
3031

3132
class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet], locationProvider: LocationProvider):
33+
val currentDri = pageContext.getDri.asScala.head
34+
3235
def link(dri: DRI): Option[String] = Option(locationProvider.resolve(dri, sourceSetRestriciton, pageContext))
3336

3437
def renderLink(name: String, dri: DRI, modifiers: AppliedAttr*) =
@@ -106,7 +109,7 @@ class ScalaHtmlRenderer(using ctx: DokkaContext) extends HtmlRenderer(ctx) {
106109
withHtml(f, buildDocumentableList(n, pageContext, ss).toString())
107110
case n: DocumentableFilter => withHtml(f, buildDocumentableFilter.toString)
108111
case mi: MemberInfo =>
109-
val memberHtml = div(renderers(pageContext)._2.memberInfo(mi.member))
112+
val memberHtml = div(renderers(pageContext)._2.fullMember(mi.member))
110113
withHtml(f, memberHtml.toString)
111114
case other => super.buildContentNode(f, node, pageContext, sourceSetRestriciton)
112115
}
@@ -116,6 +119,29 @@ class ScalaHtmlRenderer(using ctx: DokkaContext) extends HtmlRenderer(ctx) {
116119
val renderer = SignatureRenderer(pageContext, sourceSets, getLocationProvider)
117120
(renderer, new MemberRenderer(renderer, buildWithKotlinx(_, pageContext, null)))
118121

122+
private def buildNavigation(r: SignatureRenderer)(rootNav: NavigationNode): AppliedTag =
123+
val currentPageDri = r.currentDri
124+
125+
def renderNested(nav: NavigationNode): (Boolean, AppliedTag) =
126+
val isSelected = nav.getDri == currentPageDri
127+
def linkHtml(exapnded: Boolean = false) =
128+
val attrs = if (isSelected) Seq(cls := "selected expanded") else Nil
129+
a(href := r.link(nav.getDri).getOrElse("#"), attrs)(nav.getName)
130+
131+
nav.getChildren match
132+
case empty if empty.isEmpty => isSelected -> div(linkHtml())
133+
case children =>
134+
val nested = children.asScala.map(renderNested).toList
135+
val expanded = nested.exists(_._1) | nav == rootNav
136+
val attr = if expanded || isSelected then Seq(cls := "expanded") else Nil
137+
(isSelected || expanded) -> div(attr)(
138+
linkHtml(expanded),
139+
span(),
140+
nested.map(_._2)
141+
)
142+
143+
renderNested(rootNav)._2
144+
119145
private def buildDocumentableList(n: DocumentableList, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) =
120146
def render(n: ContentNode) = raw(buildWithKotlinx(n, pageContext, null))
121147

@@ -296,6 +322,8 @@ class ScalaHtmlRenderer(using ctx: DokkaContext) extends HtmlRenderer(ctx) {
296322
span(img(src := resolveRoot(page, s"project-logo/$fileName")))
297323
}.toSeq
298324

325+
val renderer = SignatureRenderer(page.asInstanceOf[ContentPage], sourceSets, getLocationProvider)
326+
299327
html(
300328
head(
301329
meta(charset := "utf-8"),
@@ -319,7 +347,9 @@ class ScalaHtmlRenderer(using ctx: DokkaContext) extends HtmlRenderer(ctx) {
319347
)
320348
),
321349
div(id := "paneSearch"),
322-
nav(id := "sideMenu"),
350+
nav(id := "sideMenu2")(
351+
summon[DocContext ].navigationNode.fold("No Navigation")(buildNavigation(renderer))
352+
),
323353
),
324354
div(id := "main")(
325355
div (id := "leftToggler")(

0 commit comments

Comments
 (0)