const RenderedComponents = {}; class Component extends HTMLElement { refs = {}; resources = { fonts: { roboto: '', nunito: '', raleway: '' }, icons: { material: '', cryptofont: '', tabler: '' }, libs: { awoo: '' } }; constructor() { super(); this.shadow = this.attachShadow({ mode: 'open' }); } style() { return null; } template() { return null; } imports() { return []; } /** * Reference an external css file * OBS: External style loading not yet fully supported with web components, causes flickering. * @param {string} path * @returns {void} */ set stylePath(path) { this.resources.style = ``; } /** * Return all the imports that a component requested. * @returns {Array} imports */ get getResources() { const imports = this.imports(); if (this.resources?.style) imports.push(this.resources.style); return imports; } /** * Return inline style tag. * @returns {string} */ async loadStyles() { let html = this.getResources.join("\n"); if (this.style()) html += ``; return html; } /** * Build the component's HTML body. * @returns {string} html */ async buildHTML() { return await this.loadStyles() + await this.template(); } /** * Create a reference for manipulating DOM elements. * @returns {Proxy} */ createRef() { return new Proxy(this.refs, { get: (target, prop) => { const ref = target[prop]; const elems = this.shadow.querySelectorAll(ref); if (elems.length > 1) return elems; const element = elems[0]; if (!element) return ref; return element; }, set: (target, prop, value) => { this.shadow.querySelector(target[prop]).innerHTML = value; return true; } }); } async render() { this.shadow.innerHTML = await this.buildHTML(); this.refs = this.createRef(); RenderedComponents[this.localName] = this; } }