1
Fork 0

Add site search with lunr.js

This commit is contained in:
Jake Howard 2021-07-03 10:30:48 +01:00
parent 42d5f4bb73
commit 2c604dafcb
Signed by: jake
GPG key ID: 57AFB45680EDD477
14 changed files with 224 additions and 10 deletions

View file

@ -7,6 +7,6 @@
"globals": {
"mermaid": true,
"$": true,
"process": true
"URLSearchParams": true
}
}

7
content/search.md Normal file
View file

@ -0,0 +1,7 @@
---
title: Search
layout: search
outputs: [json, html]
image: unsplash:mdS647i6-ZM
hidden_from_navbar: true
---

View file

@ -41,6 +41,8 @@
{{ partial "script_async.html" "js/app.js" }}
{{ block "scripts_extra" . }}{{ end }}
{{ if not .Site.IsServer }}
<script async defer data-domain="theorangeone.net" src="https://elbisualp.theorangeone.net/js/index.js"></script>
{{ end }}

View file

@ -0,0 +1,29 @@
{{ define "main" }}
{{ $pages := where .Site.RegularPages "Section" "!=" ""}}
<div id="main" class="search-page">
<div class="container">
{{ partial "content.html" . }}
<div class="input-group mt-5">
<div class="input-group-prepend">
<div class="input-group-text"><i class="fas fa-search"></i></div>
</div>
<input type="text" class="form-control" id="search-input" placeholder="Search">
</div>
<p id="search-results-count" class="mt-5 mb-4"></p>
<div class="search-results">
{{ range $pages }}
{{ partial "list_item.html" . }}
{{ end }}
</div>
<h5 id="search-results-message" class="text-center mt-5"></h5>
</div>
</div>
{{ end }}
{{ define "scripts_extra" }}
{{ partial "script_async.html" "js/search.js" }}
{{ end }}

View file

@ -0,0 +1,8 @@
{{- $result := slice -}}
{{- range (where .Site.RegularPages "Section" "!=" "") -}}
{{- $data := dict "title" .Title "content" .Plain "id" (.File.UniqueID) -}}
{{- $result = $result | append $data -}}
{{- end -}}
{{ jsonify $result }}

View file

@ -5,6 +5,12 @@
<div class="text-center m-auto text-light">
<h1 class="display-1">{{ .Site.Params.author_name }}</h1>
<h2 class="lead">{{ .Content }}</h2>
<form action='{{ (.Site.GetPage "search").RelPermalink }}' method="GET">
<div class="input-group mt-5">
<input name="q" type="text" class="form-control" id="search-input" placeholder="Search" title="Press enter to search">
</div>
</form>
</div>
</header>
{{ end }}

View file

@ -1,4 +1,4 @@
<div class="media list-page-item mb-3">
<div class="media list-page-item mb-3" data-id="{{ .File.UniqueID }}">
{{ if .Params.image }}
<div class="d-none d-md-block align-self-center img-wrapper mr-3">
<a href="{{ .RelPermalink }}">

View file

@ -3,6 +3,8 @@
{{ $pages := where .Site.RegularPages.ByTitle "Section" "" }}
{{ $nav_pages := union $pages .Site.Sections }}
{{ $nav_pages = where $nav_pages "Params.hidden_from_navbar" "!=" true }}
{{ range sort $nav_pages "LinkTitle" }}
<li class="nav-item">
<a href="{{ .RelPermalink }}" class="nav-link">

View file

@ -10,11 +10,10 @@
<div class="d-none d-lg-block">
<ul class="navbar-nav">
{{ range .Site.Data.social.navbar_accounts }}
{{ $account := index $.Site.Data.social.accounts . }}
{{ with (.Site.GetPage "search") }}
<li class="nav-item">
<a class="nav-link" href="{{ $account.link }}">
<i class="{{ $account.icon }}" title="{{ $account.name }}"></i>
<a class="nav-link" href="{{ .Permalink }}">
<i class="fas fa-search" title="Search"></i>
</a>
</li>
{{ end }}
@ -25,6 +24,14 @@
</a>
</li>
{{ end }}
{{ range .Site.Data.social.navbar_accounts }}
{{ $account := index $.Site.Data.social.accounts . }}
<li class="nav-item">
<a class="nav-link" href="{{ $account.link }}">
<i class="{{ $account.icon }}" title="{{ $account.name }}"></i>
</a>
</li>
{{ end }}
</ul>
</div>
</div>

13
package-lock.json generated
View file

@ -13,7 +13,8 @@
"elevator.js": "1.0.1",
"jquery": "3.6.0",
"lg-thumbnail": "1.2.1",
"lightgallery": "1.10.0"
"lightgallery": "1.10.0",
"lunr": "2.3.9"
},
"devDependencies": {
"broken-link-checker-local": "0.2.1",
@ -4564,6 +4565,11 @@
"yallist": "^2.1.2"
}
},
"node_modules/lunr": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="
},
"node_modules/map-age-cleaner": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
@ -11480,6 +11486,11 @@
"yallist": "^2.1.2"
}
},
"lunr": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="
},
"map-age-cleaner": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",

