import dayjs from 'dayjs'; const allTypes = ['Page', 'Post']; async function ensurePagefind() { if (window.pagefind) return Promise.resolve(window.pagefind); return import('/pagefind/pagefind.js') .then(function (mod) { mod.options({ highlightParam: 'highlight' }); window.pagefind = mod; return window.pagefind || mod.pagefind || mod.default || mod; }) .catch(function () { return new Promise(function (resolve) { const s = document.createElement('script'); s.src = '/pagefind/pagefind.js'; s.type = 'module'; s.onload = function () { resolve(window.pagefind); }; s.onerror = function () { resolve(undefined); }; document.head.appendChild(s); }); }); } function renderItem(item) { let { url, excerpt, meta: {author, date, title, type, image, image_alt, tag} } = item; //debugging console.log(item.meta); // create date const dateHTML = date ? `${dayjs(date).format('MMMM D, YYYY') }` : ''; // create hero image preview let imageHTML = ''; if (item.meta.image) { // Try to get alt text from Pagefind metadata, fallback to title, then generic text const altText = item.meta['image[alt]'] || item.meta.title || 'Search result image'; imageHTML = `${altText}`; } // create tags let tagsHTML = ''; if (item.filters && item.filters.tag && item.filters.tag.length > 0) { tagsHTML = ` ${item.filters.tag.map(tag => `${tag}`).join('')} `; } let variant; switch (type) { case 'Page': variant = 'secondary'; break; default: variant = 'primary'; break; } //output the search result return ` ${imageHTML}

${title}

${dateHTML}
${type} ${tagsHTML}
${excerpt}
`; } function renderItems(q, items) { var results = items.length == 1 ? 'result' : 'results'; document.querySelector('#results-count').innerHTML = `${items.length} ${results} for ${q}`; let content = items.map(renderItem).join(''); document.querySelector('#results').innerHTML = content; } function doSearch(isPopEvent = false) { let form = document.querySelector('form#search'); // Clear current content document.querySelector('#results').innerHTML = ''; document.querySelector('#results-count').innerHTML = ''; // Get form data let formData = new FormData(form); let q = formData.get('q'); let types = []; allTypes.map(possibleFilter => { if (formData.get(possibleFilter)) { types.push(possibleFilter); } }); let tags = []; formData.getAll('tag').forEach(tag => { if (tag) tags.push(tag); }); // Only do a search if there's a query if (q) { // Update url unless it's a popstate event if (!isPopEvent) { setWindowLocation(q, types, tags, isPopEvent); } // Show results area form.querySelector('#filter-and-results').classList.remove('filter-and-results--hidden'); // Find and display results window.pagefind .search(q, { filters: { type: { any: types }, tag: { any: tags } } }) .then(search => Promise.all(search.results.map(result => result.data())) .then(data => renderItems(q, data)) .catch(console.error) ) .catch(console.error); } } function setWindowLocation(q, types, tags) { const url = new URL(window.location); if (q) { url.searchParams.set('q', q); } url.searchParams.delete('types'); for (const type of types) { url.searchParams.append('types', type); } url.searchParams.delete('tags'); for (const tag of tags) { url.searchParams.append('tags', tag); } window.history.pushState({search: url.searchParams.toString()}, '', url); } function setFormFromLocation() { const url = new URL(window.location); let searchParams = url.searchParams; setFormFromSearchParams(searchParams); } function setFormFromSearchParams(searchParams) { let q = searchParams.get('q'); document.querySelector('form#search input#q').value = q; let types = searchParams.getAll('types'); for (const type of allTypes) { document.querySelector(`form#search input[name="${type}"]`).checked = types.includes(type); } let tags = searchParams.getAll('tags'); document.querySelectorAll('form#search input[name="tag"]').forEach(input => { input.checked = tags.includes(input.value); }); } window.addEventListener('DOMContentLoaded', _ => { ensurePagefind() .then(_ => { setFormFromLocation(); doSearch(); }) .catch(e => console.error('page find error', e)); let form = document.querySelector('form#search'); form.addEventListener('submit', e => { e.preventDefault(); doSearch(); }); // Submit form on post type change let checkboxes = form.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach(checkbox => { checkbox.addEventListener('change', _ => { doSearch(); }); }); // Using browser back or forward button window.addEventListener('popstate', event => { // Only intercept if on search page if (!['/search/', '/search'].includes(window.location.pathname)) return; ensurePagefind() .then(_ => { let searchParams = new URLSearchParams(event.state?.search ?? ''); setFormFromSearchParams(searchParams); // Don't update history as we're navigating through history! doSearch(true); }) .catch(e => console.error('page find error', e)); }); });