import { List, Node } from '../combinator/parser'; import { rnd0Z } from 'spica/random'; import { define } from 'typed-dom/dom'; export function* unwrap(nodes: List> | undefined): Iterable { if (nodes === undefined) return; for (const node of nodes) { yield node.value; } } export function invalid( syntax: string, type: string, message: string, ): Record { return { 'data-invalid-syntax': syntax, 'data-invalid-type': type, 'data-invalid-message': message, }; } export function markInvalid( el: N, syntax: string, type: string, message: string, ): N { assert(!message.endsWith('.')); return define(el, { class: void el.classList.add('invalid'), 'data-invalid-syntax': syntax, 'data-invalid-type': type, 'data-invalid-message': message, }); } export function unmarkInvalid(el: N): N { return define(el, { class: void el.classList.remove('invalid'), 'data-invalid-syntax': null, 'data-invalid-type': null, 'data-invalid-message': null, }); } export function stringify(nodes: Iterable): string { let acc = ''; for (const node of nodes) { if (typeof node === 'string') { assert(!node.includes('\n')); acc += node; } else { assert(!node.matches('br') && !node.querySelector('br')); // NOTE: Doesn't reflect line breaks. acc += node.innerText; } } return acc; } export function randomID(): string { return `random-${rnd0Z(6)}`; } export function collect(target: ParentNode, selector:string): HTMLElement[] { const acc = []; for (let el = target.firstElementChild; el; el = el?.nextElementSibling) { if (!el.matches(selector)) continue; acc.push(el as HTMLElement); } return acc; }