diff options
author | Marcin Aman <maman@virtuslab.com> | 2020-05-31 21:02:46 +0200 |
---|---|---|
committer | Paweł Marks <Kordyjan@users.noreply.github.com> | 2020-06-09 17:18:17 +0200 |
commit | 77c8777b7f66bddd374d68decd507547d356d602 (patch) | |
tree | f941a53d1591c56edd454ef40068e831a4ed9d48 | |
parent | 902b670bc764a6db4f49f96d08f2115dd08bdf9b (diff) | |
download | dokka-77c8777b7f66bddd374d68decd507547d356d602.tar.gz dokka-77c8777b7f66bddd374d68decd507547d356d602.tar.bz2 dokka-77c8777b7f66bddd374d68decd507547d356d602.zip |
Improve CSS, pages navigation tree and create anchors on page
21 files changed, 842 insertions, 378 deletions
diff --git a/core/src/main/kotlin/pages/ContentNodes.kt b/core/src/main/kotlin/pages/ContentNodes.kt index 95b7b371..d6105bec 100644 --- a/core/src/main/kotlin/pages/ContentNodes.kt +++ b/core/src/main/kotlin/pages/ContentNodes.kt @@ -13,6 +13,8 @@ interface ContentNode : WithExtraProperties<ContentNode> { val dci: DCI val sourceSets: Set<SourceSetData> val style: Set<Style> + + fun hasAnyContent(): Boolean } /** Simple text */ @@ -24,6 +26,8 @@ data class ContentText( override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() ) : ContentNode { override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = copy(extra = newExtras) + + override fun hasAnyContent(): Boolean = !text.isBlank() } // TODO: Remove @@ -34,6 +38,8 @@ data class ContentBreakLine( override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() ) : ContentNode { override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = copy(extra = newExtras) + + override fun hasAnyContent(): Boolean = true } /** Headers */ @@ -106,6 +112,8 @@ data class ContentEmbeddedResource( /** Logical grouping of [ContentNode]s */ interface ContentComposite : ContentNode { val children: List<ContentNode> + + override fun hasAnyContent(): Boolean = children.any { it.hasAnyContent() } } /** Tables */ @@ -144,7 +152,7 @@ data class ContentGroup( } /** - * @property groupName is used for finding and copying [ContentDivergentInstance]s when merging [ContentPage]s + * @property groupID is used for finding and copying [ContentDivergentInstance]s when merging [ContentPage]s */ data class ContentDivergentGroup( override val children: List<ContentDivergentInstance>, @@ -216,11 +224,11 @@ enum class ContentKind : Kind { } enum class TextStyle : Style { - Bold, Italic, Strong, Strikethrough, Paragraph, Block, Span, Monospace, Indented + Bold, Italic, Strong, Strikethrough, Paragraph, Block, Monospace, Indented, Cover, UnderCoverText } enum class ContentStyle : Style { - RowTitle, TabbedContent, TabbedContentBody, WithExtraAttributes + RowTitle, TabbedContent, WithExtraAttributes } object CommentTable: Style diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt index b28e36d6..42ca25cc 100644 --- a/core/src/main/kotlin/pages/PageNodes.kt +++ b/core/src/main/kotlin/pages/PageNodes.kt @@ -89,7 +89,6 @@ class PackagePageNode( override val name: String, override val content: ContentNode, override val dri: Set<DRI>, - override val documentable: Documentable?, override val children: List<PageNode>, override val embeddedResources: List<String> = listOf() diff --git a/plugins/base/frontend/src/main/components/app/index.scss b/plugins/base/frontend/src/main/components/app/index.scss index c267bc6e..da5042b1 100644 --- a/plugins/base/frontend/src/main/components/app/index.scss +++ b/plugins/base/frontend/src/main/components/app/index.scss @@ -17,5 +17,11 @@ html, } .search-content { - padding: 24px 41px; + padding-top: 24px; + margin: 0 41px; + position: absolute; + top: 0; + right: 0; + z-index: 8; + background-color: #f4f4f4 } diff --git a/plugins/base/frontend/src/main/components/app/index.tsx b/plugins/base/frontend/src/main/components/app/index.tsx index 1a51e699..4081dec4 100644 --- a/plugins/base/frontend/src/main/components/app/index.tsx +++ b/plugins/base/frontend/src/main/components/app/index.tsx @@ -1,49 +1,10 @@ -import React, {useEffect, useRef, useState} from 'react'; +import React from 'react'; import {WithFuzzySearchFilter} from '../search/search'; import './index.scss'; -function useComponentVisible(initialIsVisible: boolean) { - const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible); - const ref = useRef(null); - - const handleHideDropdown = (event: KeyboardEvent) => { - if (event.key === "Escape") { - setIsComponentVisible(false); - } - }; - - const handleClickOutside = (event : MouseEvent)=> { - // @ts-ignore - if (ref.current && !ref.current.contains(event.target)) { - setIsComponentVisible(false); - } - }; - - useEffect(() => { - document.addEventListener("keydown", handleHideDropdown, false); - document.addEventListener("click", handleClickOutside, false); - return () => { - document.removeEventListener("keydown", handleHideDropdown, false); - document.removeEventListener("click", handleClickOutside, false); - }; - }); - - return { ref, isComponentVisible, setIsComponentVisible }; -} - const App: React.FC = () => { - const { - ref, - isComponentVisible, - setIsComponentVisible - } = useComponentVisible(false); - - return <div ref={ref} className="search-content"> - {isComponentVisible && (<WithFuzzySearchFilter/>)} - {!isComponentVisible && ( - <span onClick={() => setIsComponentVisible(true)}> - <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><path d="M19.64 18.36l-6.24-6.24a7.52 7.52 0 1 0-1.28 1.28l6.24 6.24zM7.5 13.4a5.9 5.9 0 1 1 5.9-5.9 5.91 5.91 0 0 1-5.9 5.9z"/></svg> - </span>)} + return <div className="search-content"> + <WithFuzzySearchFilter/> </div> } diff --git a/plugins/base/frontend/src/main/components/search/search.scss b/plugins/base/frontend/src/main/components/search/search.scss new file mode 100644 index 00000000..a891283e --- /dev/null +++ b/plugins/base/frontend/src/main/components/search/search.scss @@ -0,0 +1,14 @@ +.search { + button { + border: none; + fill: #637282; + + &:focus { + outline: none; + } + } +} + +.popup-wrapper { + min-width: calc(100% - 360px) !important; +}
\ No newline at end of file diff --git a/plugins/base/frontend/src/main/components/search/search.tsx b/plugins/base/frontend/src/main/components/search/search.tsx index f0df0c98..b0fdd96a 100644 --- a/plugins/base/frontend/src/main/components/search/search.tsx +++ b/plugins/base/frontend/src/main/components/search/search.tsx @@ -1,7 +1,8 @@ import React, {useCallback, useState} from 'react'; import {Select} from '@jetbrains/ring-ui'; import '@jetbrains/ring-ui/components/input-size/input-size.scss'; -import {IWindow, Option, Props, State} from "./types"; +import './search.scss'; +import {IWindow, Option, Props} from "./types"; const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => { const [selected, onSelected] = useState<Option>(data[0]); @@ -12,6 +13,7 @@ const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => { }, [data] ); + return ( <div className="search-container"> <div className="search"> @@ -19,10 +21,22 @@ const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => { selectedLabel="Search" label="Please type page name" filter={{fuzzy: true}} + type={Select.Type.CUSTOM} clear selected={selected} data={data} + popupClassName={"popup-wrapper"} onSelect={onChangeSelected} + customAnchor={({wrapperProps, buttonProps, popup}) => ( + <span {...wrapperProps}> + <button type="button" {...buttonProps}> + <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"> + <path d="M19.64 18.36l-6.24-6.24a7.52 7.52 0 1 0-1.28 1.28l6.24 6.24zM7.5 13.4a5.9 5.9 0 1 1 5.9-5.9 5.91 5.91 0 0 1-5.9 5.9z"/> + </svg> + </button> + {popup} + </span> + )} /> </div> </div> diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index 7ebcf00e..67fa2d91 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -22,7 +22,7 @@ open class HtmlRenderer( private val sourceSetDependencyMap = with(context.sourceSetCache) { allSourceSets.map { sourceSet -> - sourceSet to allSourceSets.filter { sourceSet.dependentSourceSets.contains(it.sourceSetName ) } + sourceSet to allSourceSets.filter { sourceSet.dependentSourceSets.contains(it.sourceSetName) } }.toMap() } @@ -38,22 +38,24 @@ open class HtmlRenderer( val additionalClasses = node.style.joinToString(" ") { it.toString().toLowerCase() } return when { node.hasStyle(ContentStyle.TabbedContent) -> div(additionalClasses) { - val secondLevel = node.children.filterIsInstance<ContentComposite>().flatMap { it.children }.filterIsInstance<ContentHeader>().flatMap { it.children }.filterIsInstance<ContentText>() - val firstLevel = node.children.filterIsInstance<ContentHeader>().flatMap { it.children }.filterIsInstance<ContentText>() + val secondLevel = node.children.filterIsInstance<ContentComposite>().flatMap { it.children } + .filterIsInstance<ContentHeader>().flatMap { it.children }.filterIsInstance<ContentText>() + val firstLevel = node.children.filterIsInstance<ContentHeader>().flatMap { it.children } + .filterIsInstance<ContentText>() val renderable = firstLevel.union(secondLevel) - div(classes = "tabs-section"){ + div(classes = "tabs-section") { attributes["tabs-section"] = "tabs-section" renderable.forEachIndexed { index, node -> - button(classes = "section-tab"){ - if(index == 0 ) attributes["data-active"] = "" + button(classes = "section-tab") { + if (index == 0) attributes["data-active"] = "" attributes["data-togglable"] = node.text text(node.text) } } } - div(classes = "tabs-section-body"){ + div(classes = "tabs-section-body") { childrenCallback() } } @@ -61,7 +63,10 @@ open class HtmlRenderer( node.extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue } childrenCallback() } - node.dci.kind == ContentKind.Symbol -> div("symbol $additionalClasses") { childrenCallback() } + node.dci.kind in setOf(ContentKind.Symbol, ContentKind.Sample) -> div("symbol $additionalClasses") { + childrenCallback() + if (node.hasStyle(TextStyle.Monospace)) copyButton() + } node.dci.kind == ContentKind.BriefComment -> div("brief $additionalClasses") { childrenCallback() } node.dci.kind == ContentKind.Cover -> div("cover $additionalClasses") { filterButtons(node) @@ -80,7 +85,7 @@ open class HtmlRenderer( button(classes = "platform-tag platform-selector") { attributes["data-active"] = "" attributes["data-filter"] = it.sourceSetName - when(it.platform.key){ + when (it.platform.key) { "common" -> classes = classes + "common-like" "native" -> classes = classes + "native-like" "jvm" -> classes = classes + "jvm-like" @@ -92,40 +97,57 @@ open class HtmlRenderer( } } + private fun FlowContent.copyButton() = span(classes = "top-right-position") { + span("copy-icon") { + unsafe { + raw( + """<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M5 4H15V16H5V4ZM17 7H19V18V20H17H8V18H17V7Z" fill="black"/> + </svg>""".trimIndent() + ) + } + } + copiedPopup("Content copied to clipboard", "popup-to-left") + } + + private fun FlowContent.copiedPopup(notificationContent: String, additionalClasses: String = "") = + div("copy-popup-wrapper $additionalClasses") { + unsafe { + raw( + """ + <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M18 9C18 14 14 18 9 18C4 18 0 14 0 9C0 4 4 0 9 0C14 0 18 4 18 9ZM14.2 6.2L12.8 4.8L7.5 10.1L5.3 7.8L3.8 9.2L7.5 13L14.2 6.2Z" fill="#4DBB5F"/> + </svg> + """.trimIndent() + ) + } + span { + text(notificationContent) + } + } + override fun FlowContent.buildPlatformDependent(content: PlatformHintedContent, pageContext: ContentPage) = - buildPlatformDependent(content.sourceSets.map { it to setOf(content.inner) }.toMap(), pageContext, content.extra) + buildPlatformDependent( + content.sourceSets.map { it to setOf(content.inner) }.toMap(), + pageContext, + content.extra, + content.style + ) private fun FlowContent.buildPlatformDependent( nodes: Map<SourceSetData, Collection<ContentNode>>, pageContext: ContentPage, - extra: PropertyContainer<ContentNode> = PropertyContainer.empty() + extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), + styles: Set<Style> = emptySet() ) { - var mergedToOneSourceSet : SourceSetData? = null - div("platform-hinted") { + val contents = contentsForSourceSetDependent(nodes, pageContext) + val shouldHaveTabs = contents.size != 1 + + val styles = "platform-hinted ${styles.joinToString()}" + if (shouldHaveTabs) " with-platform-tabs" else "" + div(styles) { 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 { - elements.forEach { - buildContentNode(it, pageContext, setOf(sourceSet)) - } - }.stripDiv() - }.groupBy(Pair<SourceSetData, String>::second, Pair<SourceSetData, String>::first).entries.flatMap { (html, sourceSets) -> - sourceSets.filterNot { - sourceSetDependencyMap[it].orEmpty().any { dependency -> sourceSets.contains(dependency) } - }.map { - it to createHTML(prettyPrint = false).div(classes = "content $additionalClasses") { - if (counter++ == 0) attributes["data-active"] = "" - attributes["data-togglable"] = it.sourceSetName - unsafe { - +html - } - } - } - } - if (contents.size != 1) { + if (shouldHaveTabs) { div("platform-bookmarks-row") { attributes["data-toggle-list"] = "data-toggle-list" contents.forEachIndexed { index, pair -> @@ -134,27 +156,53 @@ open class HtmlRenderer( attributes["data-filterable-set"] = pair.first.sourceSetName if (index == 0) attributes["data-active"] = "" attributes["data-toggle"] = pair.first.sourceSetName - when( + when ( pair.first.platform.key - ){ - "common" -> classes = classes + "common-like" - "native" -> classes = classes + "native-like" - "jvm" -> classes = classes + "jvm-like" - "js" -> classes = classes + "js-like" + ) { + "common" -> classes = classes + "common-like" + "native" -> classes = classes + "native-like" + "jvm" -> classes = classes + "jvm-like" + "js" -> classes = classes + "js-like" } attributes["data-toggle"] = pair.first.sourceSetName text(pair.first.sourceSetName) } } } - } else if (nodes.size > 1) { - mergedToOneSourceSet = contents.first().first } contents.forEach { consumer.onTagContentUnsafe { +it.second } } } - mergedToOneSourceSet?.let { createPlatformTagBubbles(listOf(it)) } + } + + private fun contentsForSourceSetDependent( + nodes: Map<SourceSetData, Collection<ContentNode>>, + pageContext: ContentPage, + ): List<Pair<SourceSetData, String>> { + var counter = 0 + return nodes.toList().map { (sourceSet, elements) -> + sourceSet to createHTML(prettyPrint = false).div { + elements.forEach { + buildContentNode(it, pageContext, setOf(sourceSet)) + } + }.stripDiv() + }.groupBy( + Pair<SourceSetData, String>::second, + Pair<SourceSetData, String>::first + ).entries.flatMap { (html, sourceSets) -> + sourceSets.filterNot { + sourceSetDependencyMap[it].orEmpty().any { dependency -> sourceSets.contains(dependency) } + }.map { + it to createHTML(prettyPrint = false).div(classes = "content sourceset-depenent-content") { + if (counter++ == 0) attributes["data-active"] = "" + attributes["data-togglable"] = it.sourceSetName + unsafe { + +html + } + } + } + } } override fun FlowContent.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) { @@ -183,26 +231,38 @@ open class HtmlRenderer( val groupedDivergent = it.value.groupBy { it.second } consumer.onTagContentUnsafe { - +createHTML().div("divergent-group"){ + +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) -> - sourceSet to elements.map { e -> e.first.divergent } - }.toMap(), - pageContext - ) - if (distinct.size > 1 && groupedDivergent.size == 1) { - createPlatformTags(node, groupedDivergent.keys) + val divergentForPlatformDependent = groupedDivergent.map { (sourceSet, elements) -> + sourceSet to elements.map { e -> e.first.divergent } + }.toMap() + + val content = contentsForSourceSetDependent(divergentForPlatformDependent, pageContext) + + consumer.onTagContentUnsafe { + +createHTML().div("brief-with-platform-tags") { + consumer.onTagContentUnsafe { +it.key.first } + + consumer.onTagContentUnsafe { + +createHTML().span("pull-right") { + if ((distinct.size > 1 && groupedDivergent.size == 1) || groupedDivergent.size == 1 || content.size == 1) { + if (node.sourceSets.size != 1) { + createPlatformTags(node, setOf(content.first().first)) + } + } + } } + } + } + div("main-subrow") { + if (node.implicitlySourceSetHinted) { + buildPlatformDependent(divergentForPlatformDependent, pageContext) } else { it.value.forEach { buildContentNode(it.first.divergent, pageContext, setOf(it.second)) @@ -272,7 +332,8 @@ open class HtmlRenderer( .filter { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } } .takeIf { it.isNotEmpty() } ?.let { - withAnchor(node.dci.dri.first().toString()) { + val anchorName = node.dci.dri.first().toString() + withAnchor(anchorName) { div(classes = "table-row") { if (!style.contains(MultimoduleTable)) { attributes["data-filterable-current"] = node.sourceSets.joinToString(" ") { @@ -286,7 +347,10 @@ open class HtmlRenderer( div("main-subrow " + node.style.joinToString(" ")) { it.filter { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } } .forEach { - it.build(this, pageContext, sourceSetRestriction) + span { + it.build(this, pageContext, sourceSetRestriction) + buildAnchor(anchorName) + } if (ContentKind.shouldBePlatformTagged(node.dci.kind) && (node.sourceSets.size == 1)) createPlatformTags(node) } @@ -317,7 +381,7 @@ open class HtmlRenderer( div("platform-tags") { sourceSets.forEach { div("platform-tag") { - when(it.platform.key){ + when (it.platform.key) { "common" -> classes = classes + "common-like" "native" -> classes = classes + "native-like" "jvm" -> classes = classes + "jvm-like" @@ -331,7 +395,7 @@ open class HtmlRenderer( private fun FlowContent.createPlatformTags(node: ContentNode, sourceSetRestriction: Set<SourceSetData>? = null) { node.takeIf { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } }?.let { - createPlatformTagBubbles( node.sourceSets.filter { + createPlatformTagBubbles(node.sourceSets.filter { sourceSetRestriction == null || it in sourceSetRestriction }) } @@ -342,7 +406,7 @@ open class HtmlRenderer( pageContext: ContentPage, sourceSetRestriction: Set<SourceSetData>? ) { - when(node.dci.kind){ + when (node.dci.kind) { ContentKind.Comment -> buildDefaultTable(node, pageContext, sourceSetRestriction) else -> div(classes = "table") { node.extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue } @@ -388,13 +452,14 @@ open class HtmlRenderer( override fun FlowContent.buildHeader(level: Int, node: ContentHeader, content: FlowContent.() -> Unit) { val anchor = node.extra[SimpleAttr.SimpleAttrKey("anchor")]?.extraValue + val classes = node.style.joinToString { it.toString() }.toLowerCase() when (level) { - 1 -> h1() { withAnchor(anchor, content) } - 2 -> h2() { withAnchor(anchor, content) } - 3 -> h3() { withAnchor(anchor, content) } - 4 -> h4() { withAnchor(anchor, content) } - 5 -> h5() { withAnchor(anchor, content) } - else -> h6() { withAnchor(anchor, content) } + 1 -> h1(classes = classes) { withAnchor(anchor, content) } + 2 -> h2(classes = classes) { withAnchor(anchor, content) } + 3 -> h3(classes = classes) { withAnchor(anchor, content) } + 4 -> h4(classes = classes) { withAnchor(anchor, content) } + 5 -> h5(classes = classes) { withAnchor(anchor, content) } + else -> h6(classes = classes) { withAnchor(anchor, content) } } } @@ -420,6 +485,23 @@ open class HtmlRenderer( text(to.name) } + private fun FlowContent.buildAnchor(pointingTo: String) { + span(classes = "anchor-wrapper") { + span(classes = "anchor-icon") { + attributes["pointing-to"] = pointingTo + unsafe { + raw(""" + <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M21.2496 5.3C20.3496 4.5 19.2496 4 18.0496 4C16.8496 4 15.6496 4.5 14.8496 5.3L10.3496 9.8L11.7496 11.2L16.2496 6.7C17.2496 5.7 18.8496 5.7 19.8496 6.7C20.8496 7.7 20.8496 9.3 19.8496 10.3L15.3496 14.8L16.7496 16.2L21.2496 11.7C22.1496 10.8 22.5496 9.7 22.5496 8.5C22.5496 7.3 22.1496 6.2 21.2496 5.3Z"/> + <path d="M8.35 16.7998C7.35 17.7998 5.75 17.7998 4.75 16.7998C3.75 15.7998 3.75 14.1998 4.75 13.1998L9.25 8.6998L7.85 7.2998L3.35 11.7998C1.55 13.5998 1.55 16.3998 3.35 18.1998C4.25 19.0998 5.35 19.4998 6.55 19.4998C7.75 19.4998 8.85 19.0998 9.75 18.1998L14.25 13.6998L12.85 12.2998L8.35 16.7998Z"/> + </svg> + """.trimIndent()) + } + } + copiedPopup("Link copied to clipboard") + } + } + fun FlowContent.buildLink( to: DRI, platforms: List<SourceSetData>, @@ -497,7 +579,7 @@ open class HtmlRenderer( } } - private fun resolveLink(link: String, page: PageNode) : String = if (URI(link).isAbsolute) link else page.root(link) + private fun resolveLink(link: String, page: PageNode): String = if (URI(link).isAbsolute) link else page.root(link) open fun buildHtml(page: PageNode, resources: List<String>, content: FlowContent.() -> Unit) = createHTML().html { @@ -506,9 +588,13 @@ open class HtmlRenderer( title(page.name) with(resources) { filter { it.substringBefore('?').substringAfterLast('.') == "css" } - .forEach { link(rel = LinkRel.stylesheet, href = resolveLink(it,page)) } + .forEach { link(rel = LinkRel.stylesheet, href = resolveLink(it, page)) } filter { it.substringBefore('?').substringAfterLast('.') == "js" } - .forEach { script(type = ScriptType.textJavaScript, src = resolveLink(it,page)) { async = true } } + .forEach { + script(type = ScriptType.textJavaScript, src = resolveLink(it, page)) { + async = true + } + } } script { unsafe { +"""var pathToRoot = "${locationProvider.resolveRoot(page)}";""" } } } @@ -532,6 +618,38 @@ open class HtmlRenderer( script(type = ScriptType.textJavaScript, src = page.root("scripts/pages.js")) {} script(type = ScriptType.textJavaScript, src = page.root("scripts/main.js")) {} content() + div(classes = "footer") { + span("go-to-top-icon") { + a(href = "#container") { + unsafe { + raw( + """ + <svg width="12" height="10" viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M11.3337 9.66683H0.666992L6.00033 3.66683L11.3337 9.66683Z" fill="black"/> + <path d="M0.666992 0.333496H11.3337V1.66683H0.666992V0.333496Z" fill="black"/> + </svg> + """.trimIndent() + ) + } + } + } + span { text("© 2020 Copyright") } + span { text("Privacy Policy") } + span("pull-right") { + span { text("Sponsored and developed by Dokka") } + span(classes = "padded-icon") { + unsafe { + raw( + """ + <svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M8 0H2.3949L4.84076 2.44586L0 7.28662L0.713376 8L5.55414 3.15924L8 5.6051V0Z" fill="black"/> + </svg> + """.trimIndent() + ) + } + } + } + } } } } diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt index 45ef1457..f10c85d5 100644 --- a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt +++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt @@ -5,6 +5,8 @@ import kotlinx.html.id import kotlinx.html.table import kotlinx.html.tbody import org.jetbrains.dokka.base.renderers.platforms +import org.jetbrains.dokka.model.DEnum +import org.jetbrains.dokka.model.DEnumEntry import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.pages.PageTransformer @@ -44,12 +46,19 @@ object NavigationPageInstaller : PageTransformer { page.name, page.dri.first(), page.platforms(), - if (page !is ClasslikePageNode) - page.children.filterIsInstance<ContentPage>() - .map { visit(it) } - else - emptyList() + page.navigableChildren() ) + + private fun ContentPage.navigableChildren(): List<NavigationNode> { + if(this !is ClasslikePageNode){ + return children.filterIsInstance<ContentPage>() + .map { visit(it) } + } else if(documentable is DEnum) { + return children.filter { it is ContentPage && it.documentable is DEnumEntry }.map { visit(it as ContentPage) } + } + + return emptyList() + } } object ResourceInstaller : PageTransformer { @@ -68,6 +77,7 @@ object StyleAndScriptsAppender : PageTransformer { "scripts/navigationLoader.js", "scripts/platformContentHandler.js", "scripts/sourceset_dependencies.js", + "scripts/clipboard.js", "styles/jetbrains-mono.css" ) ) diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt index 4868d91e..5514f9e5 100644 --- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt +++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt @@ -253,7 +253,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog } private fun funType(dri: DRI, sourceSets: Set<SourceSetData>, type: TypeConstructor) = - contentBuilder.contentFor(dri, sourceSets, ContentKind.Symbol, setOf(TextStyle.Monospace)) { + contentBuilder.contentFor(dri, sourceSets) { if (type.extension) { signatureForProjection(type.projections.first()) text(".") diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index 1954cec9..bdc6d680 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -66,9 +66,11 @@ open class DefaultPageCreator( protected open fun contentForModule(m: DModule) = contentBuilder.contentFor(m) { group(kind = ContentKind.Cover) { - header(1, m.name) - sourceSetDependentHint(m.dri, m.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint){ - +contentForDescription(m) + cover(m.name) + if(contentForDescription(m).isNotEmpty()){ + sourceSetDependentHint(m.dri, m.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint, styles = setOf(TextStyle.UnderCoverText)){ + +contentForDescription(m) + } } } +contentForComments(m) @@ -81,9 +83,11 @@ open class DefaultPageCreator( protected open fun contentForPackage(p: DPackage) = contentBuilder.contentFor(p) { group(kind = ContentKind.Cover) { - header(1, "Package ${p.name}") - sourceSetDependentHint(p.dri, p.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint){ - +contentForDescription(p) + cover("Package ${p.name}") + if(contentForDescription(p).isNotEmpty()){ + sourceSetDependentHint(p.dri, p.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint, styles = setOf(TextStyle.UnderCoverText)){ + +contentForDescription(p) + } } } group(styles = setOf(ContentStyle.TabbedContent)){ @@ -92,8 +96,8 @@ open class DefaultPageCreator( 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) + +buildSignature(it) } } } @@ -109,9 +113,8 @@ open class DefaultPageCreator( 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) - contentForBrief(it) + +buildSignature(it) } } s.safeAs<WithExtraProperties<Documentable>>()?.let { it.extra[InheritorsInfo] }?.let { inheritors -> @@ -140,10 +143,10 @@ open class DefaultPageCreator( protected open fun contentForEnumEntry(e: DEnumEntry) = contentBuilder.contentFor(e) { group(kind = ContentKind.Cover) { - header(1, e.name) + cover(e.name) sourceSetDependentHint(e.dri, e.sourceSets.toSet()) { - +buildSignature(e) +contentForDescription(e) + +buildSignature(e) } } group(styles = setOf(ContentStyle.TabbedContent)){ @@ -154,10 +157,10 @@ open class DefaultPageCreator( protected open fun contentForClasslike(c: DClasslike) = contentBuilder.contentFor(c) { group(kind = ContentKind.Cover) { - header(1, c.name.orEmpty()) - sourceSetDependentHint(c.dri, c.sourceSets.toSet()) { - +buildSignature(c) + cover(c.name.orEmpty()) + sourceSetDependentHint(c.dri, c.sourceSets.toSet()) { +contentForDescription(c) + +buildSignature(c) } } @@ -174,8 +177,8 @@ open class DefaultPageCreator( ) { link(it.name, it.dri, kind = ContentKind.Main) sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint, styles = emptySet()) { - +buildSignature(it) contentForBrief(it) + +buildSignature(it) } } } @@ -183,8 +186,8 @@ open class DefaultPageCreator( 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) + +buildSignature(it) } } } @@ -215,7 +218,7 @@ open class DefaultPageCreator( val platforms = d.sourceSets.toSet() - return contentBuilder.contentFor(d) { + return contentBuilder.contentFor(d, styles = setOf(TextStyle.Block)) { val description = tags.withTypeUnnamed<Description>() if (description.any { it.value.root.children.isNotEmpty() }) { platforms.forEach { platform -> @@ -317,13 +320,11 @@ open class DefaultPageCreator( 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)) - } + platforms.map { platformData -> + val content = samples.filter { it.value.isEmpty() || platformData in it.value } + group(sourceSets = setOf(platformData), kind = ContentKind.Sample, styles = setOf(TextStyle.Monospace)) { + content.forEach { + text(it.key) } } } @@ -355,17 +356,17 @@ open class DefaultPageCreator( protected open fun contentForTypeAlias(t: DTypeAlias) = contentForMember(t) protected open fun contentForMember(d: Documentable) = contentBuilder.contentFor(d) { group(kind = ContentKind.Cover) { - header(1, d.name.orEmpty()) + cover(d.name.orEmpty()) } divergentGroup(ContentDivergentGroup.GroupID("member")) { instance(setOf(d.dri), d.sourceSets.toSet()) { - divergent(kind = ContentKind.Symbol) { - +buildSignature(d) - } - after { + before { +contentForDescription(d) +contentForComments(d) } + divergent(kind = ContentKind.Symbol) { + +buildSignature(d) + } } } } @@ -389,14 +390,14 @@ open class DefaultPageCreator( ) { elements.map { instance(setOf(it.dri), it.sourceSets.toSet()) { + before { + contentForBrief(it) + } divergent { - group(kind = ContentKind.Symbol) { + group { +buildSignature(it) } } - after { - contentForBrief(it) - } } } } diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt index b670626a..c499be6f 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt @@ -107,6 +107,16 @@ open class PageContentBuilder( ) } + fun cover( + text: String, + platformData: Set<SourceSetData> = mainPlatformData, + styles: Set<Style> = mainStyles + TextStyle.Cover, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DocumentableContentBuilder.() -> Unit = {} + ) { + header(1, text, platformData = platformData, styles = styles, extra = extra, block = block) + } + fun text( text: String, kind: Kind = ContentKind.Main, @@ -411,7 +421,7 @@ open class PageContentBuilder( block: DocumentableContentBuilder.() -> Unit ) { contentFor(dri, sourceSets, kind, styles, extra, block) - .takeIf { it.children.isNotEmpty() } + .takeIf { it.hasAnyContent() } .also { before = it } } @@ -435,7 +445,7 @@ open class PageContentBuilder( block: DocumentableContentBuilder.() -> Unit ) { contentFor(dri, sourceSets, kind, styles, extra, block) - .takeIf { it.children.isNotEmpty() } + .takeIf { it.hasAnyContent() } .also { after = it } } diff --git a/plugins/base/src/main/resources/dokka/scripts/clipboard.js b/plugins/base/src/main/resources/dokka/scripts/clipboard.js new file mode 100644 index 00000000..b00ce246 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/scripts/clipboard.js @@ -0,0 +1,52 @@ +window.addEventListener('load', () => { + document.querySelectorAll('span.copy-icon').forEach(element => { + element.addEventListener('click', (el) => copyElementsContentToClipboard(element)); + }) + + document.querySelectorAll('span.anchor-icon').forEach(element => { + element.addEventListener('click', (el) => { + if(element.hasAttribute('pointing-to')){ + const location = hrefWithoutCurrentlyUsedAnchor() + '#' + element.getAttribute('pointing-to') + copyTextToClipboard(element, location) + } + }); + }) +}) + +const copyElementsContentToClipboard = (element) => { + const selection = window.getSelection(); + const range = document.createRange(); + range.selectNodeContents(element.parentNode.parentNode); + selection.removeAllRanges(); + selection.addRange(range); + + copyAndShowPopup(element, () => selection.removeAllRanges()) +} + +const copyTextToClipboard = (element, text) => { + var textarea = document.createElement("textarea"); + textarea.textContent = text; + textarea.style.position = "fixed"; + document.body.appendChild(textarea); + textarea.select(); + + copyAndShowPopup(element, () => document.body.removeChild(textarea)) +} + +const copyAndShowPopup = (element, after) => { + try { + document.execCommand('copy'); + element.nextElementSibling.classList.add('active-popup'); + setTimeout(() => { + element.nextElementSibling.classList.remove('active-popup'); + }, 1200); + } catch (e) { + console.error('Failed to write to clipboard:', e) + } + finally { + if(after) after() + } +} + +const hrefWithoutCurrentlyUsedAnchor = () => window.location.href.split('#')[0] + diff --git a/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js b/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js index cac46d2a..c2f60ec5 100644 --- a/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js +++ b/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js @@ -14,6 +14,17 @@ window.addEventListener('load', () => { }).then(() => { revealNavigationForCurrentPage() }) + + /* Smooth scrolling support for going to the top of the page */ + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function (e) { + e.preventDefault(); + + document.querySelector(this.getAttribute('href')).scrollIntoView({ + behavior: 'smooth' + }); + }); + }); }) revealNavigationForCurrentPage = () => { diff --git a/plugins/base/src/main/resources/dokka/styles/style.css b/plugins/base/src/main/resources/dokka/styles/style.css index d573b76c..f180762b 100644 --- a/plugins/base/src/main/resources/dokka/styles/style.css +++ b/plugins/base/src/main/resources/dokka/styles/style.css @@ -4,11 +4,14 @@ :root { --breadcrumb-font-color: #A6AFBA; - --hover-link-color: #5B5DEF + --hover-link-color: #5B5DEF; + --footer-height: 64px; + --horizontal-spacing-for-content: 42px } #content { - padding: 0 41px; + padding: 0 var(--horizontal-spacing-for-content); + height: calc(100% - var(--footer-height)); } .breadcrumbs { @@ -40,7 +43,7 @@ padding: 11px 3px; font-size: 14px; color: #637282; - outline:none; + outline: none; margin: 0 8px; } @@ -55,29 +58,86 @@ } .tabs-section-body { - padding: 24px 0 + margin: 12px 0; + background-color: white; +} + +.tabs-section-body > .table { + margin: 12px 0; +} + +.tabs-section-body .with-platform-tabs > div { + margin: 0 12px; +} + +.tabs-section-body .table .with-platform-tabs > div { + margin: 0; +} + +.tabs-section-body .with-platform-tabs { + padding-top: 12px; + padding-bottom: 12px; +} + +.tabs-section-body .with-platform-tabs .sourceset-depenent-content .table-row { + background-color: #f4f4f4; + border-bottom: 2px solid white; } .cover > .platform-hinted { - padding: 24px 0 + padding-top: 24px; + margin-top: 24px; + padding-bottom: 16px; } .cover { display: flex; flex-direction: column; + width: 100%; + padding-bottom: 48px; } .tabbedcontent { - padding: 24px 0; + padding: 14px 0; } -.cover .platform-hinted .single-content > .symbol { +.cover .platform-hinted .sourceset-depenent-content > .symbol, +.cover > .symbol { background-color: white; } +.cover .platform-hinted.with-platform-tabs .sourceset-depenent-content > .symbol { + background-color: #f4f4f4; +} + +.cover .platform-hinted.with-platform-tabs .sourceset-depenent-content > .block ~ .symbol { + padding-top: 16px; + padding-left: 0; +} + +.cover .sourceset-depenent-content > .block { + padding: 16px 0; + font-size: 18px; + line-height: 28px; +} + +.cover .platform-hinted.with-platform-tabs .sourceset-depenent-content > .block { + padding: 0; + font-size: 14px; +} + +.cover ~ .divergent-group { + margin-top: 24px; + padding: 24px 8px 8px 8px; +} + +.cover ~ .divergent-group .main-subrow .symbol { + width: 100%; +} + .divergent-group { background-color: white; - padding: 12px 8px; + padding: 16px 8px; margin-bottom: 2px; } @@ -85,6 +145,10 @@ background-color: #F4F4F4; } +.title > .divergent-group:first-of-type { + padding-top: 0; +} + #container { display: flex; flex-direction: row; @@ -93,7 +157,7 @@ #main { width: 100%; - padding-left: 12px; + max-width: calc(100% - 280px); } #leftColumn { @@ -114,10 +178,9 @@ } #sideMenu { - height: 100%; + max-height: calc(100% - 90px); padding-top: 16px; position: relative; - overflow: auto; } #sideMenu img { @@ -149,7 +212,6 @@ } .overview > .navButton { - width: 100%; height: 100%; align-items: center; display: flex; @@ -169,19 +231,65 @@ background-color: #F4F4F4; align-items: center; display: flex; - padding: 8px 16px; + padding: 8px 8px 8px 16px; box-sizing: border-box; font-weight: bold; - flex-wrap: wrap; + position: relative; + line-height: 24px; +} + +.symbol span.copy-icon path { + fill: #637282; +} + +.symbol span.copy-icon:hover path { + fill: black; +} + +.copy-popup-wrapper { + display: none; + align-items: center; + position: absolute; + z-index: 1000; + background: white; + font-weight: normal; + box-shadow: 0 2px 16px 0 var(--ring-popup-shadow-color); + font-family: 'Inter', "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + width: max-content; + font-size: 14px; + cursor: default; +} + +.copy-popup-wrapper.popup-to-left { + /* since it is in position absolute we can just move it to the left to make it always appear on the left side of the icon */ + left: -15em; +} + +.copy-popup-wrapper.active-popup { + display: flex !important; +} + +.copy-popup-wrapper:hover { + font-weight: normal; +} + +.copy-popup-wrapper svg { + padding: 8px; +} + +.copy-popup-wrapper > span:last-child { + padding-right: 14px; } -#nav-submenu > .sideMenuPart { - padding-left: 0; /* packages are same level as modules */ +.symbol .top-right-position { + /* it is important for a parent to have a position: relative */ + position: absolute; + top: 8px; + right: 8px; } .sideMenuPart > .overview { height: 40px; - width: 100%; display: flex; align-items: center; position: relative; @@ -194,13 +302,14 @@ flex: 1; height: 100%; color: #637282; + overflow: hidden; } .sideMenuPart > .overview:before { box-sizing: border-box; content: ''; top: 0; - width: 200%; + width: 280px; right: 0; bottom: 0; position: absolute; @@ -255,7 +364,7 @@ body, table { font-size: 14px; line-height: 24px; margin: 0; - max-width: 1440px; + /*max-width: 1440px; TODO: This results in worse experience on ultrawide, but on 16:9/16:10 looks better.*/ } table { @@ -305,30 +414,62 @@ td:first-child { h1, h2, h3, h4, h5, h6 { color: #222; + font-weight: bold; } p, ul, ol, table, pre, dl { margin: 0 0 20px; } -h1, h2, h3 { - line-height: 1.1; +h1 { + font-weight: bold; + font-size: 40px; + line-height: 48px; + letter-spacing: -1px; } -h1 { + +h1.cover { font-size: 60px; line-height: 64px; letter-spacing: -1.5px; + + margin-left: calc(-1 * var(--horizontal-spacing-for-content)); + margin-right: calc(-1 * var(--horizontal-spacing-for-content)); + padding-left: var(--horizontal-spacing-for-content); + padding-right: var(--horizontal-spacing-for-content); + border-bottom: 1px solid #DADFE6; + + margin-bottom: 0; + padding-bottom: 32px; } h2 { color: #393939; + font-size: 31px; + line-height: 40px; + letter-spacing: -0.5px; +} + +h3 { + font-size: 20px; + line-height: 28px; + letter-spacing: -0.2px; +} + +h4 { + margin: 0; } h3, h4, h5, h6 { color: #494949; } +.UnderCoverText { + font-size: 18px; + line-height: 28px; +} + a { color: #5B5DEF; font-weight: 400; @@ -367,12 +508,6 @@ code, pre { pre { display: block; - /* - padding:8px 8px; - background: #f8f8f8; - border-radius:5px; - border:1px solid #e5e5e5; - */ overflow-x: auto; } @@ -537,10 +672,13 @@ footer { .filter-section { display: flex; - display: flex; flex-direction: row; align-self: flex-end; min-height: 30px; + position: absolute; + top: 20px; + right: 88px; + z-index: 0; } .platform-selector:hover { @@ -552,7 +690,7 @@ footer { } .platform-selector:not([data-active]) { border: 1px solid #DADFE6; - background-color: white; + background-color: transparent; color: #637282; } @@ -570,7 +708,12 @@ td.content { justify-content: space-between; } -.main-subrow > a { +.main-subrow > span { + display: flex; + position: relative; +} + +.main-subrow > span > a { text-decoration: none; font-style: normal; font-weight: 600; @@ -578,10 +721,30 @@ td.content { color: #282E34; } -.main-subrow > a:hover { +.main-subrow > span > a:hover { color: var(--hover-link-color); } +.main-subrow .anchor-icon { + padding: 0 8px; +} + +.main-subrow .anchor-icon > svg path { + fill: #637282; +} + +.main-subrow .anchor-icon:hover { + cursor: pointer; +} + +.main-subrow .anchor-icon:hover > svg path { + fill: var(--hover-link-color); +} + +.main-subrow .anchor-wrapper { + position: relative; +} + .platform-hinted { flex: auto; display: block; @@ -648,9 +811,52 @@ td.content { display: none } -.sideMenuPart[data-active] > .overview { - background: rgba(91, 93, 239, 0.15); +.brief-with-platform-tags { + display: inline-block; + width: 100%; +} + +.tabs-section-body .brief-with-platform-tags { + display: inline-flex; +} + +.brief-with-platform-tags ~ .main-subrow { + padding-top: 16px; +} + +.cover .with-platform-tabs { + background-color: white; +} + +.cover > .with-platform-tabs .platform-bookmarks-row { + margin: 0 16px; +} + +.cover > .with-platform-tabs > .content { + margin: 0 16px; + background-color: #f4f4f4; + padding: 8px 16px; +} + +.cover > .block { + padding-top: 48px; + padding-bottom: 24px; + font-size: 18px; + line-height: 28px; +} + +.cover > .block:empty { + padding-bottom: 0; +} + +.table-row .with-platform-tabs .sourceset-depenent-content .brief { + padding: 16px; + background-color: #f4f4f4; +} + +.sideMenuPart[data-active] > .overview:before { border-left: 4px solid var(--hover-link-color); + background: rgba(91, 93, 239, 0.15); } .table { @@ -662,7 +868,7 @@ td.content { display: flex; flex-direction: column; background: white; - margin-bottom: 2px; + border-bottom: 2px solid #f4f4f4; padding: 16px 24px 16px 24px; } @@ -683,7 +889,7 @@ td.content { @media print, screen and (min-width: 960px) { .keyValue { - grid-template-columns: 20% auto; + grid-template-columns: 20% 80%; } .title-row { @@ -761,3 +967,55 @@ td.content { color: #444; } } + +.footer { + clear: both; + display: flex; + align-items: center; + position: relative; + height: var(--footer-height); + border-top: 1px solid #DADFE6; + font-size: 12px; + line-height: 16px; + letter-spacing: 0.2px; + color: var(--breadcrumb-font-color); + margin-top: 48px; +} + +.footer span.go-to-top-icon { + border-radius: 2em; + padding: 11px 10px !important; + background-color: white; +} + +.footer span.go-to-top-icon path { + fill: #637282; +} + +.footer > span:first-child { + margin-left: var(--horizontal-spacing-for-content); + padding-left: 0; +} + +.footer > span:last-child { + margin-right: var(--horizontal-spacing-for-content); + padding-right: 0; +} + +.footer > span { + padding: 0 16px; +} + +.footer .padded-icon { + padding-left: 0.5em; +} + +/*For svg*/ +.footer path { + fill: var(--breadcrumb-font-color); +} + +.pull-right { + float: right; + margin-left: auto +} diff --git a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt index 31f62918..e25567e0 100644 --- a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt +++ b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt @@ -77,6 +77,9 @@ class ContentForParamsTest : AbstractCoreTest() { } divergentGroup { divergentInstance { + before { + pWrapped("comment to function") + } divergent { bareSignature( emptyMap(), @@ -88,9 +91,6 @@ class ContentForParamsTest : AbstractCoreTest() { "abc" to ParamAttributes(emptyMap(), emptySet(), "String") ) } - after { - pWrapped("comment to function") - } } } } @@ -122,6 +122,10 @@ class ContentForParamsTest : AbstractCoreTest() { } divergentGroup { divergentInstance { + before { + unnamedTag("Author") { +"Kordyjan" } + unnamedTag("Since") { +"0.11" } + } divergent { bareSignature( emptyMap(), @@ -133,10 +137,6 @@ class ContentForParamsTest : AbstractCoreTest() { "abc" to ParamAttributes(emptyMap(), emptySet(), "String") ) } - after { - unnamedTag("Author") { +"Kordyjan" } - unnamedTag("Since") { +"0.11" } - } } } } @@ -169,6 +169,11 @@ class ContentForParamsTest : AbstractCoreTest() { } divergentGroup { divergentInstance { + before { + pWrapped("comment to function") + unnamedTag("Author") { +"Kordyjan" } + unnamedTag("Since") { +"0.11" } + } divergent { bareSignature( emptyMap(), @@ -180,11 +185,6 @@ class ContentForParamsTest : AbstractCoreTest() { "abc" to ParamAttributes(emptyMap(), emptySet(), "String") ) } - after { - pWrapped("comment to function") - unnamedTag("Author") { +"Kordyjan" } - unnamedTag("Since") { +"0.11" } - } } } } @@ -216,18 +216,7 @@ class ContentForParamsTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), - "", - "", - emptySet(), - "function", - null, - "abc" to ParamAttributes(emptyMap(), emptySet(), "String") - ) - } - after { + before { pWrapped("comment to function") header(2) { +"Parameters" } group { @@ -241,6 +230,17 @@ class ContentForParamsTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } } } } @@ -274,15 +274,7 @@ class ContentForParamsTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), "", "", emptySet(), "function", null, - "first" to ParamAttributes(emptyMap(), emptySet(), "String"), - "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), - "third" to ParamAttributes(emptyMap(), emptySet(), "Double") - ) - } - after { + before { pWrapped("comment to function") header(2) { +"Parameters" } group { @@ -304,6 +296,14 @@ class ContentForParamsTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), "", "", emptySet(), "function", null, + "first" to ParamAttributes(emptyMap(), emptySet(), "String"), + "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), + "third" to ParamAttributes(emptyMap(), emptySet(), "Double") + ) + } } } } @@ -336,15 +336,7 @@ class ContentForParamsTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), "", "", emptySet(), "function", null, - "first" to ParamAttributes(emptyMap(), emptySet(), "String"), - "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), - "third" to ParamAttributes(emptyMap(), emptySet(), "Double") - ) - } - after { + before { header(2) { +"Parameters" } group { platformHinted { @@ -365,6 +357,14 @@ class ContentForParamsTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), "", "", emptySet(), "function", null, + "first" to ParamAttributes(emptyMap(), emptySet(), "String"), + "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), + "third" to ParamAttributes(emptyMap(), emptySet(), "Double") + ) + } } } } @@ -397,19 +397,7 @@ class ContentForParamsTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignatureWithReceiver( - emptyMap(), - "", - "", - emptySet(), - "String", - "function", - null, - "abc" to ParamAttributes(emptyMap(), emptySet(), "String") - ) - } - after { + before { pWrapped("comment to function") header(2) { +"Parameters" } group { @@ -427,6 +415,18 @@ class ContentForParamsTest : AbstractCoreTest() { } } } + divergent { + bareSignatureWithReceiver( + emptyMap(), + "", + "", + emptySet(), + "String", + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } } } } @@ -459,15 +459,7 @@ class ContentForParamsTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), "", "", emptySet(), "function", null, - "first" to ParamAttributes(emptyMap(), emptySet(), "String"), - "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), - "third" to ParamAttributes(emptyMap(), emptySet(), "Double") - ) - } - after { + before { pWrapped("comment to function") header(2) { +"Parameters" } group { @@ -485,6 +477,14 @@ class ContentForParamsTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), "", "", emptySet(), "function", null, + "first" to ParamAttributes(emptyMap(), emptySet(), "String"), + "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), + "third" to ParamAttributes(emptyMap(), emptySet(), "Double") + ) + } } } } @@ -520,15 +520,7 @@ class ContentForParamsTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), "", "", emptySet(), "function", null, - "first" to ParamAttributes(emptyMap(), emptySet(), "String"), - "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), - "third" to ParamAttributes(emptyMap(), emptySet(), "Double") - ) - } - after { + before { pWrapped("comment to function") unnamedTag("Author") { +"Kordyjan" } unnamedTag("Since") { +"0.11" } @@ -553,6 +545,14 @@ class ContentForParamsTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), "", "", emptySet(), "function", null, + "first" to ParamAttributes(emptyMap(), emptySet(), "String"), + "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), + "third" to ParamAttributes(emptyMap(), emptySet(), "Double") + ) + } } } } diff --git a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt index 94288f75..c9adc0bf 100644 --- a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt +++ b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt @@ -36,7 +36,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { val page = module.children.single { it.name == "test" } .children.single { it.name == "function" } as ContentPage page.content.assertNode { - group{ + group { header(1) { +"function" } } divergentGroup { @@ -82,18 +82,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), - "", - "", - emptySet(), - "function", - null, - "abc" to ParamAttributes(emptyMap(), emptySet(), "String") - ) - } - after { + before { header(2) { +"See also" } group { platformHinted { @@ -107,6 +96,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } } } } @@ -137,18 +137,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), - "", - "", - emptySet(), - "function", - null, - "abc" to ParamAttributes(emptyMap(), emptySet(), "String") - ) - } - after { + before { header(2) { +"See also" } group { platformHinted { @@ -162,6 +151,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } } } } @@ -192,18 +192,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), - "", - "", - emptySet(), - "function", - null, - "abc" to ParamAttributes(emptyMap(), emptySet(), "String") - ) - } - after { + before { header(2) { +"See also" } group { platformHinted { @@ -217,6 +206,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } } } } @@ -247,18 +247,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), - "", - "", - emptySet(), - "function", - null, - "abc" to ParamAttributes(emptyMap(), emptySet(), "String") - ) - } - after { + before { header(2) { +"See also" } group { platformHinted { @@ -272,6 +261,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } } } } @@ -305,18 +305,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), - "", - "", - emptySet(), - "function", - null, - "abc" to ParamAttributes(emptyMap(), emptySet(), "String") - ) - } - after { + before { pWrapped("random comment") unnamedTag("Author") { +"pikinier20" } unnamedTag("Since") { +"0.11" } @@ -334,6 +323,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } } } } @@ -365,18 +365,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), - "", - "", - emptySet(), - "function", - null, - "abc" to ParamAttributes(emptyMap(), emptySet(), "String") - ) - } - after { + before { header(2) { +"See also" } group { platformHinted { @@ -390,6 +379,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } } } } @@ -421,18 +421,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } divergentGroup { divergentInstance { - divergent { - bareSignature( - emptyMap(), - "", - "", - emptySet(), - "function", - null, - "abc" to ParamAttributes(emptyMap(), emptySet(), "String") - ) - } - after { + before { header(2) { +"See also" } group { platformHinted { @@ -451,6 +440,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { } } } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } } } } diff --git a/plugins/base/src/test/kotlin/enums/EnumsTest.kt b/plugins/base/src/test/kotlin/enums/EnumsTest.kt index 74fc18c4..a8b78baa 100644 --- a/plugins/base/src/test/kotlin/enums/EnumsTest.kt +++ b/plugins/base/src/test/kotlin/enums/EnumsTest.kt @@ -192,6 +192,9 @@ class EnumsTest : AbstractCoreTest() { |package enums | |enum class Test { + | /** + | Sample docs for E1 + | **/ | @SinceKotlin("1.3") | E1 |} @@ -206,6 +209,11 @@ class EnumsTest : AbstractCoreTest() { header(1) { +"E1" } platformHinted { group { + group { + + "Sample docs for E1" + } + } + group { mapOf("SinceKotlin" to setOf("version")).entries.forEach { group { group { diff --git a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt index 33d3f64c..7cdb0de3 100644 --- a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt +++ b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt @@ -164,13 +164,11 @@ class LinkableContentTest : AbstractCoreTest() { val function = classChildren.find { it.name == "printWithExclamation" } val text = function.cast<MemberPageNode>().content.cast<ContentGroup>().children.last() .cast<ContentDivergentGroup>().children.single() - .cast<ContentDivergentInstance>().after + .cast<ContentDivergentInstance>().before .cast<ContentGroup>().children.last() .cast<ContentGroup>().children.last() .cast<PlatformHintedContent>().children.single() .cast<ContentGroup>().children.single() - .cast<ContentTable>().children.single() - .cast<ContentGroup>().children.single() .cast<ContentGroup>().children.single() .cast<ContentCode>().children.single().cast<ContentText>().text Assertions.assertEquals( diff --git a/plugins/base/src/test/kotlin/markdown/LinkTest.kt b/plugins/base/src/test/kotlin/markdown/LinkTest.kt index fe5b573a..20bd24ee 100644 --- a/plugins/base/src/test/kotlin/markdown/LinkTest.kt +++ b/plugins/base/src/test/kotlin/markdown/LinkTest.kt @@ -33,15 +33,12 @@ class LinkTest : AbstractCoreTest() { configuration ) { renderingStage = { rootPageNode, _ -> - (rootPageNode.children.single().children.single() as MemberPageNode) + assertNotNull((rootPageNode.children.single().children.single() as MemberPageNode) .content - .dfs { node -> node is ContentDRILink } - .let { - assertEquals( - "parser//test/#java.lang.ClassLoader/PointingToDeclaration/", - (it as ContentDRILink).address.toString() - ) - } + .dfs { node -> + node is ContentDRILink && + node.address.toString() == "parser//test/#java.lang.ClassLoader/PointingToDeclaration/"} + ) } } } diff --git a/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt b/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt index f58b0e24..a3bf9188 100644 --- a/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt +++ b/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt @@ -90,6 +90,7 @@ fun Element.match(vararg matchers: Any): Unit = open class Tag(val name: String, vararg val matchers: Any) class Div(vararg matchers: Any): Tag("div", *matchers) class P(vararg matchers: Any): Tag("p", *matchers) +class Span(vararg matchers: Any): Tag("span", *matchers) private fun Any.accepts(n: Node) { when (this) { diff --git a/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt b/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt index 54bac755..fa129760 100644 --- a/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt +++ b/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt @@ -6,12 +6,8 @@ import org.jetbrains.dokka.base.renderers.html.HtmlRenderer import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.SourceSetData import org.jetbrains.dokka.pages.ContentDivergentGroup -import org.jetbrains.dokka.pages.TextStyle import org.junit.jupiter.api.Test -import renderers.Div -import renderers.RenderingOnlyTestBase -import renderers.TestPage -import renderers.match +import renderers.* class DivergentTest : RenderingOnlyTestBase() { private val js = SourceSetData("root", "JS", Platform.js, listOf(SourceRootImpl("pl1"))) @@ -182,10 +178,10 @@ class DivergentTest : RenderingOnlyTestBase() { HtmlRenderer(context).render(page) renderedContent.match( - Div(Div(Div(Div("a")), Div(Div())), "a+",), - Div(Div(Div(Div("bd")), Div(Div())), "bd+"), - Div(Div(Div(Div("c")), Div(Div()))), - Div(Div(Div(Div("e")), Div(Div())), "e+") + Div(Div(Span(Div(Div("NATIVE")))), Div(Div(Div("a"))),"a+"), + Div(Div(Span(Div(Div("JS")))), Div(Div(Div("bd"))),"bd+"), + Div(Div(Span(Div(Div("JVM")))), Div(Div(Div("c")))), + Div(Div(Span(Div(Div("NATIVE")))), Div(Div(Div("e"))),"e+"), ) } @@ -215,7 +211,10 @@ class DivergentTest : RenderingOnlyTestBase() { HtmlRenderer(context).render(page) renderedContent.match( Div( - "ab-", + Div( + "ab-", + Span() + ), Div(Div(Div("ab"))) ) ) @@ -283,10 +282,9 @@ class DivergentTest : RenderingOnlyTestBase() { } HtmlRenderer(context).render(page) - val r = renderedContent renderedContent.match( Div( - "ab-", + Div("ab-", Span()), Div(Div(Div("ab"))), "ab+" ) @@ -324,8 +322,8 @@ class DivergentTest : RenderingOnlyTestBase() { HtmlRenderer(context).render(page) renderedContent.match( - Div("a-", Div(Div(Div("a")), Div(Div("NATIVE"))), "ab+"), - Div("b-", Div(Div(Div("b")), Div(Div("NATIVE"))), "ab+") + Div(Div("a-", Span()), Div(Div(Div("a"))), "ab+"), + Div(Div("b-", Span()), Div(Div(Div(("b")))), "ab+") ) } }
\ No newline at end of file |