1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2024-11-23 00:59:38 +01:00
This commit is contained in:
Jan Andrle 2024-01-04 16:05:41 +01:00
parent eb920f7bbd
commit 89f9880277
Signed by: jaandrle
GPG Key ID: B3A25AED155AFFAB
6 changed files with 42 additions and 17 deletions

View File

@ -7,7 +7,7 @@ export function mnemonic(){
el("code", "O(<value>)"), " — observable: reactive value", el("code", "O(<value>)"), " — observable: reactive value",
), ),
el("li").append( el("li").append(
el("code", "O(()=> <computation>)"), " — observable: reactive value dependent on calculation using other observables", el("code", "O(()=> <computation>)"), " — read-only observable: reactive value dependent on calculation using other observables",
), ),
el("li").append( el("li").append(
el("code", "O.on(<observable>, <listener>[, <options>])"), " — listen to the observable value changes", el("code", "O.on(<observable>, <listener>[, <options>])"), " — listen to the observable value changes",

View File

@ -88,7 +88,9 @@ export function page({ pkg, info }){
el("code", "ariaset"), " and ", el("code", "classList"), "." el("code", "ariaset"), " and ", el("code", "classList"), "."
), ),
el("p").append( el("p").append(
"For computation, you can use the derived observable (see above) like ", el("code", "assign(element, { textContent: O(()=> 'Hello '+WorldObservable()) })"), "." "For computation, you can use the “derived observable” (see above) like ", el("code", "assign(element, { textContent: O(()=> 'Hello '+WorldObservable()) })"), ".",
" ",
"This is read-only observable its value is computed based on given function and updated when any observable used in the function changes."
), ),
el("p").append( el("p").append(
"To represent part of the template filled dynamically based on the observable value use ", el("code", "O.el(observable, DOMgenerator)"), ".", "To represent part of the template filled dynamically based on the observable value use ", el("code", "O.el(observable, DOMgenerator)"), ".",

View File

@ -1,4 +1,4 @@
import { style, el, O } from '../exports.js'; import { style, el, O, isObservable } from '../exports.js';
const className= style.host(thirdParty).css` const className= style.host(thirdParty).css`
:host { :host {
color: green; color: green;
@ -22,12 +22,13 @@ export function thirdParty(){
// Array.from((new URL(location)).searchParams.entries()) // Array.from((new URL(location)).searchParams.entries())
// .forEach(([ key, value ])=> O.action(store, "set", key, value)); // .forEach(([ key, value ])=> O.action(store, "set", key, value));
// O.on(store, data=> history.replaceState("", "", "?"+(new URLSearchParams(JSON.parse(JSON.stringify(data)))).toString())); // O.on(store, data=> history.replaceState("", "", "?"+(new URLSearchParams(JSON.parse(JSON.stringify(data)))).toString()));
useAdapter(store, store_adapter, { useStore(store_adapter, {
onread(data){ onread(data){
Array.from(data.entries()) Array.from(data.entries())
.forEach(([ key, value ])=> O.action(store, "set", key, value)); .forEach(([ key, value ])=> O.action(store, "set", key, value));
return store;
} }
}); })();
return el("input", { return el("input", {
className, className,
value: store().value(), value: store().value(),
@ -36,9 +37,14 @@ export function thirdParty(){
}); });
} }
function useAdapter(observable, adapter, { onread, onbeforewrite }= {}){ function useStore(adapter_in, { onread, onbeforewrite }= {}){
if(!onread) onread= observable; const adapter= typeof adapter_in === "function" ? { read: adapter_in } : adapter_in;
if(!onread) onread= O;
if(!onbeforewrite) onbeforewrite= data=> JSON.parse(JSON.stringify(data)); if(!onbeforewrite) onbeforewrite= data=> JSON.parse(JSON.stringify(data));
onread(adapter.read()); //TODO OK as synchronous return function useStoreInner(data_read){
O.on(observable, data=> adapter.write(onbeforewrite(data))); const observable= onread(adapter.read(data_read)); //TODO OK as synchronous
if(adapter.write && isObservable(observable))
O.on(observable, data=> adapter.write(onbeforewrite(data)));
return observable;
};
} }

View File

@ -12,12 +12,13 @@ export class CustomHTMLTestElement extends HTMLElement{
} }
connectedCallback(){ connectedCallback(){
if(!this.hasAttribute("pre-name")) this.setAttribute("pre-name", "default"); if(!this.hasAttribute("pre-name")) this.setAttribute("pre-name", "default");
console.log(observedAttributes(this));
this.attachShadow({ mode: "open" }).append( this.attachShadow({ mode: "open" }).append(
customElementRender(this, this.render) customElementRender(this, this.render)
); );
} }
render({ test }){ render(test){
console.log(scope.state); console.log(scope.state);
scope.host( scope.host(
on.connected(()=> console.log(CustomHTMLTestElement)), on.connected(()=> console.log(CustomHTMLTestElement)),

View File

@ -1,10 +1,11 @@
import { scope } from "./dom.js"; import { scope } from "./dom.js";
export function customElementRender(custom_element, render, props= custom_element){ export function customElementRender(custom_element, render, props= observedAttributes){
scope.push({ scope.push({
scope: custom_element, scope: custom_element,
host: (...c)=> c.length ? c.forEach(c=> c(custom_element)) : custom_element, host: (...c)=> c.length ? c.forEach(c=> c(custom_element)) : custom_element,
custom_element custom_element
}); });
if(typeof props==="function") props= props(custom_element);
const out= render.call(custom_element, props); const out= render.call(custom_element, props);
scope.pop(); scope.pop();
return out; return out;
@ -31,3 +32,17 @@ export { lifecycleToEvents as customElementWithDDE };
function wrapMethod(obj, method, apply){ function wrapMethod(obj, method, apply){
obj[method]= new Proxy(obj[method] || (()=> {}), { apply }); obj[method]= new Proxy(obj[method] || (()=> {}), { apply });
} }
function observedAttribute(instance, name){
const out= (...args)=> !args.length
? instance.getAttribute(name)
: instance.setAttribute(name, ...args);
out.attribute= name;
return out;
}
export function observedAttributes(instance){
const { observedAttributes= [] }= instance.constructor;
return observedAttributes
.map(name=> [ name.replace(/-./g, x=> x[1].toUpperCase()), name ])
.reduce((out, [ key, name ])=> ( Reflect.set(out, key, observedAttribute(instance, name)), out ), {});
}

View File

@ -18,16 +18,16 @@ const stack_watch= [];
const deps= new WeakMap(); const deps= new WeakMap();
export function observable(value, actions){ export function observable(value, actions){
if(typeof value!=="function") if(typeof value!=="function")
return create(value, actions); return create(false, value, actions);
if(isObservable(value)) return value; if(isObservable(value)) return value;
const out= create(); const out= create(true);
const contextReWatch= function(){ const contextReWatch= function(){
const [ origin, ...deps_old ]= deps.get(contextReWatch); const [ origin, ...deps_old ]= deps.get(contextReWatch);
deps.set(contextReWatch, new Set([ origin ])); deps.set(contextReWatch, new Set([ origin ]));
stack_watch.push(contextReWatch); stack_watch.push(contextReWatch);
out(value()); write(out, value());
stack_watch.pop(); stack_watch.pop();
if(!deps_old.length) return; if(!deps_old.length) return;
@ -180,9 +180,10 @@ function removeObservablesFromElements(o, listener, ...notes){
}); });
} }
function create(value, actions){ function create(is_readonly, value, actions){
const o= (...value)=> const o= is_readonly
value.length ? write(o, ...value) : read(o); ? ()=> read(o)
: (...value)=> value.length ? write(o, ...value) : read(o);
return toObservable(o, value, actions); return toObservable(o, value, actions);
} }
const protoSigal= Object.assign(Object.create(null), { const protoSigal= Object.assign(Object.create(null), {