// Inspired by `make` by Matthew Crumley (silentmatt.com) https://stackoverflow.com/a/2947012 // Licensed under AGPL-3.0, check `LICENSE` for the full text. type ElementProperties = { [key in keyof HTMLElementTagNameMap[T]]: HTMLElementTagNameMap[T][key]; } & { [key in keyof HTMLElementEventMap as `@${key}`]: ( this: HTMLElement, ev: HTMLElementEventMap[key] ) => void; }; type DOMElem = Node | string; type WithClassOrId = | `${T}#${string}` | `${T}.${string}`; export default function $el( name: T | WithClassOrId, ...desc: [Partial>, ...DOMElem[]] | DOMElem[] ): HTMLElementTagNameMap[T] { // Take off ID or class from the name let elementName = name as string; let className = ""; let id = ""; if (name.includes("#")) { [elementName, id] = name.split("#"); } if (name.includes(".")) { [elementName, className] = name.split("."); } // Create the element and add the attributes (if found) const el = document.createElement(elementName as T); if (className) { el.className = className; } if (id) { el.id = id; } const attributes = desc[0]; if (typeof attributes === "object" && !(attributes instanceof Node)) { for (const attr in attributes) { const value = attributes[attr]; if (attr.startsWith("@")) { el.addEventListener(attr.substring(1), value); } else if (attr === "dataset") { for (const key in value) { el.dataset[key] = value[key]; } } else { el[attr] = value; } } desc.shift(); } for (const item of desc as DOMElem[]) { el.appendChild(item instanceof Node ? item : document.createTextNode(item)); } return el; }