import { render } from 'lit-html'; function requestRender(ctx: any) { if (ctx.isConnected) { if (ctx.__wait) { } else { ctx.__wait = true; requestAnimationFrame(() => { Promise.resolve(true); ctx.render(); ctx.__wait = false; }); } } } /** * @property decorator * todo add more options */ export function property(): Function { return function reg(_class: Function, prop: string): void { Object.defineProperty(_class, prop, { get: function() { return this['_' + prop]; }, set: function(x: any) { const oldValue = this['_' + prop]; this['_' + prop] = x; if (this.valuesChanged && oldValue !== x) { this.valuesChanged('property', prop, oldValue, x); } if (oldValue !== x) { requestRender(this); } } }); }; } /** * @attibute- decorator * //todo add option and allow user to set attibute to follow, and own validate if changed */ export function attribute(): Function { return function reg(_class: Function, prop: string): void { Object.defineProperty(_class, prop, { get: function() { return this['_' + prop]; }, set: function(x: any) { const oldValue = this['_' + prop]; this['_' + prop] = x; if (this.valuesChanged && oldValue !== x) { this.valuesChanged('property', prop, oldValue, x); } if (oldValue !== x) { requestRender(this); } } }); // replace uppercase with lower and add '-' const attribute = prop .replace(/([a-z])([A-Z])/g, '$1-$2') .replace(/\s+/g, '-') .toLowerCase(); //create a map so we can find it later if (!(_class)._observedAttributesMap) { (_class)._observedAttributesMap = new Map(); } (_class)._observedAttributesMap.set(attribute, prop); // add to observedAttributes if ((_class)._observedAttributes) { (_class)._observedAttributes.push(attribute); } else { (_class)._observedAttributes = []; (_class)._observedAttributes.push(attribute); } }; } export function customElement(elementName: string, extended?: ElementDefinitionOptions) { return function reg(elementClass: any) { Object.defineProperty(elementClass, 'observedAttributes', { get: function() { return elementClass.prototype['_observedAttributes']; } }); const base = class extends elementClass { constructor(){ super(); } render(...result: any[]) { render(super.render.call(this, ...result), this, { eventContext: this }); if (super.updated) { //delay so it actually get a chance to update setTimeout(() => { super.updated(); }); } } connectedCallback() { if (super.connectedCallback) { super.connectedCallback.call(this); } this.render(this); } disconnectedCallback() { if (super.disconnectedCallback) { super.disconnectedCallback.call(this); } } attributeChangedCallback(name: string, oldValue: string, newValue: string) { //get map const nameProp = this['_observedAttributesMap'].get(name); this[nameProp] = newValue || ''; // if normal attributeChanged is set if (super.attributeChangedCallback) { super.attributeChangedCallback.call(this, name, oldValue, newValue); } //if our simpler method is set if (super.valuesChangedMethod) { super.valuesChangedMethod('attribute', name, oldValue, newValue); } requestRender(this); } }; if (!customElements.get(elementName)) { if (extended) { customElements.define(elementName, base, extended); } else { customElements.define(elementName, base); } } else { if ((globalThis).hmrCache) { if (extended) { customElements.define(elementName, base, extended); } else { customElements.define(elementName, base); } } } }; }