import {render} from "../vdom"; /** * The decorators proposal has changed since @babel implemented it. This code will need to change at some point... */ export function State() { return function decorator(target){ let key = target.key; let descriptor = target.descriptor; let value = undefined; let {get: oldGet, set: oldSet} = descriptor; // Add a getter/setter or replace if they're already there with something that intercepts it (this gets a whole lot easyer in the new proposal if i'm not mistaken) descriptor['get'] = oldGet || (function(){ return value }); descriptor['set'] = function(newValue){ if(newValue!==descriptor.get.call(this)){ value = newValue; this.markDirty && this.markDirty(); } if(oldSet) return oldSet.call(this, newValue); }; // CAUTION: this is dangerous. We need intend to conver regular fields to get/set methods here. delete descriptor.writable; target.kind = 'method'; // update to get and set if need be.. // CAUTION: this is again dangerous, the initialize function should be called right before the constructor, but after it was fully defined. if(target.initializer){ value = target.initializer(target); delete target.initializer; } return target; } } /** * This CustomElement class is to avoid having to do an ugly workaround in every custom-element: * Which would be replacing 'HTMLElement' with '(class extends HTMLElement{})' * * Also, it is a good starting point for implementing render() functionality, listening to props, state changes, events and whatnot (use decorators) */ export class CustomElement extends HTMLElement { connectedCallback() { this.update(); } disconnectedCallback(){ } #markedDirty; #renderedVNode; update(){ if (this.render) { let newVNode = this.render(); render(newVNode, { host: this, oldVNode: this.#renderedVNode }); this.#renderedVNode = newVNode; } this.#markedDirty=false; } markDirty() { if (!this.#markedDirty) { this.#markedDirty = requestAnimationFrame(() => this.update()); } } }