89 lines
2.7 KiB
JavaScript
89 lines
2.7 KiB
JavaScript
/*
|
|
SEARCH — debounced live search with client-side type filter.
|
|
URL ?q= is kept in sync so results are shareable/bookmarkable.
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
|
|
SR.app.initChrome();
|
|
|
|
const input = document.getElementById('searchInput');
|
|
const chipsEl = document.getElementById('typeChips');
|
|
const resultsEl = document.getElementById('results');
|
|
const countEl = document.getElementById('searchCount');
|
|
|
|
const state = { type: 'all', results: [] };
|
|
|
|
function render() {
|
|
resultsEl.replaceChildren();
|
|
const filtered = state.type === 'all' ? state.results : state.results.filter((i) => i.type === state.type);
|
|
if (!state.results.length) {
|
|
countEl.textContent = '';
|
|
resultsEl.appendChild(
|
|
SR.cards.emptyState('Nothing found', 'Check the spelling or try a different name.')
|
|
);
|
|
return;
|
|
}
|
|
countEl.textContent = `${filtered.length} of ${state.results.length} result${state.results.length === 1 ? '' : 's'}`;
|
|
if (!filtered.length) {
|
|
resultsEl.appendChild(SR.cards.emptyState('No matches for this filter', 'Try the All filter, or a different search.'));
|
|
return;
|
|
}
|
|
resultsEl.appendChild(SR.cards.grid(filtered));
|
|
}
|
|
|
|
async function runSearch(q) {
|
|
q = (q || '').trim();
|
|
if (!q) {
|
|
state.results = [];
|
|
countEl.textContent = '';
|
|
resultsEl.replaceChildren(SR.cards.emptyState('Search the catalog', 'Type a movie or show name above.'));
|
|
return;
|
|
}
|
|
countEl.textContent = 'Searching…';
|
|
try {
|
|
const data = await SR.api.search(q);
|
|
state.results = data.results.map(SR.cards.normalizeItem);
|
|
render();
|
|
} catch (e) {
|
|
SR.app.showError(e, 'search failed');
|
|
countEl.textContent = '';
|
|
}
|
|
}
|
|
|
|
const LABELS = { all: 'All', movie: 'Movies', tv: 'TV' };
|
|
for (const t of ['all', 'movie', 'tv']) {
|
|
const b = document.createElement('button');
|
|
b.type = 'button';
|
|
b.className = t === 'all' ? 'chip chip--active' : 'chip';
|
|
b.textContent = LABELS[t];
|
|
b.addEventListener('click', () => {
|
|
state.type = t;
|
|
for (const c of chipsEl.querySelectorAll('.chip')) c.classList.remove('chip--active');
|
|
b.classList.add('chip--active');
|
|
render();
|
|
});
|
|
chipsEl.appendChild(b);
|
|
}
|
|
|
|
input.addEventListener(
|
|
'input',
|
|
SR.util.debounce(() => {
|
|
const q = input.value.trim();
|
|
history.replaceState(null, '', q ? `?q=${encodeURIComponent(q)}` : 'search.html');
|
|
runSearch(q);
|
|
}, 450)
|
|
);
|
|
input.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
runSearch(input.value.trim());
|
|
}
|
|
});
|
|
|
|
const initialQ = SR.util.getParam('q') || '';
|
|
input.value = initialQ;
|
|
if (initialQ) runSearch(initialQ);
|
|
else input.focus();
|
|
})();
|