import {render} from "../vdom"; /** * The decorators proposal has changed since @babel implemented it. This code will need to change at some point... * THIS IS TOTALLY FIGGIN BROKEN!! valueMap used to be just value, but it turns out is not unique amongst decorated props. * (it appears to be run once per class definition, and thus multiple instances would share the same value-reference) */ export function State() { return function decorator(target){ let key = target.key; let descriptor = target.descriptor; let valueMap = new WeakMap(); 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 valueMap.get(this) }); descriptor['set'] = function(newValue){ let oldValue = descriptor.get.call(this); if(newValue!==oldValue){ valueMap.set(this,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){ valueMap.set(target, 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, old: this.#renderedVNode, }); this.#renderedVNode = newVNode; } this.#markedDirty=false; } markDirty() { if (!this.#markedDirty) { this.#markedDirty = requestAnimationFrame(() => this.update()); } } }