Add custom elements
This commit is contained in:
parent
c7541db1d9
commit
e8bf135424
5 changed files with 205 additions and 1 deletions
11
module/apps/elements/Icon.mjs
Normal file
11
module/apps/elements/Icon.mjs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { OFTSVGLoader } from "./SVGLoader.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
Attributes:
|
||||||
|
@property {string} name - The name of the icon, takes precedence over the path
|
||||||
|
@property {string} path - The path of the icon file
|
||||||
|
*/
|
||||||
|
export class OFTIcon extends OFTSVGLoader {
|
||||||
|
static elementName = `oft-icon`;
|
||||||
|
static _stylePath = `icon.css`;
|
||||||
|
};
|
||||||
102
module/apps/elements/SVGLoader.mjs
Normal file
102
module/apps/elements/SVGLoader.mjs
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { filePath } from "../../consts.mjs";
|
||||||
|
import { StyledShadowElement } from "./StyledShadowElement.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
Attributes:
|
||||||
|
@property {string} name - The name of the icon, takes precedence over the path
|
||||||
|
@property {string} path - The path of the icon file
|
||||||
|
*/
|
||||||
|
export class OFTSVGLoader extends StyledShadowElement(HTMLElement) {
|
||||||
|
static elementName = `oft-svg`;
|
||||||
|
static formAssociated = false;
|
||||||
|
|
||||||
|
/* Stuff for the mixin to use */
|
||||||
|
static _stylePath = `svg-loader.css`;
|
||||||
|
|
||||||
|
|
||||||
|
static _cache = new Map();
|
||||||
|
#container;
|
||||||
|
/** @type {null | string} */
|
||||||
|
_name;
|
||||||
|
/** @type {null | string} */
|
||||||
|
_path;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.#container = document.createElement(`div`);
|
||||||
|
this._shadow.appendChild(this.#container);
|
||||||
|
};
|
||||||
|
|
||||||
|
_mounted = false;
|
||||||
|
async connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
if (this._mounted) { return };
|
||||||
|
|
||||||
|
this._name = this.getAttribute(`name`);
|
||||||
|
this._path = this.getAttribute(`path`);
|
||||||
|
|
||||||
|
/*
|
||||||
|
This converts all of the double-dash prefixed properties on the element to
|
||||||
|
CSS variables so that they don't all need to be provided by doing style=""
|
||||||
|
*/
|
||||||
|
for (const attrVar of this.attributes) {
|
||||||
|
if (attrVar.name?.startsWith(`var:`)) {
|
||||||
|
const prop = attrVar.name.replace(`var:`, ``);
|
||||||
|
this.style.setProperty(`--` + prop, attrVar.value);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Try to retrieve the icon if it isn't present, try the path then default to
|
||||||
|
the slot content, as then we can have a default per-icon usage
|
||||||
|
*/
|
||||||
|
let content;
|
||||||
|
if (this._name) {
|
||||||
|
content = await this.#getIcon(filePath(`assets/${this._name}.svg`));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._path && !content) {
|
||||||
|
content = await this.#getIcon(this._path);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
this.#container.appendChild(content.cloneNode(true));
|
||||||
|
};
|
||||||
|
|
||||||
|
this._mounted = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
if (!this._mounted) { return };
|
||||||
|
|
||||||
|
this._mounted = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
async #getIcon(path) {
|
||||||
|
// Cache hit!
|
||||||
|
if (this.constructor._cache.has(path)) {
|
||||||
|
return this.constructor._cache.get(path);
|
||||||
|
};
|
||||||
|
|
||||||
|
const r = await fetch(path);
|
||||||
|
switch (r.status) {
|
||||||
|
case 200:
|
||||||
|
case 201:
|
||||||
|
break;
|
||||||
|
default: return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const svg = this.#parseSVG(await r.text());
|
||||||
|
this.constructor._cache.set(path, 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`);
|
||||||
|
};
|
||||||
|
};
|
||||||
66
module/apps/elements/StyledShadowElement.mjs
Normal file
66
module/apps/elements/StyledShadowElement.mjs
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { filePath } from "../../consts.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {HTMLElement} Base
|
||||||
|
*/
|
||||||
|
export function StyledShadowElement(Base) {
|
||||||
|
return class extends Base {
|
||||||
|
/**
|
||||||
|
* The path to the CSS that is loaded
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
static _stylePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stringified CSS to use
|
||||||
|
* @type {Map<string, string>}
|
||||||
|
*/
|
||||||
|
static _styles = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The HTML element of the stylesheet
|
||||||
|
* @type {HTMLStyleElement}
|
||||||
|
*/
|
||||||
|
_style;
|
||||||
|
|
||||||
|
/** @type {ShadowRoot} */
|
||||||
|
_shadow;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._shadow = this.attachShadow({ mode: `open` });
|
||||||
|
this._style = document.createElement(`style`);
|
||||||
|
this._shadow.appendChild(this._style);
|
||||||
|
};
|
||||||
|
|
||||||
|
#mounted = false;
|
||||||
|
connectedCallback() {
|
||||||
|
if (this.#mounted) { return };
|
||||||
|
|
||||||
|
this._getStyles();
|
||||||
|
|
||||||
|
this.#mounted = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
if (!this.#mounted) { return };
|
||||||
|
this.#mounted = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
_getStyles() {
|
||||||
|
// TODO: Cache the CSS content in a more sane way that doesn't break
|
||||||
|
const stylePath = this.constructor._stylePath;
|
||||||
|
if (this.constructor._styles.has(stylePath)) {
|
||||||
|
this._style.innerHTML = this.constructor._styles.get(stylePath);
|
||||||
|
} else {
|
||||||
|
fetch(filePath(`styles/components/${stylePath}`))
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(t => {
|
||||||
|
this.constructor._styles.set(stylePath, t);
|
||||||
|
this._style.innerHTML = t;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
22
module/apps/elements/_index.mjs
Normal file
22
module/apps/elements/_index.mjs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { OFTIcon } from "./Icon.mjs";
|
||||||
|
import { OFTSVGLoader } from "./SVGLoader.mjs";
|
||||||
|
|
||||||
|
const components = [
|
||||||
|
OFTSVGLoader,
|
||||||
|
OFTIcon,
|
||||||
|
];
|
||||||
|
|
||||||
|
export function registerCustomComponents() {
|
||||||
|
(CONFIG.CACHE ??= {}).componentListeners ??= [];
|
||||||
|
for (const component of components) {
|
||||||
|
if (!window.customElements.get(component.elementName)) {
|
||||||
|
window.customElements.define(
|
||||||
|
component.elementName,
|
||||||
|
component,
|
||||||
|
);
|
||||||
|
if (component.formAssociated) {
|
||||||
|
CONFIG.CACHE.componentListeners.push(component.elementName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
// Settings
|
// Tweaks
|
||||||
import { preventMovementHistory } from "../tweaks/preventMovementHistory.mjs";
|
import { preventMovementHistory } from "../tweaks/preventMovementHistory.mjs";
|
||||||
import { toggleMouseBroadcast } from "../tweaks/toggleMouseBroadcast.mjs";
|
import { toggleMouseBroadcast } from "../tweaks/toggleMouseBroadcast.mjs";
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import { Logger } from "../utils/Logger.mjs";
|
import { Logger } from "../utils/Logger.mjs";
|
||||||
|
import { registerCustomComponents } from "../apps/elements/_index.mjs";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is only here for setting that **require** being registered during
|
This is only here for setting that **require** being registered during
|
||||||
|
|
@ -14,6 +15,8 @@ where they ideally should be implemented.
|
||||||
Hooks.on(`init`, () => {
|
Hooks.on(`init`, () => {
|
||||||
Logger.log(`Initializing`);
|
Logger.log(`Initializing`);
|
||||||
|
|
||||||
|
registerCustomComponents();
|
||||||
|
|
||||||
preventMovementHistory();
|
preventMovementHistory();
|
||||||
toggleMouseBroadcast();
|
toggleMouseBroadcast();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue