diff --git a/_vendors.yml b/_vendors.yml
index b75d8b8..f078b62 100644
--- a/_vendors.yml
+++ b/_vendors.yml
@@ -121,11 +121,6 @@ algolia_search:
version: 4.23.3
file: dist/algoliasearch-lite.umd.js
integrity: sha256-1QNshz86RqXe/qsCBldsUu13eAX6n/O98uubKQs87UI=
-instant_search:
- name: instantsearch.js
- version: 4.67.0
- file: dist/instantsearch.production.min.js
- integrity: sha256-TW7D3X/i/W+RUgEeDppEnFT2ixv5lzplKH0c58D92dY=
local_search:
name: hexo-generator-searchdb
version: 1.4.1
diff --git a/layout/_partials/search/algolia-search.njk b/layout/_partials/search/algolia-search.njk
deleted file mode 100644
index 59f5b4e..0000000
--- a/layout/_partials/search/algolia-search.njk
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
diff --git a/layout/_partials/search/index.njk b/layout/_partials/search/index.njk
index 0a22e24..edb64d1 100644
--- a/layout/_partials/search/index.njk
+++ b/layout/_partials/search/index.njk
@@ -1,11 +1,24 @@
{%- if theme.algolia_search.enable or theme.local_search.enable %}
{%- endif %}
diff --git a/layout/_partials/search/localsearch.njk b/layout/_partials/search/localsearch.njk
deleted file mode 100644
index 6fa6f65..0000000
--- a/layout/_partials/search/localsearch.njk
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
diff --git a/layout/_third-party/search/algolia-search.njk b/layout/_third-party/search/algolia-search.njk
index 9e69c92..6ab729c 100644
--- a/layout/_third-party/search/algolia-search.njk
+++ b/layout/_third-party/search/algolia-search.njk
@@ -1,4 +1,3 @@
{{ next_vendors('algolia_search') }}
-{{ next_vendors('instant_search') }}
{{- next_js('third-party/search/algolia-search.js') }}
diff --git a/source/css/_common/components/third-party/search.styl b/source/css/_common/components/third-party/search.styl
index d5b6a15..5b229ad 100644
--- a/source/css/_common/components/third-party/search.styl
+++ b/source/css/_common/components/third-party/search.styl
@@ -79,11 +79,14 @@ if (hexo-config('local_search.enable') or hexo-config('algolia_search.enable'))
}
.search-result-container {
+ display: flex;
+ flex-direction: column;
height: calc(100% - 55px);
overflow: auto;
padding: 5px 25px;
hr {
+ flex-shrink: 0;
margin: 5px 0 10px;
&:first-child {
@@ -105,18 +108,27 @@ if (hexo-config('local_search.enable') or hexo-config('algolia_search.enable'))
border-bottom: 1px dashed $grey-light;
padding: 5px 0;
}
+
+ .search-input-container {
+ flex-grow: 1;
+ padding: 2px;
+ }
+
+ .search-result-icon {
+ color: $grey-light;
+ margin: auto;
+ }
}
}
+mark.search-keyword {
+ background: transparent;
+ border-bottom: 1px dashed $red;
+ color: $red;
+ font-weight: bold;
+}
+
if (hexo-config('algolia_search.enable')) {
- .search-input-container {
- flex-grow: 1;
-
- form {
- padding: 2px;
- }
- }
-
.search-stats {
align-items: center;
display: flex;
@@ -128,52 +140,13 @@ if (hexo-config('algolia_search.enable')) {
}
}
- .algolia-pagination {
- // Override default style of ul
- margin: 40px 0;
- opacity: 1;
- padding: 0;
-
- .pagination-item {
- display: inline-block;
- }
-
- .current .page-number {
- @extend $page-number-current;
- cursor: default;
- }
-
- .disabled-item {
- visibility: hidden;
- }
- }
-}
-
-if (hexo-config('local_search.enable')) {
- .search-popup {
- .search-input-container {
- flex-grow: 1;
- padding: 2px;
- }
-
- .no-result {
- display: flex;
- }
-
- .search-result-list {
- width: 100%;
- }
-
- .search-result-icon {
- color: $grey-light;
- margin: auto;
- }
- }
-
- mark.search-keyword {
- background: transparent;
- border-bottom: 1px dashed $red;
- color: $red;
- font-weight: bold;
+ .pagination.algolia-pagination {
+ // Override the default style of pagination
+ // Put pagination at the bottom when there is sufficient height
+ margin-top: auto;
+ padding: 40px 0;
+ // Override motion
+ // https://github.com/theme-next/hexo-theme-next/issues/537
+ visibility: visible;
}
}
diff --git a/source/images/logo-algolia-nebula-blue-full.svg b/source/images/logo-algolia-nebula-blue-full.svg
index 886c422..8b45b88 100644
--- a/source/images/logo-algolia-nebula-blue-full.svg
+++ b/source/images/logo-algolia-nebula-blue-full.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/source/js/third-party/search/algolia-search.js b/source/js/third-party/search/algolia-search.js
index 12a554c..10a70c3 100644
--- a/source/js/third-party/search/algolia-search.js
+++ b/source/js/third-party/search/algolia-search.js
@@ -1,112 +1,104 @@
-/* global instantsearch, algoliasearch, CONFIG, pjax */
+/* global CONFIG, NexT, pjax, algoliasearch */
document.addEventListener('DOMContentLoaded', () => {
const { indexName, appID, apiKey, hits } = CONFIG.algolia;
+ const client = algoliasearch(appID, apiKey);
+ const index = client.initIndex(indexName);
- const search = instantsearch({
- indexName,
- searchClient : algoliasearch(appID, apiKey),
- searchFunction: helper => {
- if (document.querySelector('.search-input').value) {
- helper.search();
- }
+ const input = document.querySelector('.search-input');
+ const container = document.querySelector('.search-result-container');
+
+ const formatHits = data => {
+ const { title, excerpt, excerptStrip, contentStripTruncate } = data._highlightResult;
+ let result = `${title.value}`;
+ const content = excerpt || excerptStrip || contentStripTruncate;
+ if (content && content.value) {
+ const div = document.createElement('div');
+ div.innerHTML = content.value;
+ result += `${div.textContent.substring(0, 100)}...
`;
}
- });
+ return result;
+ };
- if (typeof pjax === 'object') {
- search.on('render', () => {
- pjax.refresh(document.querySelector('.algolia-hits'));
+ let isSearching = false;
+ let pendingQuery = null;
+
+ const searchAlgolia = async(searchText, page = 0) => {
+ if (isSearching) {
+ pendingQuery = { searchText, page };
+ return;
+ }
+ isSearching = true;
+ const startTime = Date.now();
+ const data = await index.search(searchText, {
+ page,
+ attributesToRetrieve : ['permalink'],
+ attributesToHighlight: ['title', 'excerpt', 'excerptStrip', 'contentStripTruncate'],
+ hitsPerPage : hits.per_page || 10,
+ highlightPreTag : '',
+ highlightPostTag : ''
});
- }
-
- // Registering Widgets
- search.addWidgets([
- instantsearch.widgets.configure({
- hitsPerPage: hits.per_page || 10
- }),
-
- instantsearch.widgets.searchBox({
- container : '.search-input-container',
- placeholder : CONFIG.i18n.placeholder,
- // Hide default icons of algolia search
- showReset : false,
- showSubmit : false,
- showLoadingIndicator: false,
- cssClasses : {
- input: 'search-input'
- }
- }),
-
- instantsearch.widgets.stats({
- container: '.algolia-stats',
- templates: {
- text: data => {
- const stats = CONFIG.i18n.hits_time
- .replace('${hits}', data.nbHits)
- .replace('${time}', data.processingTimeMS);
- return `${stats}
-
`;
- }
- },
- cssClasses: {
- text: 'search-stats'
- }
- }),
-
- instantsearch.widgets.hits({
- container : '.algolia-hits',
- escapeHTML: false,
- templates : {
- item: data => {
- const { title, excerpt, excerptStrip, contentStripTruncate } = data._highlightResult;
- let result = `${title.value}`;
- const content = excerpt || excerptStrip || contentStripTruncate;
- if (content && content.value) {
- const div = document.createElement('div');
- div.innerHTML = content.value;
- result += `${div.textContent.substring(0, 100)}...
`;
+ if (data.nbHits === 0) {
+ container.innerHTML = '
';
+ } else {
+ const stats = CONFIG.i18n.hits_time
+ .replace('${hits}', data.nbHits)
+ .replace('${time}', Date.now() - startTime);
+ let pagination = '';
+ if (data.nbPages > 1) {
+ pagination += '';
}
- }),
- instantsearch.widgets.pagination({
- container: '.algolia-pagination',
- scrollTo : false,
- showFirst: false,
- showLast : false,
- templates: {
- first : '',
- last : '',
- previous: '',
- next : ''
- },
- cssClasses: {
- list : ['pagination', 'algolia-pagination'],
- item : 'pagination-item',
- link : 'page-number',
- selectedItem: 'current',
- disabledItem: 'disabled-item'
- }
- })
- ]);
+ container.innerHTML = `
+
${stats}
+

+
+
+ ${data.hits.map(formatHits).join('')}
+ ${pagination}`;
+ if (typeof pjax === 'object') pjax.refresh(container);
+ container.querySelectorAll('.page-number').forEach(element => {
+ element.addEventListener('click', async event => {
+ event.preventDefault();
+ await searchAlgolia(searchText, Number(element.dataset.index));
+ });
+ });
+ }
+ isSearching = false;
+ if (pendingQuery !== null && (pendingQuery.searchText !== searchText || pendingQuery.page !== page)) {
+ const { searchText, page } = pendingQuery;
+ pendingQuery = null;
+ searchAlgolia(searchText, page);
+ }
+ };
- search.start();
+ const inputEventFunction = async() => {
+ const searchText = input.value.trim();
+ if (searchText === '') {
+ container.innerHTML = '
';
+ return;
+ }
+ // Algolia client will automatically cache the data for same queries
+ await searchAlgolia(searchText, 0);
+ };
+
+ const debouncedSearch = NexT.utils.debounce(inputEventFunction, 500);
+ input.addEventListener('input', debouncedSearch);
// Handle and trigger popup window
document.querySelectorAll('.popup-trigger').forEach(element => {
element.addEventListener('click', () => {
document.body.classList.add('search-active');
- setTimeout(() => document.querySelector('.search-input').focus(), 500);
+ // Wait for search-popup animation to complete
+ setTimeout(() => input.focus(), 500);
});
});
diff --git a/source/js/third-party/search/local-search.js b/source/js/third-party/search/local-search.js
index 92a264d..a44ef75 100644
--- a/source/js/third-party/search/local-search.js
+++ b/source/js/third-party/search/local-search.js
@@ -13,22 +13,20 @@ document.addEventListener('DOMContentLoaded', () => {
});
const input = document.querySelector('.search-input');
+ const container = document.querySelector('.search-result-container');
const inputEventFunction = () => {
if (!localSearch.isfetched) return;
const searchText = input.value.trim().toLowerCase();
const keywords = searchText.split(/[-\s]+/);
- const container = document.querySelector('.search-result-container');
let resultItems = [];
if (searchText.length > 0) {
// Perform local searching
resultItems = localSearch.getResultItems(keywords);
}
if (keywords.length === 1 && keywords[0] === '') {
- container.classList.add('no-result');
container.innerHTML = '
';
} else if (resultItems.length === 0) {
- container.classList.add('no-result');
container.innerHTML = '
';
} else {
resultItems.sort((left, right) => {
@@ -41,7 +39,6 @@ document.addEventListener('DOMContentLoaded', () => {
});
const stats = CONFIG.i18n.hits.replace('${hits}', resultItems.length);
- container.classList.remove('no-result');
container.innerHTML = `${stats}
${resultItems.map(result => result.item).join('')}
`;
diff --git a/source/js/utils.js b/source/js/utils.js
index ebd8552..2822e7b 100644
--- a/source/js/utils.js
+++ b/source/js/utils.js
@@ -498,5 +498,14 @@ NexT.utils = {
});
intersectionObserver.observe(element);
});
+ },
+
+ debounce: function(func, wait) {
+ let timeout;
+ return function(...args) {
+ const context = this;
+ clearTimeout(timeout);
+ timeout = setTimeout(() => func.apply(context, args), wait);
+ };
}
};