Browse Source

feat: upgrade Algolia integration from v2 to v4

George Cushen 2 years ago
parent
commit
ee434ab7ef

+ 1 - 0
.prettierignore

@@ -1,5 +1,6 @@
 _vendor/
 vendor/
+wowchemy/assets/css/libs/
 
 public/
 resources/

+ 1 - 3
README.md

@@ -6,7 +6,7 @@
 [![Discord](https://img.shields.io/discord/722225264733716590?style=for-the-badge)](https://discord.com/channels/722225264733716590/742892432458252370/742895548159492138)
 [![GitHub Sponsors](https://img.shields.io/github/sponsors/gcushen?label=%E2%9D%A4%EF%B8%8F%20sponsor&style=for-the-badge)](https://github.com/sponsors/gcushen)
 [![Twitter Follow](https://img.shields.io/twitter/follow/wowchemy?label=Follow%20on%20Twitter&style=for-the-badge)](https://twitter.com/wowchemy)
-[![GitHub followers](https://img.shields.io/github/followers/gcushen?label=Follow%20on%20GH&style=for-the-badge)](https://github.com/gcushen)  
+[![GitHub followers](https://img.shields.io/github/followers/gcushen?label=Follow%20on%20GH&style=for-the-badge)](https://github.com/gcushen)
 
 # [Wowchemy](https://wowchemy.com): the website builder for [Hugo](https://gohugo.io)
 
@@ -83,5 +83,3 @@ Feel free to [_star_ the project on Github](https://github.com/wowchemy/wowchemy
 Copyright 2016-present [George Cushen](https://georgecushen.com).
 
 The [Wowchemy Hugo Themes](https://github.com/wowchemy/wowchemy-hugo-themes/) repository is released under the [MIT](https://github.com/wowchemy/wowchemy-hugo-themes/blob/main/LICENSE.md) license.
-
-[![Analytics](https://ga-beacon.appspot.com/UA-78646709-2/wowchemy-hugo-themes/readme?pixel)](https://github.com/igrigorik/ga-beacon)

+ 94 - 58
wowchemy/assets/js/algolia-search.js

@@ -6,72 +6,108 @@
  **************************************************/
 
 import {algoliaConfig, i18n, content_type} from '@params';
+import algoliasearch from 'https://cdn.jsdelivr.net/npm/algoliasearch@4/dist/algoliasearch.esm.browser.js';
+// import instantsearch from 'https://cdn.jsdelivr.net/npm/instantsearch.js@4/es/index.js'
+// import {searchBox, infiniteHits} from 'https://cdn.jsdelivr.net/npm/instantsearch.js@4/es/widgets/index.js';
 
 function getTemplate(templateName) {
   return document.querySelector(`#${templateName}-template`).innerHTML;
 }
 
-if (typeof instantsearch === 'function' && $('#search-box').length) {
-  const options = {
-    appId: algoliaConfig.appId,
-    apiKey: algoliaConfig.apiKey,
-    indexName: algoliaConfig.indexName,
-    routing: true,
-    searchParameters: {
-      hitsPerPage: 10,
-    },
-    searchFunction: function (helper) {
-      let searchResults = document.querySelector('#search-hits');
-      let commonQueries = document.querySelector('#search-common-queries');
-      if (helper.state.query === '') {
-        searchResults.style.display = 'none';
-        commonQueries.style.display = 'block';
-        return;
-      }
-      helper.search();
-      commonQueries.style.display = 'none';
-      searchResults.style.display = 'block';
-    },
-  };
-
-  const search = instantsearch(options);
+// Get query from URI.
+function getSearchQuery(name) {
+  return decodeURIComponent((location.search.split(name + '=')[1] || '').split('&')[0]).replace(/\+/g, ' ');
+}
 
-  // Initialize search box.
-  search.addWidget(
-    instantsearch.widgets.searchBox({
-      container: '#search-box',
-      autofocus: true,
-      reset: true,
-      poweredBy: algoliaConfig.poweredBy,
-      placeholder: i18n.placeholder,
-    }),
-  );
+// On page load, check for search query in URL.
+document.addEventListener('DOMContentLoaded', () => {
+  let queryURL = getSearchQuery('q');
+  if (queryURL) {
+    $('body').addClass('searching');
+    $('.search-results').css({opacity: 0, visibility: 'visible'}).animate({opacity: 1}, 200);
+    let commonQueries = document.querySelector('#search-common-queries');
+    commonQueries.style.display = 'none';
+  }
 
-  // Initialize search results.
-  search.addWidget(
-    instantsearch.widgets.infiniteHits({
-      container: '#search-hits',
-      escapeHits: true,
-      templates: {
-        empty: '<div class="search-no-results">' + i18n.no_results + '</div>',
-        item: getTemplate('search-hit-algolia'),
+  if (typeof instantsearch === 'function' && $('#search-box').length) {
+    const search = instantsearch({
+      indexName: algoliaConfig.indexName,
+      searchClient: algoliasearch(algoliaConfig.appId, algoliaConfig.apiKey), //'appId', 'apiKey'),
+      routing: {
+        router: instantsearch.routers.history({
+          parseURL() {
+            return {
+              q: getSearchQuery('q'),
+            };
+          },
+        }),
+        stateMapping: {
+          stateToRoute(uiState) {
+            const indexUiState = uiState[algoliaConfig.indexName];
+            return {
+              q: indexUiState.query,
+            };
+          },
+          routeToState(routeState) {
+            return {
+              [algoliaConfig.indexName]: {
+                query: routeState.q,
+              },
+            };
+          },
+        },
       },
-      cssClasses: {
-        showmoreButton: 'btn btn-outline-primary',
-      },
-    }),
-  );
+    });
 
-  // On render search results, localize the content type metadata.
-  search.on('render', function () {
-    $('.search-hit-type').each(function () {
-      let content_key = $(this).text();
-      if (content_key in content_type) {
-        $(this).text(content_type[content_key]);
-      }
+    // Initialize search box.
+    search.addWidget(
+      instantsearch.widgets.searchBox({
+        container: '#search-box',
+        autofocus: true,
+        showReset: true,
+        //poweredBy: algoliaConfig.poweredBy,
+        placeholder: i18n.placeholder,
+        queryHook(query, search) {
+          let searchResults = document.querySelector('#search-hits');
+          let commonQueries = document.querySelector('#search-common-queries');
+          if (query === '') {
+            searchResults.style.display = 'none';
+            commonQueries.style.display = 'block';
+            return;
+          }
+          search(query);
+          commonQueries.style.display = 'none';
+          searchResults.style.display = 'block';
+        },
+      }),
+    );
+
+    // Initialize search results.
+    search.addWidget(
+      instantsearch.widgets.infiniteHits({
+        container: '#search-hits',
+        escapeHTML: true,
+        templates: {
+          empty: '<div class="search-no-results">' + i18n.no_results + '</div>',
+          item: getTemplate('search-hit-algolia'),
+        },
+        cssClasses: {
+          loadMore: 'btn btn-outline-primary',
+        },
+      }),
+    );
+
+    // On render search results, localize the content type metadata.
+    search.on('render', function () {
+      $('.search-hit-type').each(function () {
+        let content_key = $(this).text();
+        if (content_key in content_type) {
+          $(this).text(content_type[content_key]);
+        }
+      });
     });
-  });
 
-  // Start search.
-  search.start();
-}
+    // Start search.
+    search.start();
+  }
+});

+ 7 - 2
wowchemy/assets/js/wowchemy.js

@@ -24,7 +24,7 @@ console.debug(`Environment: ${hugoEnvironment}`);
 // Dynamically get responsive navigation bar height for offsetting Scrollspy.
 function getNavBarHeight() {
   let navbar = document.getElementById('navbar-main');
-  let navbarHeight = (navbar) ? navbar.getBoundingClientRect().height : 0;
+  let navbarHeight = navbar ? navbar.getBoundingClientRect().height : 0;
   console.debug('Navbar height: ' + navbarHeight);
   return navbarHeight;
 }
@@ -364,7 +364,12 @@ function toggleSearchDialog() {
     // Show search modal.
     $('body').addClass('searching');
     $('.search-results').css({opacity: 0, visibility: 'visible'}).animate({opacity: 1}, 200);
-    $('#search-query').focus();
+    let algoliaSearchBox = document.querySelector('.ais-SearchBox-input');
+    if (algoliaSearchBox) {
+      algoliaSearchBox.focus();
+    } else {
+      $('#search-query').focus();
+    }
   }
 }
 

+ 4 - 4
wowchemy/assets/scss/wowchemy/layouts/_book.scss

@@ -238,16 +238,16 @@ ul.toc-top {
 
 #TableOfContents li {
   display: block;
-  margin-bottom: 8px;  // Lighthouse: tap targets must be at least 8px apart
+  margin-bottom: 8px; // Lighthouse: tap targets must be at least 8px apart
 }
 
 #TableOfContents li a,
 .toc-top li a {
   display: block;
-  padding: 0 1.5rem;  // Override default padding
+  padding: 0 1.5rem; // Override default padding
   color: rgba(0, 0, 0, 0.65);
-  line-height: 1.75;  // Lighthouse: min tap target size
-  font-size: 16px;  // Lighthouse: min tap target size
+  line-height: 1.75; // Lighthouse: min tap target size
+  font-size: 16px; // Lighthouse: min tap target size
 }
 
 .dark #TableOfContents li a,

+ 25 - 0
wowchemy/assets/scss/wowchemy/layouts/_search.scss

@@ -86,6 +86,31 @@
   overflow-x: hidden;
 }
 
+// Override Algolia results style
+.ais-Hits-item,
+.ais-InfiniteHits-item {
+  background: unset;
+  box-shadow: unset;
+  padding: unset;
+}
+
+// Override Algolia search box icon style
+.ais-SearchBox-form::before {
+  all: unset; // clear Algolia style
+  height: 1rem;
+  left: 1rem;
+  margin-top: -0.5rem;
+  position: absolute;
+  top: 50%;
+  width: 1rem;
+  font-family: 'Font Awesome 5 Free';
+  font-weight: 900;
+  content: '\f002';
+  font-size: 100%;
+  opacity: 0.7;
+  line-height: 1;
+}
+
 .dark #search-query {
   background-color: $sta-dark-background;
 }

+ 2 - 2
wowchemy/assets/scss/wowchemy/widgets/_about.scss

@@ -104,7 +104,7 @@ ul.ul-edu li .description p.institution {
 // For about.avatar widget
 .avatar-wrapper {
   position: relative;
-  width: 150px;  // Match image size in about.avatar widget
+  width: 150px; // Match image size in about.avatar widget
   height: 150px;
   margin-left: auto;
   margin-right: auto;
@@ -120,7 +120,7 @@ ul.ul-edu li .description p.institution {
     text-align: center;
     font-size: 20px;
     background-color: #fff;
-    color: #000;  // override parent alpha
+    color: #000; // override parent alpha
     box-shadow: 0 10px 20px rgba(0, 0, 0, 0.04), 0 2px 6px rgba(0, 0, 0, 0.04), 0 0 1px rgba(0, 0, 0, 0.04);
   }
 }

+ 5 - 10
wowchemy/data/assets.toml

@@ -49,9 +49,8 @@
   sri = "sha512-mhbv5DqBMgrWL+32MmsDOt/OAvqr/cHimk6B8y/bx/xS88MVkYGPiVv2ixKVrkywF2qHplNRUvFsAHUdxZ3Krg=="
   url = "https://cdn.jsdelivr.net/gh/julmot/mark.js@%s/dist/jquery.mark.min.js"
 [js.instantsearch]
-  version = "2.10.5"
-  sri = "sha512-fsaJ0SdBVinKOpq2u2o1kMKexDBGkEepm+4RmSEW4E2kKxkMw+3VFpWOIhFaXwxb+x9HJ2nLpKFGajju5esK1w=="
-  url = "https://cdn.jsdelivr.net/npm/instantsearch.js@%s/dist/instantsearch.min.js"
+  version = "4"  # no SRI as we use latest major version
+  url = "https://cdn.jsdelivr.net/npm/instantsearch.js@%s/dist/instantsearch.production.min.js"
 [js.anchor]
   version = "4.2.2"
   sri = "sha512-I7w3ZdSFzw5j3jU3ZkNikBNeIrl3i+hEuEdwNmqUJvwNcaBUNcijnP2gd9DtGlgVYDplfjGoD8vTNsID+lCjqg=="
@@ -84,13 +83,9 @@
   sri = "sha512-H9jrZiiopUdsLpg94A333EfumgUBpO9MdbxStdeITo+KEIMaNfHNvwyjjDJb+ERPaRS6DpyRlKbvPUasNItRyw=="
   url = "https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@%s/dist/jquery.fancybox.min.css"
 [css.instantsearch]
-  version = "2.9.0"
-  sri = "sha512-r0ZHmgMRCi4mGWwva52RW2IXabjPr7sFVpcy4QIjDVTcgwadvP33dFG6gmZDe8lS2c96dPly57jDVZ4r6t8QCg=="
-  url = "https://cdn.jsdelivr.net/npm/instantsearch.js@%s/dist/instantsearch.min.css"
-[css.instantsearchTheme]
-  version = "2.10.5"
-  sri = "sha512-AjQHx1UkDult/5TlKWqKapSLLMmNTJk7WINfp0F08inVEjpjeVPGiXOgTT9YCUyQE31xNBsajMRA7T8MQqK3cA=="
-  url = "https://cdn.jsdelivr.net/npm/instantsearch.js@%s/dist/instantsearch-theme-algolia.min.css"
+  version = "7.4.5"
+  sri = "sha256-TehzF/2QvNKhGQrrNpoOb2Ck4iGZ1J/DI4pkd2oUsBc="
+  url = "https://cdn.jsdelivr.net/npm/instantsearch.css@%s/themes/satellite-min.css"
 [css.highlight]
   version = "10.2.1"
   sri = ""  # No SRI as highlight style is determined at run time.

+ 1 - 1
wowchemy/layouts/partials/book_sidebar.html

@@ -14,7 +14,7 @@
     </div>
   </button>
 
-  {{ if eq (lower site.Params.search.provider) "wowchemy" }}
+  {{ if in (slice "wowchemy" "algolia") (lower site.Params.search.provider) }}
   <button class="form-control sidebar-search js-search d-none d-md-flex">
     <i class="fas fa-search pr-2"></i>
     <span class="sidebar-search-text">{{ i18n "search_placeholder" }}</span>

+ 0 - 1
wowchemy/layouts/partials/site_head.html

@@ -172,7 +172,6 @@
 
     {{ if eq (lower site.Params.search.provider) "algolia" }}
       {{ printf "<link rel=\"stylesheet\" href=\"%s\" integrity=\"%s\" crossorigin=\"anonymous\">" (printf $css.instantsearch.url $css.instantsearch.version) $css.instantsearch.sri | safeHTML }}
-      {{ printf "<link rel=\"stylesheet\" href=\"%s\" integrity=\"%s\" crossorigin=\"anonymous\">" (printf $css.instantsearchTheme.url $css.instantsearchTheme.version) $css.instantsearchTheme.sri | safeHTML }}
     {{ end }}
 
     {{/* Load async scripts. */}}

+ 11 - 6
wowchemy/layouts/partials/site_js.html

@@ -87,15 +87,15 @@
         <div class="search-hit">
           <div class="search-hit-content">
             <div class="search-hit-name">
-              {{ printf "<a href=\"%s\">{{{_highlightResult.title.value}}}</a>" "{{relpermalink}}" | safeHTML }}
+              {{ printf "<a href=\"%s\">{{#helpers.highlight}}{ \"attribute\": \"title\" }{{/helpers.highlight}}</a>" "{{relpermalink}}" | safeHTML }}
             </div>
             <div class="article-metadata search-hit-type">{{"{{type}}"}}</div>
-            <p class="search-hit-description">{{ safeHTML "{{{_highlightResult.summary.value}}}" }}</p>
+            <p class="search-hit-description">{{ safeHTML "{{#helpers.highlight}}{ \"attribute\": \"summary\" }{{/helpers.highlight}}" }}</p>
           </div>
         </div>
       </script>
       {{ if ($scr.Get "use_cdn") }}
-        {{ printf "<script src=\"%s\" integrity=\"%s\" crossorigin=\"anonymous\"></script>" (printf $js.instantsearch.url $js.instantsearch.version) $js.instantsearch.sri | safeHTML }}
+        {{ printf "<script src=\"%s\" crossorigin=\"anonymous\"></script>" (printf $js.instantsearch.url $js.instantsearch.version) | safeHTML }}
       {{ end }}
       {{ $algoliaConfig = dict "appId" (site.Params.search.algolia.app_id | default "") "apiKey" (site.Params.search.algolia.api_key | default "") "indexName" (site.Params.search.algolia.index_name | default "") "poweredBy" (site.Params.search.algolia.show_logo | default false) }}
     {{ end }}
@@ -123,6 +123,14 @@
       {{ $js_search_params = dict "search_config" $search_config "algoliaConfig" $algoliaConfig "i18n" $search_i18n "content_type" $content_types }}
     {{ end }}
 
+    {{ if eq $search_provider "algolia" }}
+    {{ $js_algolia_search := resources.Get "js/algolia-search.js" | js.Build (dict "format" "esm" "targetPath" (printf "%s/js/algolia-search-built.js" .Lang) "params" $js_search_params) }}
+    {{- if hugo.IsProduction -}}
+      {{- $js_algolia_search = $js_algolia_search | minify | fingerprint "md5" -}}
+    {{- end -}}
+    <script src="{{ $js_algolia_search.RelPermalink }}" type="module"></script>
+    {{ end }}
+
     {{ $js_license := printf "/*! Wowchemy v%s | https://wowchemy.com/ */\n" site.Data.wowchemy.version }}
     {{ $js_license := $js_license | printf "%s/*! Copyright 2016-present George Cushen (https://georgecushen.com/) */\n" }}
     {{ $js_license := $js_license | printf "%s/*! License: https://github.com/wowchemy/wowchemy-hugo-themes/blob/main/LICENSE.md */\n" }}
@@ -133,9 +141,6 @@
     {{ if eq $search_provider "wowchemy" }}
       {{ $js_academic_search := resources.Get "js/wowchemy-search.js" | js.Build (dict "targetPath" (printf "%s/js/wow-search-built.js" .Lang) "params" $js_search_params) }}
       {{ $js_bundle = $js_bundle | append $js_academic_search }}
-    {{ else if eq $search_provider "algolia" }}
-      {{ $js_algolia_search := resources.Get "js/algolia-search.js" | js.Build (dict "targetPath" (printf "%s/js/algolia-search-built.js" .Lang) "params" $js_search_params) }}
-      {{ $js_bundle = $js_bundle | append $js_algolia_search }}
     {{ end }}
     {{ range site.Params.plugins_js }}
       {{ $js_bundle = $js_bundle | append (resources.Get (printf "js/%s.js" .)) }}