From acdfb4ef57d52676f4eea9aa90ab900b1cdd9483 Mon Sep 17 00:00:00 2001 From: Jan Andrle Date: Thu, 24 Aug 2023 14:52:46 +0200 Subject: [PATCH] :recycle: Simplify signals logic (use functions to read/set) --- README.md | 4 ++-- src/dom.js | 9 ++------- src/signals.js | 33 ++++++++++++++++----------------- test/index.js | 12 ++++++------ 4 files changed, 26 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 9dd976f..1d6230c 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,8 @@ document.body.append( ```js const value= S(""); document.body.append( - el("span", { style: { fontWeight: "bold" }, textContent: ()=> S(value) }), + el("span", { style: { fontWeight: "bold" }, textContent: value }), el("input", { type: "text" }, - listen("change", event=> S(value, event.target, value))) + listen("change", event=> value(event.target.value))) ); ``` diff --git a/src/dom.js b/src/dom.js index 555eb95..4ba060a 100644 --- a/src/dom.js +++ b/src/dom.js @@ -20,12 +20,7 @@ export function createElementNS(tag, attributes, attributes_todo){ } export { createElementNS as elNS }; -import { watch } from './signals.js'; -function isReactive(key, attr){ - if(typeof attr !== "function") return false; - if(key.startsWith("on")) return false; - return true; -} +import { watch, isSignal } from './signals.js'; export function assign(element, ...attributes){ if(!attributes.length) return element; const is_svg= element instanceof SVGElement; @@ -34,7 +29,7 @@ export function assign(element, ...attributes){ Object.entries(Object.assign({}, ...attributes)).forEach(function assignNth([ key, attr ]){ if(key[0]==="=") return setRemoveAttr(key.slice(1), attr); if(key[0]===".") return setDelete(element, key.slice(1), attr); - if(isReactive(key, attr)) + if(isSignal(attr)) //TODO: unmounted return watch(()=> assignNth([ key, attr() ])); if(typeof attr === "object"){ switch(key){ diff --git a/src/signals.js b/src/signals.js index 1b239e5..1420926 100644 --- a/src/signals.js +++ b/src/signals.js @@ -1,23 +1,16 @@ const mark= Symbol.for("signal"); export function isSignal(candidate){ - try{ - return Reflect.has(candidate, mark); - } catch(e){ - return false; - } + try{ return Reflect.has(candidate, mark); } + catch(e){ return false; } } -export function S(signal, ...value){ - if(typeof signal==="function"){ - const out= create(); - watch(()=> S(out, signal())); - return out; - } - if(!isSignal(signal)) +export function S(signal){ + if(typeof signal!=="function") return create(signal); + if(isSignal(signal)) return signal; - if(value.length===0) - return read(signal); - return write(signal, value[0]); + const out= create(); + watch(()=> out(signal())); + return out; } const stack= []; export function watch(context){ @@ -30,16 +23,22 @@ function currentContext(){ return stack[stack.length - 1]; } 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) ]) ); - return { + const signal= (...value)=> + value.length ? write(signal, value[0]) : read(signal); + Object.assign(signal, { [mark]: true, value, listeners: new Set() - }; + }); + return signal; } function read({ value, listeners }){ const context= currentContext(); diff --git a/test/index.js b/test/index.js index 7c768d1..0e92d03 100644 --- a/test/index.js +++ b/test/index.js @@ -22,24 +22,24 @@ function component({ name= "World", surname= "" }= {}){ } `; const store= S({ name, surname }); - const full_name= S(()=> S(store.name)+" "+S(store.surname)); + const full_name= S(()=> store.name()+" "+store.surname()); listen(full_name, console.log); return el("div", { className }).append( el("p").append( el("#text", { textContent: "Hello " }), - el("strong", { textContent: ()=> S(full_name) }), + el("strong", { textContent: full_name }), el("#text", { textContent: "!" }), ), el("label").append( el("#text", { textContent: "Set name:" }), - el("input", { type: "text", value: ()=> S(store.name) }, - listen("change", ev=> S(store.name, ev.target.value))), + el("input", { type: "text", value: store.name }, + listen("change", ev=> store.name(ev.target.value))), ), el("label").append( el("#text", { textContent: "Set surname:" }), - el("input", { type: "text", value: ()=> S(store.surname) }, - listen("change", ev=> S(store.surname, ev.target.value))), + el("input", { type: "text", value: store.surname }, + listen("change", ev=> store.surname(ev.target.value))), ) ) }