diff --git a/docs/p02-elements.html b/docs/p02-elements.html index 6dd2412..bd39f96 100644 --- a/docs/p02-elements.html +++ b/docs/p02-elements.html @@ -12,7 +12,7 @@ import { classListDeclarative, chainableAppend } from "deka-dom-el"; -

# Creating element(s) (with custom attributes)

You can create a native DOM element by using the document.createElement(). It is also possible to provide a some attribute(s) declaratively using Object.assign(). More precisely, this way you can sets some IDL also known as a JavaScript property.

document.body.append( +

# Creating element(s) (with custom attributes)

You can create a native DOM element by using the document.createElement(). It is also possible to provide a some attribute(s) declaratively using Object.assign(). More precisely, this way you can sets some IDL also known as a JavaScript property.

document.body.append( document.createElement("div") ); console.log( @@ -37,7 +37,7 @@ document.body.append( { textContent: "Hello (second time)", style: { color } } ) ); -

The assign function provides improved behaviour of Object.assign(). You can declaratively sets any IDL and attribute of the given element. Function prefers IDL and fallback to the element.setAttribute if there is no writable property in the element prototype.

You can study all JavaScript elements interfaces to the corresponding HTML elements. All HTML elements inherits from HTMLElement. To see all available IDLs for example for paragraphs, see HTMLParagraphElement. Moreover, the assign provides a way to sets declaratively some convenient properties:

For processing, the assign internally uses assignAttribute and classListDeclarative.

import { assign, assignAttribute, classListDeclarative } from "./esm-with-signals.js"; +

The assign function provides improved behaviour of Object.assign(). You can declaratively sets any IDL and attribute of the given element. Function prefers IDL and fallback to the element.setAttribute if there is no writable property in the element prototype.

You can study all JavaScript elements interfaces to the corresponding HTML elements. All HTML elements inherits from HTMLElement. To see all available IDLs for example for paragraphs, see HTMLParagraphElement. Moreover, the assign provides a way to sets declaratively some convenient properties:

For processing, the assign internally uses assignAttribute and classListDeclarative.

import { assign, assignAttribute, classListDeclarative } from "./esm-with-signals.js"; const paragraph= document.createElement("p"); assignAttribute(paragraph, "textContent", "Hello, world!"); @@ -133,7 +133,7 @@ function component({ className, textContent }){ el("p", textContent) ); } -

As you can see, in case of state-less/basic components there is no difference between calling component function directly or using el function.

It is nice to use similar naming convention as native DOM API. This allows us to use the destructuring assignment syntax and keep track of the native API (things are best remembered through regular use).

# Creating non-HTML elements

Similarly to the native DOM API (document.createElementNS) for non-HTML elements we need to tell JavaScript which kind of the element to create. We can use the elNS function:

import { elNS, assign } from "./esm-with-signals.js"; +

As you can see, in case of state-less/basic components there is no difference between calling component function directly or using el function.

It is nice to use similar naming convention as native DOM API. This allows us to use the destructuring assignment syntax and keep track of the native API (things are best remembered through regular use).

# Creating non-HTML elements

Similarly to the native DOM API (document.createElementNS) for non-HTML elements we need to tell JavaScript which kind of the element to create. We can use the elNS function:

