Live Filter
This example filters an existing list as the user types, which is a good fit for HTML-first interfaces where the list already exists in the markup.
Live Demo
Section titled “Live Demo”<section data-filter> <label> Search <input type="search" data-filter-input /> </label>
<ul data-filter-list> <li data-filter-item>Alpha</li> <li data-filter-item>Beta</li> <li data-filter-item>Gamma</li> </ul></section>Component
Section titled “Component”import { defineComponent } from "ornata";
export const LiveFilter = defineComponent({ name: "LiveFilter", state: { query: { default: "" }, }, elements: { input: { resolve(root) { return root.querySelector("[data-filter-input]"); }, }, items: { queryAll: "[data-filter-item]" }, }, render: { input() { return { events: { input: (event) => { const target = event.currentTarget;
if (!(target instanceof HTMLInputElement)) return;
this.state.query = target.value; }, }, }; }, items({ index }) { const item = this.elements.items[index ?? 0]; const text = item?.textContent?.toLowerCase() || ""; const matches = text.includes(this.state.query.toLowerCase());
return { attributes: { hidden: !matches, }, }; }, },});import { defineComponent } from "ornata";
export const LiveFilter = defineComponent<{ state: { query: string; }; elements: { input: HTMLInputElement | null; items: Element[]; };}>({ name: "LiveFilter", state: { query: { default: "" }, }, elements: { input: { resolve(root) { return root.querySelector("[data-filter-input]"); }, }, items: { queryAll: "[data-filter-item]" }, }, render: { input() { return { events: { input: (event) => { const target = event.currentTarget as HTMLInputElement; this.state.query = target.value; }, }, }; }, items({ index }) { const item = this.elements.items[index ?? 0]; const text = item?.textContent?.toLowerCase() || ""; const matches = text.includes(this.state.query.toLowerCase());
return { attributes: { hidden: !matches, }, }; }, },});Why this example matters
Section titled “Why this example matters”This example highlights a nice Ornata pattern:
resolve()gives the component one explicit place to describe its DOM contractqueryAllhandles the list collection cleanly- the DOM lookup contract is defined once, then reused by render code
LiveFilter.mount("[data-filter]");LiveFilter.mount("[data-filter]");