Sfoglia il codice sorgente

Add search engine widget

Close #169
George Cushen 7 anni fa
parent
commit
bb3adc1781

+ 9 - 0
data/sri.toml

@@ -37,6 +37,9 @@
 [js.fancybox]
   version = "3.2.5"
   sri = "sha256-X5PoE3KU5l+JcX+w09p/wHl9AzK333C4hJ2I9S5mD4M="
+[js.instantsearch]
+  version = "2.9.0"
+  sri = "sha256-cJXigylnJlxvAdfFNHeS+IiMcKWS3Rf/cy3bl9bb0ng="
 
 # CSS
 
@@ -55,3 +58,9 @@
 [css.fancybox]
   version = "3.2.5"
   sri = "sha256-ygkqlh3CYSUri3LhQxzdcm0n1EQvH2Y+U5S2idbLtxs="
+[css.instantsearch]
+  version = "2.9.0"
+  sri = "sha256-ZtmLe16p4jS4/2wPwwH6NzJt460SJzgLmhKrYo5yn7g="
+[css.instantsearchTheme]
+  version = "2.9.0"
+  sri = "sha256-0vcZrdMQksHcHrY60tPnVK71jnB3wX/JpMcah5BffjA="

+ 21 - 1
exampleSite/config.toml

@@ -37,9 +37,15 @@ defaultContentLanguageInSubdir = false
 enableGitInfo = false
 
 [outputs]
-  home = [ "HTML", "CSS", "RSS" ]
+  home = [ "HTML", "CSS", "RSS", "search" ]
   section = [ "HTML", "RSS" ]
 
+[outputFormats.search]
+  baseName = "search"
+  isPlainText = true
+  mediaType = "application/json"
+  notAlternative = true
+
 # Configure BlackFriday Markdown rendering.
 #   See: https://gohugo.io/getting-started/configuration/#configure-blackfriday
 [blackfriday]
@@ -298,6 +304,20 @@ enableGitInfo = false
 #    url = "files/cv.pdf"
 #    weight = 7
 
+# Search.
+[params.search]
+  # Search provider:
+  #   0: No search engine
+  #   1: Algolia (https://www.algolia.com)
+  engine = 0
+
+  # Configuration of Algolia search engine.
+  # Paste the values from your Algolia dashboard.
+  [params.search.algolia]
+    app_id = ""
+    api_key = ""
+    index_name = ""
+
 # Taxonomies.
 [taxonomies]
   tag = "tags"

+ 12 - 0
exampleSite/content/home/search.md

@@ -0,0 +1,12 @@
++++
+# Search widget.
+widget = "search"
+active = false
+date = 2018-07-23T00:00:00
+
+title = "Search"
+subtitle = ""
+
+# Order that this section will appear in.
+weight = 66
++++

+ 8 - 0
i18n/en.yaml

@@ -140,6 +140,14 @@
 - id: projects
   translation: Projects
 
+# Search
+
+- id: search_placeholder
+  translation: Search...
+
+- id: search_no_results
+  translation: No results found
+
 # Error 404
 
 - id: page_not_found

+ 16 - 0
layouts/_default/list.search.json

@@ -0,0 +1,16 @@
+{{/* Generate the search index. */}}
+{{- $scr := $.Scratch -}}
+{{- $scr.Add "index" slice -}}
+{{- range (where .Site.RegularPages "Section" "!=" "home") -}}
+  {{- if and (not .Draft) (not .Params.private) -}}
+    {{- if .Params.abstract -}}
+      {{- $scr.Set "summary" .Params.abstract -}}
+    {{- else if .Params.summary -}}
+      {{- $scr.Set "summary" .Params.summary -}}
+    {{- else -}}
+      {{- $scr.Set "summary" .Summary -}}
+    {{- end -}}
+    {{- $scr.Add "index" (dict "objectID" .UniqueID "date" .Date.UTC.Unix "publishdate" .PublishDate "lastmod" .Lastmod.UTC.Unix "expirydate" .ExpiryDate.UTC.Unix "lang" .Lang "permalink" .Permalink "relpermalink" .RelPermalink "title" .Title "summary" ($scr.Get "summary") "authors" .Params.Authors "kind" .Kind "type" .Type "section" .Section "tags" .Params.Tags "categories" .Params.Categories)}}
+  {{- end -}}
+{{- end -}}
+{{- $scr.Get "index" | jsonify -}}

+ 29 - 0
layouts/partials/css/academic.css

@@ -242,6 +242,35 @@ small,
   max-width: 100%;
 }
 
+/*************************************************
+ *  Search.
+ **************************************************/
+
+#search-box {
+  margin-bottom: 0.5rem;
+}
+
+.search-hit em {
+  font-style: normal;
+  background-color: #FFE0B2;
+  color: #E65100;
+  border-bottom: 1px solid #E65100;
+}
+
+.search-hit-type {
+  margin-bottom: 0 !important; /* Override .article-metadata margin. */
+  text-transform: capitalize;
+}
+
+.search-hit-description {
+  font-size: 0.7rem;
+}
+
+/* Load more results button - hide when there are no more results. */
+#search-hits button[disabled] {
+  display: none;
+}
+
 /*************************************************
  *  Modals.
  **************************************************/

