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.html191
1 files changed, 0 insertions, 191 deletions
diff --git a/themes/simple/layouts/partials/search.html b/themes/simple/layouts/partials/search.html
deleted file mode 100644
index f157f03..0000000
--- a/themes/simple/layouts/partials/search.html
+++ /dev/null
@@ -1,191 +0,0 @@
1{{ $cachebuster := delimit (shuffle (split (md5 "6fab11c6669976d759d2992eff1dd5be") "" )) "" }}
2
3<section class="search-modal mb-10 hidden">
4 <input class="bg-gray-100 w-full px-3 py-2 rounded outline-none" type="search" placeholder="Search here...">
5 <section class="results bg-white border border-gray-200 shadow-md p-2 flex flex-col gap-2 rounded mt-4 hidden"></section>
6</section>
7
8<script src="https://unpkg.com/lunr/lunr.js"></script>
9<script>
10 (async function() {
11 const debounceDelay = 700;
12 const maxSearchResults = 10;
13
14 // Fetch search index generated by Hugo.
15 const req = await fetch('/index.json?v={{ $cachebuster }}');
16 window.searchDocuments = await req.json();
17
18 window.searchIndex = lunr(function() {
19 this.ref('permalink');
20 this.field('title', { boost: 20 });
21 this.field('tags', { boost: 10 });
22 this.field('summary', { boost: 1 });
23
24 window.searchDocuments.forEach(function(doc) {
25 this.add(doc);
26 }, this);
27
28 console.log('Search index processed...');
29 })
30
31 // Connect DOM and search the index.
32 let cachedSearchTerm = null;
33 const searchModal = document.querySelector('.search-modal');
34 const searchInput = document.querySelector('.search-modal input');
35 const searchResults = document.querySelector('.search-modal .results');
36
37 // Display search modal.
38 window.showSearchModal = function() {
39 searchModal.classList.remove('hidden');
40 searchInput.focus();
41 }
42
43 // Detect OS and sets proper search button text.
44 const searchButtonElement = document.querySelector('.search-button');
45 const searchButtonTextElement = document.querySelector('.search-button-text');
46 if (searchButtonElement) {
47 let searchButtonText = 'Ctrl+K';
48 if (navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
49 searchButtonText = '⌘+K';
50 }
51 searchButtonTextElement.innerText = searchButtonText;
52 searchButtonElement.classList.remove('hidden');
53 }
54
55 // On keyboard shortcut shows search modal.
56 let currentSearchResultsSectionIndex = -1;
57 const itemHoverClass = 'bg-gray-100';
58 document.addEventListener('keydown', function(event) {
59 // Handles macOS CMD+k.
60 if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
61 event.preventDefault();
62 showSearchModal();
63 }
64
65 // Handles Windows/Linux Ctrl+k.
66 if (event.ctrlKey && event.key === 'k') {
67 event.preventDefault();
68 showSearchModal();
69 }
70
71
72 // If ESC is pressed when the input is empty close the search modal.
73 if (event.key === 'Escape' || event.key === 'Esc') {
74 if (searchInput.value.length == 0) {
75 if (!searchModal.classList.contains('hidden')) {
76 cachedSearchTerm = null;
77 searchModal.classList.add('hidden');
78 searchResults.innerText = '';
79 if (!searchResults.classList.contains('hidden')) {
80 searchResults.classList.add('hidden');
81 }
82 }
83 } else {
84 const listItems = searchResults.querySelectorAll('a');
85 listItems.forEach(el => el.classList.remove(itemHoverClass));
86 currentSearchResultsSectionIndex = -1;
87 searchInput.focus();
88 }
89 }
90
91
92 // Arrow UP/DOWN movement through search results.
93 if (event.key === 'ArrowUp') {
94 if (!searchResults.classList.contains('hidden')) {
95 event.preventDefault();
96
97 const listItems = searchResults.querySelectorAll('a');
98 listItems.forEach(el => el.classList.remove(itemHoverClass));
99 currentSearchResultsSectionIndex--;
100
101 if (!listItems[currentSearchResultsSectionIndex]) {
102 currentSearchResultsSectionIndex = -1;
103 searchInput.focus();
104 }
105
106 if (currentSearchResultsSectionIndex != -1) {
107 listItems[currentSearchResultsSectionIndex].classList.add(itemHoverClass);
108 listItems[currentSearchResultsSectionIndex].focus();
109 }
110 }
111 }
112 if (event.key === 'ArrowDown') {
113 if (!searchResults.classList.contains('hidden')) {
114 event.preventDefault();
115
116 const listItems = searchResults.querySelectorAll('a');
117 listItems.forEach(el => el.classList.remove(itemHoverClass));
118 currentSearchResultsSectionIndex++;
119
120 if (!listItems[currentSearchResultsSectionIndex]) {
121 currentSearchResultsSectionIndex = 0;
122 }
123
124 if (currentSearchResultsSectionIndex != -1) {
125 listItems[currentSearchResultsSectionIndex].classList.add(itemHoverClass);
126 listItems[currentSearchResultsSectionIndex].focus();
127 }
128 }
129 }
130
131 });
132
133 // Debounce magic.
134 function debounce(func, delay) {
135 let timerId;
136 return function (...args) {
137 clearTimeout(timerId);
138 timerId = setTimeout(() => {
139 func.apply(this, args);
140 }, delay);
141 };
142 }
143
144 // Do the actual search.
145 searchInput.addEventListener('keyup', debounce((evt)=> {
146 const query = evt.target.value.trim().toLowerCase();
147 if (query.length && query != cachedSearchTerm) {
148 const results = searchIndex.search(`*${query}*`);
149
150 if (results.length == 0) {
151 if (!searchResults.classList.contains('hidden')) {
152 searchResults.classList.add('hidden');
153 }
154 } else {
155 searchResults.innerText = '';
156 searchResults.classList.remove('hidden');
157 cachedSearchTerm = query;
158
159 results.slice(0, maxSearchResults).forEach(resultItem => {
160 const item = window.searchDocuments.find(doc => doc.permalink === resultItem.ref);
161
162 const link = document.createElement('a');
163 link.href = item.permalink;
164 link.classList.add('hover:bg-gray-100', 'cursor-pointer', 'py-2', 'px-3', 'rounded');
165
166 const title = document.createElement('div');
167 title.classList.add('text-gray-800', 'font-medium');
168 title.innerHTML = item.title;
169 link.appendChild(title);
170
171 const meta = document.createElement('div');
172 meta.classList.add('text-gray-500', 'flex', 'items-center', 'gap-2');
173
174 const section = document.createElement('span');
175 section.classList.add('uppercase', 'text-xs', 'font-medium', 'bg-gray-200', 'px-1', 'rounded')
176 section.innerText = item.type;
177 meta.appendChild(section);
178
179 const summary = document.createElement('span');
180 const summaryText = item.summary.length > 80 ? `${item.summary.substring(0, 80)}...` : item.summary;
181 summary.innerHTML = summaryText;
182 meta.appendChild(summary);
183
184 link.appendChild(meta);
185 searchResults.appendChild(link);
186 });
187 }
188 }
189 }, debounceDelay));
190 })();
191</script>