Add Hot Module Replacement for SVG icons using the custom component

This commit is contained in:
Oliver-Akins 2024-04-12 23:23:42 -06:00
parent f15f3a4456
commit fde3881653
2 changed files with 29 additions and 9 deletions

View file

@ -13,7 +13,9 @@ export class DotDungeonIcon extends HTMLElement {
static _cache = new Map(); static _cache = new Map();
#style; #style;
#container; #container;
/** @type {null | string} */
_name; _name;
/** @type {null | string} */
_path; _path;
constructor() { constructor() {
@ -25,7 +27,9 @@ export class DotDungeonIcon extends HTMLElement {
this.#shadow.appendChild(this.#container); this.#shadow.appendChild(this.#container);
}; };
_mounted = false;
async connectedCallback() { async connectedCallback() {
if (this._mounted) return;
this._name = this.getAttribute(`name`); this._name = this.getAttribute(`name`);
this._path = this.getAttribute(`path`); this._path = this.getAttribute(`path`);
@ -58,20 +62,31 @@ export class DotDungeonIcon extends HTMLElement {
the slot content, as then we can have a default per-icon usage the slot content, as then we can have a default per-icon usage
*/ */
let content; let content;
// TODO: Make name request
if (this._name) { if (this._name) {
content = await this.#getIcon(`./systems/dotdungeon/assets/${this._name}.svg`); content = await this.#getIcon(`./systems/dotdungeon/assets/${this._name}.svg`);
}; };
// TODO: make path request
if (this._path && !content) { if (this._path && !content) {
content = await this.#getIcon(this._path); content = await this.#getIcon(this._path);
}; };
// TODO: insert content into DOM
if (content) { if (content) {
this.#container.appendChild(content); this.#container.appendChild(content.cloneNode(true));
}; };
/*
This is so that when we get an HMR event from Foundry we can appropriately
handle it using our logic to update the component and the icon cache.
*/
Hooks.on(`dd-hmr:svg`, (iconName, data) => {
if (this._name === iconName || this._path?.endsWith(data.path)) {
const svg = this.#parseSVG(data.content);
DotDungeonIcon._cache.set(iconName, svg);
this.#container.replaceChildren(svg.cloneNode(true));
};
});
this._mounted = true;
}; };
#embedStyles() { #embedStyles() {
@ -98,10 +113,15 @@ export class DotDungeonIcon extends HTMLElement {
}; };
console.debug(`.dungeon | Adding icon ${path} to the cache`); console.debug(`.dungeon | Adding icon ${path} to the cache`);
const temp = document.createElement(`div`); const svg = this.#parseSVG(await r.text());
temp.innerHTML = await r.text();
const svg = temp.querySelector(`svg`);
DotDungeonIcon._cache.set(path, svg); DotDungeonIcon._cache.set(path, svg);
return svg; return svg;
}; };
/** Takes an SVG string and returns it as a DOM node */
#parseSVG(content) {
const temp = document.createElement(`div`);
temp.innerHTML = content;
return temp.querySelector(`svg`);
}
}; };

View file

@ -3,8 +3,8 @@ import * as hbs from "../handlebars.mjs";
const loaders = { const loaders = {
svg(data) { svg(data) {
const iconName = data.path.split(`/`).slice(-1)[0].slice(0, -4); const iconName = data.path.split(`/`).slice(-1)[0].slice(0, -4);
console.log(`.dungeon | hot-reloading icon: ${iconName}`); console.debug(`.dungeon | hot-reloading icon: ${iconName}`);
CONFIG.CACHE.icons[iconName] = data.content; Hooks.call(`dd-hmr:svg`, iconName, data);
}, },
hbs(data) { hbs(data) {
if (!hbs.partials.some(p => data.path.endsWith(p))) { if (!hbs.partials.some(p => data.path.endsWith(p))) {