Files
h-school/src/HSchool.Client/src/ui/dom.ts
T

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();
}