mirror of
https://github.com/jaandrle/deka-dom-el
synced 2024-11-21 23:39:37 +01:00
✨ Add signals functionality and reactive attributes in assign
This commit is contained in:
parent
7a2c3e6a4b
commit
404971f484
12
README.md
12
README.md
@ -97,15 +97,13 @@ document.body.append(
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Events and dynamic parts
|
## Events and signals for reactivity
|
||||||
*investigation*:
|
*investigation*:
|
||||||
```js
|
```js
|
||||||
const output_dynamic= eventsSink(store=> ({
|
const value= S("");
|
||||||
element,
|
|
||||||
onchange: listen("change", event=> assign(store.element, { textContent: event.target.value }))
|
|
||||||
}));
|
|
||||||
document.body.append(
|
document.body.append(
|
||||||
el("span", { style: { fontWeight: "bold" }, textContent: "" }, output_dynamic.target),
|
el("span", { style: { fontWeight: "bold" }, textContent: ()=> S(value) }),
|
||||||
el("input", { type: "text" }, output_dynamic.onchange)
|
el("input", { type: "text" },
|
||||||
|
listen("change", event=> S(value, event.target, value)))
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
1
index.js
1
index.js
@ -4,3 +4,4 @@
|
|||||||
});
|
});
|
||||||
export * from "./src/dom.js";
|
export * from "./src/dom.js";
|
||||||
export * from "./src/events.js";
|
export * from "./src/events.js";
|
||||||
|
export * from "./src/signals.js";
|
||||||
|
10
src/dom.js
10
src/dom.js
@ -20,14 +20,22 @@ export function createElementNS(tag, attributes, attributes_todo){
|
|||||||
}
|
}
|
||||||
export { createElementNS as elNS };
|
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;
|
||||||
|
}
|
||||||
export function assign(element, ...attributes){
|
export function assign(element, ...attributes){
|
||||||
if(!attributes.length) return element;
|
if(!attributes.length) return element;
|
||||||
const is_svg= element instanceof SVGElement;
|
const is_svg= element instanceof SVGElement;
|
||||||
const setRemoveAttr= (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute");
|
const setRemoveAttr= (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute");
|
||||||
|
|
||||||
Object.entries(Object.assign({}, ...attributes)).forEach(function([ key, attr ]){
|
Object.entries(Object.assign({}, ...attributes)).forEach(function assignNth([ key, attr ]){
|
||||||
if(key[0]==="=") return setRemoveAttr(key.slice(1), attr);
|
if(key[0]==="=") return setRemoveAttr(key.slice(1), attr);
|
||||||
if(key[0]===".") return setDelete(element, key.slice(1), attr);
|
if(key[0]===".") return setDelete(element, key.slice(1), attr);
|
||||||
|
if(isReactive(key, attr))
|
||||||
|
return watch(()=> assignNth([ key, attr() ]));
|
||||||
if(typeof attr === "object"){
|
if(typeof attr === "object"){
|
||||||
switch(key){
|
switch(key){
|
||||||
case "style": return forEachEntries(attr, setRemove.bind(null, element.style, "Property"))
|
case "style": return forEachEntries(attr, setRemove.bind(null, element.style, "Property"))
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import { isSignal } from './signals.js';
|
||||||
export function listen(event, listener, options){
|
export function listen(event, listener, options){
|
||||||
|
if(isSignal(event)) return event.listeners.add(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){
|
||||||
|
52
src/signals.js
Normal file
52
src/signals.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const mark= Symbol.for("signal");
|
||||||
|
export function isSignal(candidate){
|
||||||
|
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))
|
||||||
|
return create(signal);
|
||||||
|
|
||||||
|
if(value.length===0)
|
||||||
|
return read(signal);
|
||||||
|
return write(signal, value[0]);
|
||||||
|
}
|
||||||
|
const stack= [];
|
||||||
|
export function watch(context){
|
||||||
|
stack.push(context);
|
||||||
|
context();
|
||||||
|
stack.pop();
|
||||||
|
};
|
||||||
|
|
||||||
|
function currentContext(){
|
||||||
|
return stack[stack.length - 1];
|
||||||
|
}
|
||||||
|
function create(value){
|
||||||
|
if(typeof value==="object" && value!==null)
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(value)
|
||||||
|
.map(([ key, value ])=> [ key, create(value) ])
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
[mark]: true,
|
||||||
|
value,
|
||||||
|
listeners: new Set()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import { el, elNS, assign, listen, dispatch } from "../index.js";
|
import { S, watch, el, elNS, assign, listen, dispatch } from "../index.js";
|
||||||
Object.assign(globalThis, { el, elNS, assign, listen, dispatch });
|
Object.assign(globalThis, { S, watch, el, elNS, assign, listen, dispatch });
|
||||||
|
|
||||||
const { style, css }= createStyle();
|
const { style, css }= createStyle();
|
||||||
globalThis.test= console.log;
|
globalThis.test= console.log;
|
||||||
@ -10,48 +10,39 @@ console.log(app, app instanceof HTMLDivElement);
|
|||||||
document.head.append(style);
|
document.head.append(style);
|
||||||
document.body.append(app);
|
document.body.append(app);
|
||||||
|
|
||||||
function component({ value= "World" }= {}){
|
function component({ name= "World", surname= "" }= {}){
|
||||||
const name= "naiveForm";
|
const className= "naiveForm";
|
||||||
css`
|
css`
|
||||||
.${name}{
|
.${className}{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
}
|
}
|
||||||
.${name} input{
|
.${className} input{
|
||||||
margin-inline-start: .5em;
|
margin-inline-start: .5em;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
const store= S({ name, surname });
|
||||||
|
const full_name= S(()=> S(store.name)+" "+S(store.surname));
|
||||||
|
listen(full_name, console.log);
|
||||||
|
|
||||||
const output= eventsSink(store=> ({
|
return el("div", { className }).append(
|
||||||
onchange: listen("change", function(event){
|
|
||||||
assign(store.element, { textContent: event.target.value });
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
const input= eventsSink(store=> ({
|
|
||||||
onchange: listen("change", function(event){
|
|
||||||
assign(store.element, { value: event.detail });
|
|
||||||
dispatch("change")(input.element);
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
return el("div", { className: name }, input.onchange).append(
|
|
||||||
el("p").append(
|
el("p").append(
|
||||||
el("#text", { textContent: "Hello " }),
|
el("#text", { textContent: "Hello " }),
|
||||||
el("strong", { textContent: value }, output.target),
|
el("strong", { textContent: ()=> S(full_name) }),
|
||||||
|
el("#text", { textContent: "!" }),
|
||||||
),
|
),
|
||||||
el("label").append(
|
el("label").append(
|
||||||
el("#text", { textContent: "Set name:" }),
|
el("#text", { textContent: "Set name:" }),
|
||||||
el("input", { type: "text", value }, output.onchange, input.target)
|
el("input", { type: "text", value: ()=> S(store.name) },
|
||||||
|
listen("change", ev=> S(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))),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
function eventsSink(fn){
|
|
||||||
const store= {
|
|
||||||
element: null,
|
|
||||||
target: function(element){ store.element= element; },
|
|
||||||
};
|
|
||||||
Object.assign(store, fn(store));
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
function createStyle(){
|
function createStyle(){
|
||||||
const style= el("style");
|
const style= el("style");
|
||||||
return {
|
return {
|
||||||
|
Loading…
Reference in New Issue
Block a user