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){
if(isSignal(event)) return event.listeners.add(listener);
if(isSignal(event)) return addSignalListener(event, listener);
return element=> element.addEventListener(event, listener, options);
}
export function dispatch(event, detail){

View File

@ -3,15 +3,41 @@ export function isSignal(candidate){
try{ return Reflect.has(candidate, mark); }
catch(e){ return false; }
}
export function S(signal){
if(typeof signal!=="function")
return create(signal);
if(isSignal(signal)) return signal;
export function addSignalListener(signal, listener){
return signal[mark].listeners.add(listener);
}
export function S(value){
if(typeof value!=="function")
return create(value);
if(isSignal(value)) return value;
const out= create();
watch(()=> out(signal()));
watch(()=> out(value()));
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= [];
export function watch(context){
stack.push(context);
@ -24,28 +50,57 @@ function currentContext(){
}
function create(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)=>
value.length ? write(signal, value[0]) : read(signal);
Object.assign(signal, {
[mark]: true,
value.length ? write(signal, value[0]) : read(signal[mark]);
return toSignal(signal, value);
}
function toSignal(signal, value){
signal[mark]= {
value,
listeners: new Set()
});
};
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 }){
const context= currentContext();
if(context) listeners.add(context);
return value;
}
function write(signal, value){
signal.value= value;
signal.listeners.forEach(fn=> fn(value))
signal[mark].value= 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";
Object.assign(globalThis, { S, watch, el, namespace, assign, on, dispatch });
import { S, reactive, watch, el, namespace, assign, on, dispatch } from "../index.js";
Object.assign(globalThis, { S, reactive, watch, el, namespace, assign, on, dispatch });
const { style, css }= createStyle();
globalThis.test= console.log;
@ -21,7 +21,7 @@ function component({ name= "World", surname= "" }= {}){
margin-inline-start: .5em;
}
`;
const store= S({ name, surname });
const store= reactive({ name, surname });
const full_name= S(()=> store.name()+" "+store.surname());
on(full_name, console.log);