mirror of
				https://github.com/jaandrle/deka-dom-el
				synced 2025-11-04 07:09:15 +01:00 
			
		
		
		
	✨ Add signal listener, reactive and wrap object functions
This commit is contained in:
		@@ -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){
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
	
 | 
						const signal=  (...value)=>
 | 
				
			||||||
	if(typeof value==="object" && value!==null)
 | 
							value.length ? write(signal, value[0]) : read(signal[mark]);
 | 
				
			||||||
		//TODO Array?
 | 
						return toSignal(signal, value);
 | 
				
			||||||
		return Object.fromEntries(
 | 
					}
 | 
				
			||||||
			Object.entries(value)
 | 
					function toSignal(signal, value){
 | 
				
			||||||
			.map(([ key, value ])=> [ key, create(value) ])
 | 
						signal[mark]= {
 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	const signal= (...value)=>
 | 
					 | 
				
			||||||
		value.length ? write(signal, value[0]) : read(signal);
 | 
					 | 
				
			||||||
	Object.assign(signal, {
 | 
					 | 
				
			||||||
		[mark]: true,
 | 
					 | 
				
			||||||
		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))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user