import { elNS, assign } from "./esm-with-signals.js"; const elSVG= elNS("http://www.w3.org/2000/svg"); const elMath= elNS("http://www.w3.org/1998/Math/MathML"); document.body.append( diff --git a/docs/p03-events.html b/docs/p03-events.html index 65ce2b6..807b4d8 100644 --- a/docs/p03-events.html +++ b/docs/p03-events.html @@ -2,7 +2,7 @@ import { on, dispatchEvent } from "deka-dom-el"; /** @type {ddeElementAddon} */ -

# Events and listenners

In JavaScript you can listen to the native DOM events of the given element by using element.addEventListener(type, listener, options). The library provides an alternative (on) accepting the differen order of the arguments:

import { el, on } from "./esm-with-signals.js"; +

# Events and listenners

In JavaScript you can listen to the native DOM events of the given element by using element.addEventListener(type, listener, options). The library provides an alternative (on) accepting the differen order of the arguments:

import { el, on } from "./esm-with-signals.js"; const log= mark=> console.log.bind(console, mark); const button= el("button", "Test click"); @@ -12,7 +12,7 @@ on("click", log("`on`"), { once: true })(button); document.body.append( button ); -

…this is actually one of the two differences. The another one is that on accepts only object as the options (but it is still optional).

The other difference is that there is no off function. You can remove listener declaratively using AbortSignal:

import { el, on } from "./esm-with-signals.js"; +

…this is actually one of the two differences. The another one is that on accepts only object as the options (but it is still optional).

The other difference is that there is no off function. You can remove listener declaratively using AbortSignal:

import { el, on } from "./esm-with-signals.js"; const log= mark=> console.log.bind(console, mark); const abort_controller= new AbortController(); @@ -67,7 +67,7 @@ document.body.append( function log({ type, detail }){ console.log({ _this: this, type, detail }); } -

For Custom elements, we will later introduce a way to replace *Callback syntax with dde:* events. The on.* functions then listen to the appropriate Custom Elements events (see Custom element lifecycle callbacks | MDN).

But, in case of regular elemnets the MutationObserver | MDN is internaly used to track these events. Therefore, there are some drawbacks:

To provide intuitive behaviour, similar also to how the life-cycle events works in other frameworks/libraries, deka-dom-el library ensures that on.connected and on.disconnected are called only once and only when the element is (dis)connected to live DOM. The solution is inspired by Vue. For using native behaviour re-(dis)connecting element, use:

# Final notes

The library also provides a method to dispatch the events.

import { el, on, dispatchEvent } from "./esm-with-signals.js"; +

For Custom elements, we will later introduce a way to replace *Callback syntax with dde:* events. The on.* functions then listen to the appropriate Custom Elements events (see Custom element lifecycle callbacks | MDN).

But, in case of regular elemnets the MutationObserver | MDN is internaly used to track these events. Therefore, there are some drawbacks:

To provide intuitive behaviour, similar also to how the life-cycle events works in other frameworks/libraries, deka-dom-el library ensures that on.connected and on.disconnected are called only once and only when the element is (dis)connected to live DOM. The solution is inspired by Vue. For using native behaviour re-(dis)connecting element, use:

# Final notes

The library also provides a method to dispatch the events.

import { el, on, dispatchEvent } from "./esm-with-signals.js"; document.body.append( el("p", "Listenning to `test` event.", on("test", console.log)).append( el("br"), diff --git a/docs/p04-signals.html b/docs/p04-signals.html index 217de81..a575c56 100644 --- a/docs/p04-signals.html +++ b/docs/p04-signals.html @@ -16,7 +16,7 @@ update(); const interval= 5*1000; setTimeout(clearInterval, 10*interval, setInterval(update, interval)); -

All this is just an example of Event-driven programming and Publish–subscribe pattern (compare for example with fpubsub library). All three parts can be in some manner independent and still connected to the same reactive entity.

Signals are implemented in the library as functions. To see current value of signal, just call it without any arguments console.log(signal()). To update the signal value, pass any argument signal('a new value'). For listenning the signal value changes, use S.on(signal, console.log).

Similarly to the on function to register DOM events listener. You can use AbortController/AbortSignal to off/stop listenning. In example, you also found the way for representing “live” piece of code computation pattern (derived signal):

import { S } from "./esm-with-signals.js"; +

All this is just an example of Event-driven programming and Publish–subscribe pattern (compare for example with fpubsub library). All three parts can be in some manner independent and still connected to the same reactive entity.

Signals are implemented in the library as functions. To see current value of signal, just call it without any arguments console.log(signal()). To update the signal value, pass any argument signal('a new value'). For listenning the signal value changes, use S.on(signal, console.log).

Similarly to the on function to register DOM events listener. You can use AbortController/AbortSignal to off/stop listenning. In example, you also found the way for representing “live” piece of code computation pattern (derived signal):

import { S } from "./esm-with-signals.js"; const signal= S(0); // computation pattern const double= S(()=> 2*signal()); @@ -106,7 +106,7 @@ function radio({ textContent, checked= false }){ " ",textContent ) } -

In some way, you can compare it with useReducer hook from React. So, the S(<data>, <actions>) pattern creates a store “machine”. We can then invoke (dispatch) registered action by calling S.action(<signal>, <name>, ...<args>) after the action call the signal calls all its listeners. This can be stopped by calling 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 (this.value).

# Reactive DOM attributes and elements

There are on basic level two distinc situation to mirror dynamic value into the DOM/UI

  1. to change some attribute(s) of existing element(s)
  2. to generate elements itself dynamically – this covers conditions and loops
import { S } from "./esm-with-signals.js"; +

In some way, you can compare it with useReducer hook from React. So, the S(<data>, <actions>) pattern creates a store “machine”. We can then invoke (dispatch) registered action by calling S.action(<signal>, <name>, ...<args>) after the action call the signal calls all its listeners. This can be stopped by calling 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 (this.value).

# Reactive DOM attributes and elements

There are on basic level two distinc situation to mirror dynamic value into the DOM/UI

  1. to change some attribute(s) of existing element(s)
  2. to generate elements itself dynamically – this covers conditions and loops
import { S } from "./esm-with-signals.js"; const count= S(0); import { el } from "./esm-with-signals.js"; diff --git a/docs/p05-scopes.html b/docs/p05-scopes.html index 70bb807..162f927 100644 --- a/docs/p05-scopes.html +++ b/docs/p05-scopes.html @@ -1,4 +1,4 @@ -`deka-dom-el` — Scopes and components

`deka-dom-el` — Scopes and components

Organizing UI into components

Using functions as UI components

For state-less components we can use functions as UI components (see “Elements” page). But in real life, we may need to handle the component live-cycle and provide JavaScript the way to properly use the Garbage collection.

// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js +`deka-dom-el` — Scopes and components

`deka-dom-el` — Scopes and components

Organizing UI into components

Using functions as UI components

For state-less components we can use functions as UI components (see “Elements” page). But in real life, we may need to handle the component live-cycle and provide JavaScript the way to properly use the Garbage collection.

// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js import { scope, el } from "deka-dom-el"; /** @type {ddeElementAddon} */

The library therefore use scopes to provide these functionalities.

# Scopes and hosts

The host is the name for the element representing the component. This is typically element returned by function. To get reference, you can use scope.host() to applly addons just use scope.host(...<addons>).

import { el, on, scope } from "./esm-with-signals.js"; @@ -114,7 +114,7 @@ function component(){ }); return el("p", textContent, onclickChange); } -

The text content of the paragraph is changing when the value of the signal textContent is changed. Internally, there is association between textContent and the paragraph similar to using S.on(textContent, /* update the paragraph */).

This listener must be removed when the component is removed from the DOM. To do it, the library assign internally on.disconnected(/* remove the listener */)(host()) to the host element.

The library DOM API and signals works ideally when used declaratively. It means, you split your app logic into three parts as it was itroduced in Signals.

/* PSEUDO-CODE!!! */ +

The text content of the paragraph is changing when the value of the signal textContent is changed. Internally, there is association between textContent and the paragraph similar to using S.on(textContent, /* update the paragraph */).

This listener must be removed when the component is removed from the DOM. To do it, the library assign internally on.disconnected(/* remove the listener */)(host()) to the host element.

The library DOM API and signals works ideally when used declaratively. It means, you split your app logic into three parts as it was itroduced in Signals.

/* PSEUDO-CODE!!! */ import { el } from "deka-dom-el"; import { S } from "deka-dom-el/signals"; function component(){ diff --git a/docs/p06-customElement.html b/docs/p06-customElement.html index f6229a7..a7b5cc8 100644 --- a/docs/p06-customElement.html +++ b/docs/p06-customElement.html @@ -10,7 +10,7 @@ S.observedAttributes; // “internal” utils import { lifecyclesToEvents } from "deka-dom-el"; -

# Custom Elements Introduction

Using custom elements

class CustomHTMLElement extends HTMLElement{ +

# Custom Elements Introduction

Using custom elements

class CustomHTMLElement extends HTMLElement{ static tagName = "custom-element"; // just suggestion, we can use `el(CustomHTMLElement.tagName)` static observedAttributes= [ "custom-attribute" ]; constructor(){ @@ -31,4 +31,4 @@ import { lifecyclesToEvents } from "deka-dom-el"; set customAttribute(value){ this.setAttribute("custom-attribute", value); } } customElements.define(CustomHTMLElement.tagName, CustomHTMLElement); -

Handy Custom Elements' Patterns

# Mnemonic

  • customElementRender(<custom-element>, <render-function>[, <properties>]) — use function to render DOM structure for given <custom-element>
  • customElementWithDDE(<custom-element>) — register <custom-element> to DDE library, see also `lifecyclesToEvents`, can be also used as decorator
  • observedAttributes(<custom-element>) — returns record of observed attributes (keys uses camelCase)
  • S.observedAttributes(<custom-element>) — returns record of observed attributes (keys uses camelCase and values are signals)
  • lifecyclesToEvents(<class-declaration>) — convert lifecycle methods to events, can be also used as decorator
\ No newline at end of file +

Handy Custom Elements' Patterns

# Mnemonic

  • customElementRender(<custom-element>, <render-function>[, <properties>]) — use function to render DOM structure for given <custom-element>
  • customElementWithDDE(<custom-element>) — register <custom-element> to DDE library, see also `lifecyclesToEvents`, can be also used as decorator
  • observedAttributes(<custom-element>) — returns record of observed attributes (keys uses camelCase)
  • S.observedAttributes(<custom-element>) — returns record of observed attributes (keys uses camelCase and values are signals)
  • lifecyclesToEvents(<class-declaration>) — convert lifecycle methods to events, can be also used as decorator
\ No newline at end of file diff --git a/docs_src/p02-elements.html.js b/docs_src/p02-elements.html.js index 5c20766..03e676c 100644 --- a/docs_src/p02-elements.html.js +++ b/docs_src/p02-elements.html.js @@ -11,6 +11,38 @@ 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); +const references= { + /** document.createElement() */ + mdn_create: { + title: "MDN documentation page for document.createElement()", + href: "https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement", + }, + /** Interface Description Language (`el.textContent`) */ + mdn_idl: { + title: "MDN page about Interface Description Language", + href: "https://developer.mozilla.org/en-US/docs/Glossary/IDL", + }, + /** HTMLElement */ + mdn_el: { + title: "MDN documentation page for HTMLElement", + href: "https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement" + }, + /** HTMLParagraphElement */ + mdn_p: { + title: "MDN documentation page for HTMLParagraphElement (`p` tag)", + href: "https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement" + }, + /** `[a, b] = [1, 2]` */ + mdn_destruct: { + title: "MDN page about destructuring assignment syntax (e.g. `[a, b] = [1, 2]`)", + href: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment", + }, + /** document.createElementNS() */ + mdn_ns: { + title: "MDN documentation page for document.createElementNS() (e.g. for SVG elements)", + href: "https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS", + } +}; /** @param {import("./types.d.ts").PageAttrs} attrs */ export function page({ pkg, info }){ const page_id= info.id; @@ -22,16 +54,10 @@ export function page({ pkg, info }){ 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()") - ), ". ", + "You can create a native DOM element by using the ", el("a", references.mdn_create).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("a", references.mdn_idl).append( el("abbr", { textContent: "IDL", title: "Interface Description Language" }) ), " also known as a JavaScript property." ), @@ -48,8 +74,8 @@ export function page({ pkg, info }){ ), 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" }), ". ", + "All HTML elements inherits from ", el("a", { textContent: "HTMLElement", ...references.mdn_el }), ". ", + "To see all available IDLs for example for paragraphs, see ", el("a", { textContent: "HTMLParagraphElement", ...references.mdn_p }), ". ", "Moreover, the ", el("code", "assign"), " provides a way to sets declaratively some convenient properties:" ), el("ul").append( @@ -108,13 +134,13 @@ export function page({ pkg, info }){ ), 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" }), + "This allows us to use ", el("a", { textContent: "the destructuring assignment syntax", ...references.mdn_destruct }), " 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", + "Similarly to the native DOM API (", el("a", references.mdn_ns).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:" ), diff --git a/docs_src/p03-events.html.js b/docs_src/p03-events.html.js index 9611ed4..b75281e 100644 --- a/docs_src/p03-events.html.js +++ b/docs_src/p03-events.html.js @@ -11,6 +11,35 @@ 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); +const references= { + /** element.addEventListener() */ + mdn_listen: { + title: "MDN documentation page for elemetn.addEventListener", + href: "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener", + }, + /** AbortSignal+element.addEventListener */ + mdn_abortListener: { + title: "MDN documentation page for using AbortSignal with element.addEventListener", + href: "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#signal", + }, + /** comparison listening options by WebReflection */ + web_events: { + href: "https://gist.github.com/WebReflection/b404c36f46371e3b1173bf5492acc944", + }, + /** Custom Element lifecycle callbacks */ + mdn_customElement: { + href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks" + }, + /** MutationObserver */ + mdn_mutation: { + href: "https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver", + }, + /** Readding the element to the DOM fix by Vue */ + vue_fix: { + title: "Vue and Web Components, lifecycle implementation readding the element to the DOM", + href: "https://vuejs.org/guide/extras/web-components.html#lifecycle", + } +}; /** @param {import("./types.d.ts").PageAttrs} attrs */ export function page({ pkg, info }){ const page_id= info.id; @@ -28,9 +57,7 @@ export function page({ pkg, info }){ 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("a", references.mdn_listen).append( el("code", "element.addEventListener(type, listener, options)") ), ".", " ", "The library provides an alternative (", el("code", "on"), ") accepting the differen order", " of the arguments:" @@ -43,7 +70,7 @@ export function page({ pkg, info }){ 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" }), + "You can remove listener declaratively using ", el("a", { textContent: "AbortSignal", ...references.mdn_abortListener }), ":" ), el(example, { src: fileURL("./components/examples/events/abortSignal.js"), page_id }), @@ -59,7 +86,7 @@ export function page({ pkg, info }){ " ", 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", { textContent: "GIST @WebReflection/web_events.md", ...references.web_events }), "." ) ), @@ -93,10 +120,10 @@ export function page({ pkg, info }){ 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" }), ")." + " listen to the appropriate Custom Elements events (see ", el("a", { textContent: "Custom element lifecycle callbacks | MDN", ...references.mdn_customElement }), ")." ), 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"), + "But, in case of regular elemnets the ", el("a", references.mdn_mutation).append(el("code", "MutationObserver"), " | MDN"), " is internaly used to track these events. Therefore, there are some drawbacks:", ), el("ul").append( @@ -116,7 +143,7 @@ export function page({ pkg, info }){ "To provide intuitive behaviour, similar also to how the life-cycle events works in other", " frameworks/libraries, deka-dom-el library ensures that ", el("code", "on.connected"), " and ", el("code", "on.disconnected"), " are called only once and only when the element", - " is (dis)connected to live DOM. The solution is inspired by ", el("a", { textContent: "Vue", href: "https://vuejs.org/guide/extras/web-components.html#lifecycle", title: "Vue and Web Components | Lifecycle" }), ".", + " is (dis)connected to live DOM. The solution is inspired by ", el("a", { textContent: "Vue", ...references.vue_fix }), ".", " For using native behaviour re-(dis)connecting element, use:" ), el("ul").append( diff --git a/docs_src/p04-signals.html.js b/docs_src/p04-signals.html.js index d7d480c..ea0eba2 100644 --- a/docs_src/p04-signals.html.js +++ b/docs_src/p04-signals.html.js @@ -11,7 +11,33 @@ import { mnemonic } from "./components/mnemonic/signals-init.js"; import { code } from "./components/code.html.js"; /** @param {string} url */ const fileURL= url=> new URL(url, import.meta.url); - +const references= { + /** Event-driven programming */ + wiki_event_driven: { + title: "Wikipedia: Event-driven programming", + href: "https://en.wikipedia.org/wiki/Event-driven_programming", + }, + /** Publish–subscribe pattern */ + wiki_pubsub: { + title: "Wikipedia: Publish–subscribe pattern", + href: "https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern", + }, + /** NPM package: fpubsub */ + fpubsub: { + title: "NPM package: fpubsub", + href: "https://www.npmjs.com/package/fpubsub", + }, + /** JS Primitives | MDN */ + mdn_primitive: { + title: "Primitive | MDN", + href: "https://developer.mozilla.org/en-US/docs/Glossary/Primitive", + }, + /** useReducer */ + mdn_use_reducer: { + title: "useReducer hook | React docs", + href: "https://react.dev/reference/react/useReducer", + } +}; /** @param {import("./types.d.ts").PageAttrs} attrs */ export function page({ pkg, info }){ const page_id= info.id; @@ -36,10 +62,10 @@ export function page({ pkg, info }){ el(example, { src: fileURL("./components/examples/signals/signals.js"), page_id }), 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" }), + el("a", { textContent: "Event-driven programming", ...references.wiki_event_driven }), " and ", - el("a", { textContent: "Publish–subscribe pattern", href: "https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern", title: "Wikipedia: Publish–subscribe pattern" }), - " (compare for example with ", el("a", { textContent: "fpubsub library", href: "https://www.npmjs.com/package/fpubsub", title: "NPM package: fpubsub" }), ").", + el("a", { textContent: "Publish–subscribe pattern", ...references.wiki_pubsub }), + " (compare for example with ", el("a", { textContent: "fpubsub library", ...references.fpubsub }), ").", " All three parts can be in some manner independent and still connected", " to the same reactive entity." ), @@ -60,7 +86,7 @@ export function page({ pkg, info }){ el(h3, "Signals and actions"), el("p").append( el("code", "S(/* 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" }), ".", + " around ", el("em", "immutable"), " ", el("a", { textContent: "primitive types", ...references.mdn_primitive }), ".", " ", "However, it may also be necessary to use reactive arrays, objects, or other complex reactive structures." ), @@ -68,7 +94,7 @@ export function page({ pkg, info }){ el("p", "…but typical user-case is object/array (maps, sets and other mutable objects):"), el(example, { src: fileURL("./components/examples/signals/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" }), + "In some way, you can compare it with ", el("a", { textContent: "useReducer", ...references.mdn_use_reducer }), " hook from React. So, the ", el("code", "S(, )"), " pattern creates", " a store “machine”. We can then invoke (dispatch) registered action by calling", " ", el("code", "S.action(, , ...)"), " after the action call", diff --git a/docs_src/p05-scopes.html.js b/docs_src/p05-scopes.html.js index 78e6cbe..733b34b 100644 --- a/docs_src/p05-scopes.html.js +++ b/docs_src/p05-scopes.html.js @@ -11,7 +11,18 @@ import { mnemonic } from "./components/mnemonic/scopes-init.js"; import { code } from "./components/code.html.js"; /** @param {string} url */ const fileURL= url=> new URL(url, import.meta.url); - +const references= { + /** Garbage collection on MDN */ + garbage_collection: { + title: "MDN documentation page for Garbage collection", + href: "https://developer.mozilla.org/en-US/docs/Glossary/Garbage_collection", + }, + /** Signals */ + signals: { + title: "Signals section on this library", + href: "./p04-signals#h-introducing-signals", + } +}; /** @param {import("./types.d.ts").PageAttrs} attrs */ export function page({ pkg, info }){ const page_id= info.id; @@ -20,7 +31,7 @@ export function page({ pkg, info }){ el("p").append( "For state-less components we can use functions as UI components (see “Elements” page).", " But in real life, we may need to handle the component live-cycle and provide", - " JavaScript the way to properly use the ", el("a", { textContent: "Garbage collection", href: "https://developer.mozilla.org/en-US/docs/Glossary/Garbage_collection", title: "Garbage collection | MDN" }), "." + " JavaScript the way to properly use the ", el("a", { textContent: "Garbage collection", ...references.garbage_collection }), "." ), el(code, { src: fileURL("./components/examples/scopes/intro.js"), page_id }), el("p").append( @@ -64,7 +75,7 @@ export function page({ pkg, info }){ ), el("p", { className: "notice" }).append( "The library DOM API and signals works ideally when used declaratively.", - " It means, you split your app logic into three parts as it was itroduced in ", el("a", { textContent: "Signals", href: "http://localhost:40911/docs/p04-signals#h-introducing-signals" }), "." + " It means, you split your app logic into three parts as it was itroduced in ", el("a", { textContent: "Signals", ...references.signals }), "." ), el(code, { src: fileURL("./components/examples/scopes/declarative.js"), page_id }), el("p").append( diff --git a/docs_src/p06-customElement.html.js b/docs_src/p06-customElement.html.js index 605e507..0233522 100644 --- a/docs_src/p06-customElement.html.js +++ b/docs_src/p06-customElement.html.js @@ -11,7 +11,18 @@ import { mnemonic } from "./components/mnemonic/customElement-init.js"; import { code } from "./components/code.html.js"; /** @param {string} url */ const fileURL= url=> new URL(url, import.meta.url); - +const references= { + /** Custom Elements on MDN */ + custom_elements: { + title: "MDN documentation page for Custom Elements", + href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements", + }, + /** Custom Elements tips from WebReflection */ + custom_elements_tips: { + title: "Ideas and tips from WebReflection", + href: "https://gist.github.com/WebReflection/ec9f6687842aa385477c4afca625bbf4", + } +}; /** @param {import("./types.d.ts").PageAttrs} attrs */ export function page({ pkg, info }){ const page_id= info.id; @@ -24,11 +35,11 @@ export function page({ pkg, info }){ el(h3, "Custom Elements Introduction"), el("p").append( - el("a", { textContent: "Using custom elements", href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements", title: "Article about custom elements on MDN" }) + el("a", { textContent: "Using custom elements", ...references.custom_elements }) ), el(code, { src: fileURL("./components/examples/customElement/native-basic.js"), page_id }), el("p").append( - el("a", { textContent: "Handy Custom Elements' Patterns", href: "https://gist.github.com/WebReflection/ec9f6687842aa385477c4afca625bbf4", title: "Ideas and tips from WebReflection" }) + el("a", { textContent: "Handy Custom Elements' Patterns", ...references.custom_elements_tips }) ), el(mnemonic)