aboutsummaryrefslogtreecommitdiff
path: root/themes/simple/layouts/partials/search.html
diff options
context:
space:
mode:
Diffstat (limited to 'themes/simple/layouts/partials/search.html')
-rw-r--r--themes/simple/layouts/partials/search.html123
1 files changed, 123 insertions, 0 deletions
diff --git a/themes/simple/layouts/partials/search.html b/themes/simple/layouts/partials/search.html
new file mode 100644
index 0000000..3e31753
--- /dev/null
+++ b/themes/simple/layouts/partials/search.html
@@ -0,0 +1,123 @@
1<section class="search-modal mb-10 hidden">
2 <input class="bg-gray-100 w-full px-3 py-2 rounded outline-none" type="search" placeholder="Search here...">
3 <section class="results bg-white border border-gray-200 shadow-md p-2 flex flex-col gap-2 rounded mt-4 hidden"></section>
4</section>
5
6<script src="https://unpkg.com/lunr/lunr.js"></script>
7<script>
8 (async function() {
9 const debounceDelay = 700;
10
11 // Fetch search index generated by Hugo.
12 const req = await fetch('/index.json');
13 window.searchDocuments = await req.json();
14
15 window.searchIndex = lunr(function() {
16 this.ref('permalink');
17 this.field('title', { boost: 20 });
18 this.field('tags', { boost: 10 });
19 this.field('summary', { boost: 1 });
20
21 window.searchDocuments.forEach(function(doc) {
22 this.add(doc);
23 }, this);
24
25 console.log('Search index processed...');
26 })
27
28 // Connect DOM and search the index.
29 let cachedSearchTerm = null;
30 const searchModal = document.querySelector('.search-modal');
31 const searchInput = document.querySelector('.search-modal input');
32 const searchResults = document.querySelector('.search-modal .results');
33
34 // Display search modal.
35 window.showSearchModal = function() {
36 searchModal.classList.remove('hidden');
37 searchInput.focus();
38 }
39
40 // Detect OS and sets proper search button text.
41 const searchButtonElement = document.querySelector('.search-button');
42 const searchButtonTextElement = document.querySelector('.search-button-text');
43 if (searchButtonElement) {
44 let searchButtonText = 'ctrl+k';
45 if (navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
46 searchButtonText = 'cmd+k';
47 }
48 searchButtonTextElement.innerText = searchButtonText;
49 searchButtonElement.classList.remove('hidden');
50 }
51
52 // On keyboard shortcut shows search modal.
53 document.addEventListener('keydown', function(event) {
54 // Handles macOS CMD+k.
55 if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
56 event.preventDefault();
57 showSearchModal();
58 }
59
60 // Handles Windows/Linux Ctrl+k.
61 if (event.ctrlKey && event.key === 'k') {
62 event.preventDefault();
63 showSearchModal();
64 }
65 });
66
67 // Debounce magic.
68 function debounce(func, delay) {
69 let timerId;
70 return function (...args) {
71 clearTimeout(timerId);
72 timerId = setTimeout(() => {
73 func.apply(this, args);
74 }, delay);
75 };
76 }
77
78 // Do the actual search.
79 searchInput.addEventListener('keyup', debounce((evt)=> {
80 const query = evt.target.value.trim().toLowerCase();
81 if (query.length && query != cachedSearchTerm) {
82 const results = searchIndex.search(query);
83
84 if (results.length == 0) {
85 searchResults.classList.add('hidden');
86 } else {
87 searchResults.innerText = '';
88 searchResults.classList.remove('hidden');
89 cachedSearchTerm = query;
90
91 results.forEach(resultItem => {
92 const item = window.searchDocuments.find(doc => doc.permalink === resultItem.ref);
93
94 const link = document.createElement('a');
95 link.href = item.permalink;
96 link.classList.add('hover:bg-gray-100', 'cursor-pointer', 'py-2', 'px-3', 'rounded');
97
98 const title = document.createElement('div');
99 title.classList.add('text-gray-800', 'font-medium');
100 title.innerHTML = item.title;
101 link.appendChild(title);
102
103 const meta = document.createElement('div');
104 meta.classList.add('text-gray-500', 'flex', 'items-center', 'gap-2');
105
106 const section = document.createElement('span');
107 section.classList.add('uppercase', 'text-xs', 'font-medium', 'bg-gray-200', 'px-1', 'rounded')
108 section.innerText = item.type;
109 meta.appendChild(section);
110
111 const summary = document.createElement('span');
112 const summaryText = item.summary.length > 80 ? `${item.summary.substring(0, 80)}...` : item.summary;
113 summary.innerHTML = summaryText;
114 meta.appendChild(summary);
115
116 link.appendChild(meta);
117 searchResults.appendChild(link);
118 });
119 }
120 }
121 }, debounceDelay));
122 })();
123</script>