aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/base/frontend/package-lock.json253
-rw-r--r--plugins/base/frontend/package.json1
-rw-r--r--plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx63
-rw-r--r--plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx13
-rw-r--r--plugins/base/frontend/src/main/components/search/search.tsx116
-rw-r--r--plugins/base/frontend/src/main/components/search/searchIcon.svg3
-rw-r--r--plugins/base/frontend/src/main/components/search/searchResultRow.tsx24
-rw-r--r--plugins/base/frontend/src/main/components/search/types.ts19
-rw-r--r--plugins/base/frontend/src/main/types/@jetbrains/index.d.ts6
-rw-r--r--plugins/base/frontend/webpack.config.js2
10 files changed, 378 insertions, 122 deletions
diff --git a/plugins/base/frontend/package-lock.json b/plugins/base/frontend/package-lock.json
index 47c54682..4ccf72f2 100644
--- a/plugins/base/frontend/package-lock.json
+++ b/plugins/base/frontend/package-lock.json
@@ -1553,6 +1553,12 @@
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
},
+ "@types/q": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
+ "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==",
+ "dev": true
+ },
"@types/react": {
"version": "16.9.36",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.36.tgz",
@@ -2254,6 +2260,12 @@
"object.assign": "^4.1.0"
}
},
+ "babel-plugin-react-svg": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-react-svg/-/babel-plugin-react-svg-3.0.3.tgz",
+ "integrity": "sha512-Pst1RWjUIiV0Ykv1ODSeceCBsFOP2Y4dusjq7/XkjuzJdvS9CjpkPMUIoO4MLlvp5PiLCeMlsOC7faEUA0gm3Q==",
+ "dev": true
+ },
"babel-plugin-transform-define": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-define/-/babel-plugin-transform-define-2.0.0.tgz",
@@ -2515,6 +2527,12 @@
}
}
},
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
+ "dev": true
+ },
"boolean": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz",
@@ -3162,6 +3180,17 @@
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
},
+ "coa": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz",
+ "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==",
+ "dev": true,
+ "requires": {
+ "@types/q": "^1.5.1",
+ "chalk": "^2.4.1",
+ "q": "^1.1.2"
+ }
+ },
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@@ -3718,6 +3747,66 @@
"postcss": "^7.0.5"
}
},
+ "css-select": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz",
+ "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-what": "^3.2.1",
+ "domutils": "^1.7.0",
+ "nth-check": "^1.0.2"
+ },
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "domutils": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
+ "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ }
+ }
+ },
+ "css-select-base-adapter": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
+ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==",
+ "dev": true
+ },
+ "css-tree": {
+ "version": "1.0.0-alpha.37",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
+ "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==",
+ "dev": true,
+ "requires": {
+ "mdn-data": "2.0.4",
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "css-what": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.3.0.tgz",
+ "integrity": "sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==",
+ "dev": true
+ },
"cssdb": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz",
@@ -3728,6 +3817,39 @@
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
},
+ "csso": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz",
+ "integrity": "sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==",
+ "dev": true,
+ "requires": {
+ "css-tree": "1.0.0-alpha.39"
+ },
+ "dependencies": {
+ "css-tree": {
+ "version": "1.0.0-alpha.39",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz",
+ "integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==",
+ "dev": true,
+ "requires": {
+ "mdn-data": "2.0.6",
+ "source-map": "^0.6.1"
+ }
+ },
+ "mdn-data": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz",
+ "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
"csstype": {
"version": "2.6.10",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz",
@@ -7423,12 +7545,24 @@
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
},
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+ "dev": true
+ },
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
"dev": true
},
+ "lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
+ "dev": true
+ },
"lodash.pad": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz",
@@ -7711,6 +7845,12 @@
}
}
},
+ "mdn-data": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
+ "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
+ "dev": true
+ },
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -8540,6 +8680,15 @@
"gauge": "~1.2.5"
}
},
+ "nth-check": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
+ "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
+ "dev": true,
+ "requires": {
+ "boolbase": "~1.0.0"
+ }
+ },
"num2fraction": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
@@ -8628,6 +8777,16 @@
"object-keys": "^1.0.11"
}
},
+ "object.getownpropertydescriptors": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz",
+ "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1"
+ }
+ },
"object.pick": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
@@ -8636,6 +8795,18 @@
"isobject": "^3.0.1"
}
},
+ "object.values": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
+ "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ }
+ },
"obuf": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
@@ -10177,6 +10348,12 @@
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
+ "q": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
+ "dev": true
+ },
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
@@ -10335,6 +10512,31 @@
"prop-types": "^15.5.7"
}
},
+ "react-svg-core": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/react-svg-core/-/react-svg-core-3.0.3.tgz",
+ "integrity": "sha512-Ws3eM3xCAwcaYeqm4Ajcz3zxBYNI6BeTWWhFR0cpOT+pWuVtozgHYK9xUM0S/ilapZgYMQDe49XgOxpvooFq4w==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.4.5",
+ "@babel/plugin-syntax-jsx": "^7.2.0",
+ "@babel/preset-react": "^7.0.0",
+ "babel-plugin-react-svg": "^3.0.3",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.isplainobject": "^4.0.6",
+ "svgo": "^1.2.2"
+ }
+ },
+ "react-svg-loader": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/react-svg-loader/-/react-svg-loader-3.0.3.tgz",
+ "integrity": "sha512-V1KnIUtvWVvc4xCig34n+f+/74ylMMugB2FbuAF/yq+QRi+WLi2hUYp9Ze3VylhA1D7ZgRygBh3Ojj8S3TPhJA==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.2.3",
+ "react-svg-core": "^3.0.3"
+ }
+ },
"react-virtualized": {
"version": "9.21.2",
"resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.21.2.tgz",
@@ -11188,6 +11390,12 @@
}
}
},
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true
+ },
"scheduler": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
@@ -11915,6 +12123,12 @@
"figgy-pudding": "^3.5.1"
}
},
+ "stable": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
+ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+ "dev": true
+ },
"stackframe": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz",
@@ -12526,6 +12740,27 @@
"integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=",
"dev": true
},
+ "svgo": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz",
+ "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "coa": "^2.0.2",
+ "css-select": "^2.0.0",
+ "css-select-base-adapter": "^0.1.1",
+ "css-tree": "1.0.0-alpha.37",
+ "csso": "^4.0.2",
+ "js-yaml": "^3.13.1",
+ "mkdirp": "~0.5.1",
+ "object.values": "^1.1.0",
+ "sax": "~1.2.4",
+ "stable": "^0.1.8",
+ "unquote": "~1.1.1",
+ "util.promisify": "~1.0.0"
+ }
+ },
"symbol-observable": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
@@ -13304,6 +13539,12 @@
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
+ "unquote": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
+ "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=",
+ "dev": true
+ },
"unset-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
@@ -13518,6 +13759,18 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
+ "util.promisify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz",
+ "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.2",
+ "has-symbols": "^1.0.1",
+ "object.getownpropertydescriptors": "^2.1.0"
+ }
+ },
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
diff --git a/plugins/base/frontend/package.json b/plugins/base/frontend/package.json
index a1bf42c2..4415dd1c 100644
--- a/plugins/base/frontend/package.json
+++ b/plugins/base/frontend/package.json
@@ -46,6 +46,7 @@
"@jetbrains/stylelint-config": "^2.0.0",
"babel-eslint": "^10.0.3",
"eslint": "^6.8.0",
+ "react-svg-loader": "^3.0.3",
"sass": "^1.26.3",
"sass-loader": "^8.0.2",
"stylelint": "^13.3.2",
diff --git a/plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx b/plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx
new file mode 100644
index 00000000..725fbaee
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx
@@ -0,0 +1,63 @@
+import {Select} from "@jetbrains/ring-ui";
+import {Option, OptionWithHighlightComponent, OptionWithSearchResult, SearchRank} from "./types";
+import fuzzyHighlight from '@jetbrains/ring-ui/components/global/fuzzy-highlight.js'
+import React from "react";
+import {SearchResultRow} from "./searchResultRow";
+
+const orderRecords = (records: OptionWithSearchResult[], searchPhrase: string): OptionWithSearchResult[] => {
+ return records.sort((a: OptionWithSearchResult, b: OptionWithSearchResult) => {
+ //Prefer higher rank
+ const byRank = b.rank - a.rank
+ if(byRank !== 0){
+ return byRank
+ }
+ //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){
+ 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 highlightMatchedPhrases = (records: OptionWithSearchResult[]): OptionWithHighlightComponent[] => {
+ // @ts-ignore
+ return records.map(record => {
+ return {
+ ...record,
+ template: <SearchResultRow searchResult={record}/>
+ }
+ })
+}
+
+export class DokkaFuzzyFilterComponent extends Select {
+ getListItems(rawFilterString: string, _: Option[]) {
+ const matchedRecords = this.props.data
+ .map((record: Option) => {
+ const bySearchKey = fuzzyHighlight(rawFilterString.trim(), record.searchKey, false)
+ if(bySearchKey.matched){
+ return {
+ ...bySearchKey,
+ ...record,
+ rank: SearchRank.SearchKeyMatch
+ }
+ }
+ return {
+ ...fuzzyHighlight(rawFilterString.trim(), record.name, false),
+ ...record,
+ rank: SearchRank.NameMatch
+ }
+ })
+ .filter((record: OptionWithSearchResult) => record.matched)
+
+ return highlightMatchedPhrases(orderRecords(matchedRecords, rawFilterString))
+ }
+} \ No newline at end of file
diff --git a/plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx b/plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx
new file mode 100644
index 00000000..ad0b5f8f
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx
@@ -0,0 +1,13 @@
+import React from "react";
+import SearchIcon from 'react-svg-loader!./searchIcon.svg';
+
+export const DokkaSearchAnchor = ({wrapperProps, buttonProps, popup}: any) => {
+ return (
+ <span {...wrapperProps}>
+ <button type="button" {...buttonProps}>
+ <SearchIcon />
+ </button>
+ {popup}
+ </span>
+ )
+} \ 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 b4c5b1cd..c7976edb 100644
--- a/plugins/base/frontend/src/main/components/search/search.tsx
+++ b/plugins/base/frontend/src/main/components/search/search.tsx
@@ -1,107 +1,10 @@
import React, {useCallback, useState} from 'react';
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";
-
-enum SearchRank {
- SearchKeyMatch = 1,
- NameMatch = 0
-}
-type OptionWithSearchResult = Option & {
- matched: boolean,
- highlight: string,
- rank: SearchRank
-}
-
-type OptionWithHighlightComponent = Option & {
- name: React.FC<SearchProps>
-}
-
-type SearchProps = {
- searchResult: OptionWithSearchResult,
-}
-
-const orderRecords = (records: OptionWithSearchResult[], searchPhrase: string): OptionWithSearchResult[] => {
- return records.sort((a: OptionWithSearchResult, b: OptionWithSearchResult) => {
- //Prefer higher rank
- const byRank = b.rank - a.rank
- if(byRank !== 0){
- return byRank
- }
- //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){
- 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> = ({searchResult}: SearchProps) => {
- const signatureFromSearchResult = (searchResult: OptionWithSearchResult): string => {
- if(searchResult.rank == SearchRank.SearchKeyMatch){
- return searchResult.name.replace(searchResult.searchKey, searchResult.highlight)
- }
- return searchResult.highlight
- }
-
- const renderHighlightMarkersAsHtml = (record: string): string => {
- return record.replace(/\*\*(.*?)\*\*/g, '<span class="phraseHighlight">$1</span>')
- }
-
- return (
- <div className="template-wrapper">
- <span dangerouslySetInnerHTML={
- {__html: renderHighlightMarkersAsHtml(signatureFromSearchResult(searchResult)) }
- }/>
- <span className="template-description">{searchResult.description}</span>
- </div>
- )
-}
-
-const highlightMatchedPhrases = (records: OptionWithSearchResult[]): OptionWithHighlightComponent[] => {
- // @ts-ignore
- return records.map(record => {
- return {
- ...record,
- template: <SearchResultRow searchResult={record}/>
- }
- })
-}
-
-class DokkaFuzzyFilterComponent extends Select {
- getListItems(rawFilterString: string, _: Option[]) {
- const matchedRecords = this.props.data
- .map((record: Option) => {
- const bySearchKey = fuzzyHighlight(rawFilterString.trim(), record.searchKey, true)
- if(bySearchKey.matched){
- return {
- ...bySearchKey,
- ...record,
- rank: SearchRank.SearchKeyMatch
- }
- }
- return {
- ...fuzzyHighlight(rawFilterString.trim(), record.name),
- ...record,
- rank: SearchRank.NameMatch
- }
- })
- .filter((record: OptionWithSearchResult) => record.matched)
-
- return highlightMatchedPhrases(orderRecords(matchedRecords, rawFilterString))
- }
-}
+import {IWindow, Option, Props} from "./types";
+import {DokkaSearchAnchor} from "./dokkaSearchAnchor";
+import {DokkaFuzzyFilterComponent} from "./dokkaFuzzyFilter";
const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => {
const [selected, onSelected] = useState<Option>(data[0]);
@@ -136,19 +39,6 @@ const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => {
)
}
-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 = () => {
let data: Option[] = [];
const pages = (window as IWindow).pages;
diff --git a/plugins/base/frontend/src/main/components/search/searchIcon.svg b/plugins/base/frontend/src/main/components/search/searchIcon.svg
new file mode 100644
index 00000000..391b1cab
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/search/searchIcon.svg
@@ -0,0 +1,3 @@
+<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> \ No newline at end of file
diff --git a/plugins/base/frontend/src/main/components/search/searchResultRow.tsx b/plugins/base/frontend/src/main/components/search/searchResultRow.tsx
new file mode 100644
index 00000000..9ae19cb9
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/search/searchResultRow.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import {OptionWithSearchResult, SearchProps, SearchRank} from "./types";
+
+export const SearchResultRow: React.FC<SearchProps> = ({searchResult}: SearchProps) => {
+ const signatureFromSearchResult = (searchResult: OptionWithSearchResult): string => {
+ if(searchResult.rank == SearchRank.SearchKeyMatch){
+ return searchResult.name.replace(searchResult.searchKey, searchResult.highlight)
+ }
+ return searchResult.highlight
+ }
+
+ const renderHighlightMarkersAsHtml = (record: string): string => {
+ return record.replace(/\*\*(.*?)\*\*/g, '<span class="phraseHighlight">$1</span>')
+ }
+
+ return (
+ <div className="template-wrapper">
+ <span dangerouslySetInnerHTML={
+ {__html: renderHighlightMarkersAsHtml(signatureFromSearchResult(searchResult)) }
+ }/>
+ <span className="template-description">{searchResult.description}</span>
+ </div>
+ )
+} \ No newline at end of file
diff --git a/plugins/base/frontend/src/main/components/search/types.ts b/plugins/base/frontend/src/main/components/search/types.ts
index a6736a2d..922935bd 100644
--- a/plugins/base/frontend/src/main/components/search/types.ts
+++ b/plugins/base/frontend/src/main/components/search/types.ts
@@ -1,3 +1,5 @@
+import React from "react";
+
export type Page = {
name: string;
kind: string;
@@ -23,7 +25,20 @@ export type Props = {
data: Option[]
};
+export enum SearchRank {
+ SearchKeyMatch = 1,
+ NameMatch = 0
+}
+export type OptionWithSearchResult = Option & {
+ matched: boolean,
+ highlight: string,
+ rank: SearchRank
+}
+
+export type OptionWithHighlightComponent = Option & {
+ name: React.FC<SearchProps>
+}
-export type State = {
- selected: any
+export type SearchProps = {
+ searchResult: OptionWithSearchResult,
}
diff --git a/plugins/base/frontend/src/main/types/@jetbrains/index.d.ts b/plugins/base/frontend/src/main/types/@jetbrains/index.d.ts
index 8ab594e3..3d209657 100644
--- a/plugins/base/frontend/src/main/types/@jetbrains/index.d.ts
+++ b/plugins/base/frontend/src/main/types/@jetbrains/index.d.ts
@@ -2,9 +2,3 @@ declare module '@jetbrains/ring-ui' {
export const Select: any;
export const List: any;
}
-
-declare module '@jetbrains/ring-ui/components/global/fuzzy-highlight.js' {
- import {Option} from "../../components/search/types";
-
- export const fuzzyHighlight: (string, string, boolean) => Option[]
-}
diff --git a/plugins/base/frontend/webpack.config.js b/plugins/base/frontend/webpack.config.js
index 559f5792..3372b37f 100644
--- a/plugins/base/frontend/webpack.config.js
+++ b/plugins/base/frontend/webpack.config.js
@@ -16,7 +16,7 @@ const webpackConfig = () => ({
entry: `${componentsPath}/root.tsx`,
resolve: {
mainFields: ['module', 'browser', 'main'],
- extensions: ['.tsx', '.ts', '.js'],
+ extensions: ['.tsx', '.ts', '.js', '.svg'],
alias: {
react: resolve('./node_modules/react'),
'react-dom': resolve('./node_modules/react-dom'),