aboutsummaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorBłażej Kardyś <bkardys@virtuslab.com>2020-01-20 17:13:28 +0100
committerPaweł Marks <Kordyjan@users.noreply.github.com>2020-01-31 15:07:06 +0100
commit71428219389d5fe429c0bad0bd31b2cda55cdfce (patch)
tree12a337a7ff55177cc2b4373b063c746310be2793 /core/src
parentc3b911f286186a790be607e9b803e3ed63c77289 (diff)
downloaddokka-71428219389d5fe429c0bad0bd31b2cda55cdfce.tar.gz
dokka-71428219389d5fe429c0bad0bd31b2cda55cdfce.tar.bz2
dokka-71428219389d5fe429c0bad0bd31b2cda55cdfce.zip
Adding changes to HTML UI render
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/kotlin/renderers/DefaultRenderer.kt1
-rw-r--r--core/src/main/kotlin/renderers/FileWriter.kt34
-rw-r--r--core/src/main/kotlin/renderers/HtmlRenderer.kt167
-rw-r--r--core/src/main/kotlin/renderers/OutputWriter.kt1
-rw-r--r--core/src/main/kotlin/resolvers/DefaultLocationProvider.kt4
-rw-r--r--core/src/main/kotlin/resolvers/LocationProvider.kt1
-rwxr-xr-xcore/src/main/resources/dokka/images/arrow_down.svg3
-rwxr-xr-xcore/src/main/resources/dokka/images/logo-icon.svg3
-rwxr-xr-xcore/src/main/resources/dokka/images/logo-text.svg6
-rw-r--r--core/src/main/resources/dokka/scripts/scripts.js11
-rw-r--r--core/src/main/resources/dokka/scripts/search.js5
-rw-r--r--core/src/main/resources/dokka/styles/style.css63
12 files changed, 273 insertions, 26 deletions
diff --git a/core/src/main/kotlin/renderers/DefaultRenderer.kt b/core/src/main/kotlin/renderers/DefaultRenderer.kt
index 1152bcc5..5e3eadbe 100644
--- a/core/src/main/kotlin/renderers/DefaultRenderer.kt
+++ b/core/src/main/kotlin/renderers/DefaultRenderer.kt
@@ -6,7 +6,6 @@ import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.single
import org.jetbrains.dokka.resolvers.LocationProvider
-
abstract class DefaultRenderer<T>(
protected val outputWriter: OutputWriter,
protected val context: DokkaContext
diff --git a/core/src/main/kotlin/renderers/FileWriter.kt b/core/src/main/kotlin/renderers/FileWriter.kt
index 5439db17..22a2e8f9 100644
--- a/core/src/main/kotlin/renderers/FileWriter.kt
+++ b/core/src/main/kotlin/renderers/FileWriter.kt
@@ -1,8 +1,10 @@
package org.jetbrains.dokka.renderers
+import com.intellij.util.io.isDirectory
import java.io.File
import java.io.IOException
-import java.nio.file.Paths
+import java.net.URI
+import java.nio.file.*
class FileWriter(val root: String, override val extension: String): OutputWriter {
private val createdFiles: MutableSet<String> = mutableSetOf()
@@ -19,15 +21,43 @@ class FileWriter(val root: String, override val extension: String): OutputWriter
val dir = Paths.get(root, path.dropLastWhile { it != '/' }).toFile()
dir.mkdirsOrFail()
Paths.get(root, "$path$ext").toFile().writeText(text)
- } catch (e : Throwable) {
+ } catch (e: Throwable) {
println("Failed to write $this. ${e.message}")
e.printStackTrace()
}
}
+ override fun writeResources(pathFrom: String, pathTo: String) {
+ val rebase = fun(path: String) =
+ "$pathTo/${path.removePrefix(pathFrom)}"
+ val dest = Paths.get(root, pathTo).toFile()
+ dest.mkdirsOrFail()
+ val uri = javaClass.getResource(pathFrom).toURI()
+ val fs = getFileSystemForURI(uri)
+ val path = fs.getPath(pathFrom)
+ for (file in Files.walk(path).iterator()) {
+ if (file.isDirectory()) {
+ val dirPath = file.toAbsolutePath().toString()
+ Paths.get(root, rebase(dirPath)).toFile().mkdirsOrFail()
+ } else {
+ val filePath = file.toAbsolutePath().toString()
+ Paths.get(root, rebase(filePath)).toFile().writeBytes(
+ javaClass.getResourceAsStream(filePath).readBytes()
+ )
+ }
+ }
+ }
+
private fun File.mkdirsOrFail() {
if (!mkdirs() && !exists()) {
throw IOException("Failed to create directory $this")
}
}
+
+ private fun getFileSystemForURI(uri: URI): FileSystem =
+ try {
+ FileSystems.newFileSystem(uri, emptyMap<String, Any>())
+ } catch (e: FileSystemAlreadyExistsException) {
+ FileSystems.getFileSystem(uri)
+ }
} \ No newline at end of file
diff --git a/core/src/main/kotlin/renderers/HtmlRenderer.kt b/core/src/main/kotlin/renderers/HtmlRenderer.kt
index 8742f202..ae120795 100644
--- a/core/src/main/kotlin/renderers/HtmlRenderer.kt
+++ b/core/src/main/kotlin/renderers/HtmlRenderer.kt
@@ -1,9 +1,13 @@
package org.jetbrains.dokka.renderers
import kotlinx.html.*
+import kotlinx.html.dom.document
import kotlinx.html.stream.appendHTML
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.Documentable
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import java.io.File
import java.net.URL
@@ -12,6 +16,29 @@ open class HtmlRenderer(
context: DokkaContext
) : DefaultRenderer<FlowContent>(outputWriter, context) {
+ private val pageList = mutableListOf<String>()
+
+ private var idCounter = 0
+ get() = ++field
+
+ private fun FlowContent.buildSideMenu(context: PageNode, node: PageNode) {
+ val children = node.children.filter { it !is MemberPageNode }
+ val className = children.ifNotEmpty { "nav$idCounter" }
+ div("sideMenuPart") {
+ className?.let { id = it }
+ div("overview") {
+ buildLink(node, context)
+ className?.let {
+ span("navButton") {
+ onClick = """document.getElementById("$it").classList.toggle("hidden");"""
+ span("navButtonContent")
+ }
+ }
+ }
+ children.forEach { buildSideMenu(context, it) }
+ }
+ }
+
override fun FlowContent.buildList(node: ContentList, pageContext: PageNode) =
if (node.ordered) ol {
buildListItems(node.children, pageContext)
@@ -89,25 +116,28 @@ open class HtmlRenderer(
}
}
- override fun FlowContent.buildNavigation(page: PageNode) {
+ override fun FlowContent.buildNavigation(page: PageNode) =
locationProvider.ancestors(page).forEach { node ->
text("/")
- buildLink(locationProvider.resolve(node, page)) {
- text(node.name)
- }
+ buildLink(node, page)
+ }
+
+ private fun FlowContent.buildLink(to: PageNode, from: PageNode) =
+ buildLink(locationProvider.resolve(to, from)) {
+ text(to.name)
}
- }
override fun buildError(node: ContentNode) {
context.logger.error("Unknown ContentNode type: $node")
}
- override fun FlowContent.buildNewLine() { br() }
-
- override fun FlowContent.buildLink(address: String, content: FlowContent.() -> Unit) {
- a (href = address, block = content)
+ override fun FlowContent.buildNewLine() {
+ br()
}
+ override fun FlowContent.buildLink(address: String, content: FlowContent.() -> Unit) =
+ a(href = address, block = content)
+
override fun FlowContent.buildCode(code: List<ContentNode>, language: String, pageContext: PageNode) {
buildNewLine()
code.forEach {
@@ -116,29 +146,126 @@ open class HtmlRenderer(
}
}
+ override fun renderPage(page: PageNode) {
+ super.renderPage(page)
+ pageList.add("""{ "name": "${page.name}", ${if (page is ClassPageNode) "\"class\": \"${page.name}\"," else ""} "location": "${locationProvider.resolve(page)}" }""")
+ }
+
override fun FlowContent.buildText(textNode: ContentText) {
text(textNode.text)
}
+ override fun render(root: PageNode) {
+ super.render(root)
+ outputWriter.write("scripts/pages", "var pages = [\n${pageList.joinToString(",\n")}\n]", ".js")
+ }
override fun buildSupportFiles() { // TODO copy file instead of reading
outputWriter.write(
"style.css",
javaClass.getResourceAsStream("/dokka/styles/style.css").reader().readText()
)
+ renderPage(searchPageNode)
+ outputWriter.writeResources("/dokka/styles", "styles")
+ outputWriter.writeResources("/dokka/scripts", "scripts")
+ outputWriter.writeResources("/dokka/images", "images")
}
- override fun buildPage(page: PageNode, content: (FlowContent, PageNode) -> Unit): String = StringBuilder().appendHTML().html {
- head {
- title(page.name)
- link(rel = LinkRel.stylesheet, href = "${locationProvider.resolveRoot(page)}style.css")
- page.embeddedResources.filter { URL(it).path.substringAfterLast('.') == "js" }
- .forEach { script(type = ScriptType.textJavaScript, src = it) { async = true } }
- }
- body {
- content(this, page)
- }
- }.toString()
+ private fun PageNode.root(path: String) =
+ "${if (this != searchPageNode) locationProvider.resolveRoot(this) else ""}$path"
+
+ override fun buildPage(page: PageNode, content: (FlowContent, PageNode) -> Unit): String =
+ StringBuilder().appendHTML().html {
+ document {
+
+ }
+ head {
+ title(page.name)
+ link(rel = LinkRel.stylesheet, href = page.root("styles/style.css"))
+ page.embeddedResources.filter {
+ URL(it).path.substringAfterLast('.') == "js"
+ }.forEach {
+ script(type = ScriptType.textJavaScript, src = it) { async = true }
+ }
+ if (page == searchPageNode) {
+ script(
+ type = ScriptType.textJavaScript,
+ src = page.root("scripts/pages.js")
+ ) { async = true }
+ }
+ }
+ body {
+ div {
+ id = "navigation"
+ div {
+ id = "searchBar"
+ form(action = page.root("-search.html"), method = FormMethod.get) {
+ id = "searchForm"
+ input(type = InputType.search, name = "query")
+ input(type = InputType.submit) { value = "Search" }
+ }
+ }
+ div {
+ id = "sideMenu"
+ img(src = page.root("images/logo-icon.svg"))
+ img(src = page.root("images/logo-text.svg"))
+ hr()
+ input(type = InputType.search) {
+ id = "navigationFilter"
+ }
+ script(
+ type = ScriptType.textJavaScript,
+ src = page.root("scripts/scripts.js")
+ ) { async = true }
+ buildSideMenu(page, locationProvider.top())
+ }
+ }
+ div {
+ id = "content"
+ if (page != searchPageNode) content(this, page)
+ else {
+ h1 {
+ id = "searchTitle"
+ text("Search results for ")
+ }
+ table {
+ tbody {
+ id = "searchTable"
+ }
+ }
+ script(
+ type = ScriptType.textJavaScript,
+ src = page.root("scripts/search.js")
+ ) { async = true }
+ }
+ }
+ }
+ }.toString()
protected open fun List<HTMLMetadata>.joinAttr() = this.joinToString(" ") { it.key + "=" + it.value }
+
+ private val searchPageNode =
+ object: PageNode {
+ override val name: String
+ get() = "Search"
+ override val content = object: ContentNode{
+ override val dci: DCI = DCI(DRI.topLevel, ContentKind.Main)
+ override val platforms: Set<PlatformData> = emptySet()
+ override val style: Set<Style> = emptySet()
+ override val extras: Set<Extra> = emptySet()
+
+ }
+ override val dri: DRI = DRI.topLevel
+ override val documentable: Documentable? = null
+ override val embeddedResources: List<String> = emptyList()
+ override val children: List<PageNode> = emptyList()
+
+ override fun modified(
+ name: String,
+ content: ContentNode,
+ embeddedResources: List<String>,
+ children: List<PageNode>
+ ): PageNode = this
+
+ }
} \ No newline at end of file
diff --git a/core/src/main/kotlin/renderers/OutputWriter.kt b/core/src/main/kotlin/renderers/OutputWriter.kt
index 84cc124d..8ef7e8b2 100644
--- a/core/src/main/kotlin/renderers/OutputWriter.kt
+++ b/core/src/main/kotlin/renderers/OutputWriter.kt
@@ -3,4 +3,5 @@ package org.jetbrains.dokka.renderers
interface OutputWriter{
val extension: String
fun write(path: String, text: String, ext: String = extension)
+ fun writeResources(pathFrom: String, pathTo: String)
} \ No newline at end of file
diff --git a/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt b/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt
index d7089f96..3412d975 100644
--- a/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt
+++ b/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt
@@ -38,6 +38,8 @@ open class DefaultLocationProvider(
else -> ancestors(node.parent()) + node
}
+ override fun top(): PageNode = pageGraphRoot
+
protected open fun findInPageGraph(dri: DRI, platforms: List<PlatformData>): PageNode? =
pageGraphRoot.dfs { it.dri == dri }
@@ -52,7 +54,7 @@ open class DefaultLocationProvider(
else -> getPath(pathNode.parent(), path + pathNode.pathName().ifEmpty { "root" })
}
- val contextNode = if (context?.children?.isEmpty() == true) context.parent() else context
+ val contextNode = if (context?.children?.isEmpty() == true && context.parent() != null) context.parent() else context
val nodePath = getPath(node).reversed()
val contextPath = getPath(contextNode).reversed()
diff --git a/core/src/main/kotlin/resolvers/LocationProvider.kt b/core/src/main/kotlin/resolvers/LocationProvider.kt
index 2da2310d..7d77ccb8 100644
--- a/core/src/main/kotlin/resolvers/LocationProvider.kt
+++ b/core/src/main/kotlin/resolvers/LocationProvider.kt
@@ -9,4 +9,5 @@ interface LocationProvider {
fun resolve(node: PageNode, context: PageNode? = null): String
fun resolveRoot(node: PageNode): String
fun ancestors(node: PageNode?): List<PageNode>
+ fun top(): PageNode
}
diff --git a/core/src/main/resources/dokka/images/arrow_down.svg b/core/src/main/resources/dokka/images/arrow_down.svg
new file mode 100755
index 00000000..89e7df47
--- /dev/null
+++ b/core/src/main/resources/dokka/images/arrow_down.svg
@@ -0,0 +1,3 @@
+<svg width="10" height="7" viewBox="0 0 10 7" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9.71824 1.66658L9.01113 0.959473L5.00497 4.96447L1.00008 0.959473L0.292969 1.66658L5.01113 6.38474L9.71824 1.66658Z" fill="#A1AAB4"/>
+</svg>
diff --git a/core/src/main/resources/dokka/images/logo-icon.svg b/core/src/main/resources/dokka/images/logo-icon.svg
new file mode 100755
index 00000000..1b3b3670
--- /dev/null
+++ b/core/src/main/resources/dokka/images/logo-icon.svg
@@ -0,0 +1,3 @@
+<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M26 26H0V0H26L12.9243 12.9747L26 26Z" fill="#F8873C"/>
+</svg>
diff --git a/core/src/main/resources/dokka/images/logo-text.svg b/core/src/main/resources/dokka/images/logo-text.svg
new file mode 100755
index 00000000..7bf3e6c5
--- /dev/null
+++ b/core/src/main/resources/dokka/images/logo-text.svg
@@ -0,0 +1,6 @@
+<svg width="83" height="27" viewBox="0 0 83 27" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M47.1611 7.6297V25.6345V25.6867H61.8428V21.8039H51.3589V10.3852H61.8428V6.50244H47.1611V7.6297Z" fill="#27282C"/>
+<path d="M82.9891 21.8039L72.778 10.3852H82.9051V6.50244H67.0586V10.3852L77.4585 21.8039H67.0586V25.6867H82.9996V21.8039H82.9891Z" fill="#27282C"/>
+<path d="M16.2978 7.76556C14.5872 6.46086 12.4463 5.67804 10.1271 5.67804C4.53357 5.67804 0 10.1871 0 15.7503C0 21.3135 4.53357 25.8226 10.1271 25.8226C12.4463 25.8226 14.5872 25.0502 16.2978 23.735V25.7182H20.4955V0H16.2978V7.76556ZM10.1271 21.8041C6.75838 21.8041 4.02984 19.0903 4.02984 15.7399C4.02984 12.3894 6.75838 9.67563 10.1271 9.67563C13.4958 9.67563 16.2243 12.3894 16.2243 15.7399C16.2138 19.0903 13.4853 21.8041 10.1271 21.8041Z" fill="#27282C"/>
+<path d="M33.9703 5.86566C28.3768 5.86566 23.8433 10.3747 23.8433 15.9379C23.8433 21.5011 28.3768 26.0102 33.9703 26.0102C39.5638 26.0102 44.0974 21.5011 44.0974 15.9379C44.0974 10.3747 39.5638 5.86566 33.9703 5.86566ZM33.9703 21.9917C30.6016 21.9917 27.8731 19.2779 27.8731 15.9275C27.8731 12.577 30.6016 9.86325 33.9703 9.86325C37.339 9.86325 40.0676 12.577 40.0676 15.9275C40.0676 19.2779 37.339 21.9917 33.9703 21.9917Z" fill="#27282C"/>
+</svg>
diff --git a/core/src/main/resources/dokka/scripts/scripts.js b/core/src/main/resources/dokka/scripts/scripts.js
new file mode 100644
index 00000000..c2e29b9f
--- /dev/null
+++ b/core/src/main/resources/dokka/scripts/scripts.js
@@ -0,0 +1,11 @@
+document.getElementById("navigationFilter").oninput = function (e) {
+ var input = e.target.value;
+ var menuParts = document.getElementsByClassName("sideMenuPart")
+ for (let part of menuParts) {
+ if(part.querySelector("a").textContent.startsWith(input)) {
+ part.classList.remove("filtered");
+ } else {
+ part.classList.add("filtered");
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/main/resources/dokka/scripts/search.js b/core/src/main/resources/dokka/scripts/search.js
new file mode 100644
index 00000000..63112ac5
--- /dev/null
+++ b/core/src/main/resources/dokka/scripts/search.js
@@ -0,0 +1,5 @@
+var query = new URLSearchParams(window.location.search).get("query");
+ document.getElementById("searchTitle").innerHTML += '"' + query + '":';
+ document.getElementById("searchTable").innerHTML = pages.filter(el => el.name.startsWith(query)).reduce((acc, element) => { return acc +
+ '<tr><td><a href="' + element.location + '">' + element.name + '</a></td></tr>'
+ }, ""); \ No newline at end of file
diff --git a/core/src/main/resources/dokka/styles/style.css b/core/src/main/resources/dokka/styles/style.css
index 60ea133f..035b4fcd 100644
--- a/core/src/main/resources/dokka/styles/style.css
+++ b/core/src/main/resources/dokka/styles/style.css
@@ -1,9 +1,68 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300i,400,700);
+
+#content {
+ margin-top: 3em;
+ margin-left: 15em;
+}
+
+#navigation {
+ position: relative
+}
+
+#sideMenu, #searchBar {
+ position: absolute;
+}
+
+#sideMenu {
+ width: 14em;
+ padding-left: 0.5em;
+}
+
+#sideMenu .sideMenuPart {
+ margin-left: 0.25em;
+}
+
+#sideMenu img {
+ margin: 1em 0.25em;
+}
+
+#sideMenu hr {
+ background: #DADFE6;
+}
+
+#searchBar {
+ width: 100%;
+}
+
+#searchForm {
+ float: right;
+}
+
+.sideMenuPart > .navButton {
+ margin-left:0.25em
+}
+
+.sideMenuPart > .overview .navButtonContent::after {
+ float: right;
+ content: url("../images/arrow_down.svg");
+}
+
+.sideMenuPart.hidden > .navButton .navButtonContent::after {
+ content: '\02192';
+}
+
+.sideMenuPart.hidden > .sideMenuPart {
+ display: none;
+}
+
+.filtered > a, .filtered > .navButton {
+ display: none;
+}
+
body, table{
- padding:50px;
font:14px/1.5 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif;
- color:#555;
+ background: #F4F4F4;
font-weight:300;
margin-left: auto;
margin-right: auto;