75 lines
2.4 KiB
JavaScript

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