+ 73 - 0
layouts/partials/footer.html

@@ -78,5 +78,78 @@
     {{ end }}
     {{ end }}
 
+    {{/* Algolia search engine. */}}
+    {{ if eq .Site.Params.search.engine 1 }}
+    {{ printf "<script src=\"https://cdnjs.cloudflare.com/ajax/libs/instantsearch.js/%s/instantsearch.min.js\" integrity=\"%s\" crossorigin=\"anonymous\"></script>" $js.instantsearch.version $js.instantsearch.sri | safeHTML }}
+    <script>
+      const content_type = {
+        'post': {{ i18n "posts" }},
+        'project': {{ i18n "projects" }},
+        'publication' : {{ i18n "publications" }},
+        'talk' : {{ i18n "talks" }}
+      };
+
+      function getTemplate(templateName) {
+        return document.querySelector(`#${templateName}-template`).innerHTML;
+      }
+
+      const options = {
+        appId: {{ .Site.Params.search.algolia.app_id }},
+        apiKey: {{ .Site.Params.search.algolia.api_key }},
+        indexName: {{ .Site.Params.search.algolia.index_name }},
+        routing: true,
+        searchParameters: {
+          hitsPerPage: 10
+        },
+        searchFunction: function(helper) {
+          let searchResults = document.querySelector('#search-hits')
+          if (helper.state.query === '') {
+            searchResults.style.display = 'none';
+            return;
+          }
+          helper.search();
+          searchResults.style.display = 'block';
+        }
+      };
+
+      const search = instantsearch(options);
+
+      // Initialize search box.
+      search.addWidget(
+        instantsearch.widgets.searchBox({
+          container: '#search-box',
+          placeholder: {{ i18n "search_placeholder" }}
+        })
+      );
+
+      // Initialize search results.
+      search.addWidget(
+        instantsearch.widgets.infiniteHits({
+          container: '#search-hits',
+          templates: {
+            empty: '<div class="search-no-results">' + {{ i18n "search_no_results" }} + '</div>',
+            item: getTemplate('search-hit')
+          },
+          cssClasses: {
+            showmoreButton: 'btn btn-primary btn-outline'
+          }
+        })
+      );
+
+      // On render search results, localize the content type metadata.
+      search.on('render', function() {
+        $('.search-hit-type').each(function( index ) {
+          let content_key = $( this ).text();
+          if (content_key in content_type) {
+            $( this ).text(content_type[content_key]);
+          }
+        });
+      });
+
+      // Start search.
+      search.start();
+    </script>
+    {{ end }}
+
   </body>
 </html>

+ 4 - 0
layouts/partials/header.html

@@ -53,6 +53,10 @@
       {{ end }}
     {{ end }}
   {{ end }}
+  {{ if eq .Site.Params.search.engine 1 }}
+    {{ printf "<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/instantsearch.js/%s/instantsearch.min.css\" integrity=\"%s\" crossorigin=\"anonymous\">" $sri.css.instantsearch.version $sri.css.instantsearch.sri | safeHTML }}
+    {{ printf "<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/instantsearch.js/%s/instantsearch-theme-algolia.min.css\" integrity=\"%s\" crossorigin=\"anonymous\">" $sri.css.instantsearchTheme.version $sri.css.instantsearchTheme.sri | safeHTML }}
+  {{ end }}
   {{ if not .Site.Params.disable_sri }}
   {{ printf "<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/%s/css/bootstrap.min.css\" integrity=\"%s\" crossorigin=\"anonymous\">" $sri.css.bootstrap.version $sri.css.bootstrap.sri | safeHTML }}
   {{ printf "<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/academicons/%s/css/academicons.min.css\" integrity=\"%s\" crossorigin=\"anonymous\">" $sri.css.academicons.version $sri.css.academicons.sri | safeHTML }}

+ 33 - 0
layouts/partials/widgets/search.html

@@ -0,0 +1,33 @@
+{{ $ := .root }}
+{{ $page := .page }}
+
+<div class="row">
+  <div class="col-xs-12 col-md-4 section-heading">
+    <h1>{{ with $page.Title }}{{ . | markdownify }}{{ end }}</h1>
+    {{ with $page.Params.subtitle }}<p>{{ . | markdownify }}</p>{{ end }}
+  </div>
+  <div class="col-xs-12 col-md-8">
+    {{ with $page.Content }}<p>{{ . | markdownify }}</p>{{ end }}
+
+    <div id="search-box">
+      <!-- Search box will appear here -->
+    </div>
+
+    <div id="search-hits">
+      <!-- Search results will appear here -->
+    </div>
+
+  </div>
+</div>
+
+<script type="text/html" id="search-hit-template">
+  <div class="search-hit">
+    <div class="search-hit-content">
+      <div class="search-hit-name">
+        {{ printf "<a href=\"%s\">{{{_highlightResult.title.value}}}</a>" "{{relpermalink}}" | safeHTML }}
+      </div>
+      <div class="article-metadata search-hit-type">{{"{{type}}"}}</div>
+      <p class="search-hit-description">{{ safeHTML "{{{_highlightResult.summary.value}}}" }}</p>
+    </div>
+  </div>
+</script>