mirror of
https://github.com/jaandrle/deka-dom-el
synced 2025-07-01 04:12:14 +02:00
💥 docs observables
This commit is contained in:
@ -1,7 +1,11 @@
|
||||
// when NPM
|
||||
import { assign, el, elNS } from "deka-dom-el";
|
||||
// https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.js
|
||||
|
||||
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.js
|
||||
import {
|
||||
assign,
|
||||
el, createElement,
|
||||
elNS, createElementNS
|
||||
} from "deka-dom-el";
|
||||
el===createElement
|
||||
elNS===createElementNS
|
||||
// “internal” utils
|
||||
import {
|
||||
assignAttribute,
|
||||
|
@ -1,7 +1,4 @@
|
||||
// when NPM
|
||||
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.js
|
||||
import { on, dispatchEvent } from "deka-dom-el";
|
||||
// https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.js
|
||||
|
||||
/**
|
||||
* @type {ddeElementAddon}
|
||||
* */
|
||||
/** @type {ddeElementAddon} */
|
||||
|
18
docs_src/components/examples/observables/actions-demo.js
Normal file
18
docs_src/components/examples/observables/actions-demo.js
Normal file
@ -0,0 +1,18 @@
|
||||
import { O } from "deka-dom-el/observables";
|
||||
const observable= O(0, {
|
||||
increaseOnlyOdd(add){
|
||||
console.info(add);
|
||||
if(add%2 === 0) return this.stopPropagation();
|
||||
this.value+= add;
|
||||
}
|
||||
});
|
||||
O.on(observable, console.log);
|
||||
const oninterval= ()=>
|
||||
O.action(observable, "increaseOnlyOdd", Math.floor(Math.random()*100));
|
||||
|
||||
const interval= 5*1000;
|
||||
setTimeout(
|
||||
clearInterval,
|
||||
10*interval,
|
||||
setInterval(oninterval, interval)
|
||||
);
|
56
docs_src/components/examples/observables/actions-todos.js
Normal file
56
docs_src/components/examples/observables/actions-todos.js
Normal file
@ -0,0 +1,56 @@
|
||||
import { O } from "deka-dom-el/observables";
|
||||
const todos= O([], {
|
||||
push(item){
|
||||
this.value.push(O(item));
|
||||
},
|
||||
pop(){
|
||||
const removed= this.value.pop();
|
||||
if(removed) O.clear(removed);
|
||||
},
|
||||
[O.symbols.onclear](){ // this covers `O.clear(todos)`
|
||||
O.clear(...this.value);
|
||||
}
|
||||
});
|
||||
|
||||
import { el, on } from "deka-dom-el";
|
||||
/** @type {ddeElementAddon<HTMLFormElement>} */
|
||||
const onsubmit= on("submit", function(event){
|
||||
event.preventDefault();
|
||||
const data= new FormData(this);
|
||||
switch (data.get("op")){
|
||||
case "A"/*dd*/:
|
||||
O.action(todos, "push", data.get("todo"));
|
||||
break;
|
||||
case "E"/*dit*/: {
|
||||
const last= todos().at(-1);
|
||||
if(!last) break;
|
||||
last(data.get("todo"));
|
||||
break;
|
||||
}
|
||||
case "R"/*emove*/:
|
||||
O.action(todos, "pop");
|
||||
break;
|
||||
}
|
||||
});
|
||||
document.body.append(
|
||||
el("ul").append(
|
||||
O.el(todos, todos=>
|
||||
todos.map(textContent=> el("li", textContent)))
|
||||
),
|
||||
el("form", null, onsubmit).append(
|
||||
el("input", { type: "text", name: "todo", placeholder: "Todo’s text" }),
|
||||
el(radio, { textContent: "Add", checked: true }),
|
||||
el(radio, { textContent: "Edit last" }),
|
||||
el(radio, { textContent: "Remove" }),
|
||||
el("button", "Submit")
|
||||
)
|
||||
);
|
||||
document.head.append(
|
||||
el("style", "form{ display: flex; flex-flow: column nowrap; }")
|
||||
);
|
||||
function radio({ textContent, checked= false }){
|
||||
return el("label").append(
|
||||
el("input", { type: "radio", name: "op", value: textContent[0], checked }),
|
||||
" ",textContent
|
||||
)
|
||||
}
|
@ -8,7 +8,7 @@ O.on(observable, v=> console.log("observable", v), { signal: ac.signal });
|
||||
O.on(double, v=> console.log("double", v), { signal: ac.signal });
|
||||
|
||||
observable(observable()+1);
|
||||
const interval= 5000;
|
||||
const interval= 5 * 1000;
|
||||
const id= setInterval(()=> observable(observable()+1), interval);
|
||||
ac.signal.addEventListener("abort",
|
||||
()=> setTimeout(()=> clearInterval(id), 2*interval));
|
||||
|
15
docs_src/components/examples/observables/dom-attrs.js
Normal file
15
docs_src/components/examples/observables/dom-attrs.js
Normal file
@ -0,0 +1,15 @@
|
||||
import { O } from "deka-dom-el/observables";
|
||||
const count= O(0);
|
||||
|
||||
import { el } from "deka-dom-el";
|
||||
document.body.append(
|
||||
el("p", O(()=> "Currently: "+count())),
|
||||
el("p", { classList: { red: O(()=> count()%2) }, dataset: { count }, textContent: "Attributes example" })
|
||||
);
|
||||
document.head.append(
|
||||
el("style", ".red { color: red; }")
|
||||
);
|
||||
|
||||
const interval= 5 * 1000;
|
||||
setTimeout(clearInterval, 10*interval,
|
||||
setInterval(()=> count(count()+1), interval));
|
26
docs_src/components/examples/observables/dom-el.js
Normal file
26
docs_src/components/examples/observables/dom-el.js
Normal file
@ -0,0 +1,26 @@
|
||||
import { O } from "deka-dom-el/observables";
|
||||
const count= O(0, {
|
||||
add(){ this.value= this.value + Math.round(Math.random()*10); }
|
||||
});
|
||||
const numbers= O([ count() ], {
|
||||
push(next){ this.value.push(next); }
|
||||
});
|
||||
|
||||
import { el } from "deka-dom-el";
|
||||
document.body.append(
|
||||
O.el(count, count=> count%2
|
||||
? el("p", "Last number is odd.")
|
||||
: el()
|
||||
),
|
||||
el("p", "Lucky numbers:"),
|
||||
el("ul").append(
|
||||
O.el(numbers, numbers=> numbers.toReversed()
|
||||
.map(n=> el("li", n)))
|
||||
)
|
||||
);
|
||||
|
||||
const interval= 5*1000;
|
||||
setTimeout(clearInterval, 10*interval, setInterval(function(){
|
||||
O.action(count, "add");
|
||||
O.action(numbers, "push", count());
|
||||
}, interval));
|
@ -1,10 +1,6 @@
|
||||
// when NPM
|
||||
import { O } from "deka-dom-el/observables";
|
||||
// https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
|
||||
|
||||
/**
|
||||
* @type {ddeObservable}
|
||||
* */
|
||||
/**
|
||||
* @type {ddeActions}
|
||||
* */
|
||||
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
|
||||
import { O, observable } from "deka-dom-el/observables";
|
||||
O===observable
|
||||
/** @type {ddeObservable} */
|
||||
/** @type {ddeAction} */
|
||||
/** @type {ddeActions} */
|
||||
|
@ -4,5 +4,9 @@ const observable= O(0);
|
||||
// β — just reacts on observable changes
|
||||
O.on(observable, console.log);
|
||||
// γ — just updates the value
|
||||
observable(observable()+1);
|
||||
setInterval(()=> observable(observable()+1), 5000);
|
||||
const update= ()=> observable(observable()+1);
|
||||
|
||||
update();
|
||||
const interval= 5*1000;
|
||||
setTimeout(clearInterval, 10*interval,
|
||||
setInterval(update, interval));
|
||||
|
25
docs_src/components/mnemonic/elements-init.js
Normal file
25
docs_src/components/mnemonic/elements-init.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { el } from "deka-dom-el";
|
||||
import { mnemonicUl } from "../mnemonicUl.html.js";
|
||||
|
||||
export function mnemonic(){
|
||||
return mnemonicUl().append(
|
||||
el("li").append(
|
||||
el("code", "assign(<element>, ...<idl-objects>): <element>"), " — assign properties to the element",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "el(<tag-name>, <primitive>)[.append(...)]: <element-from-tag-name>"), " — simple element containing only text",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "el(<tag-name>, <idl-object>)[.append(...)]: <element-from-tag-name>"), " — element with more properties",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "el(<function>, <function-argument(s)>)[.append(...)]: <element-returned-by-function>"), " — using component represented by function",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "el(<...>, <...>, ...<addons>)"), " — see following page"
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "elNS(<namespace>)(<as-el-see-above>)[.append(...)]: <element-based-on-arguments>"), " — typically SVG elements",
|
||||
)
|
||||
);
|
||||
}
|
20
docs_src/components/mnemonic/events-init.js
Normal file
20
docs_src/components/mnemonic/events-init.js
Normal file
@ -0,0 +1,20 @@
|
||||
import { el } from "deka-dom-el";
|
||||
import { mnemonicUl } from "../mnemonicUl.html.js";
|
||||
|
||||
export function mnemonic(){
|
||||
return mnemonicUl().append(
|
||||
el("li").append(
|
||||
el("code", "on(<event>, <listener>[, <options>])(<element>)"), " — just ", el("code", "<element>.addEventListener(<event>, <listener>[, <options>])")
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "on.<live-cycle>(<event>, <listener>[, <options>])(<element>)"), " — corresponds to custom elemnets callbacks ", el("code", "<live-cycle>Callback(...){...}"),
|
||||
". To connect to custom element see following page, else it is simulated by MutationObserver."
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "dispatchEvent(<event>[, <options>])(element)"), " — just ", el("code", "<element>.dispatchEvent(new Event(<event>[, <options>]))")
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "dispatchEvent(<event>[, <options>])(element, detail)"), " — just ", el("code", "<element>.dispatchEvent(new CustomEvent(<event>, { detail, ...<options> }))")
|
||||
),
|
||||
);
|
||||
}
|
28
docs_src/components/mnemonic/observables-init.js
Normal file
28
docs_src/components/mnemonic/observables-init.js
Normal file
@ -0,0 +1,28 @@
|
||||
import { el } from "deka-dom-el";
|
||||
import { mnemonicUl } from "../mnemonicUl.html.js";
|
||||
|
||||
export function mnemonic(){
|
||||
return mnemonicUl().append(
|
||||
el("li").append(
|
||||
el("code", "O(<value>)"), " — observable: reactive value",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "O(()=> <computation>)"), " — observable: reactive value dependent on calculation using other observables",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "O.on(<observable>, <listener>[, <options>])"), " — listen to the observable value changes",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "O.clear(...<observables>)"), " — off and clear observables",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "O(<value>, <actions>)"), " — observable: pattern to create complex reactive objects/arrays",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "O.action(<observable>, <action-name>, ...<action-arguments>)"), " — invoke an action for given observable"
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "O.el(<observable>, <function-returning-dom>)"), " — render partial dom structure (template) based on the current observable value",
|
||||
)
|
||||
);
|
||||
}
|
@ -7,8 +7,8 @@ ${host} h3{
|
||||
}
|
||||
`;
|
||||
import { el, simulateSlots } from "deka-dom-el";
|
||||
/** @param {Object} props @param {string} props.textContent */
|
||||
export function mnemonicUl({ textContent= "" }){
|
||||
/** @param {Object} [props] @param {string} [props.textContent] */
|
||||
export function mnemonicUl({ textContent= "" }= {}){
|
||||
if(textContent) textContent= " – "+textContent
|
||||
return simulateSlots(el("div", { className: "notice" }).append(
|
||||
el(h3, "Mnemonic"+textContent),
|
||||
|
@ -20,7 +20,7 @@ body {
|
||||
@media (min-width:768px) {
|
||||
body{
|
||||
grid-template-rows: auto auto;
|
||||
grid-template-columns: calc(var(--body-max-width) / 3) auto;
|
||||
grid-template-columns: calc(10 * var(--body-max-width) / 27) auto;
|
||||
grid-template-areas:
|
||||
"header header"
|
||||
"sidebar content"
|
||||
|
@ -3,7 +3,7 @@ import { simplePage } from "./layout/simplePage.html.js";
|
||||
import { el } from "deka-dom-el";
|
||||
import { example } from "./components/example.html.js";
|
||||
import { h3 } from "./components/pageUtils.html.js";
|
||||
import { mnemonicUl } from "./components/mnemonicUl.html.js";
|
||||
import { mnemonic } from "./components/mnemonic/elements-init.js";
|
||||
import { code } from "./components/code.html.js";
|
||||
/** @param {string} url */
|
||||
const fileURL= url=> new URL(url, import.meta.url);
|
||||
@ -116,25 +116,6 @@ export function page({ pkg, info }){
|
||||
),
|
||||
el(example, { src: fileURL("./components/examples/elements/dekaElNS.js"), page_id }),
|
||||
|
||||
el(mnemonicUl).append(
|
||||
el("li").append(
|
||||
el("code", "assign(<element>, ...<idl-objects>): <element>"), " — assign properties to the element",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "el(<tag-name>, <primitive>)[.append(...)]: <element-from-tag-name>"), " — simple element containing only text",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "el(<tag-name>, <idl-object>)[.append(...)]: <element-from-tag-name>"), " — element with more properties",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "el(<function>, <function-argument(s)>)[.append(...)]: <element-returned-by-function>"), " — using component represented by function",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "el(<...>, <...>, ...<addons>)"), " — see following page"
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "elNS(<namespace>)(<as-el-see-above>)[.append(...)]: <element-based-on-arguments>"), " — typically SVG elements",
|
||||
)
|
||||
)
|
||||
el(mnemonic)
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { simplePage } from "./layout/simplePage.html.js";
|
||||
import { el } from "deka-dom-el";
|
||||
import { example } from "./components/example.html.js";
|
||||
import { h3 } from "./components/pageUtils.html.js";
|
||||
import { mnemonicUl } from "./components/mnemonicUl.html.js";
|
||||
import { mnemonic } from "./components/mnemonic/events-init.js";
|
||||
import { code } from "./components/code.html.js";
|
||||
/** @param {string} url */
|
||||
const fileURL= url=> new URL(url, import.meta.url);
|
||||
@ -113,20 +113,6 @@ export function page({ pkg, info }){
|
||||
el("p", "The library also provides a method to dispatch the events."),
|
||||
el(example, { src: fileURL("./components/examples/events/compareDispatch.js"), page_id }),
|
||||
|
||||
el(mnemonicUl).append(
|
||||
el("li").append(
|
||||
el("code", "on(<event>, <listener>[, <options>])(<element>)"), " — just ", el("code", "<element>.addEventListener(<event>, <listener>[, <options>])")
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "on.<live-cycle>(<event>, <listener>[, <options>])(<element>)"), " — corresponds to custom elemnets callbacks ", el("code", "<live-cycle>Callback(...){...}"),
|
||||
". To connect to custom element see following page, else it is simulated by MutationObserver."
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "dispatchEvent(<event>[, <options>])(element)"), " — just ", el("code", "<element>.dispatchEvent(new Event(<event>[, <options>]))")
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "dispatchEvent(<event>[, <options>])(element, detail)"), " — just ", el("code", "<element>.dispatchEvent(new CustomEvent(<event>, { detail, ...<options> }))")
|
||||
),
|
||||
),
|
||||
el(mnemonic)
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { simplePage } from "./layout/simplePage.html.js";
|
||||
import { el } from "deka-dom-el";
|
||||
import { example } from "./components/example.html.js";
|
||||
import { h3 } from "./components/pageUtils.html.js";
|
||||
import { mnemonicUl } from "./components/mnemonicUl.html.js";
|
||||
import { mnemonic } from "./components/mnemonic/observables-init.js";
|
||||
import { code } from "./components/code.html.js";
|
||||
/** @param {string} url */
|
||||
const fileURL= url=> new URL(url, import.meta.url);
|
||||
@ -48,28 +48,54 @@ export function page({ pkg, info }){
|
||||
el("p").append(
|
||||
"Similarly to the ", el("code", "on"), " function to register DOM events listener.",
|
||||
" You can use ", el("code", "AbortController"), "/", el("code", "AbortSignal"), " to",
|
||||
" ", el("em", "off"), "/stop listenning. For representing “live” piece of code computation pattern:"
|
||||
" ", el("em", "off"), "/stop listenning. In example, you also found the way for representing",
|
||||
" “live” piece of code computation pattern (derived observable):"
|
||||
),
|
||||
el(example, { src: fileURL("./components/examples/observables/computations-abort.js"), page_id }),
|
||||
el(mnemonicUl).append(
|
||||
el("li").append(
|
||||
el("code", "O(<value>)"), " — observable: reactive value",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "O(()=> <computation>)"), " — observable: reactive value dependent on calculation using other observables",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "O.on(<observable>, <listener>[, <options>])"), " — listen to the observable value changes",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "O.clear(...<observables>)"), " — off and clear observables",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "O(<value>, <actions>)"), " — observable: pattern to create complex reactive objects/arrays",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "O.action(<observable>, <action-name>, ...<action-arguments>)"), " — invoke an action for given observable"
|
||||
)
|
||||
|
||||
el(h3, "Observables and actions"),
|
||||
el("p").append(
|
||||
el("code", "O(/* primitive */)"), " allows you to declare simple reactive variables, typically",
|
||||
" around ", el("em", "immutable"), " ", el("a", { textContent: "primitive types", title: "Primitive | MDN", href: "https://developer.mozilla.org/en-US/docs/Glossary/Primitive" }), ".",
|
||||
" ",
|
||||
"However, it may also be necessary to use reactive arrays, objects, or other complex reactive structures."
|
||||
),
|
||||
el(example, { src: fileURL("./components/examples/observables/actions-demo.js"), page_id }),
|
||||
el("p", "…but typical user-case is object/array (maps, sets and other mutable objects):"),
|
||||
el(example, { src: fileURL("./components/examples/observables/actions-todos.js"), page_id }),
|
||||
el("p").append(
|
||||
"In some way, you can compare it with ", el("a", { textContent: "useReducer", href: "https://react.dev/reference/react/useReducer", title: "useReducer hook | React docs" }),
|
||||
" hook from React. So, the ", el("code", "O(<data>, <actions>)"), " pattern creates",
|
||||
" a store “machine”. We can then invoke (dispatch) registered action by calling",
|
||||
" ", el("code", "O.action(<observable>, <name>, ...<args>)"), " after the action call",
|
||||
" the observable calls all its listeners. This can be stopped by calling ", el("code", "this.stopPropagation()"),
|
||||
" in the method representing the given action. As it can be seen in examples, the “store” value is",
|
||||
" available also in the function for given action (", el("code", "this.value"), ")."
|
||||
),
|
||||
|
||||
el(h3, "Reactive DOM attributes and elements"),
|
||||
el("p", "There are on basic level two distinc situation to mirror dynamic value into the DOM/UI"),
|
||||
el("ol").append(
|
||||
el("li", "to change some attribute(s) of existing element(s)"),
|
||||
el("li", "to generate elements itself dynamically – this covers conditions and loops")
|
||||
),
|
||||
el(example, { src: fileURL("./components/examples/observables/dom-attrs.js"), page_id }),
|
||||
el("p").append(
|
||||
"To derived attribute based on value of observable variable just use the observable as",
|
||||
" a value of the attribute (", el("code", "assign(element, { attribute: O('value') })"), ").",
|
||||
" ", el("code", "assign"), "/", el("code", "el"), " provides ways to glue reactive attributes/classes",
|
||||
" more granularly into the DOM. Just use dedicated build-in attributes ", el("code", "dataset"), ", ",
|
||||
el("code", "ariaset"), " and ", el("code", "classList"), "."
|
||||
),
|
||||
el("p").append(
|
||||
"For computation, you can use the derived observable (see above) like ", el("code", "assign(element, { textContent: O(()=> 'Hello '+WorldObservable()) })"), "."
|
||||
),
|
||||
el("p").append(
|
||||
"To represent part of the template filled dynamically based on the observable value use ", el("code", "O.el(observable, DOMgenerator)"), ".",
|
||||
" This was already used in the todo example above or see:"
|
||||
),
|
||||
el(example, { src: fileURL("./components/examples/observables/dom-el.js"), page_id }),
|
||||
|
||||
el(mnemonic)
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user