1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2024-11-24 17:39:36 +01:00

Add signal listener, reactive and wrap object functions

This commit is contained in:
Jan Andrle 2023-08-25 20:45:02 +02:00
parent 5417ecea19
commit 77cb9cf626
Signed by: jaandrle
GPG Key ID: B3A25AED155AFFAB
3 changed files with 79 additions and 24 deletions

View File

@ -1,6 +1,6 @@
import { isSignal } from './signals.js'; import { isSignal, addSignalListener } from './signals.js';
export function on(event, listener, options){ export function on(event, listener, options){
if(isSignal(event)) return event.listeners.add(listener); if(isSignal(event)) return addSignalListener(event, listener);
return element=> element.addEventListener(event, listener, options); return element=> element.addEventListener(event, listener, options);
} }
export function dispatch(event, detail){ export function dispatch(event, detail){

View File

@ -3,15 +3,41 @@ export function isSignal(candidate){
try{ return Reflect.has(candidate, mark); } try{ return Reflect.has(candidate, mark); }
catch(e){ return false; } catch(e){ return false; }
} }
export function S(signal){ export function addSignalListener(signal, listener){
if(typeof signal!=="function") return signal[mark].listeners.add(listener);
return create(signal); }
if(isSignal(signal)) return signal; export function S(value){
if(typeof value!=="function")
return create(value);
if(isSignal(value)) return value;
const out= create(); const out= create();
watch(()=> out(signal())); watch(()=> out(value()));
return out; return out;
} }
export function reactive(data){
if(isSignal(data))
return data;
if(typeof data!=="object" || data===null)
return create(data);
let type;
if(Array.isArray(data)){
type= "array";
data= data.map(v=> reactive(v));
} else if(data.toString()!=="[object Object]"){
return create(data);
} else {
type= "object";
data= Object.fromEntries(
Object.entries(data)
.map(([ key, value ])=> [ key, reactive(value) ])
);
}
const signal= (...value)=>
value.length ? write(signal, reactive(value[0])) : read(signal[mark]);
return createWrapObject(type, toSignal(signal, data));
}
const stack= []; const stack= [];
export function watch(context){ export function watch(context){
stack.push(context); stack.push(context);
@ -24,28 +50,57 @@ function currentContext(){
} }
function create(value){ function create(value){
if(isSignal(value)) return value; if(isSignal(value)) return value;
if(typeof value==="object" && value!==null)
//TODO Array?
return Object.fromEntries(
Object.entries(value)
.map(([ key, value ])=> [ key, create(value) ])
);
const signal= (...value)=> const signal= (...value)=>
value.length ? write(signal, value[0]) : read(signal); value.length ? write(signal, value[0]) : read(signal[mark]);
Object.assign(signal, { return toSignal(signal, value);
[mark]: true, }
function toSignal(signal, value){
signal[mark]= {
value, value,
listeners: new Set() listeners: new Set()
}); };
return signal; return signal;
} }
function createWrapObject(type, signal){
return new Proxy(signal, {
set(_, p, newValue){
const s= signal[mark];
if(p in s.value){
const v= s.value[p];
if(isSignal(v)) return v(newValue);
return (s.value[p]= newValue);
}
const v= reactive(newValue);
s.value[p]= v;
s.listeners.forEach(fn=> fn(s.value));
return v;
},
deleteProperty(_, p){
const s= signal[mark];
Reflect.deleteProperty(s.value, p);
s.listeners.forEach(fn=> fn(s.value));
},
get(_, p){
if(mark===p) return signal[mark];
if("array"!==type || !(p in Array.prototype) || p==="length")
return Reflect.get(signal[mark].value, p);
return (...a)=> {
const s= signal[mark];
const result= Array.prototype[p].call(s.value, ...a);
//TODO optimize!
s.value.forEach((v, i)=> Reflect.set(s.value, i, reactive(v)));
s.listeners.forEach(fn=> fn(s.value));
return result;
};
}
});
}
function read({ value, listeners }){ function read({ value, listeners }){
const context= currentContext(); const context= currentContext();
if(context) listeners.add(context); if(context) listeners.add(context);
return value; return value;
} }
function write(signal, value){ function write(signal, value){
signal.value= value; signal[mark].value= value;
signal.listeners.forEach(fn=> fn(value)) signal[mark].listeners.forEach(fn=> fn(value))
} }

View File

@ -1,5 +1,5 @@
import { S, watch, el, namespace, assign, on, dispatch } from "../index.js"; import { S, reactive, watch, el, namespace, assign, on, dispatch } from "../index.js";
Object.assign(globalThis, { S, watch, el, namespace, assign, on, dispatch }); Object.assign(globalThis, { S, reactive, watch, el, namespace, assign, on, dispatch });
const { style, css }= createStyle(); const { style, css }= createStyle();
globalThis.test= console.log; globalThis.test= console.log;
@ -21,7 +21,7 @@ function component({ name= "World", surname= "" }= {}){
margin-inline-start: .5em; margin-inline-start: .5em;
} }
`; `;
const store= S({ name, surname }); const store= reactive({ name, surname });
const full_name= S(()=> store.name()+" "+store.surname()); const full_name= S(()=> store.name()+" "+store.surname());
on(full_name, console.log); on(full_name, console.log);