diff --git a/package.json b/package.json index 8fd00fe24f..9343e38373 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "clear": "docusaurus clear" }, "dependencies": { + "@abrookins-redis/redis-sitesearch-frontend": "^1.0.2", "@docusaurus/core": "2.0.0-alpha.70", "@docusaurus/preset-classic": "2.0.0-alpha.70", "@docusaurus/theme-search-algolia": "^2.0.0-alpha.70", diff --git a/src/theme/Footer/index.js b/src/theme/Footer/index.js index 3ecca315f5..a9d47be2d1 100644 --- a/src/theme/Footer/index.js +++ b/src/theme/Footer/index.js @@ -66,8 +66,8 @@ function Footer() {
- {logo && logo.src &&
diff --git a/src/theme/Search/index.js b/src/theme/Search/index.js new file mode 100644 index 0000000000..ea8ddd2a78 --- /dev/null +++ b/src/theme/Search/index.js @@ -0,0 +1,196 @@ +import React, {useEffect} from 'react' +import RedisSiteSearch from '@abrookins-redis/redis-sitesearch-frontend/dist/redis-sitesearch.esm' + +const SEARCH_API_URL = "https://search-service.redislabs.com/search" +const SEARCH_SITE = "https://developer.redislabs.com" +const THIRTY_SECONDS = 30000 +const SEARCH_LOGO = '' + + +function escapeHtml(str) { + return str.replace(/[&<>"'\/]/g, function (s) { + var entityMap = { + "&": "&", + "<": "<", + ">": ">", + '"': '"', + "'": ''', + "/": '/' + }; + + return entityMap[s]; + }); +} + +function setWithExpiry(key, value, ttl) { + const now = new Date() + + const item = { + value: value, + expiry: now.getTime() + ttl, + } + localStorage.setItem(key, JSON.stringify(item)) +} + + +function getWithExpiry(key) { + const itemStr = localStorage.getItem(key) + if (!itemStr) { + return null + } + const item = JSON.parse(itemStr) + const now = new Date() + + if (now.getTime() > item.expiry) { + localStorage.removeItem(key) + return null + } + + return item.value +} + + +function Search(props) { + var initialized = false; + + useEffect(function onFirstMount() { + const searchLogo = document.querySelector('.redisearch-logo'), + searchDiv = document.querySelector("#redis-sitesearch") + var lastQuery + + initialized = true; + + searchLogo.addEventListener('mousedown', (e) => { + e.preventDefault() + }) + + searchDiv.addEventListener("mousedown", (e) => { + }) + + new RedisSiteSearch('#redis-sitesearch', { + debounceTime: 2, + zIndex: 100, + + search: input => { + const trimmedInput = input.trim() + const url = `${SEARCH_API_URL}?q=${trimmedInput}*&site=${SEARCH_SITE}` + + if (input.length === 0) { + return [] + } + + try { xhr.abort(); } catch(e){} + + // Save the query so we can append it to a selected result URL later. + // We use this to track search queries. + lastQuery = trimmedInput + + const cachedResults = getWithExpiry(url) + + if (cachedResults) { + return cachedResults + } + + return new Promise(resolve => { + fetch(url) + .catch(function(err) { + console.error('Error querying search API: ', err) + resolve([]) + }) + .then(function(response) { + if (response === undefined || !response.ok) { + console.error('Error querying search API: ', response) + resolve([]) + return + } + return response.json() + }) + .then(function(data) { + if (data === undefined || !data) { + return + } + // Push a fake 'no results' document if there were no results. + var results + if (!data.results.length) { + const safeInput = escapeHtml(trimmedInput) + results = [ + { + title: 'No results', + section_title: `No results found for '${safeInput}'`, + body: '', + hierarchy: [''], + }, + ] + } else { + results = data.results + } + + setWithExpiry(url, results, THIRTY_SECONDS) + resolve(results) + }) + }) + }, + + renderResult: (result, props) => { + let sectionTitle = "", + hasSectionTitle = result.section_title !== "" + + if (hasSectionTitle) { + sectionTitle = ` +
+ ${result.section_title} +
+ ` + } + + return ` +
  • +
    +
    + ${result.title} +
    +
    +
    + ${sectionTitle} +
    + ${result.body} +
    +
    +
  • + ` + }, + + getResultValue: result => "", + + // Open the selected article in + // a new window + onSubmit: result => { + if (result) { + lastQuery = encodeURIComponent(lastQuery) + window.open(`${result.url}?s=${lastQuery}`, "_top") + } + }, + + onUpdate: (results, selectedIndex) => { + const redisearchLogo = document.querySelector('.redisearch-logo') + if (results.length) { + redisearchLogo.innerHTML = SEARCH_LOGO + } else { + redisearchLogo.innerHTML = "" + } + } + }) + }, [initialized]) + + return
    + + +
    +
      +
      +
      +
      +
      +} + +export default Search; diff --git a/static/css/redis-sitesearch-overrides.css b/static/css/redis-sitesearch-overrides.css new file mode 100644 index 0000000000..29608333d6 --- /dev/null +++ b/static/css/redis-sitesearch-overrides.css @@ -0,0 +1,106 @@ +.powered-by-redisearch { + background-image: url('/img/redisearch.png'); + background-size: 173px 29px + } + + .redis-sitesearch { + max-width: 700px; + min-width: 350px + } + + .redis-sitesearch-input { + padding: 5px 5px 5px 48px + } + + redis-sitesearch-result-list-wrapper { + width: 400px + } + + .redis-sitesearch-inline { + margin-bottom: 0 + } + + + .redis-sitesearch-inline .search-left { + width: 35%; + } + + .redis-sitesearch-inline .search-left { + width: 35%; + } + + .redis-sitesearch-inline .search-right { + width: 65%; + } + + .redis-sitesearch-inline .search-root-item { + padding: 12px 16px 0px; + } + + .redis-sitesearch-inline .redis-sitesearch-result { + padding: 11px 1em 11px 11px; + } + + + + .redis-sitesearch .redis-sitesearch-result-list { + max-height: 60vh; + } + + .redis-sitesearch-inline .redis-sitesearch-result-list { + max-height: 70vh; + } + + @media screen and (max-width: 376px) { + .redis-sitesearch .redis-sitesearch-result-list { + max-height: 40vh; + } + + .redis-sitesearch-inline .redis-sitesearch-result-list { + max-height: 70vh; + } + + .search-left { + width: 30%; + } + + .search-right { + width: 70%; + } + + .redis-sitesearch-result { + padding: 9px 1.5em 9px 9px; + } + + .redis-sitesearch { + min-width: 300px; + } + + } + + @media screen and (max-width: 608px) { + .redis-sitesearch { + min-width: 100%; + margin-right: 15px; + margin-left: 15px; + } + } + + + + + @media screen and (max-height: 750px) { + .redis-sitesearch .redis-sitesearch-result-list { + max-height: 50vh; + } + + .redis-sitesearch-inline .redis-sitesearch-result-list { + max-height: 70vh; + } + + .redis-sitesearch { + margin-top: 10px; + min-width: 300px; + margin-right: 15px + } + } diff --git a/static/css/redis-sitesearch.css b/static/css/redis-sitesearch.css new file mode 100644 index 0000000000..3ea908c11c --- /dev/null +++ b/static/css/redis-sitesearch.css @@ -0,0 +1 @@ +.redis-sitesearch-input{border:1px solid #eee;border-radius:8px;width:100%;padding:12px 12px 12px 48px;box-sizing:border-box;position:relative;font-size:16px;line-height:1.5;flex:1;background-color:#eee;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PGNpcmNsZSBjeD0iMTEiIGN5PSIxMSIgcj0iOCIvPjxwYXRoIGQ9Ik0yMSAyMWwtNC00Ii8+PC9zdmc+");background-repeat:no-repeat;background-position:12px;z-index:100}.redis-sitesearch-input:focus,.redis-sitesearch-input[aria-expanded=true]{border-color:rgba(0,0,0,.12);background-color:#fff;outline:none;box-shadow:0 2px 2px rgba(0,0,0,.16)}[data-position=below] .redis-sitesearch-input[aria-expanded=true]{border-bottom-color:transparent;border-radius:8px 8px 0 0}[data-position=above] .redis-sitesearch-input[aria-expanded=true]{border-top-color:transparent;border-radius:0 0 8px 8px;z-index:2}.redis-sitesearch[data-loading=true]:after{content:"";border:3px solid rgba(0,0,0,.12);border-right-color:rgba(0,0,0,.48);border-radius:100%;width:20px;height:20px;position:absolute;right:12px;top:50%;transform:translateY(-50%);animation:rotate 1s linear infinite}.redis-sitesearch-result-list{margin:0;border:1px solid rgba(0,0,0,.12);padding:0;box-sizing:border-box;max-height:296px;overflow-y:auto;background:#fff;list-style:none;box-shadow:0 2px 2px rgba(0,0,0,.16)}[data-position=below] .redis-sitesearch-result-list{margin-top:-1px;border-top-color:transparent;border-radius:0 0 8px 8px;padding-bottom:8px}[data-position=above] .redis-sitesearch-result-list{margin-bottom:-1px;border-bottom-color:transparent;border-radius:8px 8px 0 0;padding-top:8px}.redis-sitesearch-result{cursor:default;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjY2NjIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PGNpcmNsZSBjeD0iMTEiIGN5PSIxMSIgcj0iOCIvPjxwYXRoIGQ9Ik0yMSAyMWwtNC00Ii8+PC9zdmc+");background-repeat:no-repeat;background-position:12px;width:100%;padding:11px 2.1em 11px 11px;background:transparent;display:flex;flex-wrap:wrap;justify-content:space-between}.redis-sitesearch-result:hover,.redis-sitesearch-result[aria-selected=true]{background-color:rgba(0,0,0,.06)}@keyframes rotate{0%{transform:translateY(-50%) rotate(0deg)}to{transform:translateY(-50%) rotate(359deg)}}.redis-sitesearch-result-list{display:flex;position:relative;flex-wrap:wrap;justify-content:space-between}.redis-sitesearch-result-list-wrapper .redis-sitesearch-result-list{padding-bottom:60px}.redis-sitesearch-home{max-width:700px;margin:0 auto}.redis-sitesearch-inline{max-width:100%;margin-bottom:1rem}.redis-sitesearch-input-inline{padding:6px 6px 6px 48px}.redis-sitesearch-result-list li:last-of-type{padding-bottom:80px!important}.search-root{font-size:14px;font-weight:700;width:100%}.search-root-item{margin-bottom:0;border:0;cursor:default;padding:16px 16px 8px;border-top:1px solid #eee;width:100%}.search-root-item::first{border:0}.no-section-title{margin-top:0}.search-title{font-size:14px;margin-bottom:10px;color:#5961ff;word-break:break-word;text-align:right;width:100%}.search-left{width:25%;padding-right:5px}.search-right{width:70%;padding-left:15px}.search-section-title{font-size:16px;margin-bottom:8px;width:100%}.search-body{font-size:13px;width:100%}.powered-by-redisearch{background-image:url(../images/redisearch.png);background-repeat:no-repeat;background-position:95% 60%;position:absolute;right:0;width:100%;background-color:#fff;bottom:0;height:60px;border:1px solid rgba(0,0,0,.12);box-shadow:0 2px 2px rgba(0,0,0,.16)} diff --git a/static/img/redisearch.png b/static/img/redisearch.png new file mode 100644 index 0000000000..861892d7f0 Binary files /dev/null and b/static/img/redisearch.png differ diff --git a/yarn.lock b/yarn.lock index 2c438481f4..2c44ffec9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@abrookins-redis/redis-sitesearch-frontend@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@abrookins-redis/redis-sitesearch-frontend/-/redis-sitesearch-frontend-1.0.2.tgz#f4b8f8386c27cb47293987b733b980fca1dc55c7" + integrity sha512-aDSUvffA1mbfb93DZT2o9nGLv3D5RgY0eNSSL4FUZFJvSBq3Kx+PWw8/cPNyzzemFXgDMgaxzAKaWofH30F3sA== + "@algolia/autocomplete-core@^1.0.0-alpha.35": version "1.0.0-alpha.37" resolved "https://registry.npm.taobao.org/@algolia/autocomplete-core/download/@algolia/autocomplete-core-1.0.0-alpha.37.tgz#d41a6561b38761c4967ff7aa3e1d5d0aa3ceb31c"