import * as params from '@params'; const resList = document.getElementById('searchResults'); const sInput = document.getElementById('searchInput'); const searchBox = document.getElementById('searchbox'); let fuse; let currentElement = null; let firstResult = null; let lastResult = null; const defaultFuseOptions = { distance: 100, threshold: 0.4, ignoreLocation: true, keys: ['title', 'permalink', 'summary', 'content'] }; const buildFuseOptions = () => { if (!params.fuseOpts) { return defaultFuseOptions; } return { isCaseSensitive: params.fuseOpts.iscasesensitive ?? false, includeScore: params.fuseOpts.includescore ?? false, includeMatches: params.fuseOpts.includematches ?? false, minMatchCharLength: params.fuseOpts.minmatchcharlength ?? 1, shouldSort: params.fuseOpts.shouldsort ?? true, findAllMatches: params.fuseOpts.findallmatches ?? false, keys: params.fuseOpts.keys ?? defaultFuseOptions.keys, location: params.fuseOpts.location ?? 0, threshold: params.fuseOpts.threshold ?? defaultFuseOptions.threshold, distance: params.fuseOpts.distance ?? defaultFuseOptions.distance, ignoreLocation: params.fuseOpts.ignorelocation ?? defaultFuseOptions.ignoreLocation }; }; const debounce = (fn, delay) => { let timeout; return (...args) => { clearTimeout(timeout); timeout = window.setTimeout(() => fn(...args), delay); }; }; const reset = () => { currentElement = null; firstResult = null; lastResult = null; resList.innerHTML = ''; sInput.value = ''; sInput.focus(); }; const setActiveResult = (element) => { document.querySelectorAll('.focus').forEach((item) => item.classList.remove('focus')); if (!element) { return; } element.focus(); element.parentElement?.classList.add('focus'); currentElement = element; }; const renderResults = (results) => { if (!Array.isArray(results) || results.length === 0) { resList.innerHTML = ''; firstResult = lastResult = currentElement = null; return; } const fragment = document.createDocumentFragment(); for (const result of results) { const li = document.createElement('li'); const titleText = document.createTextNode(result.item.title); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('width', '24'); svg.setAttribute('height', '24'); svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('fill', 'none'); svg.setAttribute('stroke', 'currentColor'); svg.setAttribute('stroke-width', '2'); svg.setAttribute('stroke-linecap', 'round'); svg.setAttribute('stroke-linejoin', 'round'); svg.classList.add('feather', 'feather-chevrons-right'); svg.innerHTML = ''; const link = document.createElement('a'); link.className = 'entry-link'; link.href = result.item.permalink; link.setAttribute('aria-label', result.item.title); li.appendChild(titleText); li.appendChild(svg); li.appendChild(link); fragment.appendChild(li); } resList.innerHTML = ''; resList.appendChild(fragment); firstResult = resList.firstElementChild; lastResult = resList.lastElementChild; }; const performSearch = () => { if (!fuse) { return; } const query = sInput.value.trim(); if (!query) { renderResults([]); return; } const searchOptions = params.fuseOpts?.limit ? { limit: params.fuseOpts.limit } : undefined; const results = searchOptions ? fuse.search(query, searchOptions) : fuse.search(query); renderResults(results); }; const initSearch = async () => { if (!sInput || !resList) { return; } sInput.disabled = false; sInput.focus(); try { const response = await fetch('../index.json'); if (!response.ok) { throw new Error(`Search index load failed: ${response.status}`); } const data = await response.json(); if (data) { fuse = new Fuse(data, buildFuseOptions()); } } catch (error) { console.error(error); } }; window.addEventListener('load', initSearch); sInput?.addEventListener('input', debounce(performSearch, 150)); sInput?.addEventListener('search', () => { if (!sInput.value) { reset(); } }); document.addEventListener('keydown', (event) => { const { key } = event; const active = document.activeElement; const isInSearchBox = searchBox?.contains(active); if (key === 'Escape') { reset(); return; } if (!firstResult || !isInSearchBox) { return; } if (key === 'ArrowDown') { event.preventDefault(); if (active === sInput) { setActiveResult(firstResult.querySelector('.entry-link')); } else if (active?.parentElement !== lastResult) { setActiveResult(active?.parentElement?.nextElementSibling?.querySelector('.entry-link')); } } else if (key === 'ArrowUp') { event.preventDefault(); if (active?.parentElement === firstResult) { setActiveResult(sInput); } else if (active !== sInput) { setActiveResult(active?.parentElement?.previousElementSibling?.querySelector('.entry-link')); } } else if (key === 'ArrowRight') { if (active?.matches?.('.entry-link')) { active.click(); } } });