JSFiddle - React, Tailwind, and code Playground

by Jason Miller

JavaScript

(async () => {
const {signal, computed, effect} = await import('https://esm.sh/@preact/signals-core');


function computedWithMemo(compute, getKey) {
  let lastKey, last;
  return computed(() => {
    const result = compute();
    const key = getKey(result);
    if (last !== undefined && key === lastKey) return last;
    lastKey = key;
    return last = result;
  });
}


const search = signal('');

const items = signal([
  {id:1, text:'John Doe'},
  {id:2, text:'Joan Doe'},
  {id:3, text:'Jane Doe'},
  {id:4, text:'Bob Dole'},
  {id:5, text:'Bob Cat'},
]);

const filtered = computedWithMemo(
  () => {
  	if (!search.value) return items.value;
    const pattern = new RegExp('('+search.value.replace(/([.^$[]()])/g, '\\$1')+')', 'i')
  	return items.value
    	.filter(item => item.text.match(pattern))
      .map(item => ({
      	...item,
        highlight: item.text.replace(pattern, `<b>$1</b>`)
      }));
  },
  (list) => list.reduce((str, item) => str + item.id + item.highlight, ''),
);

const searchField = document.createElement('input');
searchField.type = 'search';
searchField.oninput = (e) => search.value = e.target.value;

const results = document.createElement('ul');
const status = document.createElement('div');
document.body.append(searchField, status, results);

effect(() => {
  status.textContent = search.value
  	? `${filtered.value.length} results for ${search}:`
  	: `${filtered.value.length} items:`;
  results.replaceChildren(...filtered.value.map(item => {
    const result = document.createElement('li');
    result.innerHTML = item.highlight ?? item.text;
    return result;
  }));
});
})();