View file

@ -19,6 +19,7 @@
"elevator.js": "1.0.1",
"jquery": "3.6.0",
"lg-thumbnail": "1.2.1",
"lightgallery": "1.10.0"
"lightgallery": "1.10.0",
"lunr": "2.3.9"
}
}

View file

@ -8,7 +8,7 @@ rm -rf $OUTPUT_DIR
rm -rf $STATIC_BUILD
rm -rf $BASEDIR/resources
mkdir -p $STATIC_BUILD/js $STATIC_BUILD/css $STATIC_BUILD/audio
mkdir -p $STATIC_BUILD/css $STATIC_BUILD/audio
cp -r $BASEDIR/node_modules/lightgallery/dist/fonts $STATIC_BUILD
cp -r $STATIC_SRC/img $STATIC_BUILD/img
@ -17,7 +17,7 @@ cp -r $BASEDIR/node_modules/@fortawesome/fontawesome-free/css/all.min.css $STATI
cp $BASEDIR/node_modules/lightgallery/dist/css/lightgallery.min.css $STATIC_BUILD/css/lightgallery.css
cp -r $BASEDIR/node_modules/@fortawesome/fontawesome-free/webfonts $STATIC_BUILD
cp $STATIC_SRC/js/app.js $STATIC_BUILD/js/app.js
cp -r $STATIC_SRC/js/ $STATIC_BUILD/js/
cp $BASEDIR/node_modules/jquery/dist/jquery.min.js $STATIC_BUILD/js/jquery.min.js
cp $BASEDIR/node_modules/lightgallery/dist/js/lightgallery-all.min.js $STATIC_BUILD/js/lightgallery.min.js
cp $BASEDIR/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js $STATIC_BUILD/js/bootstrap.min.js

110
static/src/js/search.js Normal file
View file

@ -0,0 +1,110 @@
const lunr = require('lunr');
const MIN_SEARCH_LENGTH = 3;
const searchInput = $('#search-input');
const searchResultsMessage = $('#search-results-message');
const searchResultsCount = $('#search-results-count');
const searchResultsList = $('.search-results');
const searchResults = searchResultsList.children();
let searchIndex;
function showMessage(message) {
if (message) {
searchResults.hide();
searchResultsCount.hide();
searchResultsMessage.show();
searchResultsMessage.text(message);
} else {
searchResultsMessage.hide();
}
}
function getInitialSearchTerm() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('q') || '';
}
function onSearch(term) {
if (term.length < MIN_SEARCH_LENGTH) {
searchResultsCount.hide();
showMessage(
`Enter a search term (of at least ${MIN_SEARCH_LENGTH} characters) to search`
);
return;
}
showMessage('');
// do search
const results = searchIndex.search(term).map(match => match.ref);
if (!results.length) {
showMessage('No results found');
return;
}
// Show the results
searchResults.each(function() {
const el = $(this);
if (results.includes(this.dataset.id)) {
el.css('display', 'flex');
} else {
el.css('display', 'none');
}
});
// Sort results by accuracy
searchResults
.sort(function(a, b) {
const aOrder = results.indexOf(a.dataset.id);
const bOrder = results.indexOf(b.dataset.id);
if (aOrder < bOrder) {
return -1;
}
if (aOrder > bOrder) {
return 1;
}
return 0;
})
.appendTo(searchResultsList);
// Show number of results
searchResultsCount.text(`Found ${results.length} results`);
searchResultsCount.show();
}
$(document).ready(function() {
searchInput.prop('disabled', true);
const initialSearchTerm = getInitialSearchTerm();
searchInput.val(initialSearchTerm);
$.getJSON('./index.json', function(data) {
// populate the index
searchIndex = lunr(function() {
this.ref('id');
this.field('title');
this.field('content');
data.forEach(function(page) {
this.add(page);
}, this);
});
// add event handler
searchInput.on('input', function(event) {
onSearch(event.target.value);
});
// re-enable the input
searchInput.prop('disabled', false);
// Do an initial search
onSearch(searchInput.val());
// Scroll to the title if we landed on the page with a search term
if (initialSearchTerm) {
document.getElementsByTagName('h1')[0].scrollIntoView();
}
});
});

View file

@ -110,6 +110,17 @@ table td {
#index-header {
width: 100%;
height: calc(100vh - #{$footer-height} - #{$navbar-height});
#search-input {
border: 3px solid $black;
background-color: opacify($black, 0.8);
text-align: center;
color: $body-color;
&::placeholder {
color: opacify($body-color, 0.8);
}
}
}
footer {
@ -335,3 +346,23 @@ a.no-color-change {
width: 100%;
}
}
#main.search-page {
.search-results {
.list-page-item {
display: none;
}
}
#search-input {
border: 0;
background-color: opacify($black, 0.6);
color: $body-color;
}
.input-group-text {
border: 0;
background-color: $black;
color: $body-color;
}
}