diff --git a/project/Build.scala b/project/Build.scala
index 7c6f5e5953a5..1618c42c949b 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -1253,7 +1253,7 @@ object Build {
lazy val `scaladoc-js-contributors` = project.in(file("scaladoc-js/contributors")).
enablePlugins(DottyJSPlugin).
- dependsOn(`scala3-library-bootstrappedJS`).
+ dependsOn(`scaladoc-js-common`).
settings(
Test / fork := false,
scalaJSUseMainModuleInitializer := true,
diff --git a/scaladoc-js/common/src/code-snippets/CodeSnippets.scala b/scaladoc-js/common/src/code-snippets/CodeSnippets.scala
index 3d4b4af2ab64..6595aa77c372 100644
--- a/scaladoc-js/common/src/code-snippets/CodeSnippets.scala
+++ b/scaladoc-js/common/src/code-snippets/CodeSnippets.scala
@@ -4,6 +4,9 @@ import scala.scalajs.js
import org.scalajs.dom._
import org.scalajs.dom.ext._
+import utils.HTML._
+import scala.util.chaining._
+
import CodeSnippetsGlobals._
class CodeSnippets:
@@ -35,22 +38,13 @@ class CodeSnippets:
case _ =>
}
def createShowHideButton(toggleRoot: html.Element) = {
- val div = document.createElement("div")
- div.classList.add("snippet-showhide")
- val p = document.createElement("p")
- p.textContent = "Show collapsed lines"
- val showHideButton = document.createElement("label")
- showHideButton.classList.add("snippet-showhide-button")
- val checkbox = document.createElement("input").asInstanceOf[html.Input]
- checkbox.`type` = "checkbox"
- val slider = document.createElement("span")
- slider.classList.add("slider")
- showHideButton.appendChild(checkbox)
- showHideButton.appendChild(slider)
- checkbox.addEventListener("change", _ => toggleHide(toggleRoot))
- div.appendChild(showHideButton)
- div.appendChild(p)
- div
+ div(cls := "snippet-showhide")(
+ label(cls := "snippet-showhide-button")(
+ input("type" := "checkbox").tap(_.addEventListener("change", _ => toggleHide(toggleRoot))),
+ span(cls := "slider")
+ ),
+ p("Show collapsed lines")
+ )
}
toggleHide(snippet)
@@ -65,8 +59,7 @@ class CodeSnippets:
private def snippetAnchor(snippet: html.Element): Unit = snippet.querySelector(".snippet-meta .snippet-label") match {
case e: html.Element =>
val name = e.textContent.trim
- val anchor = document.createElement("a").asInstanceOf[html.Anchor]
- anchor.id = s"snippet-$name"
+ val anchor = a(id := s"snippet-$name")
snippet.insertBefore(anchor, snippet.firstChild)
case _ =>
}
@@ -75,23 +68,20 @@ class CodeSnippets:
val included = snippet.querySelectorAll("code span.include")
val pre = snippet.querySelector("pre")
if included != null && included.nonEmpty && pre != null then {
- val includesDiv = document.createElement("div")
- includesDiv.classList.add("included-section")
- includesDiv.classList.add("hideable")
- included
+ val includes = included
.collect { case e: html.Element => e }
.toList
.filter(_.hasAttribute("name"))
.map(_.getAttribute("name"))
.distinct
.map { name =>
- val a = document.createElement("a").asInstanceOf[html.Anchor]
- a.classList.add("unselectable")
- a.href = s"#snippet-$name"
- a.innerHTML = s"included $name"
- a
+ a(cls := "unselectable", href := s"#snippet-$name")(
+ "included",
+ b(name)
+ )
}
- .foreach(a => includesDiv.appendChild(a))
+
+ val includesDiv = div(cls := "included-section hideable")(includes)
snippet.insertBefore(includesDiv, pre)
}
@@ -99,30 +89,21 @@ class CodeSnippets:
private def copyRunButtons(snippet: html.Element) = {
def copyButton = {
- val div = document.createElement("div")
- val button = document.createElement("button")
- val icon = document.createElement("i")
- icon.classList.add("far")
- icon.classList.add("fa-clone")
- button.appendChild(icon)
- button.classList.add("copy-button")
- button.addEventListener("click", _ => {
- val code = snippet.querySelectorAll("code>span:not(.hidden)")
- .map(_.textContent)
- .mkString
- window.navigator.clipboard.writeText(code)
- })
- div.appendChild(button)
- div
+ div(
+ button(cls := "copy-button")(
+ i(cls := "far fa-clone")
+ ).tap(_.addEventListener("click", _ => {
+ val code = snippet.querySelectorAll("code>span:not(.hidden)")
+ .map(_.textContent)
+ .mkString
+ window.navigator.clipboard.writeText(code)
+ }))
+ )
}
def runButton = {
- val div = document.createElement("div").asInstanceOf[html.Div]
- val runButton = document.createElement("button").asInstanceOf[html.Button]
- val runIcon = document.createElement("i")
- runIcon.classList.add("fas")
- runIcon.classList.add("fa-play")
- runButton.classList.add("run-button")
- runButton.appendChild(runIcon)
+ val runButton = button(cls := "run-button")(
+ i(cls := "fas fa-play")
+ )
runButton.addEventListener("click", _ =>
if !runButton.hasAttribute("opened") then {
@@ -148,18 +129,14 @@ class CodeSnippets:
}
)
- div.appendChild(runButton)
- div
+ div(runButton)
}
def exitButton = {
- val div = document.createElement("div").asInstanceOf[html.Div]
- val exitButton = document.createElement("button").asInstanceOf[html.Element]
- val exitIcon = document.createElement("i")
- exitIcon.classList.toggle("fas")
- exitIcon.classList.toggle("fa-times")
- exitButton.classList.add("exit-button")
- div.style = "display:none;"
- exitButton.appendChild(exitIcon)
+ val exitButton = button(cls := "exit-button")(
+ i(cls := "fas fa-times")
+ )
+
+ val bdiv = div(style := "display:none;")(exitButton)
exitButton.addEventListener("click", _ =>
snippet.querySelector("pre") match {
@@ -178,22 +155,16 @@ class CodeSnippets:
case btn: html.Element => btn.parentElement.style = "display:none;"
case _ =>
}
- div.style = "display:none;"
+ bdiv.style = "display:none;"
)
- div.appendChild(exitButton)
- div
+ bdiv
}
- def toScastieButton = {
- val div = document.createElement("div").asInstanceOf[html.Div]
- val toScastieButton = document.createElement("button").asInstanceOf[html.Element]
- val toScastieIcon = document.createElement("i").asInstanceOf[html.Image]
- toScastieIcon.classList.add("fas")
- toScastieIcon.classList.add("fa-external-link-alt")
- toScastieButton.classList.add("to-scastie-button")
- div.style = "display:none;"
- toScastieButton.appendChild(toScastieIcon)
+ def toScastieButton = {
+ val toScastieButton = button(cls := "to-scastie-button")(
+ i(cls := "fas fa-external-link-alt")
+ )
toScastieButton.addEventListener("click", _ =>
snippet.querySelector(".embedded-menu li.logo") match {
@@ -202,9 +173,9 @@ class CodeSnippets:
}
)
- div.appendChild(toScastieButton)
- div
+ div("style" := "display:none;")(toScastieButton)
}
+
val buttonsSection = getButtonsSection(snippet)
buttonsSection.foreach(s =>
s.appendChild(copyButton)
diff --git a/scaladoc-js/common/src/utils/html.scala b/scaladoc-js/common/src/utils/html.scala
new file mode 100644
index 000000000000..fd49179752a2
--- /dev/null
+++ b/scaladoc-js/common/src/utils/html.scala
@@ -0,0 +1,116 @@
+package dotty.tools.scaladoc
+package utils
+
+import scala.scalajs.js
+import org.scalajs.dom.{html => domhtml, _}
+
+object HTML {
+ type TagArg = domhtml.Element | Seq[domhtml.Element | String] | String
+
+ type AttrArg = AppliedAttr | Seq[AppliedAttr]
+
+ case class Tag[T <: domhtml.Element](private val elemFactory: () => T):
+ private def textNode(s: String): Text = document.createTextNode(s)
+
+ def apply(tags: TagArg*): T = apply()(tags:_*)
+ def apply(first: AttrArg, rest: AttrArg*): T = apply((first +: rest):_*)()
+ def apply(attrs: AttrArg*)(tags: TagArg*): T =
+ val elem: T = elemFactory()
+ def unpackTags(tags: TagArg*): Unit = tags.foreach {
+ case e: domhtml.Element => elem.appendChild(e)
+ case s: String => elem.appendChild(textNode(s))
+ case elemSeq: (Seq[domhtml.Element | String] @unchecked) => unpackTags(elemSeq*)
+ }
+
+ def unpackAttributes(attrs: AttrArg*): Unit = attrs.foreach {
+ case ("id", id) => elem.id = id
+ case ("class", value) => value.split("\\s+").foreach(cls => elem.classList.add(cls))
+ case (attr, value) => elem.setAttribute(attr, value)
+ case s: Seq[AppliedAttr] => unpackAttributes(s*)
+ }
+
+ unpackTags(tags:_*)
+ unpackAttributes(attrs:_*)
+ elem
+
+ object Tag:
+ def apply[T <: domhtml.Element](s: String): Tag[T] =
+ Tag[T](() => document.createElement(s).asInstanceOf[T])
+
+ extension (s: String) def escapeReservedTokens: String =
+ s.replace("&", "&")
+ .replace("<", "<")
+ .replace(">", ">")
+ .replace("\"", """)
+ .replace("'", "'")
+
+ case class Attr(name: String):
+ def :=(value: String): AppliedAttr = (name, value)
+
+ extension (key: String) def :=(value: String): AppliedAttr =
+ (key, value)
+
+ opaque type AppliedAttr = (String, String)
+
+ val div = Tag[domhtml.Div]("div")
+ val span = Tag[domhtml.Span]("span")
+ val a = Tag[domhtml.Anchor]("a")
+ val p = Tag[domhtml.Paragraph]("p")
+ val h1 = Tag[domhtml.Heading]("h1")
+ val h2 = Tag[domhtml.Heading]("h2")
+ val h3 = Tag[domhtml.Heading]("h3")
+ val h4 = Tag[domhtml.Heading]("h4")
+ val h5 = Tag[domhtml.Heading]("h5")
+ val h6 = Tag[domhtml.Heading]("h6")
+ val dl = Tag[domhtml.DList]("dl")
+ val dd = Tag[domhtml.Element]("dd")
+ val dt = Tag[domhtml.Element]("dt")
+ val svg = Tag[domhtml.Element]("svg")
+ val button = Tag[domhtml.Button]("button")
+ val input = Tag[domhtml.Input]("input")
+ val label = Tag[domhtml.Label]("label")
+ val script = Tag[domhtml.Script]("script")
+ val link = Tag[domhtml.Link]("link")
+ val footer = Tag[domhtml.Element]("footer")
+ val htmlelem = Tag[domhtml.Html]("html")
+ val head = Tag[domhtml.Head]("head")
+ val meta = Tag[domhtml.Element]("meta")
+ val main = Tag[domhtml.Element]("main")
+ val title = Tag[domhtml.Title]("title")
+ val body = Tag[domhtml.Body]("body")
+ val nav = Tag[domhtml.Element]("nav")
+ val img = Tag[domhtml.Image]("img")
+ val ul = Tag[domhtml.UList]("ul")
+ val ol = Tag[domhtml.OList]("ol")
+ val li = Tag[domhtml.LI]("li")
+ val code = Tag[domhtml.Element]("code")
+ val pre = Tag[domhtml.Pre]("pre")
+ val table = Tag[domhtml.Table]("table")
+ val thead = Tag[domhtml.Element]("thead")
+ val tbody = Tag[domhtml.Element]("tbody")
+ val th = Tag[domhtml.TableCell]("th")
+ val tr = Tag[domhtml.TableRow]("tr")
+ val td = Tag[domhtml.TableCell]("td")
+ val b = Tag[domhtml.Element]("b")
+ val i = Tag[domhtml.Element]("i")
+
+ val cls = Attr("class")
+ val href = Attr("href")
+ val style = Attr("style")
+ val id = Attr("id")
+ val `type` = Attr("type")
+ val placeholder = Attr("placeholder")
+ val defer = Attr("defer")
+ val src = Attr("src")
+ val rel = Attr("rel")
+ val charset = Attr("charset")
+ val name = Attr("name")
+ val content = Attr("content")
+ val testId = Attr("data-test-id")
+ val alt = Attr("alt")
+ val value = Attr("value")
+ val onclick=Attr("onclick")
+ val titleAttr =Attr("title")
+ val onkeyup = Attr("onkeyup")
+
+}
\ No newline at end of file
diff --git a/scaladoc-js/contributors/src/content-contributors/ContentContributors.scala b/scaladoc-js/contributors/src/content-contributors/ContentContributors.scala
index bbc9c4787dc6..28b78cef0296 100644
--- a/scaladoc-js/contributors/src/content-contributors/ContentContributors.scala
+++ b/scaladoc-js/contributors/src/content-contributors/ContentContributors.scala
@@ -13,6 +13,8 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Success,Failure}
+import utils.HTML._
+
// Contributors widget
// see https://stackoverflow.com/a/19200303/4496364
// Copied from https://github.com/scala/docs.scala-lang/blob/main/resources/js/functions.js and rewritten to Scala.js
@@ -90,21 +92,17 @@ class ContentContributors:
getAuthorsForFilename(Globals.githubContributorsFilename.stripPrefix("/")).onComplete {
case Success(authors) =>
val maybeDiv = Option(document.getElementById("documentation-contributors"))
- maybeDiv.foreach { div =>
- authors.foreach { case FullAuthor(name, url, img) =>
- val divN = document.createElement("div")
- val imgN = document.createElement("img").asInstanceOf[html.Image]
- imgN.src = img
- val autN = document.createElement("a").asInstanceOf[html.Anchor]
- autN.href = url
- autN.text = name
- divN.appendChild(imgN)
- divN.appendChild(autN)
- div.appendChild(divN)
+ maybeDiv.foreach { mdiv =>
+ authors.foreach { case FullAuthor(name, url, imgUrl) =>
+ val inner = div(
+ img(src := imgUrl)(),
+ a(href := url)(name)
+ )
+ mdiv.appendChild(inner)
}
if authors.nonEmpty then
- div.asInstanceOf[html.Div].parentElement.classList.toggle("hidden")
+ mdiv.asInstanceOf[html.Div].parentElement.classList.toggle("hidden")
}
case Failure(err) =>
println(s"Couldn't fetch contributors. $err")
diff --git a/scaladoc-js/main/src/searchbar/SearchbarComponent.scala b/scaladoc-js/main/src/searchbar/SearchbarComponent.scala
index c795b4aa634a..d0080d0793ae 100644
--- a/scaladoc-js/main/src/searchbar/SearchbarComponent.scala
+++ b/scaladoc-js/main/src/searchbar/SearchbarComponent.scala
@@ -1,10 +1,14 @@
package dotty.tools.scaladoc
+import utils.HTML._
+
import org.scalajs.dom._
import org.scalajs.dom.ext._
import org.scalajs.dom.html.Input
import scala.scalajs.js.timers._
-import scala.concurrent.duration._
+import scala.concurrent.duration.{span => dspan, _}
+
+import scala.util.chaining._
import java.net.URI
@@ -13,120 +17,71 @@ class SearchbarComponent(engine: SearchbarEngine, inkuireEngine: InkuireJSSearch
val resultsChunkSize = 20
extension (p: PageEntry)
def toHTML =
- val wrapper = document.createElement("div").asInstanceOf[html.Div]
- wrapper.classList.add("scaladoc-searchbar-row")
- wrapper.setAttribute("result", "")
- wrapper.classList.add("monospace")
-
- val resultA = document.createElement("a").asInstanceOf[html.Anchor]
- resultA.href =
- if (p.isLocationExternal) {
- p.location
- } else {
- Globals.pathToRoot + p.location
- }
- resultA.text = s"${p.fullName}"
- resultA.onclick = (event: Event) =>
- if (document.body.contains(rootDiv)) {
- document.body.removeChild(rootDiv)
- }
-
- val location = document.createElement("span")
- location.classList.add("pull-right")
- location.classList.add("scaladoc-searchbar-location")
- location.textContent = p.description
+ val location = if (p.isLocationExternal) {
+ p.location
+ } else {
+ Globals.pathToRoot + p.location
+ }
- wrapper.appendChild(resultA)
- resultA.appendChild(location)
- wrapper.addEventListener("mouseover", {
- case e: MouseEvent => handleHover(wrapper)
- })
- wrapper
+ div(cls := "scaladoc-searchbar-row monospace", "result" := "")(
+ a(href := location)(
+ p.fullName,
+ span(cls := "pull-right scaladoc-searchbar-location")(p.description)
+ ).tap { _.onclick = (event: Event) =>
+ if (document.body.contains(rootDiv)) {
+ document.body.removeChild(rootDiv)
+ }
+ }
+ ).tap { wrapper => wrapper.addEventListener("mouseover", {
+ case e: MouseEvent => handleHover(wrapper)
+ })
+ }
extension (m: InkuireMatch)
def toHTML =
- val wrapper = document.createElement("div").asInstanceOf[html.Div]
- wrapper.classList.add("scaladoc-searchbar-row")
- wrapper.setAttribute("result", "")
- wrapper.setAttribute("inkuire-result", "")
- wrapper.classList.add("monospace")
- wrapper.setAttribute("mq", m.mq.toString)
-
- val resultA = document.createElement("a").asInstanceOf[html.Anchor]
- // Inkuire pageLocation should start with e (external)
- // or i (internal). The rest of the string is an absolute
- // or relative URL
- resultA.href =
- if (m.pageLocation(0) == 'e') {
+ val location = if (m.pageLocation(0) == 'e') {
m.pageLocation.substring(1)
} else {
Globals.pathToRoot + m.pageLocation.substring(1)
}
- resultA.text = m.functionName
- resultA.onclick = (event: Event) =>
- if (document.body.contains(rootDiv)) {
- document.body.removeChild(rootDiv)
- }
-
- val packageDiv = document.createElement("div").asInstanceOf[html.Div]
- packageDiv.classList.add("scaladoc-searchbar-inkuire-package")
-
- val packageIcon = document.createElement("span").asInstanceOf[html.Span]
- packageIcon.classList.add("micon")
- packageIcon.classList.add("pa")
- val packageSpan = document.createElement("span").asInstanceOf[html.Span]
- packageSpan.textContent = m.packageLocation
-
- val signature = document.createElement("span")
- signature.classList.add("pull-right")
- signature.classList.add("scaladoc-searchbar-inkuire-signature")
- signature.textContent = m.prettifiedSignature
-
- wrapper.appendChild(resultA)
- resultA.appendChild(signature)
- wrapper.appendChild(packageDiv)
- packageDiv.appendChild(packageIcon)
- packageDiv.appendChild(packageSpan)
- wrapper.addEventListener("mouseover", {
- case e: MouseEvent => handleHover(wrapper)
- })
- wrapper
+ div(cls := "scaladoc-searchbar-row monospace", "result" := "", "inkuire-result" := "", "mq" := m.mq.toString)(
+ a(href := location)(
+ m.functionName,
+ span(cls := "pull-right scaladoc-searchbar-inkuire-signature")(m.prettifiedSignature)
+ ).tap { _.onclick = (event: Event) =>
+ if (document.body.contains(rootDiv)) {
+ document.body.removeChild(rootDiv)
+ }
+ },
+ div(cls := "scaladoc-searchbar-inkuire-package")(
+ span(cls := "micon pa"),
+ span(m.packageLocation)
+ )
+ ).tap { wrapper => wrapper.addEventListener("mouseover", {
+ case e: MouseEvent => handleHover(wrapper)
+ })
+ }
def createKindSeparator(kind: String) =
- val kindSeparator = document.createElement("div").asInstanceOf[html.Div]
- val icon = document.createElement("span").asInstanceOf[html.Span]
- icon.classList.add("micon")
- icon.classList.add(kind.take(2))
- val name = document.createElement("span").asInstanceOf[html.Span]
- name.textContent = kind
- kindSeparator.classList.add("scaladoc-searchbar-row")
- kindSeparator.setAttribute("divider", "")
- kindSeparator.classList.add("monospace")
- kindSeparator.appendChild(icon)
- kindSeparator.appendChild(name)
- kindSeparator
+ div(cls := "scaladoc-searchbar-row monospace", "divider" := "")(
+ span(cls := s"micon ${kind.take(2)}"),
+ span(kind)
+ )
def handleNewFluffQuery(matchers: List[Matchers]) =
val result = engine.query(matchers)
val fragment = document.createDocumentFragment()
def createLoadMoreElement =
- val loadMoreElement = document.createElement("div").asInstanceOf[html.Div]
- loadMoreElement.classList.add("scaladoc-searchbar-row")
- loadMoreElement.setAttribute("loadmore", "")
- loadMoreElement.classList.add("monospace")
- val icon = document.createElement("a").asInstanceOf[html.Anchor]
- icon.classList.add("i")
- icon.classList.add("fas")
- icon.classList.add("fa-arrow-down")
- val text = document.createElement("span").asInstanceOf[html.Span]
- text.textContent = "Show more..."
- val anchor = document.createElement("a").asInstanceOf[html.Anchor]
- anchor.appendChild(icon)
- anchor.appendChild(text)
- loadMoreElement.appendChild(anchor)
- loadMoreElement.addEventListener("mouseover", _ => handleHover(loadMoreElement))
- loadMoreElement
+ div(cls := "scaladoc-searchbar-row monospace", "loadmore" := "")(
+ a(
+ a(cls := "i fas fa-arrow-down"),
+ span("Show more...")
+ )
+ ).tap { loadMoreElement => loadMoreElement
+ .addEventListener("mouseover", _ => handleHover(loadMoreElement))
+ }
+
result.groupBy(_.kind).map {
case (kind, entries) =>
val kindSeparator = createKindSeparator(kind)
@@ -158,28 +113,15 @@ class SearchbarComponent(engine: SearchbarEngine, inkuireEngine: InkuireJSSearch
while (resultsDiv.hasChildNodes()) resultsDiv.removeChild(resultsDiv.lastChild)
resultsDiv.appendChild(fragment)
- def createLoadingAnimation: raw.HTMLElement = {
- val loading = document.createElement("div").asInstanceOf[html.Div]
- loading.classList.add("loading-wrapper")
- val animation = document.createElement("div").asInstanceOf[html.Div]
- animation.classList.add("loading")
- loading.appendChild(animation)
- loading
-}
-
+ def createLoadingAnimation: raw.HTMLElement =
+ div(cls := "loading-wrapper")(
+ div(cls := "loading")
+ )
extension (s: String)
def toHTMLError =
- val wrapper = document.createElement("div").asInstanceOf[html.Div]
- wrapper.classList.add("scaladoc-searchbar-row")
- wrapper.classList.add("scaladoc-searchbar-error")
- wrapper.classList.add("monospace")
-
- val errorSpan = document.createElement("span").asInstanceOf[html.Span]
- errorSpan.classList.add("search-error")
- errorSpan.textContent = s
-
- wrapper.appendChild(errorSpan)
- wrapper
+ div(cls := "scaladoc-searchbar-row monospace", "error" := "")(
+ span(cls := "search-error")(s)
+ )
var timeoutHandle: SetTimeoutHandle = null
def handleNewQuery(query: String) =
@@ -215,57 +157,52 @@ class SearchbarComponent(engine: SearchbarEngine, inkuireEngine: InkuireJSSearch
}
private val searchIcon: html.Div =
- val span = document.createElement("span").asInstanceOf[html.Span]
- span.innerHTML = """"""
- span.id = "scaladoc-search"
- span.onclick = (event: Event) =>
- if (document.body.contains(rootDiv)) {
- document.body.removeChild(rootDiv)
- }
- else {
- document.body.appendChild(rootDiv)
- input.focus()
- }
- // open the search if the user hits the `s` key when not focused on a text input
- document.body.addEventListener("keydown", (e: KeyboardEvent) => handleGlobalKeyDown(e))
-
- val element = createNestingDiv("search-content")(
- createNestingDiv("search-container")(
- createNestingDiv("search")(
- span
+ val element = div(cls := "search-content")(
+ div(cls := "search-container")(
+ div(cls := "search")(
+ span(id := "scaladoc-search")().tap { span =>
+ span.innerHTML = """"""
+ span.onclick = (event: Event) =>
+ if (document.body.contains(rootDiv)) {
+ document.body.removeChild(rootDiv)
+ }
+ else {
+ document.body.appendChild(rootDiv)
+ inputElem.focus()
+ }
+ }
)
)
)
+ // open the search if the user hits the `s` key when not focused on a text input
+ document.body.addEventListener("keydown", (e: KeyboardEvent) => handleGlobalKeyDown(e))
+
document.getElementById("scaladoc-searchBar").appendChild(element)
element
- private val input: html.Input =
- val element = document.createElement("input").asInstanceOf[html.Input]
- element.id = "scaladoc-searchbar-input"
- element.addEventListener("input", { e =>
- val inputValue = e.target.asInstanceOf[html.Input].value
- if inputValue.isEmpty then showHints()
- else handleNewQuery(inputValue)
- })
- element.autocomplete = "off"
- element
+ private val inputElem: html.Input =
+ input(id := "scaladoc-searchbar-input").tap { element =>
+ element.addEventListener("input", { e =>
+ val inputValue = e.target.asInstanceOf[html.Input].value
+ if inputValue.isEmpty then showHints()
+ else handleNewQuery(inputValue)
+ })
+
+ element.autocomplete = "off"
+ }
private val resultsDiv: html.Div =
- val element = document.createElement("div").asInstanceOf[html.Div]
- element.id = "scaladoc-searchbar-results"
- element
+ div(id := "scaladoc-searchbar-results")
private val rootHiddenClasses = "hidden"
private val rootShowClasses = ""
- private def createNestingDiv(className: String)(innerElement: html.Element): html.Div =
- val element = document.createElement("div").asInstanceOf[html.Div]
- element.className = className
- element.appendChild(innerElement)
- element
-
private val rootDiv: html.Div =
- val element = document.createElement("div").asInstanceOf[html.Div]
+ val element = div(id := "scaladoc-searchbar")(
+ inputElem,
+ resultsDiv
+ )
+
element.addEventListener("mousedown", (e: Event) => e.stopPropagation())
searchIcon.addEventListener("mousedown", (e: Event) => e.stopPropagation())
document.body.addEventListener("mousedown", (e: Event) =>
@@ -280,9 +217,7 @@ class SearchbarComponent(engine: SearchbarEngine, inkuireEngine: InkuireJSSearch
else if e.keyCode == 13 then handleEnter()
else if e.keyCode == 27 then handleEscape()
})
- element.id = "scaladoc-searchbar"
- element.appendChild(input)
- element.appendChild(resultsDiv)
+
element
private def handleArrowUp() = {
@@ -344,7 +279,7 @@ class SearchbarComponent(engine: SearchbarEngine, inkuireEngine: InkuireJSSearch
}
private def handleEscape() = {
// clear the search input and close the search
- input.value = ""
+ inputElem.value = ""
showHints()
document.body.removeChild(rootDiv)
}
@@ -368,63 +303,32 @@ class SearchbarComponent(engine: SearchbarEngine, inkuireEngine: InkuireJSSearch
document.body.appendChild(rootDiv)
// if we focus during the event handler, the `s` gets typed into the input
- window.setTimeout(() => input.focus(), 1.0)
+ window.setTimeout(() => inputElem.focus(), 1.0)
}
}
}
}
- private case class ListRoot(elems: Seq[ListNode])
- private case class ListNode(value: String, nested: ListRoot)
-
- private def ul(nodes: ListNode*) = ListRoot(nodes)
- private def li(s: String) = ListNode(s, ListRoot(Nil))
- private def li(s: String, nested: ListRoot) = ListNode(s, nested)
-
- private def renderList: ListRoot => Option[html.UList] = {
- case ListRoot(Nil) => None
- case ListRoot(nodes) =>
- val list = document.createElement("ul").asInstanceOf[html.UList]
- nodes.foreach {
- case ListNode(txt, nested) =>
- val li = document.createElement("li").asInstanceOf[html.LI]
- li.innerHTML = txt
- renderList(nested).foreach(li.appendChild)
- list.appendChild(li)
- }
- Some(list)
- }
-
private def showHints() = {
def clearResults() = while (resultsDiv.hasChildNodes()) resultsDiv.removeChild(resultsDiv.lastChild)
- val hintsDiv = document.createElement("div").asInstanceOf[html.Div]
- hintsDiv.classList.add("searchbar-hints")
- val icon = document.createElement("span").asInstanceOf[html.Span]
- icon.classList.add("fas")
- icon.classList.add("fa-lightbulb")
- icon.classList.add("fa-5x")
- val header = document.createElement("h1").asInstanceOf[html.Heading]
- header.textContent = "A bunch of hints to make your life easier"
- val listElements: ListRoot = ul(
- li("Type a phrase to search members by name and static sites by title"),
- li("Type abbreviations cC, caCa, camCa to search for camelCase"),
+ val hintsDiv = div(cls := "searchbar-hints")(
+ span(cls := "fas fa-lightbulb fa-5x"),
+ h1("A bunch of hints to make your life easier"),
+ ul(cls := "searchbar-hints-list")(
+ li("Type a phrase to search members ", b("by name")," and static sites ", b("by title"),""),
+ li("Type abbreviations", b("cC, caCa, camCa")," to search for ", b("camelCase")),
li(
- "Type a function signature to search for members by signature using Inkuire",
+ "Type a function signature to search for members ", b("by signature")," using Inkuire",
ul(
- li("Type String => Int to find String.size, String.toInt"),
- li("Type String => String => String to find String.mkString, String.stripPrefix"),
- li("Inkuire also finds field accessors. Type Some[A] => A to find Some.value"),
- li("For more information about Inkuire see the documentation"),
+ li("Type ", b("String => Int")," to find ", b("String.size"),", ", b("String.toInt"),""),
+ li("Type ", b("String => String => String")," to find ", b("String.mkString"),", ", b("String.stripPrefix"),""),
+ li("Inkuire also finds field accessors. Type ", b("Some[A] => A")," to find ", b("Some.value"),""),
+ li("For more information about Inkuire see ", a(href := "https://docs.scala-lang.org/scala3/guides/scaladoc/search-engine.html")("the documentation")),
li("The availability of this function depends on configuration used to generate Scaladoc")
)
)
+ )
)
-
- val list = renderList(listElements).get
- list.classList.add("searchbar-hints-list")
- hintsDiv.appendChild(icon)
- hintsDiv.appendChild(header)
- hintsDiv.appendChild(list)
clearResults()
resultsDiv.appendChild(hintsDiv)
}
diff --git a/scaladoc-js/main/src/social-links/SocialLinks.scala b/scaladoc-js/main/src/social-links/SocialLinks.scala
index 5fbe42eb99e7..3e33e4065955 100644
--- a/scaladoc-js/main/src/social-links/SocialLinks.scala
+++ b/scaladoc-js/main/src/social-links/SocialLinks.scala
@@ -3,10 +3,12 @@ package dotty.tools.scaladoc
import org.scalajs.dom._
import org.scalajs.dom.ext._
+import utils.HTML._
+
class SocialLinks:
def addIcon(elem: html.Element) =
- val img = document.createElement("img").asInstanceOf[html.Image]
- img.src = s"${Globals.pathToRoot}images/${elem.getAttribute("data-icon-path")}"
- elem.appendChild(img)
+ elem.appendChild(
+ img(src := s"${Globals.pathToRoot}images/${elem.getAttribute("data-icon-path")}")()
+ )
document.querySelectorAll(".social-icon").collect { case e: html.Element => e }.foreach(addIcon)
diff --git a/scaladoc-js/main/src/versions-dropdown/DropdownHandler.scala b/scaladoc-js/main/src/versions-dropdown/DropdownHandler.scala
index 53187cf066ab..54034fb5a589 100644
--- a/scaladoc-js/main/src/versions-dropdown/DropdownHandler.scala
+++ b/scaladoc-js/main/src/versions-dropdown/DropdownHandler.scala
@@ -11,6 +11,8 @@ import org.scalajs.dom.ext.Ajax
import scala.scalajs.js
import scala.scalajs.js.JSON
+import utils.HTML._
+
trait Versions extends js.Object:
def versions: js.Dictionary[String]
@@ -23,12 +25,9 @@ class DropdownHandler:
val ver = JSON.parse(json).asInstanceOf[Versions]
val ddc = document.getElementById("dropdown-content")
for (k, v) <- ver.versions do
- var child = document.createElement("a").asInstanceOf[html.Anchor]
- child.href = v
- child.text = k
+ var child = a(href := v)(k)
ddc.appendChild(child)
- val arrow = document.createElement("span").asInstanceOf[html.Span]
- arrow.classList.add("ar")
+ val arrow = span(cls := "ar")()
document.getElementById("dropdown-button").appendChild(arrow)
private def disableButton() =