UNPKG

@zeix/ui-element

Version:

UIElement - minimal reactive framework based on Web Components

132 lines (125 loc) 3.52 kB
import { type Component, type SignalProducer, component, first, on, selection, setAttribute, setProperty, setText, } from "../../../"; import type { InputButtonProps } from "../input-button/input-button"; import type { InputCheckboxProps } from "../input-checkbox/input-checkbox"; import type { InputFieldProps } from "../input-field/input-field"; import type { InputRadiogroupProps } from "../input-radiogroup/input-radiogroup"; export type TodoAppProps = { active: Component<InputCheckboxProps>[]; completed: Component<InputCheckboxProps>[]; }; export default component( "todo-app", { active: ((el) => selection<Component<InputCheckboxProps>>( el, "input-checkbox:not([checked])", )) as SignalProducer<HTMLElement, Component<InputCheckboxProps>[]>, completed: ((el) => selection<Component<InputCheckboxProps>>( el, "input-checkbox[checked]", )) as SignalProducer<HTMLElement, Component<InputCheckboxProps>[]>, }, (el) => { const input = el.querySelector<Component<InputFieldProps>>("input-field"); if (!input) throw new Error("No input field found"); const template = el.querySelector("template"); if (!template) throw new Error("No template found"); const list = el.querySelector("ol"); if (!list) throw new Error("No list found"); return [ // Control todo input form first<TodoAppProps, Component<InputButtonProps>>( ".submit", setProperty("disabled", () => !input.length), ), first( "form", on("submit", (e: Event) => { e.preventDefault(); queueMicrotask(() => { const value = input.value.toString().trim(); if (!value) return; const li = document.importNode( template.content, true, ).firstElementChild; if (!(li instanceof HTMLLIElement)) throw new Error( "Invalid template for list item; expected <li>", ); li .querySelector("slot") ?.replaceWith(String(input.value)); list.append(li); input.clear(); }); }), ), // Control todo list first( "ol", setAttribute( "filter", () => el.querySelector<Component<InputRadiogroupProps>>( "input-radiogroup", )?.value ?? "all", ), on("click", (e: Event) => { const target = e.target as HTMLElement; if (target.localName === "button") target.closest("li")!.remove(); }), ), // Update count elements first( ".count", setText(() => String(el.active.length)), ), first<TodoAppProps, HTMLElement>( ".singular", setProperty("hidden", () => el.active.length > 1), ), first<TodoAppProps, HTMLElement>( ".plural", setProperty("hidden", () => el.active.length === 1), ), first<TodoAppProps, HTMLElement>( ".remaining", setProperty("hidden", () => !el.active.length), ), first<TodoAppProps, HTMLElement>( ".all-done", setProperty("hidden", () => !!el.active.length), ), // Control clear-completed button first<TodoAppProps, Component<InputButtonProps>>( ".clear-completed", setProperty("disabled", () => !el.completed.length), setProperty("badge", () => el.completed.length > 0 ? String(el.completed.length) : "", ), on("click", () => { const items = Array.from(el.querySelectorAll("ol li")); for (let i = items.length - 1; i >= 0; i--) { const task = items[i].querySelector("input-checkbox"); if (task?.checked) items[i].remove(); } }), ), ]; }, );