aboutsummaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorMarcin Aman <maman@virtuslab.com>2020-07-30 15:05:06 +0200
committerPaweł Marks <Kordyjan@users.noreply.github.com>2020-08-21 15:40:12 +0200
commit3c07bc1966c85de3351602c4e8798fa507c40e49 (patch)
treeef39ea18a4ec699e4cab9dc55489b43fccb1e247 /plugins
parentc2b02c1fc17b839075b7cb6fd42498a519473fae (diff)
downloaddokka-3c07bc1966c85de3351602c4e8798fa507c40e49.tar.gz
dokka-3c07bc1966c85de3351602c4e8798fa507c40e49.tar.bz2
dokka-3c07bc1966c85de3351602c4e8798fa507c40e49.zip
Create navigation search component
Diffstat (limited to 'plugins')
-rw-r--r--plugins/base/build.gradle.kts1
-rw-r--r--plugins/base/frontend/src/main/components/navigationPaneSearch/clear.svg3
-rw-r--r--plugins/base/frontend/src/main/components/navigationPaneSearch/navigationPaneSearch.scss37
-rw-r--r--plugins/base/frontend/src/main/components/navigationPaneSearch/navigationPaneSearch.tsx73
-rw-r--r--plugins/base/frontend/src/main/components/root.tsx9
-rw-r--r--plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx18
-rw-r--r--plugins/base/frontend/src/main/components/search/search.scss14
-rw-r--r--plugins/base/frontend/src/main/components/search/search.tsx1
-rw-r--r--plugins/base/frontend/src/main/components/search/types.ts1
-rw-r--r--plugins/base/frontend/src/main/scss/index.scss3
-rw-r--r--plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt4
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt3
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt2
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt59
14 files changed, 197 insertions, 31 deletions
diff --git a/plugins/base/build.gradle.kts b/plugins/base/build.gradle.kts
index a7807223..9ffecbd7 100644
--- a/plugins/base/build.gradle.kts
+++ b/plugins/base/build.gradle.kts
@@ -10,6 +10,7 @@ dependencies {
api(project(":kotlin-analysis"))
implementation("org.jsoup:jsoup:1.12.1")
+ implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.1")
testImplementation(project(":test-tools"))
testImplementation(project(":plugins:base:test-utils"))
diff --git a/plugins/base/frontend/src/main/components/navigationPaneSearch/clear.svg b/plugins/base/frontend/src/main/components/navigationPaneSearch/clear.svg
new file mode 100644
index 00000000..ad6a2026
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/navigationPaneSearch/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>
diff --git a/plugins/base/frontend/src/main/components/navigationPaneSearch/navigationPaneSearch.scss b/plugins/base/frontend/src/main/components/navigationPaneSearch/navigationPaneSearch.scss
new file mode 100644
index 00000000..b5714ca4
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/navigationPaneSearch/navigationPaneSearch.scss
@@ -0,0 +1,37 @@
+@import "src/main/scss/index.scss";
+
+$defaultHeight: 40px;
+
+div#paneSearch {
+
+ width: 248px;
+ margin: 0 auto;
+
+ input#navigation-pane-search {
+ background: $white;
+ border: 1px solid $grey-border;
+ box-sizing: border-box;
+ border-radius: 4px;
+ padding: 8px;
+ height: $defaultHeight;
+ }
+
+ .navigation-pane-search {
+ width: 100% !important;
+ padding-top: 16px;
+ }
+
+ div.paneSearchInputWrapper {
+ position: relative;
+ span.paneSearchInputClearIcon {
+ position: absolute;
+ top: calc(50% + 2px); //Just to include a border
+ right: 8px;
+ cursor: pointer;
+ }
+ }
+}
+
+.navigation-pane-popup {
+ margin-top: 1.2em;
+} \ No newline at end of file
diff --git a/plugins/base/frontend/src/main/components/navigationPaneSearch/navigationPaneSearch.tsx b/plugins/base/frontend/src/main/components/navigationPaneSearch/navigationPaneSearch.tsx
new file mode 100644
index 00000000..3174b023
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/navigationPaneSearch/navigationPaneSearch.tsx
@@ -0,0 +1,73 @@
+import React, { useCallback, useState, useEffect } from 'react';
+import {Select, List } from '@jetbrains/ring-ui';
+import { DokkaFuzzyFilterComponent } from '../search/dokkaFuzzyFilter';
+import { IWindow, Option } from '../search/types';
+import './navigationPaneSearch.scss';
+import ClearIcon from 'react-svg-loader!./clear.svg';
+
+export const NavigationPaneSearch = () => {
+ const defaultWidth = 300
+
+ const [navigationList, setNavigationList] = useState<Option[]>([]);
+ const [selected, onSelected] = useState<Option | null>(null);
+ const [minWidth, setMinWidth] = useState<number>(defaultWidth);
+ const [filterValue, setFilterValue] = useState<string>('')
+
+ const onChangeSelected = useCallback(
+ (element: Option) => {
+ window.location.replace(`${(window as IWindow).pathToRoot}${element.location}`)
+ onSelected(element);
+ },
+ [selected]
+ );
+
+ const onFilter = (filterValue: string, filteredRecords?: Option[]) => {
+ if(filteredRecords){
+ const requiredWidth = Math.max(...filteredRecords.map(e => e.label.length*9), defaultWidth)
+ setMinWidth(requiredWidth)
+ }
+ setFilterValue(filterValue)
+ }
+
+ const onClearClick = () => {
+ setFilterValue('')
+ }
+
+ useEffect(() => {
+ const pathToRoot = (window as IWindow).pathToRoot
+ const url = pathToRoot.endsWith('/') ? `${pathToRoot}scripts/navigation-pane.json` : `${pathToRoot}/scripts/navigation-pane.json`
+ fetch(url)
+ .then(response => response.json())
+ .then((result) => {
+ setNavigationList(result.map((record: Option) => {
+ return {
+ ...record,
+ rgItemType: List.ListProps.Type.CUSTOM
+ }
+ }))
+ },
+ (error) => {
+ console.error('failed to fetch navigationPane data', error)
+ setNavigationList([])
+ })
+ }, [])
+
+
+ return <div className={"paneSearchInputWrapper"}>
+ <DokkaFuzzyFilterComponent
+ id="navigation-pane-search"
+ className="navigation-pane-search"
+ inputPlaceholder="Title filter"
+ clear={true}
+ type={Select.Type.INPUT_WITHOUT_CONTROLS}
+ filter={{fuzzy:true, value: filterValue}}
+ selected={selected}
+ data={navigationList}
+ popupClassName={"navigation-pane-popup"}
+ onSelect={onChangeSelected}
+ onFilter={onFilter}
+ minWidth={minWidth}
+ />
+ <span className={"paneSearchInputClearIcon"} onClick={onClearClick}><ClearIcon /></span>
+ </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 70ed9550..9f3ecba9 100644
--- a/plugins/base/frontend/src/main/components/root.tsx
+++ b/plugins/base/frontend/src/main/components/root.tsx
@@ -4,15 +4,24 @@ import RedBox from 'redbox-react';
import App from "./app";
import './app/index.scss';
+import { NavigationPaneSearch } from './navigationPaneSearch/navigationPaneSearch';
const appEl = document.getElementById('searchBar');
const rootEl = document.createElement('div');
+const renderNavigationPane = () => {
+ render(
+ <NavigationPaneSearch />,
+ document.getElementById('paneSearch')
+ )
+}
+
let renderApp = () => {
render(
<App/>,
rootEl
);
+ renderNavigationPane();
};
// @ts-ignore
diff --git a/plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx b/plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx
index 725fbaee..2d9dcb3d 100644
--- a/plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx
+++ b/plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx
@@ -39,10 +39,20 @@ const highlightMatchedPhrases = (records: OptionWithSearchResult[]): OptionWithH
}
export class DokkaFuzzyFilterComponent extends Select {
+ componentDidUpdate(prevProps, prevState) {
+ super.componentDidUpdate(prevProps, prevState)
+ if(this.props.filter && this.state.filterValue != this.props.filter.value){
+ this.setState({
+ filterValue: this.props.filter.value,
+ })
+ }
+ }
+
getListItems(rawFilterString: string, _: Option[]) {
+ const filterPhrase = (rawFilterString ? rawFilterString : '').trim()
const matchedRecords = this.props.data
.map((record: Option) => {
- const bySearchKey = fuzzyHighlight(rawFilterString.trim(), record.searchKey, false)
+ const bySearchKey = fuzzyHighlight(filterPhrase, record.searchKey, false)
if(bySearchKey.matched){
return {
...bySearchKey,
@@ -51,13 +61,15 @@ export class DokkaFuzzyFilterComponent extends Select {
}
}
return {
- ...fuzzyHighlight(rawFilterString.trim(), record.name, false),
+ ...fuzzyHighlight(filterPhrase, record.name, false),
...record,
rank: SearchRank.NameMatch
}
})
.filter((record: OptionWithSearchResult) => record.matched)
- return highlightMatchedPhrases(orderRecords(matchedRecords, rawFilterString))
+ this.props.onFilter(filterPhrase, matchedRecords)
+
+ return highlightMatchedPhrases(orderRecords(matchedRecords, filterPhrase))
}
} \ No newline at end of file
diff --git a/plugins/base/frontend/src/main/components/search/search.scss b/plugins/base/frontend/src/main/components/search/search.scss
index 1068fe7a..e708ef84 100644
--- a/plugins/base/frontend/src/main/components/search/search.scss
+++ b/plugins/base/frontend/src/main/components/search/search.scss
@@ -14,20 +14,12 @@
min-width: calc(100% - 360px) !important;
}
-.indented {
- text-indent: 10px;
-}
-
-.disabled {
- color: gray;
-}
-
.template-wrapper {
+ height: 32px;
display: grid;
grid-template-columns: auto auto;
-
- span.phraseHighlight {
- font-weight: bold;
+ span {
+ line-height: 32px;
}
}
diff --git a/plugins/base/frontend/src/main/components/search/search.tsx b/plugins/base/frontend/src/main/components/search/search.tsx
index c7976edb..ef26c662 100644
--- a/plugins/base/frontend/src/main/components/search/search.tsx
+++ b/plugins/base/frontend/src/main/components/search/search.tsx
@@ -20,6 +20,7 @@ const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => {
<div className="search-container">
<div className="search">
<DokkaFuzzyFilterComponent
+ id="pages-search"
selectedLabel="Search"
label="Please type page name"
filter={true}
diff --git a/plugins/base/frontend/src/main/components/search/types.ts b/plugins/base/frontend/src/main/components/search/types.ts
index 922935bd..11e2edf8 100644
--- a/plugins/base/frontend/src/main/components/search/types.ts
+++ b/plugins/base/frontend/src/main/components/search/types.ts
@@ -6,7 +6,6 @@ export type Page = {
location: string;
searchKey: string;
description: string;
- disabled: boolean;
}
export type Option = Page & {
diff --git a/plugins/base/frontend/src/main/scss/index.scss b/plugins/base/frontend/src/main/scss/index.scss
index 74af970d..18e2861b 100644
--- a/plugins/base/frontend/src/main/scss/index.scss
+++ b/plugins/base/frontend/src/main/scss/index.scss
@@ -1 +1,4 @@
@import "~@jetbrains/ring-ui/components/global/variables.css";
+
+$white: #FFFFFF;
+$grey-border: #A6AFBA
diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
index 83a0fc5b..3d0ced06 100644
--- a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
+++ b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
@@ -7,6 +7,7 @@ import kotlinx.coroutines.runBlocking
import org.jetbrains.dokka.DokkaException
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.resolvers.local.LocationProvider
+import org.jetbrains.dokka.base.resolvers.local.resolveOrThrow
import org.jetbrains.dokka.model.DisplaySourceSet
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
@@ -168,6 +169,9 @@ abstract class DefaultRenderer<T>(
is RenderingStrategy.Copy -> outputWriter.writeResources(strategy.from, path)
is RenderingStrategy.Write -> outputWriter.write(path, strategy.text, "")
is RenderingStrategy.Callback -> outputWriter.write(path, strategy.instructions(this, page), ".html")
+ is RenderingStrategy.LocationResolvableWrite -> outputWriter.write(path, strategy.contentToResolve { dri, sourcesets ->
+ locationProvider.resolveOrThrow(dri, sourcesets)
+ }, "")
RenderingStrategy.DoNothing -> Unit
}
else -> throw AssertionError(
diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
index 5747c6da..22fb3e63 100644
--- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
@@ -675,6 +675,9 @@ open class HtmlRenderer(
id = "logo"
}
div {
+ id = "paneSearch"
+ }
+ div {
id = "sideMenu"
}
}
diff --git a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
index a6a16412..f569ed12 100644
--- a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
@@ -38,7 +38,7 @@ class NavigationPage(val root: NavigationNode) : RendererSpecificPage {
}
}
-class NavigationNode(
+data class NavigationNode(
val name: String,
val dri: DRI,
val sourceSets: Set<DisplaySourceSet>,
diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
index 407e5899..6e779931 100644
--- a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
@@ -1,20 +1,18 @@
package org.jetbrains.dokka.base.renderers.html
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
+import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import kotlinx.html.h1
import kotlinx.html.id
import kotlinx.html.table
import kotlinx.html.tbody
-import org.jetbrains.dokka.DokkaConfiguration
-import org.jetbrains.dokka.Platform
-import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.renderers.sourceSets
-import org.jetbrains.dokka.base.resolvers.local.LocationProvider
+import org.jetbrains.dokka.links.DRI
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.plugability.plugin
-import org.jetbrains.dokka.plugability.querySingle
import org.jetbrains.dokka.transformers.pages.PageTransformer
@@ -40,19 +38,45 @@ object SearchPageInstaller : PageTransformer {
}
object NavigationPageInstaller : PageTransformer {
- override fun invoke(input: RootPageNode) = input.modified(
- children = input.children + NavigationPage(
- input.children.filterIsInstance<ContentPage>().single()
- .let(NavigationPageInstaller::visit)
+ private val mapper = jacksonObjectMapper()
+
+ private data class NavigationNodeView(
+ val name: String,
+ val label: String = name,
+ val searchKey: String = name,
+ @get:JsonSerialize(using = ToStringSerializer::class) val dri: DRI,
+ val location: String
+ ) {
+ companion object {
+ fun from(node: NavigationNode, location: String): NavigationNodeView =
+ NavigationNodeView(name = node.name, dri = node.dri, location = location)
+ }
+ }
+
+ override fun invoke(input: RootPageNode): RootPageNode {
+ val nodes = input.children.filterIsInstance<ContentPage>().single()
+ .let(NavigationPageInstaller::visit)
+
+ val page = RendererSpecificResourcePage(
+ name = "scripts/navigation-pane.json",
+ children = emptyList(),
+ strategy = RenderingStrategy.LocationResolvableWrite { resolver ->
+ val flattened = flattenNavigationNodes(listOf(nodes))
+ val view = flattened.map { NavigationNodeView.from(it, resolver(it.dri, it.sourceSets)) }
+ mapper.writeValueAsString(view)
+ })
+
+ return input.modified(
+ children = input.children + page + NavigationPage(nodes)
)
- )
+ }
private fun visit(page: ContentPage): NavigationNode =
NavigationNode(
- page.name,
- page.dri.first(),
- page.sourceSets(),
- page.navigableChildren()
+ name = page.name,
+ dri = page.dri.first(),
+ sourceSets = page.sourceSets(),
+ children = page.navigableChildren()
)
private fun ContentPage.navigableChildren(): List<NavigationNode> =
@@ -63,6 +87,11 @@ object NavigationPageInstaller : PageTransformer {
children.filter { it is ContentPage && it.documentable is DEnumEntry }.map { visit(it as ContentPage) }
else -> emptyList()
}.sortedBy { it.name.toLowerCase() }
+
+ private tailrec fun flattenNavigationNodes(nodes: List<NavigationNode>, acc: List<NavigationNode> = emptyList()): List<NavigationNode> {
+ if(nodes.isEmpty()) return acc
+ return flattenNavigationNodes(nodes.flatMap { it.children }, nodes.map { it.copy(children = emptyList()) } + acc)
+ }
}
object ResourceInstaller : PageTransformer {