1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2025-07-01 04:12:14 +02:00

💥 simulateSlots

This commit is contained in:
2023-11-24 16:16:08 +01:00
parent 6a293d75a6
commit fb02635d24
20 changed files with 1263 additions and 1299 deletions

View File

@ -1,43 +1,36 @@
import "./global.css.js";
import { simplePage } from "./layout/simplePage.html.js";
import { el } from "deka-dom-el";
import { header } from "./layout/head.html.js";
import { example } from "./components/example.html.js";
import { prevNext } from "./components/pageUtils.html.js";
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el().append(
el(header, { info, pkg }),
el("main").append(
el("p", "The library tries to provide pure JavaScript tool(s) to create reactive interfaces."),
el("p").append(
"We start with creating and modifying a static elements and end up with UI templates.",
" ",
el("i").append(
"From ", el("code", "document.createElement"), " to ", el("code", "el"), "."
),
" ",
"Then we go through the native events system and the way to include it declaratively in UI templates.",
" ",
el("i").append(
"From ", el("code", "element.addEventListener"), " to ", el("code", "on"), "."
),
return el(simplePage, { info, pkg }).append(
el("p", "The library tries to provide pure JavaScript tool(s) to create reactive interfaces."),
el("p").append(
"We start with creating and modifying a static elements and end up with UI templates.",
" ",
el("i").append(
"From ", el("code", "document.createElement"), " to ", el("code", "el"), "."
),
el("p").append(
"Next step is providing interactivity not only for our UI templates.",
" ",
"We introduce signals (", el("code", "S"), ") and how them incorporate to UI templates.",
" ",
"Then we go through the native events system and the way to include it declaratively in UI templates.",
" ",
el("i").append(
"From ", el("code", "element.addEventListener"), " to ", el("code", "on"), "."
),
el("p").append(
"Now we will clarify how the signals are incorporated into our templates with regard ",
"to application performance. This is not the only reason the library uses ",
el("code", "scope"), "s. We will look at how they work in components represented ",
"in JavaScript by functions."
),
el(example, { src: new URL("./components/examples/helloWorld.js", import.meta.url), page_id }),
el(prevNext, info)
)
),
el("p").append(
"Next step is providing interactivity not only for our UI templates.",
" ",
"We introduce signals (", el("code", "S"), ") and how them incorporate to UI templates.",
),
el("p").append(
"Now we will clarify how the signals are incorporated into our templates with regard ",
"to application performance. This is not the only reason the library uses ",
el("code", "scope"), "s. We will look at how they work in components represented ",
"in JavaScript by functions."
),
el(example, { src: new URL("./components/examples/helloWorld.js", import.meta.url), page_id }),
);
}

View File

@ -0,0 +1,16 @@
import "../global.css.js";
import { el, simulateSlots } from "deka-dom-el";
import { header } from "./head.html.js";
import { prevNext } from "../components/pageUtils.html.js";
/** @param {import("../types.d.ts").PageAttrs} attrs */
export function simplePage({ pkg, info }){
return simulateSlots(el().append(
el(header, { info, pkg }),
el("main").append(
el("slot"),
el(prevNext, info)
)
));
}

View File

@ -1,147 +1,139 @@
import "./global.css.js";
import { simplePage } from "./layout/simplePage.html.js";
import { el } from "deka-dom-el";
import { header } from "./layout/head.html.js";
import { example } from "./components/example.html.js";
import { h3, prevNext } from "./components/pageUtils.html.js";
import { h3 } from "./components/pageUtils.html.js";
/** @param {string} url */
const fileURL= url=> new URL(url, import.meta.url);
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el().append(
el(header, { info, pkg }),
el("main").append(
el("h2", "Native JavaScript DOM elements creations"),
el("p", "Lets go through all patterns we would like to use and what needs to be improved for better experience."),
el(h3, "Creating element(s) (with custom attributes)"),
el("p").append(
"You can create a native DOM element by using the ",
el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement", title: "MDN documentation for document.createElement()" }).append(
el("code", "document.createElement()")
), ". ",
"It is also possible to provide a some attribute(s) declaratively using ", el("code", "Object.assign()"), ". ",
"More precisely, this way you can sets some ",
el("a", {
href: "https://developer.mozilla.org/en-US/docs/Glossary/IDL",
title: "MDN page about Interface Description Language"
}).append(
el("abbr", { textContent: "IDL", title: "Interface Description Language" })
), " also known as a JavaScript property."
return el(simplePage, { info, pkg }).append(
el("h2", "Native JavaScript DOM elements creations"),
el("p", "Lets go through all patterns we would like to use and what needs to be improved for better experience."),
el(h3, "Creating element(s) (with custom attributes)"),
el("p").append(
"You can create a native DOM element by using the ",
el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement", title: "MDN documentation for document.createElement()" }).append(
el("code", "document.createElement()")
), ". ",
"It is also possible to provide a some attribute(s) declaratively using ", el("code", "Object.assign()"), ". ",
"More precisely, this way you can sets some ",
el("a", {
href: "https://developer.mozilla.org/en-US/docs/Glossary/IDL",
title: "MDN page about Interface Description Language"
}).append(
el("abbr", { textContent: "IDL", title: "Interface Description Language" })
), " also known as a JavaScript property."
),
el(example, { src: fileURL("./components/examples/elements/nativeCreateElement.js"), page_id }),
el("p").append(
"To make this easier, you can use the ", el("code", "el"), " function. ",
"Internally in basic examples, it is wrapper around ", el("code", "assign(document.createElement(…), { … })"), "."
),
el(example, { src: fileURL("./components/examples/elements/dekaCreateElement.js"), page_id }),
el("p").append(
"The ", el("code", "assign"), " function provides improved behaviour of ", el("code", "Object.assign()"), ". ",
"You can declaratively sets any IDL and attribute of the given element. ",
"Function prefers IDL and fallback to the ", el("code", "element.setAttribute"), " if there is no writable property in the element prototype."
),
el("p").append(
"You can study all JavaScript elements interfaces to the corresponding HTML elements. ",
"All HTML elements inherits from ", el("a", { textContent: "HTMLElement", href: "https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement" }), ". ",
"To see all available IDLs for example for paragraphs, see ", el("a", { textContent: "HTMLParagraphElement", href: "https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement" }), ". ",
"Moreover, the ", el("code", "assign"), " provides a way to sets declaratively some convenient properties:"
),
el("ul").append(
el("li").append(
"It is possible to sets ", el("code", "data-*"), "/", el("code", "aria-*"), " attributes using object notation."
),
el(example, { src: fileURL("./components/examples/elements/nativeCreateElement.js"), page_id }),
el("p").append(
"To make this easier, you can use the ", el("code", "el"), " function. ",
"Internally in basic examples, it is wrapper around ", el("code", "assign(document.createElement(…), { … })"), "."
el("li").append(
"In opposite, it is also possible to sets ", el("code", "data-*"), "/", el("code", "aria-*"), " attribute using camelCase notation."
),
el(example, { src: fileURL("./components/examples/elements/dekaCreateElement.js"), page_id }),
el("p").append(
"The ", el("code", "assign"), " function provides improved behaviour of ", el("code", "Object.assign()"), ". ",
"You can declaratively sets any IDL and attribute of the given element. ",
"Function prefers IDL and fallback to the ", el("code", "element.setAttribute"), " if there is no writable property in the element prototype."
el("li").append(
"You can use string or object as a value for ", el("code", "style"), " property."
),
el("p").append(
"You can study all JavaScript elements interfaces to the corresponding HTML elements. ",
"All HTML elements inherits from ", el("a", { textContent: "HTMLElement", href: "https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement" }), ". ",
"To see all available IDLs for example for paragraphs, see ", el("a", { textContent: "HTMLParagraphElement", href: "https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement" }), ". ",
"Moreover, the ", el("code", "assign"), " provides a way to sets declaratively some convenient properties:"
el("li").append(
el("code", "className"), " (IDL preffered)/", el("code", "class"), " are ways to add CSS class to the element. ",
"You can use string (similarly to ", el("code", "class=\"…\"") ," syntax in HTML) or array of strings. ",
"This is handy to concat conditional classes."
),
el("li").append(
"Use ", el("code", "classList"), " to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.",
),
el("li").append(
"The ", el("code", "assign"), " also accepts the ", el("code", "undefined"), " as a value for any property to remove it from the element declaratively. ",
"Also for some IDL the corresponding attribute is removed as it can be confusing. ",
el("em").append(
"For example, natievly the elements ", el("code", "id"), " is removed by setting the IDL to an empty string."
)
),
el("li").append(
"You can use ", el("code", "="), " or ", el("code", "."), " to force processing given key as attribute/property of the element."
)
),
el("p").append(
"For processing, the ", el("code", "assign"), " internally uses ", el("code", "assignAttribute"), " and ", el("code", "classListDeclarative"), "."
),
el(example, { src: fileURL("./components/examples/elements/dekaAssign.js"), page_id }),
el(h3, "Native JavaScript templating"),
el("p", "By default, the native JS has no good way to define HTML template using DOM API:"),
el(example, { src: fileURL("./components/examples/elements/nativeAppend.js"), page_id }),
el("p").append(
"This library therefore overwrites the ", el("code", "append"), " method of created elements to always return parent element."
),
el(example, { src: fileURL("./components/examples/elements/dekaAppend.js"), page_id }),
el(h3, "Basic (state-less) components"),
el("p").append(
"You can use functions for encapsulation (repeating) logic. ",
"The ", el("code", "el"), " accepts function as first argument. ",
"In that case, the function should return dom elements and the second argument for ", el("code", "el"), " is argument for given element."
),
el(example, { src: fileURL("./components/examples/elements/dekaBasicComponent.js"), page_id }),
el("p").append(
"As you can see, in case of state-less/basic components there is no difference",
" between calling component function directly or using ", el("code", "el"), " function.",
),
el("p", { className: "notice" }).append(
"It is nice to use similar naming convention as native DOM API. ",
"This allows us to use ", el("a", { textContent: "the destructuring assignment syntax", href: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment", title: "Destructuring assignment | MDN" }),
" and keep track of the native API (things are best remembered through regular use).",
),
el(h3, "Creating non-HTML elements"),
el("p").append(
"Similarly to the native DOM API (", el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS", title: "MDN" }).append(el("code", "document.createElementNS")), ") for non-HTML elements",
" we need to tell JavaScript which kind of the element to create.",
" We can use the ", el("code", "elNS"), " function:"
),
el(example, { src: fileURL("./components/examples/elements/dekaElNS.js"), page_id }),
el("div", { className: "notice" }).append(
el("p", "Mnemonic"),
el("ul").append(
el("li").append(
"It is possible to sets ", el("code", "data-*"), "/", el("code", "aria-*"), " attributes using object notation."
el("code", "assign(<element>, ...<idl-objects>): <element>"), " — assign properties to the element",
),
el("li").append(
"In opposite, it is also possible to sets ", el("code", "data-*"), "/", el("code", "aria-*"), " attribute using camelCase notation."
el("code", "el(<tag-name>, <primitive>)[.append(...)]: <element-from-tag-name>"), " — simple element containing only text",
),
el("li").append(
"You can use string or object as a value for ", el("code", "style"), " property."
el("code", "el(<tag-name>, <idl-object>)[.append(...)]: <element-from-tag-name>"), " — element with more properties",
),
el("li").append(
el("code", "className"), " (IDL preffered)/", el("code", "class"), " are ways to add CSS class to the element. ",
"You can use string (similarly to ", el("code", "class=\"…\"") ," syntax in HTML) or array of strings. ",
"This is handy to concat conditional classes."
el("code", "el(<function>, <function-argument(s)>)[.append(...)]: <element-returned-by-function>"), " — using component represented by function",
),
el("li").append(
"Use ", el("code", "classList"), " to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.",
el("code", "el(<...>, <...>, ...<addons>)"), " — see following page"
),
el("li").append(
"The ", el("code", "assign"), " also accepts the ", el("code", "undefined"), " as a value for any property to remove it from the element declaratively. ",
"Also for some IDL the corresponding attribute is removed as it can be confusing. ",
el("em").append(
"For example, natievly the elements ", el("code", "id"), " is removed by setting the IDL to an empty string."
)
),
el("li").append(
"You can use ", el("code", "="), " or ", el("code", "."), " to force processing given key as attribute/property of the element."
el("code", "elNS(<namespace>)(<as-el-see-above>)[.append(...)]: <element-based-on-arguments>"), " — typically SVG elements",
)
),
el("p").append(
"For processing, the ", el("code", "assign"), " internally uses ", el("code", "assignAttribute"), " and ", el("code", "classListDeclarative"), "."
),
el(example, { src: fileURL("./components/examples/elements/dekaAssign.js"), page_id }),
el(h3, "Native JavaScript templating"),
el("p", "By default, the native JS has no good way to define HTML template using DOM API:"),
el(example, { src: fileURL("./components/examples/elements/nativeAppend.js"), page_id }),
el("p").append(
"This library therefore overwrites the ", el("code", "append"), " method of created elements to always return parent element."
),
el(example, { src: fileURL("./components/examples/elements/dekaAppend.js"), page_id }),
el(h3, "Basic (state-less) components"),
el("p").append(
"You can use functions for encapsulation (repeating) logic. ",
"The ", el("code", "el"), " accepts function as first argument. ",
"In that case, the function should return dom elements and the second argument for ", el("code", "el"), " is argument for given element."
),
el(example, { src: fileURL("./components/examples/elements/dekaBasicComponent.js"), page_id }),
el("p").append(
"As you can see, in case of state-less/basic components there is no difference",
" between calling component function directly or using ", el("code", "el"), " function.",
),
el("p", { className: "notice" }).append(
"It is nice to use similar naming convention as native DOM API. ",
"This allows us to use ", el("a", { textContent: "the destructuring assignment syntax", href: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment", title: "Destructuring assignment | MDN" }),
" and keep track of the native API (things are best remembered through regular use).",
),
el(h3, "Creating non-HTML elements"),
el("p").append(
"Similarly to the native DOM API (", el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS", title: "MDN" }).append(el("code", "document.createElementNS")), ") for non-HTML elements",
" we need to tell JavaScript which kind of the element to create.",
" We can use the ", el("code", "elNS"), " function:"
),
el(example, { src: fileURL("./components/examples/elements/dekaElNS.js"), page_id }),
el("div", { className: "notice" }).append(
el("p", "Mnemonic"),
el("ul").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(prevNext, info)
)
)
),
);
}

View File

@ -1,139 +1,131 @@
import "./global.css.js";
import { simplePage } from "./layout/simplePage.html.js";
import { el } from "deka-dom-el";
import { header } from "./layout/head.html.js";
import { example } from "./components/example.html.js";
import { h3, prevNext } from "./components/pageUtils.html.js";
import { h3 } from "./components/pageUtils.html.js";
/** @param {string} url */
const fileURL= url=> new URL(url, import.meta.url);
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el().append(
el(header, { info, pkg }),
el("main").append(
el("h2", "Listenning to the native DOM events and other Addons"),
return el(simplePage, { info, pkg }).append(
el("h2", "Listenning to the native DOM events and other Addons"),
el("p").append(
"We quickly introduce helper to listening to the native DOM events.",
" ",
"And library syntax/pattern so-called ", el("em", "Addon"), " to",
" incorporate not only this in UI templates declaratively."
),
el(h3, "Events and listenners"),
el("p").append(
"In JavaScript you can listen to the native DOM events of the given element by using ",
el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener", title: "addEventListener on MDN" }).append(
el("code", "element.addEventListener(type, listener, options)")
), ".",
" ",
"The library provides an alternative (", el("code", "on"), ") accepting the differen order",
" of the arguments:"
),
el(example, { src: fileURL("./components/examples/events/compare.js"), page_id }),
el("p").append(
"…this is actually one of the two differences. The another one is that ", el("code", "on"),
" accepts only object as the ", el("code", "options"), " (but it is still optional)."
),
el("p", { className: "notice" }).append(
"The other difference is that there is ", el("strong", "no"), " ", el("code", "off"), " function.",
" ",
"You can remove listener declaratively using ", el("a", { textContent: "AbortSignal", href: "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#signal", title: "part of addEventListener on MDN" }),
":"
),
el(example, { src: fileURL("./components/examples/events/abortSignal.js"), page_id }),
el("div", { className: "notice" }).append(
el("p", "So, there are (typically) three ways to handle events. You can use:"),
el("ul").append(
el("li").append( el("code", `el("button", { textContent: "click me", "=onclick": "console.log(event)" })`)),
el("li").append( el("code", `el("button", { textContent: "click me", onclick: console.log })`)),
el("li").append( el("code", `el("button", { textContent: "click me" }, on("click", console.log))`))
),
el("p").append(
"We quickly introduce helper to listening to the native DOM events.",
"In the first example we force to use HTML attribute (it corresponds to ", el("code", `<button onclick="console.log(event)">click me</button>`), ").",
" ",
"And library syntax/pattern so-called ", el("em", "Addon"), " to",
" incorporate not only this in UI templates declaratively."
),
el(h3, "Events and listenners"),
el("p").append(
"In JavaScript you can listen to the native DOM events of the given element by using ",
el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener", title: "addEventListener on MDN" }).append(
el("code", "element.addEventListener(type, listener, options)")
), ".",
el("em", "Side note: this can be useful in case of SSR."),
" ",
"The library provides an alternative (", el("code", "on"), ") accepting the differen order",
" of the arguments:"
),
el(example, { src: fileURL("./components/examples/events/compare.js"), page_id }),
el("p").append(
"…this is actually one of the two differences. The another one is that ", el("code", "on"),
" accepts only object as the ", el("code", "options"), " (but it is still optional)."
),
el("p", { className: "notice" }).append(
"The other difference is that there is ", el("strong", "no"), " ", el("code", "off"), " function.",
" ",
"You can remove listener declaratively using ", el("a", { textContent: "AbortSignal", href: "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#signal", title: "part of addEventListener on MDN" }),
":"
),
el(example, { src: fileURL("./components/examples/events/abortSignal.js"), page_id }),
el("div", { className: "notice" }).append(
el("p", "So, there are (typically) three ways to handle events. You can use:"),
el("ul").append(
el("li").append( el("code", `el("button", { textContent: "click me", "=onclick": "console.log(event)" })`)),
el("li").append( el("code", `el("button", { textContent: "click me", onclick: console.log })`)),
el("li").append( el("code", `el("button", { textContent: "click me" }, on("click", console.log))`))
),
el("p").append(
"In the first example we force to use HTML attribute (it corresponds to ", el("code", `<button onclick="console.log(event)">click me</button>`), ").",
" ",
el("em", "Side note: this can be useful in case of SSR."),
" ",
"To study difference, you can read a nice summary here: ", el("a", { href: "https://gist.github.com/WebReflection/b404c36f46371e3b1173bf5492acc944", textContent: "GIST @WebReflection/web_events.md" }), "."
)
),
"To study difference, you can read a nice summary here: ", el("a", { href: "https://gist.github.com/WebReflection/b404c36f46371e3b1173bf5492acc944", textContent: "GIST @WebReflection/web_events.md" }), "."
)
),
el(h3, "Addons"),
el("p").append(
"From practical point of view, ", el("em", "Addons"), " are just functions that accept any html element",
" as their first parameter. You can see that the ", el("code", "on(…)"), " fullfills this requirement."
el(h3, "Addons"),
el("p").append(
"From practical point of view, ", el("em", "Addons"), " are just functions that accept any html element",
" as their first parameter. You can see that the ", el("code", "on(…)"), " fullfills this requirement."
),
el("p").append(
"You can use Addons as ≥3rd argument of ", el("code", "el"), " function. This way is possible to extends",
" you templates by additional (3rd party) functionalities. But for now mainly, you can add events listeners:"
),
el(example, { src: fileURL("./components/examples/events/templateWithListeners.js"), page_id }),
el("p").append(
"As the example shows, you can also provide types in JSDoc+TypeScript by using global type ", el("code", "ddeElementAddon"), ".",
" ",
"Also notice, you can use Addons to get element reference.",
),
el(h3, "Life-cycle events"),
el("p").append(
"Addons are called immediately when the element is created, even it is not connected to live DOM yet.",
" ",
"Therefore, you can understand the Addon to be “oncreate” event."
),
el("p").append(
"The library provide three additional live-cycle events corresponding to how they are named in",
" a case of custom elements: ", el("code", "on.connected"), ", ", el("code", "on.disconnected"),
" and ", el("code", "on.attributeChanged"), "."
),
el(example, { src: fileURL("./components/examples/events/live-cycle.js"), page_id }),
el("p").append(
"For Custom elements, we will later introduce a way to replace ", el("code", "*Callback"),
" syntax with ", el("code", "dde:*"), " events. The ", el("code", "on.*"), " functions then",
" listen to the appropriate Custom Elements events (see ", el("a", { textContent: "Custom element lifecycle callbacks | MDN", href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks" }), ")."
),
el("p").append(
"But, in case of regular elemnets the ", el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver" }).append(el("code", "MutationObserver"), " | MDN"),
" is internaly used to track these events. Therefore, there are some drawbacks:",
),
el("ul").append(
el("li").append(
"To proper listener registration, you need to use ", el("code", "on.*"), " not `on(\"dde:*\", …)`!"
),
el("p").append(
"You can use Addons as ≥3rd argument of ", el("code", "el"), " function. This way is possible to extends",
" you templates by additional (3rd party) functionalities. But for now mainly, you can add events listeners:"
),
el(example, { src: fileURL("./components/examples/events/templateWithListeners.js"), page_id }),
el("p").append(
"As the example shows, you can also provide types in JSDoc+TypeScript by using global type ", el("code", "ddeElementAddon"), ".",
el("li").append(
"Use sparingly! Internally, library must loop of all registered events and fires event properly.",
" ",
"Also notice, you can use Addons to get element reference.",
),
el(h3, "Life-cycle events"),
el("p").append(
"Addons are called immediately when the element is created, even it is not connected to live DOM yet.",
el("strong", "It is good practice to use the fact that if an element is removed, its children are also removed!"),
" ",
"Therefore, you can understand the Addon to be “oncreate” event."
),
el("p").append(
"The library provide three additional live-cycle events corresponding to how they are named in",
" a case of custom elements: ", el("code", "on.connected"), ", ", el("code", "on.disconnected"),
" and ", el("code", "on.attributeChanged"), "."
),
el(example, { src: fileURL("./components/examples/events/live-cycle.js"), page_id }),
el("p").append(
"For Custom elements, we will later introduce a way to replace ", el("code", "*Callback"),
" syntax with ", el("code", "dde:*"), " events. The ", el("code", "on.*"), " functions then",
" listen to the appropriate Custom Elements events (see ", el("a", { textContent: "Custom element lifecycle callbacks | MDN", href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks" }), ")."
),
el("p").append(
"But, in case of regular elemnets the ", el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver" }).append(el("code", "MutationObserver"), " | MDN"),
" is internaly used to track these events. Therefore, there are some drawbacks:",
"In this spirit, we will introduce later the ", el("strong", "host"), " syntax to register",
" clean up procedures when the component is removed from the app."
),
),
el(h3, "Final notes"),
el("p", "The library also provides a method to dispatch the events."),
el(example, { src: fileURL("./components/examples/events/compareDispatch.js"), page_id }),
el("div", { className: "notice" }).append(
el("p", "Mnemonic"),
el("ul").append(
el("li").append(
"To proper listener registration, you need to use ", el("code", "on.*"), " not `on(\"dde:*\", …)`!"
el("code", "on(<event>, <listener>[, <options>])(<element>)"), " — just ", el("code", "<element>.addEventListener(<event>, <listener>[, <options>])")
),
el("li").append(
"Use sparingly! Internally, library must loop of all registered events and fires event properly.",
" ",
el("strong", "It is good practice to use the fact that if an element is removed, its children are also removed!"),
" ",
"In this spirit, we will introduce later the ", el("strong", "host"), " syntax to register",
" clean up procedures when the component is removed from the app."
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(h3, "Final notes"),
el("p", "The library also provides a method to dispatch the events."),
el(example, { src: fileURL("./components/examples/events/compareDispatch.js"), page_id }),
el("div", { className: "notice" }).append(
el("p", "Mnemonic"),
el("ul").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(prevNext, info)
)
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> }))")
),
)
),
);
}

View File

@ -1,80 +1,75 @@
import "./global.css.js";
import { simplePage } from "./layout/simplePage.html.js";
import { el } from "deka-dom-el";
import { header } from "./layout/head.html.js";
import { example } from "./components/example.html.js";
import { h3, prevNext } from "./components/pageUtils.html.js";
import { h3 } from "./components/pageUtils.html.js";
/** @param {string} url */
const fileURL= url=> new URL(url, import.meta.url);
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el().append(
el(header, { info, pkg }),
el("main").append(
el("h2", "Using signals to manage reactivity"),
el("p").append(
"How a program responds to variable data or user",
" interactions is one of the fundamental problems of programming.",
" If we desire to solve the issue in a declarative manner,",
" signals may be a viable approach.",
),
el(example, { src: fileURL("./components/examples/signals/intro.js"), page_id }),
el(h3, "Introducing signals"),
el("p").append(
"Using signals, we split program logic into the three parts.",
" Firstly (α), we create a variable (constant) representing reactive",
" value. Somewhere later, we can register (β) a logic reacting",
" to the signal value changes. Similarly, in a remaining part (γ), we",
" can update the signal value."
),
el("p").append(
"All this is just an example of ",
el("a", { textContent: "Event-driven programming", href: "https://en.wikipedia.org/wiki/Event-driven_programming", title: "Wikipedia: Event-driven programming" }),
" and ",
el("a", { textContent: "Publishsubscribe pattern", href: "https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern", title: "Wikipedia: Publishsubscribe pattern" }),
" (compare for example with ", el("a", { textContent: "fpubsub library", href: "https://www.npmjs.com/package/fpubsub", title: "NPM package: fpubsub" }), ").",
" All three parts can be in some manner independent and still connected",
" to the same reactive entity."
),
el("p").append(
"Signals are implemented in the library as functions. To see current value",
" of signal, just call it without any arguments ", el("code", "console.log(signal())"), ".",
" To update the signal value, pass any argument ", el("code", "signal('a new value')"), ".",
" For listenning the signal value changes, use ", el("code", "S.on(signal, console.log)"), "."
),
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(example, { src: fileURL("./components/examples/signals/computations-abort.js"), page_id }),
el("div", { className: "notice" }).append(
el("p", "Mnemonic"),
el("ul").append(
el("li").append(
el("code", "S(<value>)"), " — signal: reactive value",
),
el("li").append(
el("code", "S(()=> <computation>)"), " — signal: reactive value dependent on calculation using other signals",
),
el("li").append(
el("code", "S.on(<signal>, <listener>[, <options>])"), " — listen to the signal value changes",
),
el("li").append(
el("code", "S.clear(...<signals>)"), " — off and clear signals",
),
el("li").append(
el("code", "S(<value>, <actions>)"), " — signal: pattern to create complex reactive objects/arrays",
),
el("li").append(
el("code", "S.action(<signal>, <action-name>, ...<action-arguments>)"), " — invoke an action for given signal"
)
return el(simplePage, { info, pkg }).append(
el("h2", "Using signals to manage reactivity"),
el("p").append(
"How a program responds to variable data or user",
" interactions is one of the fundamental problems of programming.",
" If we desire to solve the issue in a declarative manner,",
" signals may be a viable approach.",
),
el(example, { src: fileURL("./components/examples/signals/intro.js"), page_id }),
el(h3, "Introducing signals"),
el("p").append(
"Using signals, we split program logic into the three parts.",
" Firstly (α), we create a variable (constant) representing reactive",
" value. Somewhere later, we can register (β) a logic reacting",
" to the signal value changes. Similarly, in a remaining part (γ), we",
" can update the signal value."
),
el("p").append(
"All this is just an example of ",
el("a", { textContent: "Event-driven programming", href: "https://en.wikipedia.org/wiki/Event-driven_programming", title: "Wikipedia: Event-driven programming" }),
" and ",
el("a", { textContent: "Publishsubscribe pattern", href: "https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern", title: "Wikipedia: Publishsubscribe pattern" }),
" (compare for example with ", el("a", { textContent: "fpubsub library", href: "https://www.npmjs.com/package/fpubsub", title: "NPM package: fpubsub" }), ").",
" All three parts can be in some manner independent and still connected",
" to the same reactive entity."
),
el("p").append(
"Signals are implemented in the library as functions. To see current value",
" of signal, just call it without any arguments ", el("code", "console.log(signal())"), ".",
" To update the signal value, pass any argument ", el("code", "signal('a new value')"), ".",
" For listenning the signal value changes, use ", el("code", "S.on(signal, console.log)"), "."
),
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(example, { src: fileURL("./components/examples/signals/computations-abort.js"), page_id }),
el("div", { className: "notice" }).append(
el("p", "Mnemonic"),
el("ul").append(
el("li").append(
el("code", "S(<value>)"), " — signal: reactive value",
),
el("li").append(
el("code", "S(()=> <computation>)"), " — signal: reactive value dependent on calculation using other signals",
),
el("li").append(
el("code", "S.on(<signal>, <listener>[, <options>])"), " — listen to the signal value changes",
),
el("li").append(
el("code", "S.clear(...<signals>)"), " — off and clear signals",
),
el("li").append(
el("code", "S(<value>, <actions>)"), " — signal: pattern to create complex reactive objects/arrays",
),
el("li").append(
el("code", "S.action(<signal>, <action-name>, ...<action-arguments>)"), " — invoke an action for given signal"
)
),
)
)
),
);
}

1
docs_src/types.d.ts vendored
View File

@ -25,4 +25,5 @@ declare global{
interface ddePublicElementTagNameMap{
["custom-test"]: CustomHTMLTestElement;
}
function test(): ddeHTMLParagraphElement
}