Add Search functionality (#729)
* Install `lunar-search` component * Add lightweight search
This commit is contained in:
parent
311f439c80
commit
18ed2a2080
|
@ -66,6 +66,7 @@ module.exports = {
|
||||||
copyright: `Copyright © ${new Date().getFullYear()} Solana Foundation`,
|
copyright: `Copyright © ${new Date().getFullYear()} Solana Foundation`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
plugins: [require.resolve('docusaurus-lunr-search')],
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
"@docusaurus/preset-classic",
|
"@docusaurus/preset-classic",
|
||||||
|
|
|
@ -1743,6 +1743,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
|
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
|
||||||
},
|
},
|
||||||
|
"@types/parse5": {
|
||||||
|
"version": "5.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz",
|
||||||
|
"integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw=="
|
||||||
|
},
|
||||||
"@types/q": {
|
"@types/q": {
|
||||||
"version": "1.5.4",
|
"version": "1.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
|
||||||
|
@ -2556,6 +2561,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
||||||
"integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY="
|
"integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY="
|
||||||
},
|
},
|
||||||
|
"bcp-47-match": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w=="
|
||||||
|
},
|
||||||
"bcrypt-pbkdf": {
|
"bcrypt-pbkdf": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||||
|
@ -3365,6 +3375,11 @@
|
||||||
"simple-swizzle": "^0.2.2"
|
"simple-swizzle": "^0.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"color-support": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
|
||||||
|
},
|
||||||
"colorette": {
|
"colorette": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz",
|
||||||
|
@ -3495,6 +3510,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
|
||||||
"integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA=="
|
"integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA=="
|
||||||
},
|
},
|
||||||
|
"console-control-strings": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
||||||
|
},
|
||||||
"constants-browserify": {
|
"constants-browserify": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
|
||||||
|
@ -3864,6 +3884,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
|
||||||
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
|
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
|
||||||
},
|
},
|
||||||
|
"css-selector-parser": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g=="
|
||||||
|
},
|
||||||
"css-tree": {
|
"css-tree": {
|
||||||
"version": "1.0.0-alpha.37",
|
"version": "1.0.0-alpha.37",
|
||||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
|
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
|
||||||
|
@ -4271,6 +4296,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"direction": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ=="
|
||||||
|
},
|
||||||
"dns-equal": {
|
"dns-equal": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
|
||||||
|
@ -4315,6 +4345,69 @@
|
||||||
"esutils": "^2.0.2"
|
"esutils": "^2.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"docusaurus-lunr-search": {
|
||||||
|
"version": "2.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/docusaurus-lunr-search/-/docusaurus-lunr-search-2.1.7.tgz",
|
||||||
|
"integrity": "sha512-RHrt8li7VIu1ohR+EDM86gSmitTokNFVtc9036wQ9FYZRiHUNoDN+OUPZ65P0nhUiQIAiYkPPxTymDzXTXrmTg==",
|
||||||
|
"requires": {
|
||||||
|
"autocomplete.js": "^0.37.0",
|
||||||
|
"classnames": "^2.2.6",
|
||||||
|
"gauge": "^3.0.0",
|
||||||
|
"hast-util-select": "^4.0.0",
|
||||||
|
"hast-util-to-text": "^2.0.0",
|
||||||
|
"hogan.js": "^3.0.2",
|
||||||
|
"lunr": "^2.3.8",
|
||||||
|
"lunr-languages": "^1.4.0",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"rehype-parse": "^7.0.1",
|
||||||
|
"to-vfile": "^6.1.0",
|
||||||
|
"unified": "^9.0.0",
|
||||||
|
"unist-util-is": "^4.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"autocomplete.js": {
|
||||||
|
"version": "0.37.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/autocomplete.js/-/autocomplete.js-0.37.1.tgz",
|
||||||
|
"integrity": "sha512-PgSe9fHYhZEsm/9jggbjtVsGXJkPLvd+9mC7gZJ662vVL5CRWEtm/mIrrzCx0MrNxHVwxD5d00UOn6NsmL2LUQ==",
|
||||||
|
"requires": {
|
||||||
|
"immediate": "^3.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hast-util-from-parse5": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-3ZYnfKenbbkhhNdmOQqgH10vnvPivTdsOJCri+APn0Kty+nRkDHArnaX9Hiaf8H+Ig+vkNptL+SRY/6RwWJk1Q==",
|
||||||
|
"requires": {
|
||||||
|
"@types/parse5": "^5.0.0",
|
||||||
|
"ccount": "^1.0.0",
|
||||||
|
"hastscript": "^5.0.0",
|
||||||
|
"property-information": "^5.0.0",
|
||||||
|
"vfile": "^4.0.0",
|
||||||
|
"web-namespaces": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parse5": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
|
||||||
|
},
|
||||||
|
"rehype-parse": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==",
|
||||||
|
"requires": {
|
||||||
|
"hast-util-from-parse5": "^6.0.0",
|
||||||
|
"parse5": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unist-util-is": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dom-converter": {
|
"dom-converter": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
|
||||||
|
@ -5662,6 +5755,52 @@
|
||||||
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
|
||||||
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
|
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
|
||||||
},
|
},
|
||||||
|
"gauge": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-VSxauaaCsLOTerAyzunAYGgK3iaWZvOL1BCvBvf/IhDWrczPAf1tUqn05DOCJOOe4k3vOdX6fHhJIvF2UtCMhw==",
|
||||||
|
"requires": {
|
||||||
|
"aproba": "^1.0.3 || ^2.0.0",
|
||||||
|
"color-support": "^1.1.2",
|
||||||
|
"console-control-strings": "^1.0.0",
|
||||||
|
"has-unicode": "^2.0.1",
|
||||||
|
"signal-exit": "^3.0.0",
|
||||||
|
"string-width": "^1.0.1 || ^2.0.0",
|
||||||
|
"strip-ansi": "^3.0.1 || ^4.0.0",
|
||||||
|
"wide-align": "^1.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
||||||
|
"requires": {
|
||||||
|
"is-fullwidth-code-point": "^2.0.0",
|
||||||
|
"strip-ansi": "^4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||||
|
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"gensync": {
|
"gensync": {
|
||||||
"version": "1.0.0-beta.1",
|
"version": "1.0.0-beta.1",
|
||||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
|
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
|
||||||
|
@ -5900,6 +6039,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
|
||||||
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
|
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
|
||||||
},
|
},
|
||||||
|
"has-unicode": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
|
||||||
|
},
|
||||||
"has-value": {
|
"has-value": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
|
||||||
|
@ -6008,6 +6152,16 @@
|
||||||
"xtend": "^4.0.1"
|
"xtend": "^4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"hast-util-has-property": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg=="
|
||||||
|
},
|
||||||
|
"hast-util-is-element": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ=="
|
||||||
|
},
|
||||||
"hast-util-parse-selector": {
|
"hast-util-parse-selector": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.4.tgz",
|
||||||
|
@ -6028,6 +6182,27 @@
|
||||||
"zwitch": "^1.0.0"
|
"zwitch": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"hast-util-select": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-FL8rsw0eleGk4Itt3LdSiUvTDZoD1Uz79c/JxVuIjT6uKqOX+gTna0zVu4/lT4Aifw/Z7rdH5wLXtyfp/d+Gdw==",
|
||||||
|
"requires": {
|
||||||
|
"bcp-47-match": "^1.0.0",
|
||||||
|
"comma-separated-tokens": "^1.0.0",
|
||||||
|
"css-selector-parser": "^1.0.0",
|
||||||
|
"direction": "^1.0.0",
|
||||||
|
"hast-util-has-property": "^1.0.0",
|
||||||
|
"hast-util-is-element": "^1.0.0",
|
||||||
|
"hast-util-to-string": "^1.0.0",
|
||||||
|
"hast-util-whitespace": "^1.0.0",
|
||||||
|
"not": "^0.1.0",
|
||||||
|
"nth-check": "^1.0.0",
|
||||||
|
"property-information": "^5.0.0",
|
||||||
|
"space-separated-tokens": "^1.0.0",
|
||||||
|
"unist-util-visit": "^2.0.0",
|
||||||
|
"zwitch": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"hast-util-to-parse5": {
|
"hast-util-to-parse5": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-5.1.2.tgz",
|
||||||
|
@ -6040,6 +6215,26 @@
|
||||||
"zwitch": "^1.0.0"
|
"zwitch": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"hast-util-to-string": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-eK0MxRX47AV2eZ+Lyr18DCpQgodvaS3fAQO2+b9Two9F5HEoRPhiUMNzoXArMJfZi2yieFzUBMRl3HNJ3Jus3w=="
|
||||||
|
},
|
||||||
|
"hast-util-to-text": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-idXqFGmKInLKcFMbLvh0fldmV94o+aOdXL/z5H5XhPhUp/5vzycu7i15c8V9kC6W3XgGHg2uuiIcRJlWtESVfQ==",
|
||||||
|
"requires": {
|
||||||
|
"hast-util-is-element": "^1.0.0",
|
||||||
|
"repeat-string": "^1.0.0",
|
||||||
|
"unist-util-find-after": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hast-util-whitespace": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A=="
|
||||||
|
},
|
||||||
"hastscript": {
|
"hastscript": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz",
|
||||||
|
@ -7343,6 +7538,16 @@
|
||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lunr": {
|
||||||
|
"version": "2.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
|
||||||
|
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="
|
||||||
|
},
|
||||||
|
"lunr-languages": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-YWfZDExJN/MJEVE/DbM4AuVRLsqeHi+q3wmECMsWjGIOkd5mr9DUNos7fv8f5do9VLRMYXIzFjn+N4+KPI9pQA=="
|
||||||
|
},
|
||||||
"make-dir": {
|
"make-dir": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
||||||
|
@ -7959,6 +8164,11 @@
|
||||||
"sort-keys": "^1.0.0"
|
"sort-keys": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"not": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/not/-/not-0.1.0.tgz",
|
||||||
|
"integrity": "sha1-yWkcF0bFXc++VMvYvU/wQbwrUZ0="
|
||||||
|
},
|
||||||
"npm-run-path": {
|
"npm-run-path": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
|
||||||
|
@ -12022,6 +12232,22 @@
|
||||||
"is-number": "^7.0.0"
|
"is-number": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"to-vfile": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw==",
|
||||||
|
"requires": {
|
||||||
|
"is-buffer": "^2.0.0",
|
||||||
|
"vfile": "^4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"is-buffer": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"toidentifier": {
|
"toidentifier": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||||
|
@ -12238,6 +12464,21 @@
|
||||||
"resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz",
|
||||||
"integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw=="
|
"integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw=="
|
||||||
},
|
},
|
||||||
|
"unist-util-find-after": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ==",
|
||||||
|
"requires": {
|
||||||
|
"unist-util-is": "^4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"unist-util-is": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"unist-util-generated": {
|
"unist-util-generated": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.5.tgz",
|
||||||
|
@ -13572,6 +13813,43 @@
|
||||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
|
||||||
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
|
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
|
||||||
},
|
},
|
||||||
|
"wide-align": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||||
|
"requires": {
|
||||||
|
"string-width": "^1.0.2 || 2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
||||||
|
"requires": {
|
||||||
|
"is-fullwidth-code-point": "^2.0.0",
|
||||||
|
"strip-ansi": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||||
|
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"word-wrap": {
|
"word-wrap": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
"@docusaurus/theme-search-algolia": "^2.0.0-alpha.32",
|
"@docusaurus/theme-search-algolia": "^2.0.0-alpha.32",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"clsx": "^1.1.1",
|
"clsx": "^1.1.1",
|
||||||
|
"docusaurus-lunr-search": "^2.1.7",
|
||||||
"eslint": "^7.3.1",
|
"eslint": "^7.3.1",
|
||||||
"eslint-plugin-react": "^7.20.0",
|
"eslint-plugin-react": "^7.20.0",
|
||||||
"prettier": "^2.0.5",
|
"prettier": "^2.0.5",
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,106 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2017-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useRef, useCallback } from "react";
|
||||||
|
import classnames from "classnames";
|
||||||
|
import { useHistory } from "@docusaurus/router";
|
||||||
|
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
||||||
|
const Search = props => {
|
||||||
|
const initialized = useRef(false);
|
||||||
|
const searchBarRef = useRef(null);
|
||||||
|
const history = useHistory();
|
||||||
|
const { siteConfig = {} } = useDocusaurusContext();
|
||||||
|
const { baseUrl } = siteConfig;
|
||||||
|
const initAlgolia = (searchDocs, searchIndex, DocSearch) => {
|
||||||
|
new DocSearch({
|
||||||
|
searchDocs,
|
||||||
|
searchIndex,
|
||||||
|
inputSelector: "#search_input_react",
|
||||||
|
// Override algolia's default selection event, allowing us to do client-side
|
||||||
|
// navigation and avoiding a full page refresh.
|
||||||
|
handleSelected: (_input, _event, suggestion) => {
|
||||||
|
const url = baseUrl + suggestion.url;
|
||||||
|
// Use an anchor tag to parse the absolute url into a relative url
|
||||||
|
// Alternatively, we can use new URL(suggestion.url) but its not supported in IE
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
// Algolia use closest parent element id #__docusaurus when a h1 page title does not have an id
|
||||||
|
// So, we can safely remove it. See https://github.com/facebook/docusaurus/issues/1828 for more details.
|
||||||
|
|
||||||
|
history.push(url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSearchDoc = () =>
|
||||||
|
process.env.NODE_ENV === "production"
|
||||||
|
? fetch(`${baseUrl}search-doc.json`).then((content) => content.json())
|
||||||
|
: Promise.resolve([]);
|
||||||
|
|
||||||
|
const getLunrIndex = () =>
|
||||||
|
process.env.NODE_ENV === "production"
|
||||||
|
? fetch(`${baseUrl}lunr-index.json`).then((content) => content.json())
|
||||||
|
: Promise.resolve([]);
|
||||||
|
|
||||||
|
const loadAlgolia = () => {
|
||||||
|
if (!initialized.current) {
|
||||||
|
Promise.all([
|
||||||
|
getSearchDoc(),
|
||||||
|
getLunrIndex(),
|
||||||
|
import("./lib/DocSearch"),
|
||||||
|
import("./algolia.css")
|
||||||
|
]).then(([searchDocs, searchIndex, { default: DocSearch }]) => {
|
||||||
|
initAlgolia(searchDocs, searchIndex, DocSearch);
|
||||||
|
});
|
||||||
|
initialized.current = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleSearchIconClick = useCallback(
|
||||||
|
e => {
|
||||||
|
if (!searchBarRef.current.contains(e.target)) {
|
||||||
|
searchBarRef.current.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
props.handleSearchBarToggle(!props.isSearchBarExpanded);
|
||||||
|
},
|
||||||
|
[props.isSearchBarExpanded]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="navbar__search" key="search-box">
|
||||||
|
<span
|
||||||
|
aria-label="expand searchbar"
|
||||||
|
role="button"
|
||||||
|
className={classnames("search-icon", {
|
||||||
|
"search-icon-hidden": props.isSearchBarExpanded
|
||||||
|
})}
|
||||||
|
onClick={toggleSearchIconClick}
|
||||||
|
onKeyDown={toggleSearchIconClick}
|
||||||
|
tabIndex={0}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
id="search_input_react"
|
||||||
|
type="search"
|
||||||
|
placeholder="Search"
|
||||||
|
aria-label="Search"
|
||||||
|
className={classnames(
|
||||||
|
"navbar__search-input",
|
||||||
|
{ "search-bar-expanded": props.isSearchBarExpanded },
|
||||||
|
{ "search-bar": !props.isSearchBarExpanded }
|
||||||
|
)}
|
||||||
|
onClick={loadAlgolia}
|
||||||
|
onMouseOver={loadAlgolia}
|
||||||
|
onFocus={toggleSearchIconClick}
|
||||||
|
onBlur={toggleSearchIconClick}
|
||||||
|
ref={searchBarRef}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Search;
|
|
@ -0,0 +1,306 @@
|
||||||
|
import Hogan from "hogan.js";
|
||||||
|
import LunrSearchAdapter from "./lunar-search";
|
||||||
|
import autocomplete from "autocomplete.js";
|
||||||
|
import templates from "./templates";
|
||||||
|
import utils from "./utils";
|
||||||
|
import $ from "autocomplete.js/zepto";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an autocomplete dropdown to an input field
|
||||||
|
* @function DocSearch
|
||||||
|
* @param {Object} options.searchDocs Search Documents
|
||||||
|
* @param {Object} options.searchIndex Lune searchIndexes
|
||||||
|
* @param {string} options.inputSelector CSS selector that targets the input
|
||||||
|
* value.
|
||||||
|
* @param {Object} [options.autocompleteOptions] Options to pass to the underlying autocomplete instance
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
class DocSearch {
|
||||||
|
constructor({
|
||||||
|
searchDocs,
|
||||||
|
searchIndex,
|
||||||
|
inputSelector,
|
||||||
|
debug = false,
|
||||||
|
queryDataCallback = null,
|
||||||
|
autocompleteOptions = {
|
||||||
|
debug: false,
|
||||||
|
hint: false,
|
||||||
|
autoselect: true
|
||||||
|
},
|
||||||
|
transformData = false,
|
||||||
|
queryHook = false,
|
||||||
|
handleSelected = false,
|
||||||
|
enhancedSearchInput = false,
|
||||||
|
layout = "collumns"
|
||||||
|
}) {
|
||||||
|
this.input = DocSearch.getInputFromSelector(inputSelector);
|
||||||
|
this.queryDataCallback = queryDataCallback || null;
|
||||||
|
const autocompleteOptionsDebug =
|
||||||
|
autocompleteOptions && autocompleteOptions.debug
|
||||||
|
? autocompleteOptions.debug
|
||||||
|
: false;
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
autocompleteOptions.debug = debug || autocompleteOptionsDebug;
|
||||||
|
this.autocompleteOptions = autocompleteOptions;
|
||||||
|
this.autocompleteOptions.cssClasses =
|
||||||
|
this.autocompleteOptions.cssClasses || {};
|
||||||
|
this.autocompleteOptions.cssClasses.prefix =
|
||||||
|
this.autocompleteOptions.cssClasses.prefix || "ds";
|
||||||
|
const inputAriaLabel =
|
||||||
|
this.input &&
|
||||||
|
typeof this.input.attr === "function" &&
|
||||||
|
this.input.attr("aria-label");
|
||||||
|
this.autocompleteOptions.ariaLabel =
|
||||||
|
this.autocompleteOptions.ariaLabel || inputAriaLabel || "search input";
|
||||||
|
|
||||||
|
this.isSimpleLayout = layout === "simple";
|
||||||
|
|
||||||
|
this.client = new LunrSearchAdapter(searchDocs, searchIndex);
|
||||||
|
|
||||||
|
if (enhancedSearchInput) {
|
||||||
|
this.input = DocSearch.injectSearchBox(this.input);
|
||||||
|
}
|
||||||
|
this.autocomplete = autocomplete(this.input, autocompleteOptions, [
|
||||||
|
{
|
||||||
|
source: this.getAutocompleteSource(transformData, queryHook),
|
||||||
|
templates: {
|
||||||
|
suggestion: DocSearch.getSuggestionTemplate(this.isSimpleLayout),
|
||||||
|
footer: templates.footer,
|
||||||
|
empty: DocSearch.getEmptyTemplate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const customHandleSelected = handleSelected;
|
||||||
|
this.handleSelected = customHandleSelected || this.handleSelected;
|
||||||
|
|
||||||
|
// We prevent default link clicking if a custom handleSelected is defined
|
||||||
|
if (customHandleSelected) {
|
||||||
|
$(".algolia-autocomplete").on("click", ".ds-suggestions a", event => {
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.autocomplete.on(
|
||||||
|
"autocomplete:selected",
|
||||||
|
this.handleSelected.bind(null, this.autocomplete.autocomplete)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.autocomplete.on(
|
||||||
|
"autocomplete:shown",
|
||||||
|
this.handleShown.bind(null, this.input)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (enhancedSearchInput) {
|
||||||
|
DocSearch.bindSearchBoxEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static injectSearchBox(input) {
|
||||||
|
input.before(templates.searchBox);
|
||||||
|
const newInput = input
|
||||||
|
.prev()
|
||||||
|
.prev()
|
||||||
|
.find("input");
|
||||||
|
input.remove();
|
||||||
|
return newInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bindSearchBoxEvent() {
|
||||||
|
$('.searchbox [type="reset"]').on("click", function () {
|
||||||
|
$("input#docsearch").focus();
|
||||||
|
$(this).addClass("hide");
|
||||||
|
autocomplete.autocomplete.setVal("");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("input#docsearch").on("keyup", () => {
|
||||||
|
const searchbox = document.querySelector("input#docsearch");
|
||||||
|
const reset = document.querySelector('.searchbox [type="reset"]');
|
||||||
|
reset.className = "searchbox__reset";
|
||||||
|
if (searchbox.value.length === 0) {
|
||||||
|
reset.className += " hide";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the matching input from a CSS selector, null if none matches
|
||||||
|
* @function getInputFromSelector
|
||||||
|
* @param {string} selector CSS selector that matches the search
|
||||||
|
* input of the page
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
static getInputFromSelector(selector) {
|
||||||
|
const input = $(selector).filter("input");
|
||||||
|
return input.length ? $(input[0]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the `source` method to be passed to autocomplete.js. It will query
|
||||||
|
* the Algolia index and call the callbacks with the formatted hits.
|
||||||
|
* @function getAutocompleteSource
|
||||||
|
* @param {function} transformData An optional function to transform the hits
|
||||||
|
* @param {function} queryHook An optional function to transform the query
|
||||||
|
* @returns {function} Method to be passed as the `source` option of
|
||||||
|
* autocomplete
|
||||||
|
*/
|
||||||
|
getAutocompleteSource(transformData, queryHook) {
|
||||||
|
return (query, callback) => {
|
||||||
|
if (queryHook) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
query = queryHook(query) || query;
|
||||||
|
}
|
||||||
|
this.client.search(query).then(hits => {
|
||||||
|
if (
|
||||||
|
this.queryDataCallback &&
|
||||||
|
typeof this.queryDataCallback == "function"
|
||||||
|
) {
|
||||||
|
this.queryDataCallback(hits);
|
||||||
|
}
|
||||||
|
if (transformData) {
|
||||||
|
hits = transformData(hits) || hits;
|
||||||
|
}
|
||||||
|
callback(DocSearch.formatHits(hits));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a list of hits returned by the API, will reformat them to be used in
|
||||||
|
// a Hogan template
|
||||||
|
static formatHits(receivedHits) {
|
||||||
|
const clonedHits = utils.deepClone(receivedHits);
|
||||||
|
const hits = clonedHits.map(hit => {
|
||||||
|
if (hit._highlightResult) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
hit._highlightResult = utils.mergeKeyWithParent(
|
||||||
|
hit._highlightResult,
|
||||||
|
"hierarchy"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return utils.mergeKeyWithParent(hit, "hierarchy");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Group hits by category / subcategory
|
||||||
|
let groupedHits = utils.groupBy(hits, "lvl0");
|
||||||
|
$.each(groupedHits, (level, collection) => {
|
||||||
|
const groupedHitsByLvl1 = utils.groupBy(collection, "lvl1");
|
||||||
|
const flattenedHits = utils.flattenAndFlagFirst(
|
||||||
|
groupedHitsByLvl1,
|
||||||
|
"isSubCategoryHeader"
|
||||||
|
);
|
||||||
|
groupedHits[level] = flattenedHits;
|
||||||
|
});
|
||||||
|
groupedHits = utils.flattenAndFlagFirst(groupedHits, "isCategoryHeader");
|
||||||
|
|
||||||
|
// Translate hits into smaller objects to be send to the template
|
||||||
|
return groupedHits.map(hit => {
|
||||||
|
const url = DocSearch.formatURL(hit);
|
||||||
|
const category = utils.getHighlightedValue(hit, "lvl0");
|
||||||
|
const subcategory = utils.getHighlightedValue(hit, "lvl1") || category;
|
||||||
|
const displayTitle = utils
|
||||||
|
.compact([
|
||||||
|
utils.getHighlightedValue(hit, "lvl2") || subcategory,
|
||||||
|
utils.getHighlightedValue(hit, "lvl3"),
|
||||||
|
utils.getHighlightedValue(hit, "lvl4"),
|
||||||
|
utils.getHighlightedValue(hit, "lvl5"),
|
||||||
|
utils.getHighlightedValue(hit, "lvl6")
|
||||||
|
])
|
||||||
|
.join(
|
||||||
|
'<span class="aa-suggestion-title-separator" aria-hidden="true"> › </span>'
|
||||||
|
);
|
||||||
|
const text = utils.getSnippetedValue(hit, "content");
|
||||||
|
const isTextOrSubcategoryNonEmpty =
|
||||||
|
(subcategory && subcategory !== "") ||
|
||||||
|
(displayTitle && displayTitle !== "");
|
||||||
|
const isLvl1EmptyOrDuplicate =
|
||||||
|
!subcategory || subcategory === "" || subcategory === category;
|
||||||
|
const isLvl2 =
|
||||||
|
displayTitle && displayTitle !== "" && displayTitle !== subcategory;
|
||||||
|
const isLvl1 =
|
||||||
|
!isLvl2 &&
|
||||||
|
(subcategory && subcategory !== "" && subcategory !== category);
|
||||||
|
const isLvl0 = !isLvl1 && !isLvl2;
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLvl0,
|
||||||
|
isLvl1,
|
||||||
|
isLvl2,
|
||||||
|
isLvl1EmptyOrDuplicate,
|
||||||
|
isCategoryHeader: hit.isCategoryHeader,
|
||||||
|
isSubCategoryHeader: hit.isSubCategoryHeader,
|
||||||
|
isTextOrSubcategoryNonEmpty,
|
||||||
|
category,
|
||||||
|
subcategory,
|
||||||
|
title: displayTitle,
|
||||||
|
text,
|
||||||
|
url
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static formatURL(hit) {
|
||||||
|
const { url, anchor } = hit;
|
||||||
|
if (url) {
|
||||||
|
const containsAnchor = url.indexOf("#") !== -1;
|
||||||
|
if (containsAnchor) return url;
|
||||||
|
else if (anchor) return `${hit.url}#${hit.anchor}`;
|
||||||
|
return url;
|
||||||
|
} else if (anchor) return `#${hit.anchor}`;
|
||||||
|
/* eslint-disable */
|
||||||
|
console.warn("no anchor nor url for : ", JSON.stringify(hit));
|
||||||
|
/* eslint-enable */
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getEmptyTemplate() {
|
||||||
|
return args => Hogan.compile(templates.empty).render(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSuggestionTemplate(isSimpleLayout) {
|
||||||
|
const stringTemplate = isSimpleLayout
|
||||||
|
? templates.suggestionSimple
|
||||||
|
: templates.suggestion;
|
||||||
|
const template = Hogan.compile(stringTemplate);
|
||||||
|
return suggestion => template.render(suggestion);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSelected(input, event, suggestion, datasetNumber, context = {}) {
|
||||||
|
// Do nothing if click on the suggestion, as it's already a <a href>, the
|
||||||
|
// browser will take care of it. This allow Ctrl-Clicking on results and not
|
||||||
|
// having the main window being redirected as well
|
||||||
|
if (context.selectionMethod === "click") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.setVal("");
|
||||||
|
window.location.assign(suggestion.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleShown(input) {
|
||||||
|
const middleOfInput = input.offset().left + input.width() / 2;
|
||||||
|
let middleOfWindow = $(document).width() / 2;
|
||||||
|
|
||||||
|
if (isNaN(middleOfWindow)) {
|
||||||
|
middleOfWindow = 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
const alignClass =
|
||||||
|
middleOfInput - middleOfWindow >= 0
|
||||||
|
? "algolia-autocomplete-right"
|
||||||
|
: "algolia-autocomplete-left";
|
||||||
|
const otherAlignClass =
|
||||||
|
middleOfInput - middleOfWindow < 0
|
||||||
|
? "algolia-autocomplete-right"
|
||||||
|
: "algolia-autocomplete-left";
|
||||||
|
const autocompleteWrapper = $(".algolia-autocomplete");
|
||||||
|
if (!autocompleteWrapper.hasClass(alignClass)) {
|
||||||
|
autocompleteWrapper.addClass(alignClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (autocompleteWrapper.hasClass(otherAlignClass)) {
|
||||||
|
autocompleteWrapper.removeClass(otherAlignClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocSearch;
|
|
@ -0,0 +1,146 @@
|
||||||
|
import lunr from "@generated/lunr.client";
|
||||||
|
lunr.tokenizer.separator = /[\s\-/]+/;
|
||||||
|
|
||||||
|
class LunrSearchAdapter {
|
||||||
|
constructor(searchDocs, searchIndex) {
|
||||||
|
this.searchDocs = searchDocs;
|
||||||
|
this.lunrIndex = lunr.Index.load(searchIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLunrResult(input) {
|
||||||
|
return this.lunrIndex.query(function (query) {
|
||||||
|
const tokens = lunr.tokenizer(input);
|
||||||
|
query.term(tokens, {
|
||||||
|
boost: 10
|
||||||
|
});
|
||||||
|
query.term(tokens, {
|
||||||
|
wildcard: lunr.Query.wildcard.TRAILING
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getHit(doc, formattedTitle, formattedContent) {
|
||||||
|
return {
|
||||||
|
hierarchy: {
|
||||||
|
lvl0: doc.pageTitle || doc.title,
|
||||||
|
lvl1: doc.type === 0 ? null : doc.title
|
||||||
|
},
|
||||||
|
url: doc.url,
|
||||||
|
_snippetResult: formattedContent ? {
|
||||||
|
content: {
|
||||||
|
value: formattedContent,
|
||||||
|
matchLevel: "full"
|
||||||
|
}
|
||||||
|
} : null,
|
||||||
|
_highlightResult: {
|
||||||
|
hierarchy: {
|
||||||
|
lvl0: {
|
||||||
|
value: doc.type === 0 ? formattedTitle || doc.title : doc.pageTitle,
|
||||||
|
},
|
||||||
|
lvl1:
|
||||||
|
doc.type === 0
|
||||||
|
? null
|
||||||
|
: {
|
||||||
|
value: formattedTitle || doc.title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
getTitleHit(doc, position, length) {
|
||||||
|
const start = position[0];
|
||||||
|
const end = position[0] + length;
|
||||||
|
let formattedTitle = doc.title.substring(0, start) + '<span class="algolia-docsearch-suggestion--highlight">' + doc.title.substring(start, end) + '</span>' + doc.title.substring(end, doc.title.length);
|
||||||
|
return this.getHit(doc, formattedTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
getKeywordHit(doc, position, length) {
|
||||||
|
const start = position[0];
|
||||||
|
const end = position[0] + length;
|
||||||
|
let formattedTitle = doc.title + '<br /><i>Keywords: ' + doc.keywords.substring(0, start) + '<span class="algolia-docsearch-suggestion--highlight">' + doc.keywords.substring(start, end) + '</span>' + doc.keywords.substring(end, doc.keywords.length) + '</i>'
|
||||||
|
return this.getHit(doc, formattedTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
getContentHit(doc, position) {
|
||||||
|
const start = position[0];
|
||||||
|
const end = position[0] + position[1];
|
||||||
|
let previewStart = start;
|
||||||
|
let previewEnd = end;
|
||||||
|
let ellipsesBefore = true;
|
||||||
|
let ellipsesAfter = true;
|
||||||
|
for (let k = 0; k < 3; k++) {
|
||||||
|
const nextSpace = doc.content.lastIndexOf(' ', previewStart - 2);
|
||||||
|
const nextDot = doc.content.lastIndexOf('.', previewStart - 2);
|
||||||
|
if ((nextDot > 0) && (nextDot > nextSpace)) {
|
||||||
|
previewStart = nextDot + 1;
|
||||||
|
ellipsesBefore = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (nextSpace < 0) {
|
||||||
|
previewStart = 0;
|
||||||
|
ellipsesBefore = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
previewStart = nextSpace + 1;
|
||||||
|
}
|
||||||
|
for (let k = 0; k < 10; k++) {
|
||||||
|
const nextSpace = doc.content.indexOf(' ', previewEnd + 1);
|
||||||
|
const nextDot = doc.content.indexOf('.', previewEnd + 1);
|
||||||
|
if ((nextDot > 0) && (nextDot < nextSpace)) {
|
||||||
|
previewEnd = nextDot;
|
||||||
|
ellipsesAfter = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (nextSpace < 0) {
|
||||||
|
previewEnd = doc.content.length;
|
||||||
|
ellipsesAfter = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
previewEnd = nextSpace;
|
||||||
|
}
|
||||||
|
let preview = doc.content.substring(previewStart, start);
|
||||||
|
if (ellipsesBefore) {
|
||||||
|
preview = '... ' + preview;
|
||||||
|
}
|
||||||
|
preview += '<span class="algolia-docsearch-suggestion--highlight">' + doc.content.substring(start, end) + '</span>';
|
||||||
|
preview += doc.content.substring(end, previewEnd);
|
||||||
|
if (ellipsesAfter) {
|
||||||
|
preview += ' ...';
|
||||||
|
}
|
||||||
|
return this.getHit(doc, null, preview);
|
||||||
|
|
||||||
|
}
|
||||||
|
search(input) {
|
||||||
|
return new Promise((resolve, rej) => {
|
||||||
|
const results = this.getLunrResult(input);
|
||||||
|
const hits = [];
|
||||||
|
results.length > 5 && (results.length = 5);
|
||||||
|
this.titleHitsRes = []
|
||||||
|
this.contentHitsRes = []
|
||||||
|
results.forEach(result => {
|
||||||
|
const doc = this.searchDocs[result.ref];
|
||||||
|
const { metadata } = result.matchData;
|
||||||
|
for (let i in metadata) {
|
||||||
|
if (metadata[i].title) {
|
||||||
|
if (!this.titleHitsRes.includes(result.ref)) {
|
||||||
|
const position = metadata[i].title.position[0]
|
||||||
|
hits.push(this.getTitleHit(doc, position, input.length));
|
||||||
|
this.titleHitsRes.push(result.ref);
|
||||||
|
}
|
||||||
|
} else if (metadata[i].content) {
|
||||||
|
const position = metadata[i].content.position[0]
|
||||||
|
hits.push(this.getContentHit(doc, position))
|
||||||
|
} else if (metadata[i].keywords) {
|
||||||
|
const position = metadata[i].keywords.position[0]
|
||||||
|
hits.push(this.getKeywordHit(doc, position, input.length));
|
||||||
|
this.titleHitsRes.push(result.ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
hits.length > 5 && (hits.length = 5);
|
||||||
|
resolve(hits);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LunrSearchAdapter;
|
|
@ -0,0 +1,114 @@
|
||||||
|
const prefix = 'algolia-docsearch';
|
||||||
|
const suggestionPrefix = `${prefix}-suggestion`;
|
||||||
|
const footerPrefix = `${prefix}-footer`;
|
||||||
|
|
||||||
|
/* eslint-disable max-len */
|
||||||
|
|
||||||
|
const templates = {
|
||||||
|
suggestion: `
|
||||||
|
<a class="${suggestionPrefix}
|
||||||
|
{{#isCategoryHeader}}${suggestionPrefix}__main{{/isCategoryHeader}}
|
||||||
|
{{#isSubCategoryHeader}}${suggestionPrefix}__secondary{{/isSubCategoryHeader}}
|
||||||
|
"
|
||||||
|
aria-label="Link to the result"
|
||||||
|
href="{{{url}}}"
|
||||||
|
>
|
||||||
|
<div class="${suggestionPrefix}--category-header">
|
||||||
|
<span class="${suggestionPrefix}--category-header-lvl0">{{{category}}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="${suggestionPrefix}--wrapper">
|
||||||
|
<div class="${suggestionPrefix}--subcategory-column">
|
||||||
|
<span class="${suggestionPrefix}--subcategory-column-text">{{{subcategory}}}</span>
|
||||||
|
</div>
|
||||||
|
{{#isTextOrSubcategoryNonEmpty}}
|
||||||
|
<div class="${suggestionPrefix}--content">
|
||||||
|
<div class="${suggestionPrefix}--subcategory-inline">{{{subcategory}}}</div>
|
||||||
|
<div class="${suggestionPrefix}--title">{{{title}}}</div>
|
||||||
|
{{#text}}<div class="${suggestionPrefix}--text">{{{text}}}</div>{{/text}}
|
||||||
|
</div>
|
||||||
|
{{/isTextOrSubcategoryNonEmpty}}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
`,
|
||||||
|
suggestionSimple: `
|
||||||
|
<div class="${suggestionPrefix}
|
||||||
|
{{#isCategoryHeader}}${suggestionPrefix}__main{{/isCategoryHeader}}
|
||||||
|
{{#isSubCategoryHeader}}${suggestionPrefix}__secondary{{/isSubCategoryHeader}}
|
||||||
|
suggestion-layout-simple
|
||||||
|
">
|
||||||
|
<div class="${suggestionPrefix}--category-header">
|
||||||
|
{{^isLvl0}}
|
||||||
|
<span class="${suggestionPrefix}--category-header-lvl0 ${suggestionPrefix}--category-header-item">{{{category}}}</span>
|
||||||
|
{{^isLvl1}}
|
||||||
|
{{^isLvl1EmptyOrDuplicate}}
|
||||||
|
<span class="${suggestionPrefix}--category-header-lvl1 ${suggestionPrefix}--category-header-item">
|
||||||
|
{{{subcategory}}}
|
||||||
|
</span>
|
||||||
|
{{/isLvl1EmptyOrDuplicate}}
|
||||||
|
{{/isLvl1}}
|
||||||
|
{{/isLvl0}}
|
||||||
|
<div class="${suggestionPrefix}--title ${suggestionPrefix}--category-header-item">
|
||||||
|
{{#isLvl2}}
|
||||||
|
{{{title}}}
|
||||||
|
{{/isLvl2}}
|
||||||
|
{{#isLvl1}}
|
||||||
|
{{{subcategory}}}
|
||||||
|
{{/isLvl1}}
|
||||||
|
{{#isLvl0}}
|
||||||
|
{{{category}}}
|
||||||
|
{{/isLvl0}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="${suggestionPrefix}--wrapper">
|
||||||
|
{{#text}}
|
||||||
|
<div class="${suggestionPrefix}--content">
|
||||||
|
<div class="${suggestionPrefix}--text">{{{text}}}</div>
|
||||||
|
</div>
|
||||||
|
{{/text}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
footer: `
|
||||||
|
<div class="${footerPrefix}">
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
empty: `
|
||||||
|
<div class="${suggestionPrefix}">
|
||||||
|
<div class="${suggestionPrefix}--wrapper">
|
||||||
|
<div class="${suggestionPrefix}--content ${suggestionPrefix}--no-results">
|
||||||
|
<div class="${suggestionPrefix}--title">
|
||||||
|
<div class="${suggestionPrefix}--text">
|
||||||
|
No results found for query <b>"{{query}}"</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
searchBox: `
|
||||||
|
<form novalidate="novalidate" onsubmit="return false;" class="searchbox">
|
||||||
|
<div role="search" class="searchbox__wrapper">
|
||||||
|
<input id="docsearch" type="search" name="search" placeholder="Search the docs" autocomplete="off" required="required" class="searchbox__input"/>
|
||||||
|
<button type="submit" title="Submit your search query." class="searchbox__submit" >
|
||||||
|
<svg width=12 height=12 role="img" aria-label="Search">
|
||||||
|
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-search-13"></use>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button type="reset" title="Clear the search query." class="searchbox__reset hide">
|
||||||
|
<svg width=12 height=12 role="img" aria-label="Reset">
|
||||||
|
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-clear-3"></use>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="svg-icons" style="height: 0; width: 0; position: absolute; visibility: hidden">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<symbol id="sbx-icon-clear-3" viewBox="0 0 40 40"><path d="M16.228 20L1.886 5.657 0 3.772 3.772 0l1.885 1.886L20 16.228 34.343 1.886 36.228 0 40 3.772l-1.886 1.885L23.772 20l14.342 14.343L40 36.228 36.228 40l-1.885-1.886L20 23.772 5.657 38.114 3.772 40 0 36.228l1.886-1.885L16.228 20z" fill-rule="evenodd"></symbol>
|
||||||
|
<symbol id="sbx-icon-search-13" viewBox="0 0 40 40"><path d="M26.806 29.012a16.312 16.312 0 0 1-10.427 3.746C7.332 32.758 0 25.425 0 16.378 0 7.334 7.333 0 16.38 0c9.045 0 16.378 7.333 16.378 16.38 0 3.96-1.406 7.593-3.746 10.426L39.547 37.34c.607.608.61 1.59-.004 2.203a1.56 1.56 0 0 1-2.202.004L26.807 29.012zm-10.427.627c7.322 0 13.26-5.938 13.26-13.26 0-7.324-5.938-13.26-13.26-13.26-7.324 0-13.26 5.936-13.26 13.26 0 7.322 5.936 13.26 13.26 13.26z" fill-rule="evenodd"></symbol>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default templates;
|
|
@ -0,0 +1,270 @@
|
||||||
|
import $ from "autocomplete.js/zepto";
|
||||||
|
|
||||||
|
const utils = {
|
||||||
|
/*
|
||||||
|
* Move the content of an object key one level higher.
|
||||||
|
* eg.
|
||||||
|
* {
|
||||||
|
* name: 'My name',
|
||||||
|
* hierarchy: {
|
||||||
|
* lvl0: 'Foo',
|
||||||
|
* lvl1: 'Bar'
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* Will be converted to
|
||||||
|
* {
|
||||||
|
* name: 'My name',
|
||||||
|
* lvl0: 'Foo',
|
||||||
|
* lvl1: 'Bar'
|
||||||
|
* }
|
||||||
|
* @param {Object} object Main object
|
||||||
|
* @param {String} property Main object key to move up
|
||||||
|
* @return {Object}
|
||||||
|
* @throws Error when key is not an attribute of Object or is not an object itself
|
||||||
|
*/
|
||||||
|
mergeKeyWithParent(object, property) {
|
||||||
|
if (object[property] === undefined) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
if (typeof object[property] !== 'object') {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
const newObject = $.extend({}, object, object[property]);
|
||||||
|
delete newObject[property];
|
||||||
|
return newObject;
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
* Group all objects of a collection by the value of the specified attribute
|
||||||
|
* If the attribute is a string, use the lowercase form.
|
||||||
|
*
|
||||||
|
* eg.
|
||||||
|
* groupBy([
|
||||||
|
* {name: 'Tim', category: 'dev'},
|
||||||
|
* {name: 'Vincent', category: 'dev'},
|
||||||
|
* {name: 'Ben', category: 'sales'},
|
||||||
|
* {name: 'Jeremy', category: 'sales'},
|
||||||
|
* {name: 'AlexS', category: 'dev'},
|
||||||
|
* {name: 'AlexK', category: 'sales'}
|
||||||
|
* ], 'category');
|
||||||
|
* =>
|
||||||
|
* {
|
||||||
|
* 'devs': [
|
||||||
|
* {name: 'Tim', category: 'dev'},
|
||||||
|
* {name: 'Vincent', category: 'dev'},
|
||||||
|
* {name: 'AlexS', category: 'dev'}
|
||||||
|
* ],
|
||||||
|
* 'sales': [
|
||||||
|
* {name: 'Ben', category: 'sales'},
|
||||||
|
* {name: 'Jeremy', category: 'sales'},
|
||||||
|
* {name: 'AlexK', category: 'sales'}
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* @param {array} collection Array of objects to group
|
||||||
|
* @param {String} property The attribute on which apply the grouping
|
||||||
|
* @return {array}
|
||||||
|
* @throws Error when one of the element does not have the specified property
|
||||||
|
*/
|
||||||
|
groupBy(collection, property) {
|
||||||
|
const newCollection = {};
|
||||||
|
$.each(collection, (index, item) => {
|
||||||
|
if (item[property] === undefined) {
|
||||||
|
throw new Error(`[groupBy]: Object has no key ${property}`);
|
||||||
|
}
|
||||||
|
let key = item[property];
|
||||||
|
if (typeof key === 'string') {
|
||||||
|
key = key.toLowerCase();
|
||||||
|
}
|
||||||
|
// fix #171 the given data type of docsearch hits might be conflict with the properties of the native Object,
|
||||||
|
// such as the constructor, so we need to do this check.
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(newCollection, key)) {
|
||||||
|
newCollection[key] = [];
|
||||||
|
}
|
||||||
|
newCollection[key].push(item);
|
||||||
|
});
|
||||||
|
return newCollection;
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
* Return an array of all the values of the specified object
|
||||||
|
* eg.
|
||||||
|
* values({
|
||||||
|
* foo: 42,
|
||||||
|
* bar: true,
|
||||||
|
* baz: 'yep'
|
||||||
|
* })
|
||||||
|
* =>
|
||||||
|
* [42, true, yep]
|
||||||
|
* @param {object} object Object to extract values from
|
||||||
|
* @return {array}
|
||||||
|
*/
|
||||||
|
values(object) {
|
||||||
|
return Object.keys(object).map(key => object[key]);
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
* Flattens an array
|
||||||
|
* eg.
|
||||||
|
* flatten([1, 2, [3, 4], [5, 6]])
|
||||||
|
* =>
|
||||||
|
* [1, 2, 3, 4, 5, 6]
|
||||||
|
* @param {array} array Array to flatten
|
||||||
|
* @return {array}
|
||||||
|
*/
|
||||||
|
flatten(array) {
|
||||||
|
const results = [];
|
||||||
|
array.forEach(value => {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
results.push(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
value.forEach(subvalue => {
|
||||||
|
results.push(subvalue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
* Flatten all values of an object into an array, marking each first element of
|
||||||
|
* each group with a specific flag
|
||||||
|
* eg.
|
||||||
|
* flattenAndFlagFirst({
|
||||||
|
* 'devs': [
|
||||||
|
* {name: 'Tim', category: 'dev'},
|
||||||
|
* {name: 'Vincent', category: 'dev'},
|
||||||
|
* {name: 'AlexS', category: 'dev'}
|
||||||
|
* ],
|
||||||
|
* 'sales': [
|
||||||
|
* {name: 'Ben', category: 'sales'},
|
||||||
|
* {name: 'Jeremy', category: 'sales'},
|
||||||
|
* {name: 'AlexK', category: 'sales'}
|
||||||
|
* ]
|
||||||
|
* , 'isTop');
|
||||||
|
* =>
|
||||||
|
* [
|
||||||
|
* {name: 'Tim', category: 'dev', isTop: true},
|
||||||
|
* {name: 'Vincent', category: 'dev', isTop: false},
|
||||||
|
* {name: 'AlexS', category: 'dev', isTop: false},
|
||||||
|
* {name: 'Ben', category: 'sales', isTop: true},
|
||||||
|
* {name: 'Jeremy', category: 'sales', isTop: false},
|
||||||
|
* {name: 'AlexK', category: 'sales', isTop: false}
|
||||||
|
* ]
|
||||||
|
* @param {object} object Object to flatten
|
||||||
|
* @param {string} flag Flag to set to true on first element of each group
|
||||||
|
* @return {array}
|
||||||
|
*/
|
||||||
|
flattenAndFlagFirst(object, flag) {
|
||||||
|
const values = this.values(object).map(collection =>
|
||||||
|
collection.map((item, index) => {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
item[flag] = index === 0;
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return this.flatten(values);
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
* Removes all empty strings, null, false and undefined elements array
|
||||||
|
* eg.
|
||||||
|
* compact([42, false, null, undefined, '', [], 'foo']);
|
||||||
|
* =>
|
||||||
|
* [42, [], 'foo']
|
||||||
|
* @param {array} array Array to compact
|
||||||
|
* @return {array}
|
||||||
|
*/
|
||||||
|
compact(array) {
|
||||||
|
const results = [];
|
||||||
|
array.forEach(value => {
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
results.push(value);
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
* Returns the highlighted value of the specified key in the specified object.
|
||||||
|
* If no highlighted value is available, will return the key value directly
|
||||||
|
* eg.
|
||||||
|
* getHighlightedValue({
|
||||||
|
* _highlightResult: {
|
||||||
|
* text: {
|
||||||
|
* value: '<mark>foo</mark>'
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* text: 'foo'
|
||||||
|
* }, 'text');
|
||||||
|
* =>
|
||||||
|
* '<mark>foo</mark>'
|
||||||
|
* @param {object} object Hit object returned by the Algolia API
|
||||||
|
* @param {string} property Object key to look for
|
||||||
|
* @return {string}
|
||||||
|
**/
|
||||||
|
getHighlightedValue(object, property) {
|
||||||
|
if (
|
||||||
|
object._highlightResult &&
|
||||||
|
object._highlightResult.hierarchy_camel &&
|
||||||
|
object._highlightResult.hierarchy_camel[property] &&
|
||||||
|
object._highlightResult.hierarchy_camel[property].matchLevel &&
|
||||||
|
object._highlightResult.hierarchy_camel[property].matchLevel !== 'none' &&
|
||||||
|
object._highlightResult.hierarchy_camel[property].value
|
||||||
|
) {
|
||||||
|
return object._highlightResult.hierarchy_camel[property].value;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
object._highlightResult &&
|
||||||
|
object._highlightResult &&
|
||||||
|
object._highlightResult[property] &&
|
||||||
|
object._highlightResult[property].value
|
||||||
|
) {
|
||||||
|
return object._highlightResult[property].value;
|
||||||
|
}
|
||||||
|
return object[property];
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
* Returns the snippeted value of the specified key in the specified object.
|
||||||
|
* If no highlighted value is available, will return the key value directly.
|
||||||
|
* Will add starting and ending ellipsis (…) if we detect that a sentence is
|
||||||
|
* incomplete
|
||||||
|
* eg.
|
||||||
|
* getSnippetedValue({
|
||||||
|
* _snippetResult: {
|
||||||
|
* text: {
|
||||||
|
* value: '<mark>This is an unfinished sentence</mark>'
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* text: 'This is an unfinished sentence'
|
||||||
|
* }, 'text');
|
||||||
|
* =>
|
||||||
|
* '<mark>This is an unfinished sentence</mark>…'
|
||||||
|
* @param {object} object Hit object returned by the Algolia API
|
||||||
|
* @param {string} property Object key to look for
|
||||||
|
* @return {string}
|
||||||
|
**/
|
||||||
|
getSnippetedValue(object, property) {
|
||||||
|
if (
|
||||||
|
!object._snippetResult ||
|
||||||
|
!object._snippetResult[property] ||
|
||||||
|
!object._snippetResult[property].value
|
||||||
|
) {
|
||||||
|
return object[property];
|
||||||
|
}
|
||||||
|
let snippet = object._snippetResult[property].value;
|
||||||
|
|
||||||
|
if (snippet[0] !== snippet[0].toUpperCase()) {
|
||||||
|
snippet = `…${snippet}`;
|
||||||
|
}
|
||||||
|
if (['.', '!', '?'].indexOf(snippet[snippet.length - 1]) === -1) {
|
||||||
|
snippet = `${snippet}…`;
|
||||||
|
}
|
||||||
|
return snippet;
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
* Deep clone an object.
|
||||||
|
* Note: This will not clone functions and dates
|
||||||
|
* @param {object} object Object to clone
|
||||||
|
* @return {object}
|
||||||
|
*/
|
||||||
|
deepClone(object) {
|
||||||
|
return JSON.parse(JSON.stringify(object));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default utils;
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2017-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
background-image: var(--ifm-navbar-search-input-icon);
|
||||||
|
height: auto;
|
||||||
|
width: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
line-height: 32px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon-hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 360px) {
|
||||||
|
.search-bar {
|
||||||
|
width: 0 !important;
|
||||||
|
background: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar-expanded {
|
||||||
|
width: 9rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
display: inline;
|
||||||
|
vertical-align: sub;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue