Files
fiddle/public/js/npm-search.js

75 lines
2.2 KiB
JavaScript

let debounceTimer = null;
export function initNpmSearch(onSelect) {
const input = document.getElementById('npm-search-input');
const results = document.getElementById('npm-search-results');
if (!input || !results) return;
input.addEventListener('input', () => {
clearTimeout(debounceTimer);
const q = input.value.trim();
if (q.length < 2) {
results.classList.add('hidden');
results.innerHTML = '';
return;
}
debounceTimer = setTimeout(() => searchNpm(q, results, input, onSelect), 300);
});
input.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
results.classList.add('hidden');
}
});
document.addEventListener('click', (e) => {
if (!input.contains(e.target) && !results.contains(e.target)) {
results.classList.add('hidden');
}
});
}
async function searchNpm(query, container, input, onSelect) {
try {
const res = await fetch(`https://registry.npmjs.org/-/v1/search?text=${encodeURIComponent(query)}&size=8`);
if (!res.ok) return;
const data = await res.json();
const packages = data.objects || [];
if (!packages.length) {
container.innerHTML = '<div class="npm-no-results">No packages found</div>';
container.classList.remove('hidden');
return;
}
container.innerHTML = packages.map(p => {
const pkg = p.package;
const desc = (pkg.description || '').slice(0, 80);
return `<div class="npm-result" data-name="${esc(pkg.name)}" data-version="${esc(pkg.version)}">
<div class="npm-result-name">${esc(pkg.name)} <span class="npm-result-version">${esc(pkg.version)}</span></div>
<div class="npm-result-desc">${esc(desc)}</div>
</div>`;
}).join('');
container.querySelectorAll('.npm-result').forEach(el => {
el.addEventListener('click', () => {
const name = el.dataset.name;
const url = `https://esm.sh/${name}`;
onSelect({ name, url, type: 'js' });
input.value = '';
container.classList.add('hidden');
});
});
container.classList.remove('hidden');
} catch (_) {
container.classList.add('hidden');
}
}
function esc(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}