diff --git a/test/components/webComponent.js b/test/components/webComponent.js index bbb9630..04ae0c0 100644 --- a/test/components/webComponent.js +++ b/test/components/webComponent.js @@ -1,64 +1,56 @@ import { el } from "../../index.js"; import { S } from "../../src/signals.js"; -const store= new WeakMap(); -Object.assign(S, { - customElementParams(_this){ - const observedProperties= store.get(_this.constructor).observedProperties; - observedProperties.forEach(p=> _this[p]); - return store.has(_this) ? store.get(_this) : getAttributes(_this); - }, - customElementPrototype(cls){ - } -}); +const { hasOwnProperty }= Object.prototype; +const store= attrsPropsToSignals([ "test" ]); /** * Compatible with `npx-wca test/components/webComponent.js` * */ export class CustomHTMLTestElement extends HTMLElement{ static get observedAttributes(){ - return [ "name" ]; + return [ "name", "pre-name" ]; } connectedCallback(){ - customElementRender(this, this.attachShadow({ mode: "open" }), this.render); + this.attachShadow({ mode: "open" }).append( + customElementRender(this, store.toRender(this), this.render) + ); } - render({ name, test }, host){ + render({ name, test, preName }, host){ host(on.connected(console.log)); - return el("p", { className: test, textContent: name }); + return el("p").append( + el("#text", { textContent: name }), + el("#text", { textContent: test }), + el("#text", { textContent: preName }), + ); } } customElementsAssign( CustomHTMLTestElement, reflectObservedAttributes, - lifecycleToEvents(true), - attrsPropsToSignals([ "test" ]) + lifecycleToEvents(false), + store.connect ); customElements.define("custom-test", CustomHTMLTestElement); -function customElementRender(_this, root, render){ +function customElementRender(_this, attrs, render){ const host= (...a)=> a.length ? a[0](_this) : _this; - const attrs= S.customElementParams ? S.customElementParams(_this) : getAttributes(_this); - root.appendChild(render(attrs, host)); -} -function getAttributes(_this){ - return Object.fromEntries(_this.getAttributeNames().map(n=> [ n, _this.getAttribute(n) ])); + return render(attrs, host); } /** @returns {HTMLElement} */ function customElementsAssign(class_base, ...automatize){ - automatize.forEach(a=> a(class_base, getStore)); - function getStore(t){ - if(store.has(t)) return store.get(t); - const s= {}; - store.set(t, s); - return s; - } + automatize.forEach(a=> a(class_base)); } function reflectObservedAttributes(c){ - for(const name of c.observedAttributes) - Reflect.defineProperty(c.prototype, name, { + for(const name of c.observedAttributes){ + const name_camel= name.replace(/([a-z])-([a-z])/g, (_, l, r)=> l+r.toUpperCase()); + if(hasOwnProperty.call(c.prototype, name_camel)) + continue; + Reflect.defineProperty(c.prototype, name_camel, { get(){ return this.getAttribute(name); }, set(value){ this.setAttribute(name, value); } }); + } } function lifecycleToEvents(is_attrs){ return function(c){ @@ -78,34 +70,51 @@ function lifecycleToEvents(is_attrs){ }; } function attrsPropsToSignals(props= []){ - return function(c, getStore){ - const store= getStore(c); - store.observedProperties= props; - wrapMethod(c.prototype, "attributeChangedCallback", function(target, thisArg, detail){ - const [ name, _, value ]= detail; - const s= getStore(thisArg); - if(s[name]) s[name](value); - else s[name]= S(value); - - target.apply(thisArg, detail); - }); - for(const name of props){ - Reflect.defineProperty(c.prototype, name, { - get(){ - const s= getStore(this); - if(s[name]) return s[name](); - const out= S(undefined); - s[name]= out; - return out(); - }, - set(value){ - const s= getStore(this); - if(s[name]) s[name](value); - else s[name]= S(value); - } + const store_attrs= new WeakMap(); + const store_props= new WeakMap(); + return { + toRender(target){ + const out= {}; + const sattrs= get(store_attrs, target); + target.constructor.observedAttributes.forEach(function(name){ + const name_camel= name.replace(/([a-z])-([a-z])/g, (_, l, r)=> l+r.toUpperCase()); + if(!hasOwnProperty.call(sattrs, name)) sattrs[name]= S(undefined); + out[name_camel]= sattrs[name]; }); + const sprops= get(store_props, target); + props.forEach(p=> !hasOwnProperty.call(sprops, p) && (sprops[p]= S(undefined))); + return Object.assign(out, sprops); + }, + connect(c){ + wrapMethod(c.prototype, "attributeChangedCallback", function(target, thisArg, detail){ + const [ name, _, value ]= detail; + const s= get(store_attrs, thisArg); + if(s[name]) s[name](value); + else s[name]= S(value); + + target.apply(thisArg, detail); + }); + for(const name of props){ + Reflect.defineProperty(c.prototype, name, { + get(){ + const s= get(store_props, this); + if(s[name]) return s[name](); + }, + set(value){ + const s= get(store_props, this); + if(s[name]) s[name](value); + else s[name]= S(value); + } + }); + } } }; + function get(store, t){ + if(store.has(t)) return store.get(t); + const s= {}; + store.set(t, s); + return s; + } } function wrapMethod(obj, method, apply){ obj[method]= new Proxy(obj[method] || (()=> {}), { apply });