aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/frontend/src/main/components/search
diff options
context:
space:
mode:
authorMarcin Aman <maman@virtuslab.com>2020-07-28 16:57:07 +0200
committerPaweł Marks <Kordyjan@users.noreply.github.com>2020-08-03 13:47:51 +0200
commitd0f707af0199a99085dc40693628acbf7e543abe (patch)
tree8819f1b78fb7a5ff88e824f23cce1632b6dfd8d3 /plugins/base/frontend/src/main/components/search
parent460cdf8bc9c54efd736b10ca58a865f034bf2e84 (diff)
downloaddokka-d0f707af0199a99085dc40693628acbf7e543abe.tar.gz
dokka-d0f707af0199a99085dc40693628acbf7e543abe.tar.bz2
dokka-d0f707af0199a99085dc40693628acbf7e543abe.zip
Highlight searched phrase
Diffstat (limited to 'plugins/base/frontend/src/main/components/search')
-rw-r--r--plugins/base/frontend/src/main/components/search/search.scss4
-rw-r--r--plugins/base/frontend/src/main/components/search/search.tsx116
-rw-r--r--plugins/base/frontend/src/main/components/search/types.ts2
3 files changed, 94 insertions, 28 deletions
diff --git a/plugins/base/frontend/src/main/components/search/search.scss b/plugins/base/frontend/src/main/components/search/search.scss
index cc5a61ac..1068fe7a 100644
--- a/plugins/base/frontend/src/main/components/search/search.scss
+++ b/plugins/base/frontend/src/main/components/search/search.scss
@@ -25,6 +25,10 @@
.template-wrapper {
display: grid;
grid-template-columns: auto auto;
+
+ span.phraseHighlight {
+ font-weight: bold;
+ }
}
.template-name {
diff --git a/plugins/base/frontend/src/main/components/search/search.tsx b/plugins/base/frontend/src/main/components/search/search.tsx
index c7b36654..14bf3c24 100644
--- a/plugins/base/frontend/src/main/components/search/search.tsx
+++ b/plugins/base/frontend/src/main/components/search/search.tsx
@@ -1,10 +1,81 @@
import React, {useCallback, useState} from 'react';
-import {Select} from '@jetbrains/ring-ui';
-import {List} from '@jetbrains/ring-ui';
+import {Select, List} from '@jetbrains/ring-ui';
+import fuzzyHighlight from '@jetbrains/ring-ui/components/global/fuzzy-highlight.js'
import '@jetbrains/ring-ui/components/input-size/input-size.scss';
import './search.scss';
import {IWindow, Option, Props, Page} from "./types";
+type OptionWithSearchResult = Option & {
+ matched: boolean,
+ highlight: string
+}
+
+type OptionWithHighlightComponent = Option & {
+ name: React.FC<SearchProps>
+}
+
+type SearchProps = {
+ page: Option,
+ label: string
+}
+
+const orderRecords = (records: OptionWithSearchResult[], searchPhrase: string): OptionWithSearchResult[] => {
+ return records.sort((a: OptionWithSearchResult, b: OptionWithSearchResult) => {
+ //Prefer exact matches
+ const aIncludes = a.name.toLowerCase().includes(searchPhrase.toLowerCase()) ? 1 : 0
+ const bIncludes = b.name.toLowerCase().includes(searchPhrase.toLowerCase()) ? 1 : 0
+ const byIncludes = bIncludes - aIncludes
+ if(byIncludes != 0 && aIncludes == 1){
+ return byIncludes
+ }
+
+ //Prefer matches that are closer
+ const byFirstMatchedPosition = a.highlight.indexOf("**") - b.highlight.indexOf("**")
+ if(byFirstMatchedPosition == 0) {
+ return a.name.toLowerCase().localeCompare(b.name.toLowerCase())
+ }
+ return byFirstMatchedPosition
+ })
+}
+
+const SearchResultRow: React.FC<SearchProps> = ({label, page}: SearchProps) => {
+ const withSignature = page.name.replace(page.searchKey, label)
+
+ return (
+ <div className="template-wrapper">
+ <span dangerouslySetInnerHTML={
+ {__html: withSignature.replace(/\*\*(.*?)\*\*/g, '<span class="phraseHighlight">$1</span>') }
+ }/>
+ <span className="template-description">{page.description}</span>
+ </div>
+ )
+}
+
+const highlightMatchedPhrases = (records: OptionWithSearchResult[]): OptionWithHighlightComponent[] => {
+ // @ts-ignore
+ return records.map(record => {
+ return {
+ ...record,
+ template: <SearchResultRow label={record.highlight} page={record}/>
+ }
+ })
+}
+
+class DokkaFuzzyFilterComponent extends Select {
+ getListItems(rawFilterString: string, _: Option[]) {
+ const matchedRecords = this.props.data
+ .map((record: Option) => {
+ return {
+ ...fuzzyHighlight(rawFilterString.trim(), record.searchKey),
+ ...record
+ }
+ })
+ .filter((record: OptionWithSearchResult) => record.matched)
+
+ return highlightMatchedPhrases(orderRecords(matchedRecords, rawFilterString))
+ }
+}
+
const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => {
const [selected, onSelected] = useState<Option>(data[0]);
const onChangeSelected = useCallback(
@@ -18,43 +89,37 @@ const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => {
return (
<div className="search-container">
<div className="search">
- <Select
+ <DokkaFuzzyFilterComponent
selectedLabel="Search"
label="Please type page name"
- filter={{fuzzy: true}}
+ filter={true}
type={Select.Type.CUSTOM}
clear
+ renderOptimization
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>
- )}
+ customAnchor={({wrapperProps, buttonProps, popup}) =>
+ <DokkaSearchAnchor wrapperProps={wrapperProps} buttonProps={buttonProps} popup={popup}/>
+ }
/>
</div>
</div>
)
}
-const templateGenerator = (page:Page) => {
- let classGenerator = (page:Page) => {
- let classes = ""
- if(page.level !== undefined) classes = classes + " indented"
- if(page.disabled) classes = classes + " disabled"
- return classes
- }
- return <div className="template-wrapper">
- <span className= {classGenerator(page)}>{page.name}</span>
- <span className="template-description">{page.description}</span>
- </div>
+const DokkaSearchAnchor = ({wrapperProps, buttonProps, popup}) => {
+ return (
+ <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>
+ )
}
export const WithFuzzySearchFilter = () => {
@@ -66,7 +131,6 @@ export const WithFuzzySearchFilter = () => {
label: page.searchKey,
key: i + 1,
type: page.kind,
- template: templateGenerator(page),
rgItemType: List.ListProps.Type.CUSTOM
}));
}
diff --git a/plugins/base/frontend/src/main/components/search/types.ts b/plugins/base/frontend/src/main/components/search/types.ts
index 881a16d8..a6736a2d 100644
--- a/plugins/base/frontend/src/main/components/search/types.ts
+++ b/plugins/base/frontend/src/main/components/search/types.ts
@@ -3,8 +3,6 @@ export type Page = {
kind: string;
location: string;
searchKey: string;
- level: number;
- index: string;
description: string;
disabled: boolean;
}