UNPKG

@zeix/ui-element

Version:

UIElement - a HTML-first library for reactive Web Components

125 lines (117 loc) 2.81 kB
import { type Component, component, fromSelector, on, pass, read, requireDescendant, setAttribute, setText, show, } from '../../..' import type { BasicButtonProps } from '../basic-button/basic-button' import type { FormCheckboxProps } from '../form-checkbox/form-checkbox' import '../form-textbox/form-textbox' export type ModuleTodoProps = { readonly active: HTMLElement[] readonly completed: HTMLElement[] } export default component( 'module-todo', { active: fromSelector('form-checkbox:not([checked])'), completed: fromSelector('form-checkbox[checked]'), }, (el, { first }) => { const textbox = requireDescendant(el, 'form-textbox') const template = requireDescendant(el, 'template') const list = requireDescendant(el, 'ol') const filter = el.querySelector('form-radiogroup') return [ // Control todo input form first<Component<BasicButtonProps>>( '.submit', pass({ disabled: () => !textbox.length, }), ), first( 'form', on('submit', (e: Event) => { e.preventDefault() queueMicrotask(() => { const value = textbox.value.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(value) list.append(li) textbox.clear() }) }), ), // Control todo list first( 'ol', setAttribute('filter', read(filter, 'value', 'all')), on('click', (e: Event) => { const target = e.target as HTMLElement if (target.closest('button')) target.closest('li')!.remove() }), ), // Update count elements first( '.count', setText(() => String(el.active.length)), ), first( '.singular', show(() => el.active.length === 1), ), first( '.plural', show(() => el.active.length > 1), ), first( '.remaining', show(() => !!el.active.length), ), first( '.all-done', show(() => !el.active.length), ), // Control clear-completed button first<Component<BasicButtonProps>>( '.clear-completed', pass({ disabled: () => !el.completed.length, 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< HTMLElement & FormCheckboxProps >('form-checkbox') if (task?.checked) items[i].remove() } }), ), ] }, ) declare global { interface HTMLElementTagNameMap { 'module-todo': Component<ModuleTodoProps> } }