/**
* Creates an HTML tag. Many helper functions are provided as shortcuts for
* creating commmon elements.
* @param name The tag name.
* @param children The children of the tag. Handles different types
* differently:
*
* 1. `HTMLElement`s and `Text`s are appended as is (Not cloned).
* 2. `Attr`s are cloned and then attached to the tag itself.
* 3. `EventListener`s are attached to the tag itself.
* 4. `Shadow`s are attached to the tag itself.
* 5. `Array`s are iterated through and handled as other children.
* 4. `Object`s are parsed as JSON objects of HTML attributes and
* attached to the tag itself.
* 5. All else is converted to `Text` and appended to the tag.
*
* @returns The created HTML tag.
*/
export const tag = (name, ...children) => {
const htmlTag = document.createElement(name);
const process = (unprocessedChildren) => {
for (let i = 0; i < unprocessedChildren.length; i++) {
const child = unprocessedChildren[i];
if (child instanceof HTMLElement || child instanceof Text) {
htmlTag.appendChild(child);
}
else if (child instanceof Attr) {
handleAttributeNode(htmlTag, child);
}
else if (child instanceof EventListener) {
htmlTag.addEventListener(child.type, child.callback, child.options);
}
else if (child instanceof Shadow) {
const shadowRoot = htmlTag.attachShadow({ mode: 'open' });
shadowRoot.adoptedStyleSheets = child.sheets;
for (let k = 0; k < child.children.length; k++) {
const childChild = child.children[k];
shadowRoot.appendChild(childChild);
}
}
else if (child instanceof Array) {
process(child);
}
else if ((child === null || child === void 0 ? void 0 : child.constructor) === Object) {
handleAttributeObject(htmlTag, child);
}
else if (child !== null && child !== undefined) {
htmlTag.appendChild(document.createTextNode(child));
}
}
};
process(children);
return htmlTag;
};
/**
* Adds an attribute node to a tag safely.
* @param htmlTag The HTML tag.
* @param attrNode The HTML attribute.
*/
const handleAttributeNode = (htmlTag, attrNode) => {
if (htmlTag.hasAttribute(attrNode.name)) {
const currentValue = htmlTag.getAttribute(attrNode.name);
htmlTag.setAttribute(attrNode.name, currentValue + ' ' + attrNode.value);
}
else {
htmlTag.setAttributeNode(attrNode.cloneNode());
}
};
/**
* Adds attributes to a tag from a JSON object.
* @param htmlTag The HTML tag.
* @param attrObj The attributes as a JSON object.
*/
const handleAttributeObject = (htmlTag, attrObj) => {
const keys = Object.keys(attrObj);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const newValue = attrObj[key] instanceof Array
? attrObj[key].join(' ')
: attrObj[key];
if (htmlTag.hasAttribute(key)) {
const currentValue = htmlTag.getAttribute(key);
htmlTag.setAttribute(key, currentValue + ' ' + newValue);
}
else {
htmlTag.setAttribute(key, newValue);
}
}
};
/**
* Creates an HTML attribute.
* @param key The attribute name.
* @param value The attribute value.
* @returns The attribute node.
*/
export const attr = (key, value) => {
const node = document.createAttribute(key);
node.value = value;
return node;
};
/**
* An event container. Serves only to represent a type/callback pair for a
* potential future `HTMLElement.addEventListener()`.
*/
export class EventListener {
/**
* @param type The event type.
* @param callback The callback function.
* @param options The event listener options.
*/
constructor(type, callback, options) {
this.type = type;
this.callback = callback;
this.options = options;
}
}
/**
* Creates a number of event containers for a callback function.
* @param types The event types separated by spaces.
* @param callback The callback function.
* @param options The event listener options.
* @returns The event containers.
*/
export const on = (types, callback, options) => {
return types
.split(' ')
.map((type) => new EventListener(type, callback, options));
};
/**
* A shadow root container. Serves only to represent the components that make
* up a potential future `HTMLElement.attachShadow()`.
*/
export class Shadow {
/**
* @param children The children of the shadow root.
* @param sheets The CSS stylesheets adopted by this shadow root.
*/
constructor(children, sheets) {
this.children = children;
this.sheets = sheets;
}
}
/**
* Creates a shadow root that can be attached to an element.
* @param children
* The children that the shadow root contains.
* @returns The created shadow root.
*/
export const shadow = (...children) => {
let components = [];
let sheets = [];
const process = (unprocessedChildren) => {
for (let i = 0; i < unprocessedChildren.length; i++) {
const child = unprocessedChildren[i];
if (child instanceof CSSStyleSheet) {
sheets.push(child);
}
else if (child instanceof HTMLElement || child instanceof Text) {
components.push(child);
}
else if (child instanceof Array) {
process(child);
}
else if (child !== null && child !== undefined) {
components.push(document.createTextNode(child));
}
}
};
process(children);
return new Shadow(components, sheets);
};
/**
* Creates a shortcut tag function.
* @param name The name of the tag.
* @param x Arguments that should be applied to every tag created
* with this shortcut.
* @returns The shortcut function.
*/
export const shortTag = (name, ...x) => (...y) => tag(name, ...x, ...y);
export const h1 = shortTag('h1');
export const h2 = shortTag('h2');
export const h3 = shortTag('h3');
export const h4 = shortTag('h4');
export const h5 = shortTag('h6');
export const h6 = shortTag('h6');
export const a = shortTag('a');
export const b = shortTag('b');
export const br = shortTag('br');
export const button = shortTag('button');
export const code = shortTag('code');
export const dd = shortTag('dd');
export const div = shortTag('div');
export const dl = shortTag('dl');
export const dt = shortTag('dt');
export const em = shortTag('em');
export const form = shortTag('form');
export const hr = shortTag('hr');
export const i = shortTag('i');
export const img = shortTag('img');
export const li = shortTag('li');
export const ol = shortTag('ol');
export const p = shortTag('p');
export const pre = shortTag('pre');
export const q = shortTag('q');
export const s = shortTag('s');
export const section = shortTag('section');
export const span = shortTag('span');
export const strong = shortTag('strong');
export const sub = shortTag('sub');
export const sup = shortTag('sup');
export const table = shortTag('table');
export const td = shortTag('td');
export const textarea = shortTag('textarea');
export const th = shortTag('th');
export const tr = shortTag('tr');
export const u = shortTag('u');
export const ul = shortTag('ul');
export const main = shortTag('main');
export const footer = shortTag('footer');
export const header = shortTag('header');
export const details = shortTag('details');
export const slot = shortTag('slot');