diff options
author | WingLim <643089849@qq.com> | 2021-06-13 11:47:02 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-13 11:47:02 +0300 |
commit | 00dd4c03613926c598b7d987285f25169ecd33e8 (patch) | |
tree | 6d8ded8e566d86315a4b43c906b69b06a8ae5ca9 | |
parent | 44cb34cbb4d07e4d56a00629518eeafa2a166574 (diff) |
feat(search): search in articles page (#13)
* feat: add search input
* feat: use fuse to search
* refactor: use div as results container
* refactor: init fuse once
* feat: set threshold 0.4
* feat: set fuse threshold to 0.3
0.3 search result looks better 0.4
* refactor: simplify result render
* chore: make search input full width
* docs: use yaml header
* feat: add categories and tags filter
* fix: searching in contents
* fix: search input is empty
* refactor: remove unused function
* fix: add listener in articles page
* refactor: rename articles to articlesList
* docs: add comments
* refactor: show categories only
* feat: limit categories count
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | assets/js/features.js | 250 | ||||
-rw-r--r-- | assets/sass/components/_search.scss | 53 | ||||
-rw-r--r-- | assets/sass/main.scss | 1 | ||||
-rw-r--r-- | exampleSite/config.yaml | 5 | ||||
-rw-r--r-- | exampleSite/content/post/emoji-support.md | 17 | ||||
-rw-r--r-- | exampleSite/content/post/footnote.md | 2 | ||||
-rw-r--r-- | exampleSite/content/post/markdown-syntax.md | 30 | ||||
-rw-r--r-- | exampleSite/content/post/placeholder-text.md | 19 | ||||
-rw-r--r-- | exampleSite/content/post/test-katex.md | 2 | ||||
-rw-r--r-- | exampleSite/content/post/test-mathjax.md | 2 | ||||
-rw-r--r-- | layouts/_default/archives.html | 15 | ||||
-rw-r--r-- | layouts/index.json | 6 | ||||
-rw-r--r-- | layouts/partials/footer.html | 1 |
14 files changed, 311 insertions, 99 deletions
@@ -66,6 +66,13 @@ If you want to disable the float footnotes, add below params. enableFootnotes: false ``` +Limit how many categories filter show above search input. + +Default is `5` +```yaml +maxCategoryToShow: 10 +``` + ### Layout `articles` layout is for showing all articles you write. diff --git a/assets/js/features.js b/assets/js/features.js index afc63b8..9d40716 100644 --- a/assets/js/features.js +++ b/assets/js/features.js @@ -1,65 +1,72 @@ -(function(d){ - let enableFootnotes = false - if (d.currentScript) { - enableFootnotes = d.currentScript.dataset['enableFootnotes'] == 'true' - } - renderFootnotes = function () { - const removeEl = (el) => { - if (!el) return; - el.remove ? el.remove() : el.parentNode.removeChild(el); - }; +let show = function (elem) { + elem.style.display = 'block'; +}; +let hide = function (elem) { + elem.style.display = 'none'; +}; - const insertAfter = (target, sib) => { - target.after ? target.after(sib) : ( - target.parentNode.insertBefore(sib, target.nextSibling) - ); - }; +(function (d) { + let enableFootnotes = false + if (d.currentScript) { + enableFootnotes = d.currentScript.dataset['enableFootnotes'] == 'true' + } + renderFootnotes = function () { + const removeEl = (el) => { + if (!el) return; + el.remove ? el.remove() : el.parentNode.removeChild(el); + }; - const insideOut = (el) => { - var p = el.parentNode, x = el.innerHTML, - c = document.createElement('div'); // a tmp container - insertAfter(p, c); - c.appendChild(el); - el.innerHTML = ''; - el.appendChild(p); - p.innerHTML = x; // let the original parent have the content of its child - insertAfter(c, c.firstElementChild); - removeEl(c); - }; + const insertAfter = (target, sib) => { + target.after ? target.after(sib) : ( + target.parentNode.insertBefore(sib, target.nextSibling) + ); + }; - document.querySelectorAll('.footnotes > ol > li[id^="fn"], #refs > div[id^="ref-"]').forEach(function (fn) { - a = document.querySelectorAll('a[href="#' + fn.id + '"]'); - if (a.length === 0) return; - a.forEach(function (el) { el.removeAttribute('href') }); - a = a[0]; - side = document.createElement('div'); - side.className = 'side side-right'; - if (/^fn/.test(fn.id)) { - side.innerHTML = fn.innerHTML; - var number = a.innerText; // footnote number - side.firstElementChild.innerHTML = '<span class="bg-number">' + number + - '</span> ' + side.firstElementChild.innerHTML; - removeEl(side.querySelector('a[href^="#fnref"]')); // remove backreference - a.parentNode.tagName === 'SUP' && insideOut(a); - } else { - side.innerHTML = fn.outerHTML; - a = a.parentNode; - } - insertAfter(a, side); - a.classList.add('note-ref'); - removeEl(fn); - }) - document.querySelectorAll('.footnotes, #refs').forEach(function (fn) { - var items = fn.children; - if (fn.id === 'refs') return items.length === 0 && removeEl(fn); - // there must be a <hr> and an <ol> left - if (items.length !== 2 || items[0].tagName !== 'HR' || items[1].tagName !== 'OL') return; - items[1].childElementCount === 0 && removeEl(fn); - }); - }; - if (enableFootnotes) { - renderFootnotes() - } + const insideOut = (el) => { + var p = el.parentNode, x = el.innerHTML, + c = document.createElement('div'); // a tmp container + insertAfter(p, c); + c.appendChild(el); + el.innerHTML = ''; + el.appendChild(p); + p.innerHTML = x; // let the original parent have the content of its child + insertAfter(c, c.firstElementChild); + removeEl(c); + }; + + document.querySelectorAll('.footnotes > ol > li[id^="fn"], #refs > div[id^="ref-"]').forEach(function (fn) { + a = document.querySelectorAll('a[href="#' + fn.id + '"]'); + if (a.length === 0) return; + a.forEach(function (el) { el.removeAttribute('href') }); + a = a[0]; + side = document.createElement('div'); + side.className = 'side side-right'; + if (/^fn/.test(fn.id)) { + side.innerHTML = fn.innerHTML; + var number = a.innerText; // footnote number + side.firstElementChild.innerHTML = '<span class="bg-number">' + number + + '</span> ' + side.firstElementChild.innerHTML; + removeEl(side.querySelector('a[href^="#fnref"]')); // remove backreference + a.parentNode.tagName === 'SUP' && insideOut(a); + } else { + side.innerHTML = fn.outerHTML; + a = a.parentNode; + } + insertAfter(a, side); + a.classList.add('note-ref'); + removeEl(fn); + }) + document.querySelectorAll('.footnotes, #refs').forEach(function (fn) { + var items = fn.children; + if (fn.id === 'refs') return items.length === 0 && removeEl(fn); + // there must be a <hr> and an <ol> left + if (items.length !== 2 || items[0].tagName !== 'HR' || items[1].tagName !== 'OL') return; + items[1].childElementCount === 0 && removeEl(fn); + }); + }; + if (enableFootnotes) { + renderFootnotes() + } })(document); renderAnchor = function () { @@ -177,4 +184,127 @@ switchDarkMode = function () { // handle user click switch dark mode button applyCustomDarkModeSettings(toggleCustomDarkMode()); }) -}();
\ No newline at end of file +}(); + +initFuse = function () { + const fuseOptions = { + shouldSort: true, + threshold: 0.3, + location: 0, + distance: 100, + maxPatternLength: 32, + minMatchCharLength: 1, + useExtendedSearch: true, + keys: [ + { name: "title", weight: 0.8 }, + { name: "contents", weight: 0.5 }, + { name: "tags", weight: 0.3 }, + { name: "categories", weight: 0.3 } + ] + }; + + fetch('/index.json') + .then(function (response) { + if (response.status !== 200) { + console.error('[' + response.status + ']Error:', response.statusText); + return; + } + response.json().then(function (pages) { + let fuse = new Fuse(pages, fuseOptions); + window.fuse = fuse; + }) + + }) + .catch(function (err) { + console.error('[Fetch]Error:', err); + }); +}() + +const searchInput = document.getElementById('search-query'); +const searchResults = document.getElementById('search-results') +const articlesList = document.getElementById('articles-list') +if (searchInput != undefined) { + searchInput.addEventListener("input", function () { + let value = searchInput.value + executeSearch(buildSearchValue(value)); + }) +} + +let searchFilter = new Map() +buildSearchValue = function(value) { + let filter = [] + if (searchFilter.size == 0 && value.length == 0) { + return "" + } + searchFilter.forEach((v, k) => { + let object = {} + if (v == "categories") { + object = { + categories: k + } + } + filter.push(object) + }) + if (value != undefined && value.length != 0) { + let orObject = { + $or: [ + {title: value}, + // fuse extended search, 'value is include-match + // more details: https://fusejs.io/examples.html#extended-search + {contents: "'"+value} + ] + } + filter.push(orObject) + } + return { + $and: filter + } +} + +filterSelect = function(element) { + let value = element.dataset.value + let type = element.dataset.type + if (element.classList.contains('active')) { + searchFilter.delete(value) + element.classList.remove('active') + } else { + searchFilter.set(value, type) + element.classList.add('active') + } + executeSearch(buildSearchValue("")) +} + +executeSearch = function(value) { + if (value.length != 0) { + hide(articlesList) + show(searchResults) + } else { + hide(searchResults) + show(articlesList) + } + + var result = fuse.search(value); + if (result.length > 0) { + populateResults(result); + } else { + searchResults.innerHTML = '<p>Sorry, nothing matched that search.</p>'; + } + + function populateResults(results) { + searchResults.innerHTML = "" + + results.forEach(function (value) { + let item = value.item + let html = ` + <div class="post"> + <a href="${item.permalink}"> + <div class="post-row"> + <time>${item.date}</time> + <h3>${item.title}</h3> + </div> + </a> + </div>` + searchResults.innerHTML += html; + }); + } +} diff --git a/assets/sass/components/_search.scss b/assets/sass/components/_search.scss new file mode 100644 index 0000000..4f499b8 --- /dev/null +++ b/assets/sass/components/_search.scss @@ -0,0 +1,53 @@ +#search-query { + padding: .8rem 1rem; + background: var(--light-background); + border: 2px solid var(--border); + width: 100%; + border-radius: .35rem; + font-size: 1rem; + -webkit-appearance: none; + margin-left: auto; + margin-right: auto; + margin-bottom: 4rem; + outline-offset: 0; +} +#search-results { + display: none; +} + +.filter-container { + display: flex; + justify-content: flex-start; + margin-bottom: 1.5rem; + flex-wrap: wrap; +} + +.filter-item { + font-size: .85rem; + padding: .5rem .75rem; + background: #ebf1fe; + border-radius: 4px; + margin-right: .5rem; + margin-bottom: .5rem; + font-weight: 600; + cursor: pointer; + + &:hover { + background: #d8e3fd; + color: #3972f4 + } + + &:last-of-type { + margin-right: 0; + } + + &.active { + background: #5183f5; + color: #fff; + + &:hover { + color: #fff; + background: #2161f2 + } + } +}
\ No newline at end of file diff --git a/assets/sass/main.scss b/assets/sass/main.scss index 34b9aab..7195e76 100644 --- a/assets/sass/main.scss +++ b/assets/sass/main.scss @@ -15,6 +15,7 @@ @import "components/helpers"; @import "components/table"; @import "components/tags"; +@import "components/search"; @import "components/post"; @import "components/highlight"; diff --git a/exampleSite/config.yaml b/exampleSite/config.yaml index e205454..71ffccf 100644 --- a/exampleSite/config.yaml +++ b/exampleSite/config.yaml @@ -25,4 +25,7 @@ markup: lineNos: true goldmark: renderer: - unsafe: true
\ No newline at end of file + unsafe: true + +outputs: + home: ["HTML", "RSS", "JSON"]
\ No newline at end of file diff --git a/exampleSite/content/post/emoji-support.md b/exampleSite/content/post/emoji-support.md index dc3589a..7c7ea76 100644 --- a/exampleSite/content/post/emoji-support.md +++ b/exampleSite/content/post/emoji-support.md @@ -1,12 +1,11 @@ -+++ -author = "Hugo Authors" -title = "Emoji Support" -date = "2019-03-05" -description = "Guide to emoji usage in Hugo" -tags = [ - "emoji", -] -+++ +--- +author: "Hugo Authors" +title: "Emoji Support" +date: "2019-03-05" +description: "Guide to emoji usage in Hugo" +tags: +- emoji +--- Emoji can be enabled in a Hugo project in a number of ways. <!--more--> diff --git a/exampleSite/content/post/footnote.md b/exampleSite/content/post/footnote.md index e986eaa..48c3cac 100644 --- a/exampleSite/content/post/footnote.md +++ b/exampleSite/content/post/footnote.md @@ -1,6 +1,8 @@ --- author: Hugo Authors title: Footnote test +categories: +- syntax date: 2021-05-31 --- diff --git a/exampleSite/content/post/markdown-syntax.md b/exampleSite/content/post/markdown-syntax.md index 06990d7..098f2cd 100644 --- a/exampleSite/content/post/markdown-syntax.md +++ b/exampleSite/content/post/markdown-syntax.md @@ -1,20 +1,16 @@ -+++ -author = "Hugo Authors" -title = "Markdown Syntax Guide" -date = "2019-03-11" -description = "Sample article showcasing basic Markdown syntax and formatting for HTML elements." -tags = [ - "markdown", - "css", - "html", -] -categories = [ - "themes", - "syntax", -] -series = ["Themes Guide"] -aliases = ["migrate-from-jekyl"] -+++ +--- +author: "Hugo Authors" +title: "Markdown Syntax Guide" +date: "2019-03-11" +description: "Sample article showcasing basic Markdown syntax and formatting for HTML elements." +tags: +- markdown +- css +- html +categories: +- themes +- syntax +--- This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme. <!--more--> diff --git a/exampleSite/content/post/placeholder-text.md b/exampleSite/content/post/placeholder-text.md index 9ed5f69..d619368 100644 --- a/exampleSite/content/post/placeholder-text.md +++ b/exampleSite/content/post/placeholder-text.md @@ -1,13 +1,12 @@ -+++ -author = "Hugo Authors" -title = "Placeholder Text" -date = "2019-03-09" -description = "Lorem Ipsum Dolor Si Amet" -tags = [ - "markdown", - "text", -] -+++ +--- +author: "Hugo Authors" +title: "Placeholder Text" +date: "2019-03-09" +description: "Lorem Ipsum Dolor Si Amet" +tags: +- markdown +- text +--- Lorem est tota propiore conpellat pectoribus de pectora summo. <!--more-->Redit teque digerit hominumque toris verebor lumina non cervice subde tollit usus habet Arctonque, furores quas nec ferunt. Quoque montibus nunc caluere tempus inhospita parcite confusaque translucet patri vestro qui optatis lumine cognoscere flos nubis! Fronde ipsamque patulos Dryopen deorum. diff --git a/exampleSite/content/post/test-katex.md b/exampleSite/content/post/test-katex.md index 6e15fc9..164a175 100644 --- a/exampleSite/content/post/test-katex.md +++ b/exampleSite/content/post/test-katex.md @@ -3,6 +3,8 @@ author: Hugo Authors title: Katex support date: 2021-05-22 description: "KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web." +categories: +- math katex: true --- diff --git a/exampleSite/content/post/test-mathjax.md b/exampleSite/content/post/test-mathjax.md index d9116dc..02b9374 100644 --- a/exampleSite/content/post/test-mathjax.md +++ b/exampleSite/content/post/test-mathjax.md @@ -3,6 +3,8 @@ author: Hugo Authors title: MathJax support date: 2021-05-22 description: A brief guide to setup MathJax +categories: +- math mathjax: true --- diff --git a/layouts/_default/archives.html b/layouts/_default/archives.html index 32f4937..06b2840 100644 --- a/layouts/_default/archives.html +++ b/layouts/_default/archives.html @@ -11,7 +11,19 @@ <section> <div class="container"> - <section> + <div class="filter-container"> + {{ $maxCategoryToShow := $.Site.Params.maxCategoryToShow | default 5 }} + {{ range .Site.Taxonomies.categories.TaxonomyArray | first $maxCategoryToShow }} + <div class="filter-item" data-value="{{ .Page.Title }}" data-type="categories" onclick="filterSelect(this)"> + {{ .Page.Title }}<sup>{{ .Count }}</sup> + </div> + {{ end }} + </div> + <input id="search-query" type="search" placeholder="Search for anything..."> + + <div id="search-results"> + </div> + <section id="articles-list"> {{ range $pages.GroupByDate "2006" }} <section> <h2>{{ .Key }}</h2> @@ -23,7 +35,6 @@ <time>{{ .Date.Format "Jan 02" }}</time> <h3>{{ .Title }}</h3> </div> - <!--<div class="new-post">New!</div>--> </a> </div> {{ end }} diff --git a/layouts/index.json b/layouts/index.json new file mode 100644 index 0000000..d86ec6f --- /dev/null +++ b/layouts/index.json @@ -0,0 +1,6 @@ +{{- $.Scratch.Add "index" slice -}} +{{- range .Site.RegularPages -}} + {{- $date := .Date.Format "Jan 02"}} + {{- $.Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "categories" .Params.categories "contents" .Plain "permalink" .Permalink "date" $date) -}} +{{- end -}} +{{- $.Scratch.Get "index" | jsonify -}}
\ No newline at end of file diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html index c57d22f..eb44ed3 100644 --- a/layouts/partials/footer.html +++ b/layouts/partials/footer.html @@ -22,6 +22,7 @@ <script defer src="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/contrib/auto-render.min.js" integrity="sha384-vZTG03m+2yp6N6BNi5iM4rW4oIwk5DfcNdFfxkk9ZWpDriOkXX8voJBFrAO7MpVl" crossorigin="anonymous" onload="renderMathInElement(document.querySelector('.article-post'));"></script> {{- end }} + <script defer src="https://cdn.jsdelivr.net/npm/fuse.js@6.4.6/dist/fuse.min.js"></script> {{ $features := resources.Get "js/features.js" | minify | fingerprint }} <script async src="{{ $features.RelPermalink }}" data-enable-footnotes="{{ .Site.Params.enableFootnotes | default true }}"></script> </footer>
\ No newline at end of file |