Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offline dokka #1999

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/org/jetbrains/taskUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ fun Task.dependsOnMavenLocalPublication() {
val Project.isLocalPublication: Boolean
get() = gradle.startParameter.taskNames.any {
it.endsWith("publishToMavenLocal", ignoreCase = true) ||
it.endsWith("pTML", ignoreCase = true) ||
it.endsWith("integrationTest", ignoreCase = true) ||
it.endsWith("check", ignoreCase = true) ||
it.endsWith("test", ignoreCase = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { useState, useCallback, useEffect } from "react";
import { NavigationRecord } from "./types";
import _ from "lodash";

type NavigationRecordProps = {
record: NavigationRecord;
currentPageId: string;
tabsIncludedInNavigation: string[];
parentCallback: (expanded: boolean) => void
}

type NavigationRowAttributes = {
"data-active": string;
}

const shouldBeExpandedBecauseMatchesDirectly = (tabFromUrl: string | null, currentPageId: string, record: NavigationRecord, tabsIncludedInNavigation: string[]) => {
if (tabFromUrl && (currentPageId + '/' + tabFromUrl.toLowerCase()) == record.id) {
return true
}
return (!tabFromUrl || !tabsIncludedInNavigation.includes(tabFromUrl)) && currentPageId == record.id
}

const NavigationRecord: React.FC<NavigationRecordProps> = ({ record, currentPageId, tabsIncludedInNavigation, parentCallback }: NavigationRecordProps) => {
const [currentTab, setCurrentTab] = useState<string | null>(() => {
return new URLSearchParams(window.location.search).get('active-tab')
})
const [expanded, setExpanded] = useState<boolean>(() => {
return shouldBeExpandedBecauseMatchesDirectly(currentTab, currentPageId, record, tabsIncludedInNavigation)
});
const clickHandler = useCallback(() => setExpanded(!expanded), [expanded]);

useEffect(() => {
if (expanded) parentCallback(expanded)
});

useEffect(() => {
window.addEventListener('tab-changed', (evt) => {
//This event is required to inform react about tab changes, since push state is not available in standard event listeners api
setCurrentTab(evt.detail)
})
})

const children = record.children.map(e => <NavigationRecord record={e}
currentPageId={currentPageId}
parentCallback={setExpanded}
tabsIncludedInNavigation={tabsIncludedInNavigation}/>)
let activeAttributes: NavigationRowAttributes | null = null
if(shouldBeExpandedBecauseMatchesDirectly(currentTab, currentPageId, record, tabsIncludedInNavigation)) {
activeAttributes = { "data-active": "" }
}

return <div className={expanded ? "sideMenuPart" : "sideMenuPart hidden"} id={record.id} {...activeAttributes}>
<div className="overview">
<a href={pathToRoot + record.location}>{record.name}</a>
{record.children.length > 0 &&
<span className="navButton pull-right" onClick={clickHandler}>
<span className="navButtonContent"/>
</span>
}
</div>
{children}
</div>
}

export type NavigationProps = {
records: NavigationRecord[];
tabsIncludedInNavigation: string[];
currentPageId: string;
}

export const Navigation: React.FC<NavigationProps> = ({ records, tabsIncludedInNavigation, currentPageId, currentTab }: NavigationProps) => {
const dummyCallback = (_: boolean) => { }
return <div>
{currentTab}
{records.map(record => <NavigationRecord record={record}
currentPageId={currentPageId}
parentCallback={dummyCallback}
tabsIncludedInNavigation={tabsIncludedInNavigation}/>)}
</div>
}
6 changes: 6 additions & 0 deletions plugins/base/frontend/src/main/components/navigation/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type NavigationRecord = {
id: string;
name: string;
location: string;
children: NavigationRecord[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { DokkaFuzzyFilterComponent } from '../search/dokkaFuzzyFilter';
import { IWindow, Option } from '../search/types';
import './navigationPaneSearch.scss';
import ClearIcon from 'react-svg-loader!./clear.svg';
import { relativizeUrlForRequest } from '../utils/requests';

export const NavigationPaneSearch = () => {
const [navigationList, setNavigationList] = useState<Option[]>([]);
Expand Down Expand Up @@ -32,21 +31,15 @@ export const NavigationPaneSearch = () => {
}

useEffect(() => {
fetch(relativizeUrlForRequest('scripts/navigation-pane.json'))
.then(response => response.json())
.then((result) => {
setNavigationList(result.map((record: Option, idx: number) => {
return {
...record,
key: idx,
rgItemType: List.ListProps.Type.CUSTOM
}
}))
},
(error) => {
console.error('failed to fetch navigationPane data', error)
setNavigationList([])
})
// if(navigationPane){
// setNavigationList(navigationPane.map((record: Option, idx: number) => {
// return {
// ...record,
// key: idx,
// rgItemType: List.ListProps.Type.CUSTOM
// }
// }))
// }
}, [])


Expand Down
8 changes: 8 additions & 0 deletions plugins/base/frontend/src/main/components/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import App from "./app";
import './app/index.scss';
import { NavigationPaneSearch } from './navigationPaneSearch/navigationPaneSearch';
import { PageSummary } from './pageSummary/pageSummary';
import { Navigation } from './navigation/navigation';

const renderNavigationPane = () => {
render(
Expand Down Expand Up @@ -36,10 +37,17 @@ const renderMainSearch = () => {
render(<App />, document.getElementById('searchBar'));
}


const renderNavigation = () => {
render(<Navigation records={navigation} currentPageId={document.getElementById("content").attributes["pageIds"].value}
tabsIncludedInNavigation={["Properties", "Functions", "Types"]}/>, document.getElementById("sideMenu"));
}

let renderApp = () => {
renderMainSearch();
renderNavigationPane();
renderOnThisPage();
renderNavigation();

document.removeEventListener('DOMContentLoaded', renderApp);
};
Expand Down
33 changes: 14 additions & 19 deletions plugins/base/frontend/src/main/components/search/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import './search.scss';
import { IWindow, Option, Props } from "./types";
import { DokkaSearchAnchor } from "./dokkaSearchAnchor";
import { DokkaFuzzyFilterComponent } from "./dokkaFuzzyFilter";
import { relativizeUrlForRequest } from '../utils/requests';

const WithFuzzySearchFilterComponent: React.FC<Props> = ({ data }: Props) => {
const [selected, onSelected] = useState<Option>(data[0]);
Expand Down Expand Up @@ -42,28 +41,24 @@ const WithFuzzySearchFilterComponent: React.FC<Props> = ({ data }: Props) => {
)
}

if(typeof pages === 'undefined'){
var pages = [];
}

export const WithFuzzySearchFilter = () => {
const [navigationList, setNavigationList] = useState<Option[]>([]);

useEffect(() => {
fetch(relativizeUrlForRequest('scripts/pages.json'))
.then(response => response.json())
.then((result) => {
setNavigationList(result.map((record: Option, idx: number) => {
return {
...record,
label: record.name,
key: idx,
type: record.kind,
rgItemType: List.ListProps.Type.CUSTOM
}
}))
},
(error) => {
console.error('failed to fetch pages data', error)
setNavigationList([])
})
}, [])
setNavigationList(pages.map((record: Option, idx: number) => {
return {
...record,
label: record.name,
key: idx,
type: record.kind,
rgItemType: List.ListProps.Type.CUSTOM
}
}))
}, [pages])

return <WithFuzzySearchFilterComponent data={navigationList} />;
};
7 changes: 0 additions & 7 deletions plugins/base/frontend/src/main/components/utils/requests.tsx

This file was deleted.

4 changes: 0 additions & 4 deletions plugins/base/src/main/kotlin/DokkaBase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,6 @@ class DokkaBase : DokkaPlugin() {
htmlPreprocessors providing ::NavigationPageInstaller order { after(rootCreator) }
}

val navigationSearchInstaller by extending {
htmlPreprocessors providing ::NavigationSearchInstaller order { after(rootCreator) }
}

val scriptsInstaller by extending {
htmlPreprocessors providing ::ScriptsInstaller order { after(rootCreator) }
}
Expand Down
10 changes: 10 additions & 0 deletions plugins/base/src/main/kotlin/renderers/html/JsSerializer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.jetbrains.dokka.base.renderers.html

class JsSerializer {
fun serialize(content: Iterable<SearchRecord>): String = content.joinToString(separator = ",", prefix = "[", postfix = "]") { serialize(it) }

fun serialize(record: SearchRecord): String =
"""
{ name: "${record.name}", description: ${ record.description?.let { "\"$it\"" } ?: "null" }, location: "${record.location}", searchKeys: ${record.searchKeys.joinToString(prefix = "[", postfix = "]"){ "\"$it\""}}}
""".trimIndent()
}
61 changes: 2 additions & 59 deletions plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
Original file line number Diff line number Diff line change
@@ -1,67 +1,10 @@
package org.jetbrains.dokka.base.renderers.html

import kotlinx.html.*
import kotlinx.html.stream.createHTML
import org.jetbrains.dokka.base.renderers.pageId
import org.jetbrains.dokka.base.templating.AddToNavigationCommand
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.DisplaySourceSet
import org.jetbrains.dokka.model.WithChildren
import org.jetbrains.dokka.pages.PageNode
import org.jetbrains.dokka.pages.RendererSpecificPage
import org.jetbrains.dokka.pages.RenderingStrategy
import org.jetbrains.dokka.plugability.DokkaContext

class NavigationPage(val root: NavigationNode, val moduleName: String, val context: DokkaContext) :
RendererSpecificPage {
override val name = "navigation"

override val children = emptyList<PageNode>()

override fun modified(name: String, children: List<PageNode>) = this

override val strategy = RenderingStrategy<HtmlRenderer> {
createHTML().visit(root, this)
}

private fun <R> TagConsumer<R>.visit(node: NavigationNode, renderer: HtmlRenderer): R = with(renderer) {
if (context.configuration.delayTemplateSubstitution) {
templateCommand(AddToNavigationCommand(moduleName)) {
visit(node, "${moduleName}-nav-submenu", renderer)
}
} else {
visit(node, "${moduleName}-nav-submenu", renderer)
}
}

private fun <R> TagConsumer<R>.visit(node: NavigationNode, navId: String, renderer: HtmlRenderer): R =
with(renderer) {
div("sideMenuPart") {
id = navId
attributes["pageId"] = "${moduleName}::${node.pageId}"
div("overview") {
buildLink(node.dri, node.sourceSets.toList()) { buildBreakableText(node.name) }
if (node.children.isNotEmpty()) {
span("navButton pull-right") {
onClick = """document.getElementById("$navId").classList.toggle("hidden");"""
span("navButtonContent")
}
}
}
node.children.withIndex().forEach { (n, p) -> visit(p, "$navId-$n", renderer) }
}
}
}

data class NavigationNode(
val id: String,
val name: String,
val dri: DRI,
val sourceSets: Set<DisplaySourceSet>,
val location: String?,
override val children: List<NavigationNode>
) : WithChildren<NavigationNode>

fun NavigationPage.transform(block: (NavigationNode) -> NavigationNode) =
NavigationPage(root.transform(block), moduleName, context)

fun NavigationNode.transform(block: (NavigationNode) -> NavigationNode) =
run(block).let { NavigationNode(it.name, it.dri, it.sourceSets, it.children.map(block)) }
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.jetbrains.dokka.base.renderers.html

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.jetbrains.dokka.Platform
import org.jetbrains.dokka.base.renderers.sourceSets
import org.jetbrains.dokka.base.templating.AddToSearch
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.DisplaySourceSet
Expand All @@ -11,8 +12,6 @@ import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.transformers.pages.PageTransformer

typealias PageId = String

data class SearchRecord(
val name: String,
val description: String? = null,
Expand All @@ -34,8 +33,9 @@ open class SearchbarDataInstaller(val context: DokkaContext) : PageTransformer {
}

private val mapper = jacksonObjectMapper()
private val jsSerializer = JsSerializer()

open fun generatePagesList(pages: List<PageWithId>, locationResolver: PageResolver): List<SearchRecord> =
open fun generatePagesList(pages: List<PageWithId>, locationResolver: DriResolver): List<SearchRecord> =
pages.map { pageWithId ->
createSearchRecord(
name = pageWithId.displayableSignature,
Expand Down Expand Up @@ -64,16 +64,16 @@ open class SearchbarDataInstaller(val context: DokkaContext) : PageTransformer {
else -> emptyList()
}

private fun resolveLocation(locationResolver: PageResolver, page: ContentPage): String? =
locationResolver(page, null).also { location ->
private fun resolveLocation(locationResolver: DriResolver, page: ContentPage): String? =
locationResolver(page.dri.first(), page.sourceSets()).also { location ->
if (location.isNullOrBlank()) context.logger.warn("Cannot resolve path for ${page.dri}")
}

override fun invoke(input: RootPageNode): RootPageNode {
val page = RendererSpecificResourcePage(
name = "scripts/pages.json",
name = "scripts/pages.js",
children = emptyList(),
strategy = RenderingStrategy.PageLocationResolvableWrite { resolver ->
strategy = RenderingStrategy.DriLocationResolvableWrite { resolver ->
val content = input.withDescendants().fold(emptyList<PageWithId>()) { pageList, page ->
pageList + processPage(page)
}.run {
Expand All @@ -83,11 +83,13 @@ open class SearchbarDataInstaller(val context: DokkaContext) : PageTransformer {
if (context.configuration.delayTemplateSubstitution) {
mapper.writeValueAsString(AddToSearch(context.configuration.moduleName, content))
} else {
mapper.writeValueAsString(content)
"var pages = " + mapper.writeValueAsString(content)
}
})

return input.modified(children = input.children + page)
return input
.modified(children = input.children + page)
.transformContentPagesTree { it.modified(embeddedResources = listOf(page.name) + it.embeddedResources) }
}
}

Expand Down
Loading