90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
/** Tiny helpers so the screens can build DOM without a framework or string templates. */
|
|
|
|
type Child = Node | string | null | undefined | false;
|
|
|
|
interface ElementOptions {
|
|
class?: string;
|
|
text?: string;
|
|
title?: string;
|
|
type?: string;
|
|
disabled?: boolean;
|
|
hidden?: boolean;
|
|
src?: string;
|
|
alt?: string;
|
|
placeholder?: string;
|
|
value?: string;
|
|
rows?: number;
|
|
autocomplete?: string;
|
|
maxlength?: string;
|
|
dataset?: Record<string, string>;
|
|
onClick?: (event: Event) => void;
|
|
onInput?: (event: Event) => void;
|
|
}
|
|
|
|
export function el<K extends keyof HTMLElementTagNameMap>(
|
|
tag: K,
|
|
options: ElementOptions = {},
|
|
...children: Child[]
|
|
): HTMLElementTagNameMap[K] {
|
|
const element = document.createElement(tag);
|
|
|
|
if (options.class !== undefined) element.className = options.class;
|
|
if (options.text !== undefined) element.textContent = options.text;
|
|
if (options.title !== undefined) element.title = options.title;
|
|
if (options.type !== undefined) element.setAttribute('type', options.type);
|
|
if (options.disabled !== undefined) element.toggleAttribute('disabled', options.disabled);
|
|
if (options.hidden !== undefined) element.toggleAttribute('hidden', options.hidden);
|
|
if (options.src !== undefined && 'src' in element) {
|
|
(element as HTMLImageElement).src = options.src;
|
|
}
|
|
|
|
if (options.alt !== undefined && 'alt' in element) {
|
|
(element as HTMLImageElement).alt = options.alt;
|
|
}
|
|
|
|
if (options.placeholder !== undefined && 'placeholder' in element) {
|
|
(element as HTMLInputElement | HTMLTextAreaElement).placeholder = options.placeholder;
|
|
}
|
|
|
|
if (options.autocomplete !== undefined && 'autocomplete' in element) {
|
|
(element as HTMLInputElement).autocomplete = options.autocomplete;
|
|
}
|
|
|
|
if (options.maxlength !== undefined && 'maxLength' in element) {
|
|
(element as HTMLInputElement).maxLength = Number.parseInt(options.maxlength, 10);
|
|
}
|
|
|
|
if (options.value !== undefined && 'value' in element) {
|
|
(element as HTMLInputElement | HTMLTextAreaElement).value = options.value;
|
|
}
|
|
|
|
if (options.rows !== undefined && element instanceof HTMLTextAreaElement) {
|
|
element.rows = options.rows;
|
|
}
|
|
|
|
if (options.onInput !== undefined) element.addEventListener('input', options.onInput);
|
|
|
|
if (options.onClick !== undefined) element.addEventListener('click', options.onClick);
|
|
|
|
for (const [key, value] of Object.entries(options.dataset ?? {})) {
|
|
element.dataset[key] = value;
|
|
}
|
|
|
|
append(element, children);
|
|
return element;
|
|
}
|
|
|
|
export function append(parent: Node, children: Child[]): void {
|
|
for (const child of children) {
|
|
if (child === null || child === undefined || child === false) {
|
|
continue;
|
|
}
|
|
|
|
parent.appendChild(typeof child === 'string' ? document.createTextNode(child) : child);
|
|
}
|
|
}
|
|
|
|
export function clear(element: Element): void {
|
|
element.replaceChildren();
|
|
}
|