From 885bf34ac6d9b9a5974cab35d9dd5a224b0ccc4c Mon Sep 17 00:00:00 2001 From: Marcin Aman Date: Tue, 19 May 2020 10:23:18 +0200 Subject: Tabs for sections v1 --- .../src/main/kotlin/renderers/html/HtmlRenderer.kt | 73 +++++-- .../pages/sourcelinks/SourceLinksTransformer.kt | 26 ++- .../documentables/DefaultPageCreator.kt | 237 ++++++++++++--------- 3 files changed, 206 insertions(+), 130 deletions(-) (limited to 'plugins/base') diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index c62c5d3c..792eb1dc 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -7,6 +7,7 @@ import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.renderers.DefaultRenderer import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.SourceSetData +import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin @@ -36,6 +37,30 @@ open class HtmlRenderer( ) { val additionalClasses = node.style.joinToString(" ") { it.toString().toLowerCase() } return when { + node.hasStyle(ContentStyle.TabbedContent) -> div(additionalClasses) { + val secondLevel = node.children.filterIsInstance().flatMap { it.children }.filterIsInstance().flatMap { it.children }.filterIsInstance() + val firstLevel = node.children.filterIsInstance().flatMap { it.children }.filterIsInstance() + + val renderable = firstLevel.union(secondLevel) + + div(classes = "tabs-section"){ + attributes["tabs-section"] = "tabs-section" + renderable.forEachIndexed { index, node -> + button(classes = "section-tab"){ + if(index == 0 ) attributes["data-active"] = "" + attributes["data-togglable"] = node.text + text(node.text) + } + } + } + div(classes = "tabs-section-body"){ + childrenCallback() + } + } + node.hasStyle(ContentStyle.WithExtraAttributes) -> div() { + node.extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue } + childrenCallback() + } 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() } @@ -46,15 +71,18 @@ open class HtmlRenderer( } override fun FlowContent.buildPlatformDependent(content: PlatformHintedContent, pageContext: ContentPage) = - buildPlatformDependent(content.sourceSets.map { it to setOf(content.inner) }.toMap(), pageContext) + buildPlatformDependent(content.sourceSets.map { it to setOf(content.inner) }.toMap(), pageContext, content.extra) private fun FlowContent.buildPlatformDependent( nodes: Map>, - pageContext: ContentPage + pageContext: ContentPage, + extra: PropertyContainer = PropertyContainer.empty() ) { var mergedToOneSourceSet : SourceSetData? = null div("platform-hinted") { attributes["data-platform-hinted"] = "data-platform-hinted" + extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue } + val additionalClasses = if(nodes.toList().size == 1) "single-content" else "" var counter = 0 val contents = nodes.toList().map { (sourceSet, elements) -> sourceSet to createHTML(prettyPrint = false).div { @@ -66,7 +94,7 @@ open class HtmlRenderer( sourceSets.filterNot { sourceSetDependencyMap[it].orEmpty().any { dependency -> sourceSets.contains(dependency) } }.map { - it to createHTML(prettyPrint = false).div(classes = "content") { + it to createHTML(prettyPrint = false).div(classes = "content $additionalClasses") { if (counter++ == 0) attributes["data-active"] = "" attributes["data-togglable"] = it.sourceSetName unsafe { @@ -130,27 +158,29 @@ open class HtmlRenderer( distinct.forEach { val groupedDivergent = it.value.groupBy { it.second } - consumer.onTagContentUnsafe { +it.key.first } consumer.onTagContentUnsafe { - +createHTML(prettyPrint = false).div("main-subrow") { - if (node.implicitlySourceSetHinted) { - buildPlatformDependent( - groupedDivergent.map { (sourceSet, elements) -> - sourceSet to elements.map { e -> e.first.divergent } - }.toMap(), - pageContext - ) - if (distinct.size > 1 && groupedDivergent.size == 1) { - createPlatformTags(node, groupedDivergent.keys) - } - } else { - it.value.forEach { - buildContentNode(it.first.divergent, pageContext, setOf(it.second)) + +createHTML().div("divergent-group"){ + consumer.onTagContentUnsafe { +it.key.first } + div("main-subrow") { + if (node.implicitlySourceSetHinted) { + buildPlatformDependent( + groupedDivergent.map { (sourceSet, elements) -> + sourceSet to elements.map { e -> e.first.divergent } + }.toMap(), + pageContext + ) + if (distinct.size > 1 && groupedDivergent.size == 1) { + createPlatformTags(node, groupedDivergent.keys) + } + } else { + it.value.forEach { + buildContentNode(it.first.divergent, pageContext, setOf(it.second)) + } } } + consumer.onTagContentUnsafe { +it.key.second } } } - consumer.onTagContentUnsafe { +it.key.second } } } @@ -273,6 +303,7 @@ open class HtmlRenderer( sourceSetRestriction: Set? ) { div(classes = "table") { + node.extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue } node.children.forEach { buildRow(it, pageContext, sourceSetRestriction) } @@ -435,4 +466,6 @@ fun List.joinAttr() = joinToString(" ") { it.extraKey + "=" + it.ext private fun String.stripDiv() = drop(5).dropLast(6) // TODO: Find a way to do it without arbitrary trims private val PageNode.isNavigable: Boolean - get() = this !is RendererSpecificPage || strategy != RenderingStrategy.DoNothing \ No newline at end of file + get() = this !is RendererSpecificPage || strategy != RenderingStrategy.DoNothing + +fun PropertyContainer.extraHtmlAttributes() = allOfType() \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt index c0b8233c..e324c5bb 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt @@ -70,7 +70,8 @@ class SourceLinksTransformer(val context: DokkaContext, val builder: PageContent }, DCI(node.dri, ContentKind.Source), node.documentable!!.sourceSets.toSet(), - style = emptySet() + style = emptySet(), + extra = mainExtra + SimpleAttr.header("Sources") ) } @@ -91,9 +92,22 @@ class SourceLinksTransformer(val context: DokkaContext, val builder: PageContent private fun ContentNode.addTable(table: ContentGroup): ContentNode = when (this) { - is ContentGroup -> copy( - children = children + table - ) + is ContentGroup -> { + if(hasTabbedContent()){ + copy( + children = children.map { + if(it.hasStyle(ContentStyle.TabbedContent) && it is ContentGroup){ + it.copy(children = it.children + table) + } else { + it + } + } + ) + } else { + copy(children = children + table) + } + + } else -> ContentGroup( children = listOf(this, table), extra = this.extra, @@ -114,4 +128,6 @@ data class SourceLink(val path: String, val url: String, val lineSuffix: String? constructor(sourceLinkDefinition: DokkaConfiguration.SourceLinkDefinition, sourceSetData: SourceSetData) : this( sourceLinkDefinition.path, sourceLinkDefinition.url, sourceLinkDefinition.lineSuffix, sourceSetData ) -} \ No newline at end of file +} + +fun ContentGroup.hasTabbedContent(): Boolean = children.any { it.hasStyle(ContentStyle.TabbedContent) } \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index d4ee88d8..409b0d6f 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -7,6 +7,7 @@ import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.Doc import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.model.properties.WithExtraProperties import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.utilities.DokkaLogger @@ -67,6 +68,7 @@ open class DefaultPageCreator( group(kind = ContentKind.Cover) { header(1, m.name) } + +contentForDescription(m) +contentForComments(m) block("Packages", 2, ContentKind.Packages, m.packages, m.sourceSets.toSet()) { link(it.name, it.dri) @@ -78,14 +80,17 @@ open class DefaultPageCreator( protected open fun contentForPackage(p: DPackage) = contentBuilder.contentFor(p) { group(kind = ContentKind.Cover) { header(1, "Package ${p.name}") + +contentForDescription(p) } - +contentForComments(p) - +contentForScope(p, p.dri, p.sourceSets) - block("Type aliases", 2, ContentKind.TypeAliases, p.typealiases, p.sourceSets.toSet()) { - link(it.name, it.dri, kind = ContentKind.Main) - sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint) { - +buildSignature(it) - contentForBrief(it) + group(styles = setOf(ContentStyle.TabbedContent)){ + +contentForComments(p) + +contentForScope(p, p.dri, p.sourceSets) + block("Type aliases", 2, ContentKind.TypeAliases, p.typealiases, p.sourceSets.toSet(), extra = mainExtra + SimpleAttr.header("Type aliases")) { + link(it.name, it.dri, kind = ContentKind.Main) + sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint, styles = emptySet()) { + +buildSignature(it) + contentForBrief(it) + } } } } @@ -95,9 +100,9 @@ open class DefaultPageCreator( dri: DRI, sourceSets: List ) = contentBuilder.contentFor(s as Documentable) { - divergentBlock("Types", s.classlikes, ContentKind.Classlikes) - divergentBlock("Functions", s.functions, ContentKind.Functions) - block("Properties", 2, ContentKind.Properties, s.properties, sourceSets.toSet()) { + divergentBlock("Types", s.classlikes, ContentKind.Classlikes, extra = mainExtra + SimpleAttr.header("Types")) + divergentBlock("Functions", s.functions, ContentKind.Functions, extra = mainExtra + SimpleAttr.header( "Functions")) + block("Properties", 2, ContentKind.Properties, s.properties, sourceSets.toSet(), extra = mainExtra + SimpleAttr.header( "Properties")) { link(it.name, it.dri, kind = ContentKind.Main) sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint) { +buildSignature(it) @@ -122,7 +127,8 @@ open class DefaultPageCreator( }, DCI(setOf(dri), ContentKind.Inheritors), sourceSets.toSet(), - style = emptySet() + style = emptySet(), + extra = mainExtra + SimpleAttr.header( "Inheritors") ) } } @@ -132,9 +138,12 @@ open class DefaultPageCreator( group(kind = ContentKind.Cover) { header(1, e.name) +buildSignature(e) + +contentForDescription(e) + } + group(styles = setOf(ContentStyle.TabbedContent)){ + +contentForComments(e) + +contentForScope(e, e.dri, e.sourceSets) } - +contentForComments(e) - +contentForScope(e, e.dri, e.sourceSets) } protected open fun contentForClasslike(c: DClasslike) = contentBuilder.contentFor(c) { @@ -142,36 +151,39 @@ open class DefaultPageCreator( header(1, c.name.orEmpty()) sourceSetDependentHint(c.dri, c.sourceSets.toSet()) { +buildSignature(c) + +contentForDescription(c) } } - +contentForComments(c) - - if (c is WithConstructors) { - block( - "Constructors", - 2, - ContentKind.Constructors, - c.constructors.filter { it.extra[PrimaryConstructorExtra] == null }, - c.sourceSets.toSet() - ) { - link(it.name, it.dri, kind = ContentKind.Main) - sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint) { - +buildSignature(it) - contentForBrief(it) + + group(styles = setOf(ContentStyle.TabbedContent)) { + +contentForComments(c) + if (c is WithConstructors) { + block( + "Constructors", + 2, + ContentKind.Constructors, + c.constructors.filter { it.extra[PrimaryConstructorExtra] == null }, + c.sourceSets.toSet(), + extra = PropertyContainer.empty() + SimpleAttr.header("Constructors") + ) { + link(it.name, it.dri, kind = ContentKind.Main) + sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint, styles = emptySet()) { + +buildSignature(it) + contentForBrief(it) + } } } - } - if (c is DEnum) { - block("Entries", 2, ContentKind.Classlikes, c.entries, c.sourceSets.toSet()) { - link(it.name, it.dri) - sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint) { - +buildSignature(it) - contentForBrief(it) + if (c is DEnum) { + block("Entries", 2, ContentKind.Classlikes, c.entries, c.sourceSets.toSet(), extra = mainExtra + SimpleAttr.header("Entries"), styles = emptySet()) { + link(it.name, it.dri) + sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint) { + +buildSignature(it) + contentForBrief(it) + } } } + +contentForScope(c, c.dri, c.sourceSets) } - - +contentForScope(c, c.dri, c.sourceSets) } @Suppress("UNCHECKED_CAST") @@ -188,16 +200,16 @@ open class DefaultPageCreator( private inline fun GroupedTags.isNotEmptyForTag(): Boolean = this[T::class]?.isNotEmpty() ?: false - protected open fun contentForComments( + protected open fun contentForDescription( d: Documentable - ): List { + ): ContentNode { val tags: GroupedTags = d.documentation.flatMap { (pd, doc) -> doc.children.asSequence().map { pd to it }.toList() }.groupBy { it.second::class } val platforms = d.sourceSets - fun DocumentableContentBuilder.contentForDescription() { + return contentBuilder.contentFor(d) { val description = tags.withTypeUnnamed() if (description.any { it.value.root.children.isNotEmpty() }) { platforms.forEach { platform -> @@ -208,34 +220,60 @@ open class DefaultPageCreator( } } } + + val unnamedTags: List> = + tags.filterNot { (k, _) -> k.isSubclassOf(NamedTagWrapper::class) || k in specialTags } + .map { (_, v) -> v.mapNotNull { (k,v) -> k?.let { it to v } }.toMap() } + platforms.forEach { platform -> + unnamedTags.forEach { pdTag -> + pdTag[platform]?.also { tag -> + group(sourceSets = setOf(platform)) { + header(4, tag.toHeaderString()) + comment(tag.root) + } + } + } + } } + } + + protected open fun contentForComments( + d: Documentable + ): List { + val tags: GroupedTags = d.documentation.flatMap { (pd, doc) -> + doc.children.asSequence().map { pd to it }.toList() + }.groupBy { it.second::class } + + val platforms = d.sourceSets fun DocumentableContentBuilder.contentForParams() { if (tags.isNotEmptyForTag()) { - val receiver = tags.withTypeUnnamed() - val params = tags.withTypeNamed() - platforms.forEach { - header(4, "Parameters", kind = ContentKind.Parameters, platformData = setOf(it)) - } - table(kind = ContentKind.Parameters) { - platforms.flatMap { platform -> - val receiverRow = receiver[platform]?.let { - buildGroup(sourceSets = setOf(platform), kind = ContentKind.Parameters) { - text("", styles = mainStyles + ContentStyle.RowTitle) - comment(it.root) - } - } + header(2, "Parameters") + group(extra = mainExtra + SimpleAttr.header("Parameters"), styles = setOf(ContentStyle.WithExtraAttributes)){ + sourceSetDependentHint(sourceSets = platforms.toSet(), kind = ContentKind.SourceSetDependantHint) { + val receiver = tags.withTypeUnnamed() + val params = tags.withTypeNamed() + table(kind = ContentKind.Parameters) { + platforms.flatMap { platform -> + val receiverRow = receiver[platform]?.let { + buildGroup(sourceSets = setOf(platform), kind = ContentKind.Parameters) { + text("", styles = mainStyles + ContentStyle.RowTitle) + comment(it.root) + } + } - val paramRows = params.mapNotNull { (_, param) -> - param[platform]?.let { - buildGroup(sourceSets = setOf(platform), kind = ContentKind.Parameters) { - text(it.name, kind = ContentKind.Parameters, styles = mainStyles + ContentStyle.RowTitle) - comment(it.root) + val paramRows = params.mapNotNull { (_, param) -> + param[platform]?.let { + buildGroup(sourceSets = setOf(platform), kind = ContentKind.Parameters) { + text(it.name, kind = ContentKind.Parameters, styles = mainStyles + ContentStyle.RowTitle) + comment(it.root) + } + } } + + listOfNotNull(receiverRow) + paramRows } } - - listOfNotNull(receiverRow) + paramRows } } } @@ -243,18 +281,20 @@ open class DefaultPageCreator( fun DocumentableContentBuilder.contentForSeeAlso() { if (tags.isNotEmptyForTag()) { - val seeAlsoTags = tags.withTypeNamed() - platforms.forEach { - header(4, "See also", kind = ContentKind.Comment, platformData = setOf(it)) - } - table(kind = ContentKind.Sample) { - platforms.flatMap { platform -> - seeAlsoTags.mapNotNull { (_, see) -> - see[platform]?.let { - buildGroup(sourceSets = setOf(platform), kind = ContentKind.Comment, styles = mainStyles + ContentStyle.RowTitle) { - if (it.address != null) link(it.name, it.address!!, kind = ContentKind.Comment) - else text(it.name, kind = ContentKind.Comment) - comment(it.root) + header(2, "See also") + group(extra = mainExtra + SimpleAttr.header("See also"), styles = setOf(ContentStyle.WithExtraAttributes)){ + sourceSetDependentHint(sourceSets = platforms.toSet(), kind = ContentKind.SourceSetDependantHint) { + val seeAlsoTags = tags.withTypeNamed() + table(kind = ContentKind.Sample) { + platforms.flatMap { platform -> + seeAlsoTags.mapNotNull { (_, see) -> + see[platform]?.let { + buildGroup(sourceSets = setOf(platform), kind = ContentKind.Comment, styles = mainStyles + ContentStyle.RowTitle) { + if (it.address != null) link(it.name, it.address!!, kind = ContentKind.Comment) + else text(it.name, kind = ContentKind.Comment) + comment(it.root) + } + } } } } @@ -266,13 +306,17 @@ open class DefaultPageCreator( fun DocumentableContentBuilder.contentForSamples() { val samples = tags.withTypeNamed() if (samples.isNotEmpty()) { - platforms.forEach { platformData -> - val content = samples.filter { it.value.isEmpty() || platformData in it.value } - if (content.isNotEmpty()) { - group(sourceSets = setOf(platformData)) { - header(4, "Samples", kind = ContentKind.Comment) - content.forEach { - comment(Text(it.key)) + header(2, "Samples") + group(extra = mainExtra + SimpleAttr.header("Samples"), styles = setOf(ContentStyle.WithExtraAttributes)){ + sourceSetDependentHint(sourceSets = platforms.toSet(), kind = ContentKind.SourceSetDependantHint) { + table(kind = ContentKind.Sample) { + platforms.map { platformData -> + val content = samples.filter { it.value.isEmpty() || platformData in it.value } + buildGroup(sourceSets = setOf(platformData), styles = setOf(ContentStyle.RowTitle)) { + content.forEach { + comment(Text(it.key)) + } + } } } } @@ -280,32 +324,11 @@ open class DefaultPageCreator( } } - fun DocumentableContentBuilder.contentForUnnamedTags() { - val unnamedTags: List> = - tags.filterNot { (k, _) -> k.isSubclassOf(NamedTagWrapper::class) || k in specialTags } - .map { (_, v) -> v.mapNotNull { (k,v) -> k?.let { it to v } }.toMap() } - platforms.forEach { platform -> - unnamedTags.forEach { pdTag -> - pdTag[platform]?.also { tag -> - group(sourceSets = setOf(platform)) { - header(4, tag.toHeaderString()) - comment(tag.root) - } - } - } - } - } - return contentBuilder.contentFor(d) { if (tags.isNotEmpty()) { - header(3, "Description") - sourceSetDependentHint(sourceSets = platforms.toSet()) { - contentForDescription() - contentForSamples() - contentForParams() - contentForUnnamedTags() - contentForSeeAlso() - } + contentForSamples() + contentForSeeAlso() + contentForParams() } }.children } @@ -323,13 +346,16 @@ open class DefaultPageCreator( protected open fun contentForFunction(f: DFunction) = contentForMember(f) protected open fun contentForTypeAlias(t: DTypeAlias) = contentForMember(t) protected open fun contentForMember(d: Documentable) = contentBuilder.contentFor(d) { - header(1, d.name.orEmpty()) + group(kind = ContentKind.Cover) { + header(1, d.name.orEmpty()) + } divergentGroup(ContentDivergentGroup.GroupID("member")) { instance(setOf(d.dri), d.sourceSets.toSet()) { divergent(kind = ContentKind.Symbol) { +buildSignature(d) } after { + +contentForDescription(d) +contentForComments(d) } } @@ -339,11 +365,12 @@ open class DefaultPageCreator( protected open fun DocumentableContentBuilder.divergentBlock( name: String, collection: Collection, - kind: ContentKind + kind: ContentKind, + extra: PropertyContainer = mainExtra, ) { if (collection.any()) { header(2, name) - table(kind) { + table(kind, extra = extra) { collection.groupBy { it.name }.map { (elementName, elements) -> // This groupBy should probably use LocationProvider buildGroup(elements.map { it.dri }.toSet(), elements.flatMap { it.sourceSets }.toSet(), kind = kind) { link(elementName.orEmpty(), elements.first().dri, kind = kind) -- cgit