mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-02-24 01:22:26 +01:00
135 lines
3.5 KiB
JavaScript
135 lines
3.5 KiB
JavaScript
const REG_KEY = `registered as custom element`;
|
|
|
|
// helper function
|
|
function NotImplemented(instance, fname) {
|
|
console.warn(
|
|
`missing implementation for ${fname}(...data) in ${instance.__proto__.constructor.name}`
|
|
);
|
|
}
|
|
|
|
// helper function for turning "ClassName" into "class-name"
|
|
function getElementTagName(cls) {
|
|
return cls.prototype.constructor.name.replace(
|
|
/([A-Z])([a-z])/g,
|
|
(a, b, c, d) => {
|
|
const r = `${b.toLowerCase()}${c}`;
|
|
return d > 0 ? `-${r}` : r;
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* This is an enrichment class to make working with custom elements
|
|
* actually pleasant, rather than a ridiculous exercise in figuring
|
|
* out a low-level spec.
|
|
*/
|
|
class CustomElement extends HTMLElement {
|
|
static register(cls) {
|
|
if (!cls[REG_KEY]) {
|
|
const tagName = cls.tagName || getElementTagName(cls);
|
|
customElements.define(tagName, cls);
|
|
cls[REG_KEY] = true;
|
|
return customElements.whenDefined(tagName);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
static get tagName() {
|
|
return getElementTagName(this);
|
|
}
|
|
|
|
constructor(options = {}) {
|
|
super();
|
|
|
|
if (!customElements.resolveScope) {
|
|
customElements.resolveScope = function(scope) {
|
|
try {
|
|
return scope.getRootNode().host;
|
|
} catch (e) {
|
|
console.warn(e);
|
|
}
|
|
return window;
|
|
}
|
|
}
|
|
|
|
this._options = options;
|
|
|
|
const route = {
|
|
childList: (record) => {
|
|
this.handleChildChanges(
|
|
Array.from(record.addedNodes),
|
|
Array.from(record.removedNodes)
|
|
);
|
|
this.render();
|
|
},
|
|
attributes: (record) => {
|
|
this.handleAttributeChange(record.attributeName, record.oldValue, this.getAttribute(record.attributeName));
|
|
this.render();
|
|
},
|
|
};
|
|
|
|
// Set up a mutation observer because there are no custom
|
|
// element lifecycle functions for changes to the childNodes
|
|
// nodelist.
|
|
|
|
this._observer = new MutationObserver((records) => {
|
|
if (this.isConnected) {
|
|
records.forEach((record) => {
|
|
// console.log(record);
|
|
route[record.type](record);
|
|
});
|
|
}
|
|
});
|
|
|
|
this._observer.observe(this, {
|
|
childList: true,
|
|
attributes: true,
|
|
});
|
|
|
|
// Set up an open shadow DOM, because the web is open,
|
|
// and hiding your internals is ridiculous.
|
|
|
|
const shadowProps = {
|
|
mode: `open`,
|
|
delegatesFocus: !!this._options.focus,
|
|
};
|
|
|
|
this._shadow = this.attachShadow(shadowProps);
|
|
this._style = document.createElement(`style`);
|
|
this._style.textContent = this.getStyle();
|
|
|
|
if (this._options.header !== false)
|
|
this._header = document.createElement(`header`);
|
|
if (this._options.slot !== false && this._options.void !== true)
|
|
this._slot = document.createElement(`slot`);
|
|
if (this._options.footer !== false)
|
|
this._footer = document.createElement(`footer`);
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.render();
|
|
}
|
|
|
|
handleChildChanges(added, removed) {
|
|
if (!this._options.void) NotImplemented(this, `handleChildChanges`);
|
|
}
|
|
|
|
handleAttributeChange(name, oldValue, newValue) {
|
|
NotImplemented(this, `handleAttributeChange`);
|
|
}
|
|
|
|
getStyle() {
|
|
return ``;
|
|
}
|
|
|
|
render() {
|
|
this._shadow.innerHTML = ``;
|
|
this._shadow.append(this._style);
|
|
if (this._options.header !== false) this._shadow.append(this._header);
|
|
if (this._options.slot !== false) this._shadow.append(this._slot);
|
|
if (this._options.footer !== false) this._shadow.append(this._footer);
|
|
}
|
|
}
|
|
|
|
export { CustomElement };
|