Spaces:
Running
Running
import { $el } from "rgthree/common/utils_dom.js"; | |
const CSS_STYLE_SHEETS = new Map<string, string>(); | |
const HTML_TEMPLATE_FILES = new Map<string, string>(); | |
/** | |
* Fetches the stylesheet for the component, matched by the element name (minus the "rgthree-" | |
* prefix). | |
*/ | |
async function getStyleSheet(name: string) { | |
if (!CSS_STYLE_SHEETS.has(name)) { | |
try { | |
const response = await fetch( | |
`rgthree/common/components/${name.replace("rgthree-", "").replace(/\-/g, "_")}.css`, | |
); | |
const text = await response.text(); | |
CSS_STYLE_SHEETS.set(name, text); | |
} catch (e) { | |
alert("Error loading rgthree custom component css."); | |
} | |
} | |
return CSS_STYLE_SHEETS.get(name)!; | |
} | |
/** | |
* Fetches the stylesheet for the component, matched by the element name (minus the "rgthree-" | |
* prefix). | |
*/ | |
async function getTemplateMarkup(name: string) { | |
if (!HTML_TEMPLATE_FILES.has(name)) { | |
try { | |
const response = await fetch( | |
`rgthree/common/components/${name.replace("rgthree-", "").replace(/\-/g, "_")}.html`, | |
); | |
const text = await response.text(); | |
HTML_TEMPLATE_FILES.set(name, text); | |
} catch (e) { | |
// alert("Error loading rgthree custom component markup."); | |
} | |
} | |
return HTML_TEMPLATE_FILES.get(name)!; | |
} | |
/** | |
* A base custom element. | |
*/ | |
export abstract class RgthreeCustomElement extends HTMLElement { | |
static NAME = "rgthree-override"; | |
static create<T extends RgthreeCustomElement>(): T { | |
if (this.name === "rgthree-override") { | |
throw new Error("Must override component NAME"); | |
} | |
if (!customElements.get(this.name)) { | |
customElements.define(this.NAME, this as unknown as CustomElementConstructor); | |
} | |
return document.createElement(this.NAME) as T; | |
} | |
protected connected: boolean = false; | |
protected shadow!: ShadowRoot; | |
protected readonly templates = new Map<string, HTMLTemplateElement>(); | |
onFirstConnected(): void { | |
// Optionally overridden. | |
}; | |
onReconnected(): void { | |
// Optionally overridden. | |
}; | |
onConnected(): void { | |
// Optionally overridden. | |
}; | |
onDisconnected(): void { | |
// Optionally overridden. | |
}; | |
async connectedCallback() { | |
const elementName = (this.constructor as any).NAME as string; | |
const wasConnected = this.connected; | |
if (!wasConnected) { | |
this.connected = true; | |
} | |
if (!this.shadow) { | |
const [stylesheet, markup] = await Promise.all([ | |
getStyleSheet(elementName), | |
getTemplateMarkup(elementName), | |
]); | |
if (markup) { | |
const temp = $el('div') | |
const templatesMarkup = markup.match(/<template[^]*?<\/template>/gm) || []; | |
for(const markup of templatesMarkup) { | |
temp.innerHTML = markup; | |
const template = temp.children[0]; | |
if (!(template instanceof HTMLTemplateElement)) { | |
throw new Error('Not a template element.'); | |
} | |
const id = template.getAttribute('id'); | |
if (!id) { | |
throw new Error('Not template id.'); | |
} | |
this.templates.set(id, template); | |
} | |
} | |
this.shadow = this.attachShadow({mode: "open"}); | |
const sheet = new CSSStyleSheet(); | |
sheet.replaceSync(stylesheet); | |
this.shadow.adoptedStyleSheets = [sheet]; | |
let template; | |
if (this.templates.has(elementName)) { | |
template = this.templates.get(elementName); | |
} else if (this.templates.has(elementName.replace('rgthree-', ''))) { | |
template = this.templates.get(elementName.replace('rgthree-', '')) | |
} | |
if (template) { | |
this.shadow.appendChild(template.content.cloneNode(true)); | |
} | |
this.onFirstConnected(); | |
} else { | |
this.onReconnected(); | |
} | |
this.onConnected(); | |
} | |
disconnectedCallback() { | |
this.connected = false; | |
this.onDisconnected() | |
} | |
} | |