aboutsummaryrefslogtreecommitdiff
path: root/plugins/base
diff options
context:
space:
mode:
authorMarcin Aman <marcin.aman@gmail.com>2020-10-09 14:38:13 +0200
committerGitHub <noreply@github.com>2020-10-09 14:38:13 +0200
commit3a5dfac12cf61d82b244026990b4d0f86d3b24b9 (patch)
tree13dda105ec0986278097519db0b102d2ea914cb6 /plugins/base
parentabe5113df0aeece35fc959e6b3b15ee125fe101c (diff)
downloaddokka-3a5dfac12cf61d82b244026990b4d0f86d3b24b9.tar.gz
dokka-3a5dfac12cf61d82b244026990b4d0f86d3b24b9.tar.bz2
dokka-3a5dfac12cf61d82b244026990b4d0f86d3b24b9.zip
On this page component (#1504)
Diffstat (limited to 'plugins/base')
-rw-r--r--plugins/base/frontend/package-lock.json18
-rw-r--r--plugins/base/frontend/package.json2
-rw-r--r--plugins/base/frontend/src/main/components/assets/clear.svg3
-rw-r--r--plugins/base/frontend/src/main/components/assets/searchIcon.svg (renamed from plugins/base/frontend/src/main/components/search/searchIcon.svg)0
-rw-r--r--plugins/base/frontend/src/main/components/pageSummary/pageSummary.scss70
-rw-r--r--plugins/base/frontend/src/main/components/pageSummary/pageSummary.tsx53
-rw-r--r--plugins/base/frontend/src/main/components/root.tsx44
-rw-r--r--plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx2
-rw-r--r--plugins/base/frontend/src/main/scss/index.scss4
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt32
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt2
-rw-r--r--plugins/base/src/main/resources/dokka/scripts/navigation-loader.js2
-rw-r--r--plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js7
13 files changed, 210 insertions, 29 deletions
diff --git a/plugins/base/frontend/package-lock.json b/plugins/base/frontend/package-lock.json
index ed81a875..ef4bd987 100644
--- a/plugins/base/frontend/package-lock.json
+++ b/plugins/base/frontend/package-lock.json
@@ -1582,6 +1582,14 @@
"@types/react": "*"
}
},
+ "@types/react-scrollspy": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@types/react-scrollspy/-/react-scrollspy-3.3.3.tgz",
+ "integrity": "sha512-HLaQStNl141NngJoczBNAK71GRmmbO25p+JiJyEI0bUvH9MKO8B52SI3kVb1PU48S/n1SiNbabDn3D1p37/1bA==",
+ "requires": {
+ "@types/react": "*"
+ }
+ },
"@types/unist": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz",
@@ -10570,6 +10578,16 @@
"xtend": "^4.0.1"
}
},
+ "react-scrollspy": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/react-scrollspy/-/react-scrollspy-3.4.3.tgz",
+ "integrity": "sha512-c2QZpMPWxm1HF71h1EqaxBldx2zLYO0aZ24Bcuo2mUWF79T+F6qOtr7XJCzUDm99NOwhVKQD01a7A8VC6c90CQ==",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "classnames": "^2.2.5",
+ "prop-types": "^15.5.10"
+ }
+ },
"react-sortable-hoc": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-1.11.0.tgz",
diff --git a/plugins/base/frontend/package.json b/plugins/base/frontend/package.json
index c86d324d..90e9d49c 100644
--- a/plugins/base/frontend/package.json
+++ b/plugins/base/frontend/package.json
@@ -30,12 +30,14 @@
"@types/node": "^12.12.36",
"@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0",
+ "@types/react-scrollspy": "^3.3.3",
"babel-loader": "^8.0.6",
"lodash": "^4.17.19",
"postcss-import": "^12.0.1",
"postcss-preset-env": "^6.7.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
+ "react-scrollspy": "^3.4.3",
"redbox-react": "^1.6.0",
"ts-loader": "^7.0.0",
"typescript": "^3.8.3",
diff --git a/plugins/base/frontend/src/main/components/assets/clear.svg b/plugins/base/frontend/src/main/components/assets/clear.svg
new file mode 100644
index 00000000..ddb8450f
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/assets/clear.svg
@@ -0,0 +1,3 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.1374 1.80464L6.94205 5.99996L11.1374 10.1953L10.1947 11.138L5.99935 6.94267L1.80403 11.138L0.861328 10.1953L5.05664 5.99996L0.861328 1.80464L1.80403 0.861938L5.99935 5.05725L10.1947 0.861938L11.1374 1.80464Z" fill="#637282"/>
+</svg> \ No newline at end of file
diff --git a/plugins/base/frontend/src/main/components/search/searchIcon.svg b/plugins/base/frontend/src/main/components/assets/searchIcon.svg
index 391b1cab..391b1cab 100644
--- a/plugins/base/frontend/src/main/components/search/searchIcon.svg
+++ b/plugins/base/frontend/src/main/components/assets/searchIcon.svg
diff --git a/plugins/base/frontend/src/main/components/pageSummary/pageSummary.scss b/plugins/base/frontend/src/main/components/pageSummary/pageSummary.scss
new file mode 100644
index 00000000..400631e6
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/pageSummary/pageSummary.scss
@@ -0,0 +1,70 @@
+@import "src/main/scss/index.scss";
+
+.page-summary {
+ background: $white;
+ border: 1px solid $grey-border;
+ position: fixed;
+ top: 25%;
+ max-height: 70vh;
+ right: -2px;
+ width: 250px;
+ z-index: 8;
+ transition: width .2s;
+
+ &.hidden {
+ width: 3em;
+ writing-mode: vertical-rl;
+ text-orientation: mixed;
+
+ .content-wrapper {
+ h4 {
+ margin: 0;
+ padding: 8px;
+ }
+ }
+ }
+
+ .content-wrapper {
+ padding: 0.5em 0 1em 0;
+ letter-spacing: 0.2px;
+
+ h4 {
+ margin: 0 2em;
+ font-weight: 600;
+ }
+
+ ul {
+ list-style-type: none;
+ width: 100%;
+ padding: 0;
+ margin: 1em 0;
+ overflow-x: hidden;
+ overflow-y: auto;
+ max-height: 60vh;
+
+ li {
+ width: 100%;
+ padding: 4px 0;
+
+ &:hover {
+ background: $list-background-hover;
+ }
+
+ &>a {
+ margin: 0 2em;
+ }
+
+ &.selected {
+ border-left: 4px solid $hover-link-color;
+ }
+ }
+ }
+ }
+}
+
+@media screen and (max-width: 759px){
+ /* hide it on smaller screens since it looks super weird when displayed with hidden menu */
+ .page-summary {
+ display: none;
+ }
+} \ No newline at end of file
diff --git a/plugins/base/frontend/src/main/components/pageSummary/pageSummary.tsx b/plugins/base/frontend/src/main/components/pageSummary/pageSummary.tsx
new file mode 100644
index 00000000..1aa65d9b
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/pageSummary/pageSummary.tsx
@@ -0,0 +1,53 @@
+import React, { useState, useEffect } from "react";
+import './pageSummary.scss'
+import _ from "lodash";
+import Scrollspy from 'react-scrollspy'
+
+type PageSummaryProps = {
+ entries: PageSummaryEntry[],
+}
+
+type PageSummaryEntry = {
+ location: string,
+ label: string,
+ sourceSets: SourceSetFilterKey[]
+}
+
+type SourceSetFilterKey = string
+
+export const PageSummary: React.FC<PageSummaryProps> = ({ entries }: PageSummaryProps) => {
+ const [hidden, setHidden] = useState<Boolean>(true);
+ const [displayableEntries, setDisplayableEntries] = useState<PageSummaryEntry[]>(entries)
+
+ const handleMouseHover = () => {
+ setHidden(!hidden)
+ }
+
+ useEffect(() => {
+ const handeEvent = (event: CustomEvent<SourceSetFilterKey[]>) => {
+ const displayable = entries.filter((entry) => entry.sourceSets.some((sourceset) => event.detail.includes(sourceset)))
+ setDisplayableEntries(displayable)
+ }
+
+ window.addEventListener('sourceset-filter-change', handeEvent)
+ return () => window.removeEventListener('sourceset-filter-change', handeEvent)
+ }, [entries])
+
+ let classnames = "page-summary"
+ if (hidden) classnames += " hidden"
+
+ return (
+ <div
+ className={classnames}
+ onMouseEnter={handleMouseHover}
+ onMouseLeave={handleMouseHover}
+ >
+ <div className={"content-wrapper"}>
+ <h4>On this page</h4>
+ {!hidden && <Scrollspy items={displayableEntries.map((e) => e.location)} currentClassName="selected">
+ {displayableEntries.map((item) => <li><a href={'#' + item.location}>{item.label}</a></li>)}
+ </Scrollspy>}
+ </div>
+ </div>
+ )
+} \ No newline at end of file
diff --git a/plugins/base/frontend/src/main/components/root.tsx b/plugins/base/frontend/src/main/components/root.tsx
index ad93d731..cb070dfb 100644
--- a/plugins/base/frontend/src/main/components/root.tsx
+++ b/plugins/base/frontend/src/main/components/root.tsx
@@ -1,30 +1,50 @@
import React from 'react';
-import {render} from 'react-dom';
+import { render } from 'react-dom';
import RedBox from 'redbox-react';
+import _ from "lodash";
import App from "./app";
import './app/index.scss';
import { NavigationPaneSearch } from './navigationPaneSearch/navigationPaneSearch';
+import { PageSummary } from './pageSummary/pageSummary';
const appEl = document.getElementById('searchBar');
const rootEl = document.createElement('div');
const renderNavigationPane = () => {
- const element = document.getElementById('paneSearch')
- if(element){
- render(
- <NavigationPaneSearch />,
- document.getElementById('paneSearch')
- )
- }
+ render(
+ <NavigationPaneSearch />,
+ document.getElementById('paneSearch')
+ )
+}
+
+const renderOnThisPage = () => {
+ document.addEventListener('DOMContentLoaded', () => {
+ for (const e of document.querySelectorAll('.tabs-section-body > div[data-togglable]')) {
+ const entries = Array.from(e.querySelectorAll('a[anchor-label]')).map((element: HTMLElement) => {
+ return {
+ location: element.getAttribute('data-name'),
+ label: element.getAttribute('anchor-label'),
+ sourceSets: _.sortBy(element.getAttribute('data-filterable-set').split(' '))
+ }
+ })
+ const unique = _.uniqBy(entries, ({label}) => label)
+ if (unique.length) {
+ const element = document.createElement('div')
+ render(<PageSummary entries={unique} />, element)
+ e.appendChild(element)
+ }
+ }
+ })
}
let renderApp = () => {
render(
- <App/>,
- rootEl
+ <App />,
+ rootEl
);
renderNavigationPane();
+ renderOnThisPage();
};
// @ts-ignore
@@ -32,8 +52,8 @@ if (module.hot) {
const renderAppHot = renderApp;
const renderError = (error: Error) => {
render(
- <RedBox error={error}/>,
- rootEl
+ <RedBox error={error} />,
+ rootEl
);
};
diff --git a/plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx b/plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx
index ad0b5f8f..a502f589 100644
--- a/plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx
+++ b/plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import SearchIcon from 'react-svg-loader!./searchIcon.svg';
+import SearchIcon from 'react-svg-loader!../assets/searchIcon.svg';
export const DokkaSearchAnchor = ({wrapperProps, buttonProps, popup}: any) => {
return (
diff --git a/plugins/base/frontend/src/main/scss/index.scss b/plugins/base/frontend/src/main/scss/index.scss
index 18e2861b..4f5a498b 100644
--- a/plugins/base/frontend/src/main/scss/index.scss
+++ b/plugins/base/frontend/src/main/scss/index.scss
@@ -1,4 +1,6 @@
@import "~@jetbrains/ring-ui/components/global/variables.css";
$white: #FFFFFF;
-$grey-border: #A6AFBA
+$grey-border: #DADFE6;
+$list-background-hover: rgba(91, 93, 239, 0.15);
+$hover-link-color: #5B5DEF;
diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
index 07cba90c..8f6129a6 100644
--- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
@@ -13,6 +13,7 @@ import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint
import org.jetbrains.dokka.base.transformers.pages.sourcelinks.hasTabbedContent
import org.jetbrains.dokka.base.renderers.isImage
import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.CompositeSourceSetID
import org.jetbrains.dokka.model.DisplaySourceSet
import org.jetbrains.dokka.model.properties.PropertyContainer
import org.jetbrains.dokka.model.sourceSetIDs
@@ -105,7 +106,7 @@ open class HtmlRenderer(
}
node.hasStyle(TextStyle.Paragraph) -> p(additionalClasses) { childrenCallback() }
node.hasStyle(TextStyle.Block) -> div(additionalClasses) { childrenCallback() }
- node.isAnchorable -> buildAnchor(node.anchor, node.anchorLabel!!) { childrenCallback() }
+ node.isAnchorable -> buildAnchor(node.anchor!!, node.anchorLabel!!, node.sourceSetsFilters) { childrenCallback() }
else -> childrenCallback()
}
}
@@ -379,7 +380,7 @@ open class HtmlRenderer(
sourceSetRestriction: Set<DisplaySourceSet>?,
style: Set<Style>
) {
- anchorFromNode(contextNode)
+ buildAnchor(contextNode)
div(classes = "table-row") {
div("main-subrow " + contextNode.style.joinToString(separator = " ")) {
buildRowHeaderLink(toRender, pageContext, sourceSetRestriction, contextNode.anchor, "w-100")
@@ -397,7 +398,7 @@ open class HtmlRenderer(
sourceSetRestriction: Set<DisplaySourceSet>?,
style: Set<Style>
) {
- anchorFromNode(contextNode)
+ buildAnchor(contextNode)
div(classes = "table-row") {
addSourceSetFilteringAttributes(contextNode)
div {
@@ -423,7 +424,7 @@ open class HtmlRenderer(
sourceSetRestriction: Set<DisplaySourceSet>?,
style: Set<Style>
) {
- anchorFromNode(contextNode)
+ buildAnchor(contextNode)
div(classes = "table-row") {
addSourceSetFilteringAttributes(contextNode)
div("main-subrow keyValue " + contextNode.style.joinToString(separator = " ")) {
@@ -449,7 +450,7 @@ open class HtmlRenderer(
toRender: List<ContentNode>,
pageContext: ContentPage,
sourceSetRestriction: Set<DisplaySourceSet>?,
- anchorDestination: String,
+ anchorDestination: String?,
classes: String = ""
) {
toRender.filter { it is ContentLink || it.hasStyle(ContentStyle.RowTitle) }.takeIf { it.isNotEmpty() }?.let {
@@ -458,7 +459,7 @@ open class HtmlRenderer(
.forEach {
span("inline-flex") {
it.build(this, pageContext, sourceSetRestriction)
- if(it is ContentLink) buildAnchorCopyButton(anchorDestination)
+ if(it is ContentLink && !anchorDestination.isNullOrBlank()) buildAnchorCopyButton(anchorDestination)
}
}
}
@@ -581,19 +582,21 @@ open class HtmlRenderer(
}
}
- private fun FlowContent.buildAnchor(anchor: String, anchorLabel: String, content: FlowContent.() -> Unit) {
+ private fun FlowContent.buildAnchor(anchor: String, anchorLabel: String, sourceSets: String, content: FlowContent.() -> Unit) {
a {
attributes["data-name"] = anchor
attributes["anchor-label"] = anchorLabel
+ attributes["id"] = anchor
+ attributes["data-filterable-set"] = sourceSets
}
content()
}
- private fun FlowContent.buildAnchor(anchor: String, anchorLabel: String) =
- buildAnchor(anchor, anchorLabel) {}
+ private fun FlowContent.buildAnchor(anchor: String, anchorLabel: String, sourceSets: String) =
+ buildAnchor(anchor, anchorLabel, sourceSets) {}
- private fun FlowContent.anchorFromNode(node: ContentNode) {
- node.anchorLabel?.let { label -> buildAnchor(node.anchor, label) }
+ private fun FlowContent.buildAnchor(node: ContentNode) {
+ node.anchorLabel?.let { label -> buildAnchor(node.anchor!!, label, node.sourceSetsFilters) }
}
@@ -846,5 +849,8 @@ val ContentNode.isAnchorable: Boolean
val ContentNode.anchorLabel: String?
get() = extra[SymbolAnchorHint]?.anchorName
-val ContentNode.anchor: String
- get() = (dci.dri.first().toString() + "/" + extra[SymbolAnchorHint]?.contentKind + "/" + sourceSets.joinToString { it.sourceSetIDs.all.joinToString() }).urlEncoded()
+val ContentNode.anchor: String?
+ get() = extra[SymbolAnchorHint]?.contentKind?.let { contentKind -> (dci.dri.first().toString() + "/" + contentKind + "/" + sourceSets.joinToString { it.sourceSetIDs.all.joinToString() }).urlEncoded() }
+
+val ContentNode.sourceSetsFilters: String
+ get() = sourceSets.sourceSetIDs.joinToString(" ") { it.toString() }
diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
index 6c3c0330..4582fd67 100644
--- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
+++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
@@ -544,7 +544,7 @@ open class DefaultPageCreator(
) {
elements.map {
instance(setOf(it.dri), it.sourceSets.toSet(), extra = PropertyContainer.withAll(SymbolAnchorHint(it.name ?: "", kind))) {
- divergent {
+ divergent(extra = PropertyContainer.empty()) {
group {
+buildSignature(it)
}
diff --git a/plugins/base/src/main/resources/dokka/scripts/navigation-loader.js b/plugins/base/src/main/resources/dokka/scripts/navigation-loader.js
index c2f60ec5..0d9948ad 100644
--- a/plugins/base/src/main/resources/dokka/scripts/navigation-loader.js
+++ b/plugins/base/src/main/resources/dokka/scripts/navigation-loader.js
@@ -16,7 +16,7 @@ window.addEventListener('load', () => {
})
/* Smooth scrolling support for going to the top of the page */
- document.querySelectorAll('a[href^="#"]').forEach(anchor => {
+ document.querySelectorAll('.footer a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
diff --git a/plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js b/plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js
index 022aca4f..26dd9424 100644
--- a/plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js
+++ b/plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js
@@ -114,6 +114,10 @@ function initializeFiltering() {
let cached = window.localStorage.getItem('inactive-filters')
if (cached) {
let parsed = JSON.parse(cached)
+ //Events are used by react to get values in 'on this page'
+ const event = new CustomEvent('sourceset-filter-change', { detail: parsed });
+ window.dispatchEvent(event);
+
filteringContext.activeFilters = filteringContext.restrictedDependencies
.filter(q => parsed.indexOf(q) == -1 )
} else {
@@ -215,6 +219,9 @@ function refreshFiltering() {
elem.setAttribute("data-filterable-current", platformList.join(' '))
}
)
+ const event = new CustomEvent('sourceset-filter-change', { detail: sourcesetList });
+ window.dispatchEvent(event);
+
refreshFilterButtons()
refreshPlatformTabs()
}