2023-08-23 16:24:29 +00:00
|
|
|
// 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<T extends keyof HTMLElementTagNameMap> = {
|
2024-10-12 21:29:48 +00:00
|
|
|
[key in keyof HTMLElementTagNameMap[T]]:
|
|
|
|
| HTMLElementTagNameMap[T][key]
|
|
|
|
| string;
|
2023-08-23 16:24:29 +00:00
|
|
|
} & {
|
2024-10-12 21:29:48 +00:00
|
|
|
[key in keyof HTMLElementEventMap as `@${key}`]: (
|
|
|
|
this: HTMLElement,
|
|
|
|
ev: HTMLElementEventMap[key],
|
|
|
|
) => void;
|
2023-08-23 16:24:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
type DOMElem = Node | string;
|
|
|
|
|
|
|
|
type WithClassOrId<T extends keyof HTMLElementTagNameMap> =
|
2024-10-12 21:29:48 +00:00
|
|
|
| `${T}#${string}`
|
|
|
|
| `${T}.${string}`;
|
2023-08-23 16:24:29 +00:00
|
|
|
|
2023-08-23 17:21:22 +00:00
|
|
|
export function $el<T extends keyof HTMLElementTagNameMap>(
|
2024-10-12 21:29:48 +00:00
|
|
|
name: T | WithClassOrId<T>,
|
|
|
|
...desc: [Partial<ElementProperties<T>>, ...DOMElem[]] | DOMElem[]
|
2023-08-23 16:24:29 +00:00
|
|
|
): HTMLElementTagNameMap[T] {
|
2024-10-12 21:29:48 +00:00
|
|
|
// Take off ID or class from the name
|
|
|
|
let elementName = name as string;
|
|
|
|
let className = "";
|
|
|
|
let id = "";
|
2023-08-23 16:24:29 +00:00
|
|
|
|
2024-10-12 21:29:48 +00:00
|
|
|
if (name.includes("#")) {
|
|
|
|
[elementName, id] = name.split("#");
|
|
|
|
}
|
2023-08-23 16:24:29 +00:00
|
|
|
|
2024-10-12 21:29:48 +00:00
|
|
|
if (name.includes(".")) {
|
|
|
|
[elementName, className] = name.split(".");
|
|
|
|
}
|
2023-08-23 16:24:29 +00:00
|
|
|
|
2024-10-12 21:29:48 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2023-08-23 16:24:29 +00:00
|
|
|
|
2024-10-12 21:29:48 +00:00
|
|
|
const attributes = desc[0];
|
|
|
|
if (typeof attributes === "object" && !(attributes instanceof Node)) {
|
|
|
|
for (const attr in attributes) {
|
|
|
|
const value = (attributes as Record<string, unknown>)[attr];
|
|
|
|
if (attr.startsWith("@")) {
|
|
|
|
el.addEventListener(
|
|
|
|
attr.substring(1),
|
|
|
|
value as EventListenerOrEventListenerObject,
|
|
|
|
);
|
|
|
|
} else if (attr === "dataset") {
|
|
|
|
for (const key in value as Record<string, unknown>) {
|
|
|
|
el.dataset[key] = (value as Record<string, string>)[key];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
el[attr as keyof HTMLElementTagNameMap[T]] =
|
|
|
|
value as HTMLElementTagNameMap[T][keyof HTMLElementTagNameMap[T]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
desc.shift();
|
|
|
|
}
|
2023-08-23 16:24:29 +00:00
|
|
|
|
2024-10-12 21:29:48 +00:00
|
|
|
for (const item of desc as DOMElem[]) {
|
|
|
|
el.appendChild(item instanceof Node ? item : document.createTextNode(item));
|
|
|
|
}
|
2023-08-23 16:24:29 +00:00
|
|
|
|
2024-10-12 21:29:48 +00:00
|
|
|
return el;
|
2023-08-23 16:24:29 +00:00
|
|
|
}
|
2023-08-23 17:21:22 +00:00
|
|
|
|
|
|
|
export default $el;
|