diff options
author | Filip Zybała <fzybala@virtuslab.com> | 2020-05-21 09:51:54 +0200 |
---|---|---|
committer | Paweł Marks <Kordyjan@users.noreply.github.com> | 2020-05-28 16:07:46 +0200 |
commit | 0f1c461d20336444bc667713954ea2879cc0a396 (patch) | |
tree | d1adc3fa2b4178f78147bde1fa0e06875d4b1930 /plugins/base | |
parent | 5f9299b074355e3f636da6eb6e1f9283f06ab8c7 (diff) | |
download | dokka-0f1c461d20336444bc667713954ea2879cc0a396.tar.gz dokka-0f1c461d20336444bc667713954ea2879cc0a396.tar.bz2 dokka-0f1c461d20336444bc667713954ea2879cc0a396.zip |
Added filtering to HTML pages
Diffstat (limited to 'plugins/base')
5 files changed, 195 insertions, 4 deletions
diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index a6052a9e..382b8f86 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -174,4 +174,10 @@ class DokkaBase : DokkaPlugin() { ) } order { after(rootCreator) } } + + val sourcesetDependencyAppender by extending { + htmlPreprocessors providing ::SourcesetDependencyAppender order { after(rootCreator)} + } + + }
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index 52f7024a..b089c71a 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -63,13 +63,35 @@ open class HtmlRenderer( } node.dci.kind == ContentKind.Symbol -> div("symbol $additionalClasses") { childrenCallback() } node.dci.kind == ContentKind.BriefComment -> div("brief $additionalClasses") { childrenCallback() } - node.dci.kind == ContentKind.Cover -> div("cover $additionalClasses") { childrenCallback() } + node.dci.kind == ContentKind.Cover -> div("cover $additionalClasses") { + filterButtons(node) + childrenCallback() + } node.hasStyle(TextStyle.Paragraph) -> p(additionalClasses) { childrenCallback() } node.hasStyle(TextStyle.Block) -> div(additionalClasses) { childrenCallback() } else -> childrenCallback() } } + private fun FlowContent.filterButtons(group: ContentGroup) { + div(classes = "filter-section") { + id = "filter-section" + group.sourceSets.forEach { + button(classes = "platform-tag platform-selector") { + attributes["data-active"] = "" + attributes["data-filter"] = it.sourceSetName + when(it.platform.key){ + "common" -> classes = classes + "common-like" + "native" -> classes = classes + "native-like" + "jvm" -> classes = classes + "jvm-like" + "js" -> classes = classes + "js-like" + } + text(it.sourceSetName) + } + } + } + } + override fun FlowContent.buildPlatformDependent(content: PlatformHintedContent, pageContext: ContentPage) = buildPlatformDependent(content.sourceSets.map { it to setOf(content.inner) }.toMap(), pageContext, content.extra) @@ -108,6 +130,8 @@ open class HtmlRenderer( attributes["data-toggle-list"] = "data-toggle-list" contents.forEachIndexed { index, pair -> button(classes = "platform-bookmark") { + attributes["data-filterable-current"] = pair.first.sourceSetName + attributes["data-filterable-set"] = pair.first.sourceSetName if (index == 0) attributes["data-active"] = "" attributes["data-toggle"] = pair.first.sourceSetName when( @@ -160,8 +184,15 @@ open class HtmlRenderer( consumer.onTagContentUnsafe { +createHTML().div("divergent-group"){ + attributes["data-filterable-current"] = groupedDivergent.keys.joinToString(" ") { + it.sourceSetName + } + attributes["data-filterable-set"] = groupedDivergent.keys.joinToString(" ") { + it.sourceSetName + } consumer.onTagContentUnsafe { +it.key.first } div("main-subrow") { + if (node.implicitlySourceSetHinted) { buildPlatformDependent( groupedDivergent.map { (sourceSet, elements) -> @@ -242,6 +273,12 @@ open class HtmlRenderer( ?.let { withAnchor(node.dci.dri.first().toString()) { div(classes = "table-row") { + attributes["data-filterable-current"] = node.sourceSets.joinToString(" ") { + it.sourceSetName + } + attributes["data-filterable-set"] = node.sourceSets.joinToString(" ") { + it.sourceSetName + } it.filterIsInstance<ContentLink>().takeIf { it.isNotEmpty() }?.let { div("main-subrow " + node.style.joinToString(" ")) { it.filter { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } } diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt index 710407c5..45ef1457 100644 --- a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt +++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt @@ -6,6 +6,7 @@ import kotlinx.html.table import kotlinx.html.tbody import org.jetbrains.dokka.base.renderers.platforms import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.pages.PageTransformer @@ -66,8 +67,33 @@ object StyleAndScriptsAppender : PageTransformer { "styles/style.css", "scripts/navigationLoader.js", "scripts/platformContentHandler.js", + "scripts/sourceset_dependencies.js", "styles/jetbrains-mono.css" ) ) } +} + +class SourcesetDependencyAppender(val context: DokkaContext) : PageTransformer{ + override fun invoke(input: RootPageNode): RootPageNode { + val dependenciesMap = context.configuration.passesConfigurations.map { + it.sourceSetName to it.dependentSourceSets + }.toMap() + fun createDependenciesJson() : String = "sourceset_dependencies = '{${ + dependenciesMap.entries.joinToString(", ") { + "\"${it.key}\": [${it.value.joinToString(","){ + "\"$it\"" + }}]" + } + }}'" + val deps = RendererSpecificResourcePage( + name = "scripts/sourceset_dependencies.js", + children = emptyList(), + strategy = RenderingStrategy.Write(createDependenciesJson()) + ) + + return input.modified( + children = input.children + deps + ) + } }
\ No newline at end of file diff --git a/plugins/base/src/main/resources/dokka/scripts/platformContentHandler.js b/plugins/base/src/main/resources/dokka/scripts/platformContentHandler.js index 72c8daae..cd993587 100644 --- a/plugins/base/src/main/resources/dokka/scripts/platformContentHandler.js +++ b/plugins/base/src/main/resources/dokka/scripts/platformContentHandler.js @@ -1,13 +1,71 @@ +filteringContext = { + dependencies: {}, + restrictedDependencies: [], + activeFilters: [] +} window.addEventListener('load', () => { + initializeFiltering() document.querySelectorAll("div[data-platform-hinted]") .forEach(elem => elem.addEventListener('click', (event) => togglePlatformDependent(event,elem))) document.querySelectorAll("div[tabs-section]") - .forEach(elem => elem.addEventListener('click', (event) => toggleSections(event))) + .forEach(elem => elem.addEventListener('click', (event) => toggleSections(event))) + document.getElementById('filter-section').addEventListener('click', (event) => filterButtonHandler(event)) document.querySelector(".tabs-section-body") .querySelector("div[data-togglable]") .setAttribute("data-active", "") }) +function filterButtonHandler(event) { + if(event.target.tagName == "BUTTON" && event.target.hasAttribute("data-filter")) { + let sourceset = event.target.getAttribute("data-filter") + if(filteringContext.activeFilters.indexOf(sourceset) != -1) { + filterSourceset(sourceset) + } else { + unfilterSourceset(sourceset) + } + } + refreshFilterButtons(); + refreshPlatformTabs() +} + +function initializeFiltering() { + filteringContext.dependencies = JSON.parse(sourceset_dependencies) + document.querySelectorAll("#filter-section > button") + .forEach(p => filteringContext.restrictedDependencies.push(p.getAttribute("data-filter"))) + Object.keys(filteringContext.dependencies).forEach(p => { + filteringContext.dependencies[p] = filteringContext.dependencies[p] + .filter(q => -1 !== filteringContext.restrictedDependencies.indexOf(q)) + }) + filteringContext.activeFilters = filteringContext.restrictedDependencies +} + +function refreshFilterButtons() { + document.querySelectorAll("#filter-section > button") + .forEach(f => { + if(filteringContext.activeFilters.indexOf(f.getAttribute("data-filter")) != -1){ + f.setAttribute("data-active","") + } else { + f.removeAttribute("data-active") + } + }) +} + +function filterSourceset(sourceset) { + filteringContext.activeFilters = filteringContext.activeFilters.filter(p => p != sourceset) + refreshFiltering() +} + +function unfilterSourceset(sourceset) { + if(filteringContext.activeFilters.length == 0) { + filteringContext.activeFilters = filteringContext.dependencies[sourceset].concat([sourceset]) + refreshFiltering() + } else { + filteringContext.activeFilters.push(sourceset) + refreshFiltering() + } +} + + function toggleSections(evt){ if(!evt.target.getAttribute("data-togglable")) return @@ -50,3 +108,38 @@ function togglePlatformDependent(e, container) { } } } + +function refreshFiltering() { + let sourcesetList = filteringContext.activeFilters + document.querySelectorAll("[data-filterable-set]") + .forEach( + elem => { + let platformList = elem.getAttribute("data-filterable-set").split(' ').filter(v => -1 !== sourcesetList.indexOf(v)) + elem.setAttribute("data-filterable-current", platformList.join(' ')) + } + ) +} + +function refreshPlatformTabs() { + document.querySelectorAll(".platform-hinted > .platform-bookmarks-row").forEach( + p => { + let active = false; + let firstAvailable = null + p.childNodes.forEach( + element => { + if(element.getAttribute("data-filterable-current") != ''){ + if( firstAvailable == null) { + firstAvailable = element + } + if(element.hasAttribute("data-active")) { + active = true; + } + } + } + ) + if( active == false && firstAvailable !== null ) { + firstAvailable.click() + } + } + ) +} diff --git a/plugins/base/src/main/resources/dokka/styles/style.css b/plugins/base/src/main/resources/dokka/styles/style.css index 671dfe8a..d9927046 100644 --- a/plugins/base/src/main/resources/dokka/styles/style.css +++ b/plugins/base/src/main/resources/dokka/styles/style.css @@ -62,6 +62,11 @@ padding: 24px 0 } +.cover { + display: flex; + flex-direction: column; +} + .tabbedcontent { padding: 24px 0; } @@ -483,9 +488,11 @@ footer { display: flex; flex-direction: row; padding: 4px 8px; - height: 16px; + height: 24px; border-radius: 100px; - + box-sizing: border-box; + border: 1px solid transparent; + margin: 2px; font-family: Inter, Arial, sans-serif; font-size: 12px; font-weight: 400; @@ -494,6 +501,7 @@ footer { line-height: normal; letter-spacing: normal; text-align: center; + outline: none; color: #fff @@ -528,6 +536,27 @@ footer { color: white; } +.filter-section { + display: flex; + display: flex; + flex-direction: row; + align-self: flex-end; + min-height: 30px; +} + +.platform-selector:hover { + border: 1px solid #A6AFBA !important; +} + +[data-filterable-current=''] { + display: none !important; +} +.platform-selector:not([data-active]) { + border: 1px solid #DADFE6; + background-color: white; + color: #637282; +} + td.content { padding-left: 24px; padding-top: 16px; |