79 lines
2.7 KiB
JavaScript

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());
}
}
}