mirror of
https://github.com/jaandrle/deka-dom-el
synced 2025-07-01 12:22:15 +02:00
⚡ Adds Web Componens page into the doc and doc enhancements (#21)
This commit is contained in:
@ -14,15 +14,14 @@ ${host}{
|
||||
--shiki-token-punctuation: var(--code);
|
||||
--shiki-token-link: #EE0000;
|
||||
white-space: pre;
|
||||
tab-size: 2;${""/* TODO: allow custom tab size?! */}
|
||||
tab-size: 2; /* TODO: allow custom tab size?! */
|
||||
overflow: scroll;
|
||||
}
|
||||
${host}[data-js=todo]{
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--standard-border-radius);
|
||||
margin-bottom: 1rem;
|
||||
${/* to fix shift when → dataJS=done */""}
|
||||
margin-top: 18.4px;
|
||||
margin-top: 18.4px; /* to fix shift when → dataJS=done */
|
||||
padding: 1rem 1.4rem;
|
||||
}
|
||||
`;
|
||||
|
@ -0,0 +1,23 @@
|
||||
import { customElementWithDDE, el, on } from "deka-dom-el";
|
||||
export class HTMLCustomElement extends HTMLElement{
|
||||
static tagName= "custom-element";
|
||||
connectedCallback(){
|
||||
this.append(
|
||||
el("p", "Hello from custom element!")
|
||||
);
|
||||
}
|
||||
}
|
||||
customElementWithDDE(HTMLCustomElement);
|
||||
customElements.define(HTMLCustomElement.tagName, HTMLCustomElement);
|
||||
|
||||
const instance= el(HTMLCustomElement.tagName);
|
||||
on.connected( // preffered
|
||||
e=> console.log("Element connected to the DOM (v1):", e)
|
||||
)(instance);
|
||||
instance.addEventListener(
|
||||
"dde:connected",
|
||||
e=> console.log("Element connected to the DOM (v2):", e)
|
||||
);
|
||||
document.body.append(
|
||||
instance,
|
||||
);
|
33
docs_src/components/examples/customElement/dde.js
Normal file
33
docs_src/components/examples/customElement/dde.js
Normal file
@ -0,0 +1,33 @@
|
||||
import {
|
||||
customElementRender,
|
||||
customElementWithDDE,
|
||||
} from "deka-dom-el";
|
||||
export class HTMLCustomElement extends HTMLElement{
|
||||
static tagName= "custom-element";
|
||||
static observedAttributes= [ "attr" ];
|
||||
connectedCallback(){
|
||||
customElementRender(
|
||||
this,
|
||||
this.attachShadow({ mode: "open" }),
|
||||
ddeComponent
|
||||
);
|
||||
}
|
||||
set attr(value){ this.setAttribute("attr", value); }
|
||||
get attr(){ return this.getAttribute("attr"); }
|
||||
}
|
||||
|
||||
import { el, on, scope } from "deka-dom-el";
|
||||
function ddeComponent({ attr }){
|
||||
scope.host(
|
||||
on.connected(e=> console.log(e.target.outerHTML)),
|
||||
);
|
||||
return el().append(
|
||||
el("p", `Hello from Custom Element with attribute '${attr}'`)
|
||||
);
|
||||
}
|
||||
customElementWithDDE(HTMLCustomElement);
|
||||
customElements.define(HTMLCustomElement.tagName, HTMLCustomElement);
|
||||
|
||||
document.body.append(
|
||||
el(HTMLCustomElement.tagName, { attr: "Attribute" })
|
||||
);
|
@ -1,5 +1,5 @@
|
||||
class CustomHTMLElement extends HTMLElement{
|
||||
static tagName = "custom-element"; // just suggestion, we can use `el(CustomHTMLElement.tagName)`
|
||||
export class HTMLCustomElement extends HTMLElement{
|
||||
static tagName= "custom-element"; // just suggestion, we can use `el(HTMLCustomElement.tagName)`
|
||||
static observedAttributes= [ "custom-attribute" ];
|
||||
constructor(){
|
||||
super();
|
||||
@ -18,4 +18,4 @@ class CustomHTMLElement extends HTMLElement{
|
||||
get customAttribute(){ return this.getAttribute("custom-attribute"); }
|
||||
set customAttribute(value){ this.setAttribute("custom-attribute", value); }
|
||||
}
|
||||
customElements.define(CustomHTMLElement.tagName, CustomHTMLElement);
|
||||
customElements.define(HTMLCustomElement.tagName, HTMLCustomElement);
|
||||
|
@ -0,0 +1,42 @@
|
||||
import {
|
||||
customElementRender,
|
||||
customElementWithDDE,
|
||||
observedAttributes,
|
||||
el, on, scope
|
||||
} from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
export class HTMLCustomElement extends HTMLElement{
|
||||
static tagName= "custom-element";
|
||||
static observedAttributes= [ "attr" ];
|
||||
connectedCallback(){
|
||||
console.log(observedAttributes(this));
|
||||
customElementRender(
|
||||
this,
|
||||
this.attachShadow({ mode: "open" }),
|
||||
ddeComponent,
|
||||
S.observedAttributes
|
||||
);
|
||||
}
|
||||
set attr(value){ this.setAttribute("attr", value); }
|
||||
get attr(){ return this.getAttribute("attr"); }
|
||||
}
|
||||
|
||||
/** @param {{ attr: ddeSignal<string, {}> }} props */
|
||||
function ddeComponent({ attr }){
|
||||
scope.host(
|
||||
on.connected(e=> console.log(e.target.outerHTML)),
|
||||
);
|
||||
return el().append(
|
||||
el("p", S(()=> `Hello from Custom Element with attribute '${attr()}'`))
|
||||
);
|
||||
}
|
||||
customElementWithDDE(HTMLCustomElement);
|
||||
customElements.define(HTMLCustomElement.tagName, HTMLCustomElement);
|
||||
|
||||
document.body.append(
|
||||
el(HTMLCustomElement.tagName, { attr: "Attribute" })
|
||||
);
|
||||
setTimeout(
|
||||
()=> document.querySelector(HTMLCustomElement.tagName).setAttribute("attr", "New Value"),
|
||||
3*750
|
||||
);
|
69
docs_src/components/examples/customElement/shadowRoot.js
Normal file
69
docs_src/components/examples/customElement/shadowRoot.js
Normal file
@ -0,0 +1,69 @@
|
||||
import {
|
||||
el,
|
||||
customElementRender,
|
||||
customElementWithDDE,
|
||||
} from "deka-dom-el";
|
||||
function ddeComponent(){
|
||||
return el().append(
|
||||
el("style", `
|
||||
.red{ color: firebrick; }
|
||||
`),
|
||||
el("p", { className: "red" }).append(
|
||||
"Hello from ", el("slot", "Custom Element"), "!"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export class A extends HTMLElement{
|
||||
static tagName= "custom-element-without";
|
||||
connectedCallback(){
|
||||
customElementRender(
|
||||
this,
|
||||
this,
|
||||
ddeComponent
|
||||
);
|
||||
}
|
||||
}
|
||||
customElementWithDDE(A);
|
||||
customElements.define(A.tagName, A);
|
||||
export class B extends HTMLElement{
|
||||
static tagName= "custom-element-open";
|
||||
connectedCallback(){
|
||||
customElementRender(
|
||||
this,
|
||||
this.attachShadow({ mode: "open" }),
|
||||
ddeComponent
|
||||
);
|
||||
}
|
||||
}
|
||||
customElementWithDDE(B);
|
||||
customElements.define(B.tagName, B);
|
||||
export class C extends HTMLElement{
|
||||
static tagName= "custom-element-closed";
|
||||
connectedCallback(){
|
||||
customElementRender(
|
||||
this,
|
||||
this.attachShadow({ mode: "closed" }),
|
||||
ddeComponent
|
||||
);
|
||||
}
|
||||
}
|
||||
customElementWithDDE(C);
|
||||
customElements.define(C.tagName, C);
|
||||
|
||||
document.body.append(
|
||||
el(A.tagName).append("Without shadowRoot"),
|
||||
el("hr"),
|
||||
el(B.tagName).append("Open shadowRoot"),
|
||||
el("hr"),
|
||||
el(C.tagName).append("Closed shadowRoot"),
|
||||
el("style", `
|
||||
.red{ color: crimson; }
|
||||
`),
|
||||
);
|
||||
console.log(A.tagName, "expect modifications");
|
||||
document.body.querySelector(A.tagName).querySelector("p").textContent+= " (editable with JS)";
|
||||
console.log(B.tagName, "expect modifications");
|
||||
document.body.querySelector(B.tagName).shadowRoot.querySelector("p").textContent+= " (editable with JS)";
|
||||
console.log(C.tagName, "expect error ↓");
|
||||
document.body.querySelector(C.tagName).querySelector("p").textContent+= " (editable with JS)";
|
41
docs_src/components/examples/customElement/simulateSlots.js
Normal file
41
docs_src/components/examples/customElement/simulateSlots.js
Normal file
@ -0,0 +1,41 @@
|
||||
import {
|
||||
customElementRender,
|
||||
customElementWithDDE,
|
||||
el,
|
||||
simulateSlots
|
||||
} from "deka-dom-el";
|
||||
export class HTMLCustomElement extends HTMLElement{
|
||||
static tagName= "custom-slotting";
|
||||
connectedCallback(){
|
||||
const c= ()=> simulateSlots(this, ddeComponent());
|
||||
customElementRender(this, this, c);
|
||||
}
|
||||
}
|
||||
customElementWithDDE(HTMLCustomElement);
|
||||
customElements.define(HTMLCustomElement.tagName, HTMLCustomElement);
|
||||
|
||||
document.body.append(
|
||||
el(HTMLCustomElement.tagName),
|
||||
el(HTMLCustomElement.tagName).append(
|
||||
"Slot"
|
||||
),
|
||||
el(ddeComponentSlot),
|
||||
el(ddeComponentSlot).append(
|
||||
"Slot"
|
||||
),
|
||||
);
|
||||
|
||||
function ddeComponent(){
|
||||
return el().append(
|
||||
el("p").append(
|
||||
"Hello ", el("slot", "World")
|
||||
)
|
||||
);
|
||||
}
|
||||
function ddeComponentSlot(){
|
||||
return simulateSlots(el().append(
|
||||
el("p").append(
|
||||
"Hello ", el("slot", "World")
|
||||
)
|
||||
));
|
||||
}
|
6
docs_src/components/examples/introducing/3ps.js
Normal file
6
docs_src/components/examples/introducing/3ps.js
Normal file
@ -0,0 +1,6 @@
|
||||
// pseudo code!
|
||||
const onchage=
|
||||
event=>
|
||||
console.log("Reacting to the:", event); // A
|
||||
input.addEventListener("change", onchange); // B
|
||||
input.dispatchEvent(new Event("change")); // C
|
@ -1,15 +1,15 @@
|
||||
import { el } from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
const clicks= S(0);
|
||||
const clicks= S(0); // A
|
||||
document.body.append(
|
||||
el().append(
|
||||
el("p", S(()=>
|
||||
"Hello World "+"🎉".repeat(clicks())
|
||||
"Hello World "+"🎉".repeat(clicks()) // B
|
||||
)),
|
||||
el("button", {
|
||||
type: "button",
|
||||
onclick: ()=> clicks(clicks()+1),
|
||||
textContent: "Fire"
|
||||
onclick: ()=> clicks(clicks()+1), // C
|
||||
textContent: "Fire",
|
||||
})
|
||||
)
|
||||
);
|
@ -4,7 +4,7 @@ import { mnemonicUl } from "../mnemonicUl.html.js";
|
||||
export function mnemonic(){
|
||||
return mnemonicUl().append(
|
||||
el("li").append(
|
||||
el("code", "customElementRender(<custom-element>, <render-function>[, <properties>])"), " — use function to render DOM structure for given <custom-element>",
|
||||
el("code", "customElementRender(<custom-element>, <connect-target>, <render-function>[, <properties>])"), " — use function to render DOM structure for given <custom-element>",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "customElementWithDDE(<custom-element>)"), " — register <custom-element> to DDE library, see also `lifecyclesToEvents`, can be also used as decorator",
|
||||
@ -17,6 +17,12 @@ export function mnemonic(){
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "lifecyclesToEvents(<class-declaration>)"), " — convert lifecycle methods to events, can be also used as decorator",
|
||||
)
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "simulateSlots(<class-instance>, <body>[, <mapper>])"), " — simulate slots for Custom Elements without shadow DOM",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "simulateSlots(<dde-component>[, <mapper>])"), " — simulate slots for “dde”/functional components",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,41 +1,62 @@
|
||||
import { t, T } from "./utils/index.js";
|
||||
export const info= {
|
||||
href: "./",
|
||||
title: "Introduction",
|
||||
description: "Introducing a library.",
|
||||
title: t`Introduction`,
|
||||
description: t`Introducing a library.`,
|
||||
};
|
||||
|
||||
import { el } from "deka-dom-el";
|
||||
import { simplePage } from "./layout/simplePage.html.js";
|
||||
import { h3 } from "./components/pageUtils.html.js";
|
||||
import { example } from "./components/example.html.js";
|
||||
import { code } from "./components/code.html.js";
|
||||
/** @param {string} url */
|
||||
const fileURL= url=> new URL(url, import.meta.url);
|
||||
const references= {
|
||||
w_mvv:{
|
||||
title: t`Wikipedia: Model–view–viewmodel`,
|
||||
href: "https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel",
|
||||
},
|
||||
w_mvc: {
|
||||
title: t`Wikipedia: Model–view–controller`,
|
||||
href: "https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller",
|
||||
},
|
||||
};
|
||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
|
||||
export function page({ pkg, info }){
|
||||
const page_id= info.id;
|
||||
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"), "."
|
||||
),
|
||||
" ",
|
||||
"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(
|
||||
"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 }),
|
||||
el("p", t`The library tries to provide pure JavaScript tool(s) to create reactive interfaces using …`),
|
||||
|
||||
el(h3, t`Event-driven programming (3 parts separation ≡ 3PS)`),
|
||||
el("p").append(t`
|
||||
Let's introduce the basic principle on which the library is built. We'll use the JavaScript listener as
|
||||
a starting point.
|
||||
`),
|
||||
el(code, { src: fileURL("./components/examples/introducing/3ps.js"), page_id }),
|
||||
el("p").append(...T`
|
||||
As we can see, in the code at location “A” we define ${el("em", t`how to react`)} when the function
|
||||
is called with any event as an argument. At that moment, we ${el("em", t`don't care who/why/how`)}
|
||||
the function was called. Similarly, at point “B”, we reference to a function to be called on the event
|
||||
${el("em", t`without caring`)} what the function will do at that time. Finally, at point “C”, we tell
|
||||
the application that a change has occurred, in the input, and we ${el("em", t`don't care if/how someone`)}
|
||||
is listening for the event.
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/introducing/helloWorld.js"), page_id }),
|
||||
el("p").append(...T`
|
||||
The library introduces a new “type” of variable/constant called ${el("em", t`signal`)} allowing us to
|
||||
to use introduced 3PS pattern in our applications. As you can see it in the example above.
|
||||
`),
|
||||
el("p").append(...T`
|
||||
Also please notice that there is very similar 3PS pattern used for separate creation of UI and
|
||||
business logic.
|
||||
`),
|
||||
el("p").append(...T`
|
||||
The 3PS is very simplified definition of the pattern. There are more deep/academic definitions more precisely
|
||||
describe usage in specific situations, see for example ${el("a", { textContent: t`MVVM`, ...references.w_mvv })}
|
||||
or ${el("a", { textContent: t`MVC`, ...references.w_mvc })}.
|
||||
`),
|
||||
|
||||
el(h3, t`Organization of the documentation`),
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { T, t } from "./utils/index.js";
|
||||
export const info= {
|
||||
title: "Elements",
|
||||
description: "Basic concepts of elements modifications and creations.",
|
||||
title: t`Elements`,
|
||||
description: t`Basic concepts of elements modifications and creations.`,
|
||||
};
|
||||
|
||||
import { el } from "deka-dom-el";
|
||||
@ -11,113 +12,142 @@ 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: t`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: t`MDN page about Interface Description Language`,
|
||||
href: "https://developer.mozilla.org/en-US/docs/Glossary/IDL",
|
||||
},
|
||||
/** HTMLElement */
|
||||
mdn_el: {
|
||||
title: t`MDN documentation page for HTMLElement`,
|
||||
href: "https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement"
|
||||
},
|
||||
/** HTMLParagraphElement */
|
||||
mdn_p: {
|
||||
title: t`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: t`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: t`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;
|
||||
return el(simplePage, { info, pkg }).append(
|
||||
el("h2", "Native JavaScript DOM elements creations"),
|
||||
el("p", "Let’s go through all patterns we would like to use and what needs to be improved for better experience."),
|
||||
el("h2", t`Native JavaScript DOM elements creations`),
|
||||
el("p", t`
|
||||
Let’s go through all patterns we would like to use and what needs to be improved for better experience.
|
||||
`),
|
||||
|
||||
el(code, { src: fileURL("./components/examples/elements/intro.js"), page_id }),
|
||||
|
||||
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(h3, t`Creating element(s) (with custom attributes)`),
|
||||
el("p").append(...T`
|
||||
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", references.mdn_idl).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("p").append(...T`
|
||||
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("p").append(...T`
|
||||
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(...T`
|
||||
You can study all JavaScript elements interfaces to the corresponding HTML elements. 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(
|
||||
el("li").append(
|
||||
"It is possible to sets ", el("code", "data-*"), "/", el("code", "aria-*"), " attributes using object notation."
|
||||
),
|
||||
el("li").append(
|
||||
"In opposite, it is also possible to sets ", el("code", "data-*"), "/", el("code", "aria-*"), " attribute using camelCase notation."
|
||||
),
|
||||
el("li").append(
|
||||
"You can use string or object as a value for ", el("code", "style"), " property."
|
||||
),
|
||||
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 element’s ", 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("li").append(...T`
|
||||
It is possible to sets ${el("code", "data-*")}/${el("code", "aria-*")} attributes using object notation.
|
||||
`),
|
||||
el("li").append(...T`
|
||||
In opposite, it is also possible to sets ${el("code", "data-*")}/${el("code", "aria-*")} attribute
|
||||
using camelCase notation.
|
||||
`),
|
||||
el("li").append(...T`You can use string or object as a value for ${el("code", "style")} property.`),
|
||||
el("li").append(...T`
|
||||
${el("code", "className")} (IDL – preffered)/${el("code", "class")} are ways to add CSS classes
|
||||
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(...T`
|
||||
Use ${el("code", "classList")} to toggle specific classes. This will be handy later when
|
||||
the reactivity via signals is beeing introduced.
|
||||
`),
|
||||
el("li").append(...T`
|
||||
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(...T`For example, natievly the element’s ${el("code", "id")}
|
||||
is removed by setting the IDL to an empty string.`)}
|
||||
`),
|
||||
el("li").append(...T`
|
||||
You can use ${el("code", "=")} or ${el("code", ".")} to force processing given key as attribute/property
|
||||
of the element.
|
||||
`)
|
||||
),
|
||||
el("p").append(...T`
|
||||
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(h3, t`Native JavaScript templating`),
|
||||
el("p", t`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("p").append(...T`
|
||||
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(h3, t`Basic (state-less) components`),
|
||||
el("p").append(...T`
|
||||
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("p").append(...T`
|
||||
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(...T`
|
||||
It is nice to use similar naming convention as native DOM API. This allows us to use
|
||||
${el("a", { textContent: t`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",
|
||||
" we need to tell JavaScript which kind of the element to create.",
|
||||
" We can use the ", el("code", "elNS"), " function:"
|
||||
),
|
||||
el(h3, t`Creating non-HTML elements`),
|
||||
el("p").append(...T`
|
||||
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:
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/elements/dekaElNS.js"), page_id }),
|
||||
|
||||
el(mnemonic)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { T, t } from "./utils/index.js";
|
||||
export const info= {
|
||||
title: "Events and Addons",
|
||||
description: "Using not only events in UI declaratively.",
|
||||
title: t`Events and Addons`,
|
||||
description: t`Using not only events in UI declaratively.`,
|
||||
};
|
||||
|
||||
import { el } from "deka-dom-el";
|
||||
@ -11,121 +12,136 @@ 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: t`MDN documentation page for elemetn.addEventListener`,
|
||||
href: "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener",
|
||||
},
|
||||
/** AbortSignal+element.addEventListener */
|
||||
mdn_abortListener: {
|
||||
title: t`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: t`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;
|
||||
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("h2", t`Listenning to the native DOM events and other Addons`),
|
||||
el("p").append(...T`
|
||||
We quickly introduce helper to listening to the native DOM events. And library syntax/pattern so-called
|
||||
${el("em", t`Addon`)} to incorporate not only this in UI templates declaratively.
|
||||
`),
|
||||
|
||||
el(code, { src: fileURL("./components/examples/events/intro.js"), page_id }),
|
||||
|
||||
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(h3, t`Events and listenners`),
|
||||
el("p").append(...T`
|
||||
In JavaScript you can listen to the native DOM events of the given element by using
|
||||
${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:
|
||||
`),
|
||||
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("p").append(...T`
|
||||
…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(...T`
|
||||
The other difference is that there is ${el("strong", "no")} ${el("code", "off")} function. You can remove
|
||||
listener declaratively using ${el("a", { textContent: "AbortSignal", ...references.mdn_abortListener })}:
|
||||
`),
|
||||
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("p", t`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" }), "."
|
||||
)
|
||||
el("p").append(...T`
|
||||
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", t`Side note:
|
||||
this can be useful in case of SSR.`)} To study difference, you can read a nice summary here:
|
||||
${el("a", { textContent: "GIST @WebReflection/web_events.md", ...references.web_events })}.
|
||||
`)
|
||||
),
|
||||
|
||||
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(h3, t`Addons`),
|
||||
el("p").append(...T`
|
||||
From practical point of view, ${el("em", t`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(...T`
|
||||
You can use Addons as ≥3rd argument of ${el("code", "el")} function. This way is possible to extends your
|
||||
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("p").append(...T`
|
||||
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, t`Life-cycle events`),
|
||||
el("p").append(...T`
|
||||
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(...T`
|
||||
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("p").append(...T`
|
||||
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: t`Custom element lifecycle callbacks | MDN`, ...references.mdn_customElement })}).
|
||||
`),
|
||||
el("p").append(...T`
|
||||
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(
|
||||
el("li").append(
|
||||
"To proper listener registration, you need to use ", el("code", "on.*"), " not `on(\"dde:*\", …)`!"
|
||||
),
|
||||
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("p").append(
|
||||
"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" }), ".",
|
||||
" For using native behaviour re-(dis)connecting element, use:"
|
||||
el("li").append(...T`
|
||||
To proper listener registration, you need to use ${el("code", "on.*")} not \`on("dde:*", …)\`!
|
||||
`),
|
||||
el("li").append(...T`
|
||||
Use sparingly! Internally, library must loop of all registered events and fires event properly.
|
||||
${el("strong", t`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", t`host`)} syntax to
|
||||
register, clean up procedures when the component is removed from the app.
|
||||
`),
|
||||
),
|
||||
el("p").append(...T`
|
||||
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", ...references.vue_fix })}. For using
|
||||
native behaviour re-(dis)connecting element, use:
|
||||
`),
|
||||
el("ul").append(
|
||||
el("li").append("custom ", el("code", "MutationObserver"), " or logic in (dis)", el("code", "connectedCallback"), " or…"),
|
||||
el("li").append("re-add ", el("code", "on.connected"), " or ", el("code", "on.disconnected"), " listeners.")
|
||||
el("li").append(...T`custom ${el("code", "MutationObserver")} or logic in (dis)${el("code", "connectedCallback")} or…`),
|
||||
el("li").append(...T`re-add ${el("code", "on.connected")} or ${el("code", "on.disconnected")} listeners.`)
|
||||
),
|
||||
|
||||
el(h3, "Final notes"),
|
||||
el("p", "The library also provides a method to dispatch the events."),
|
||||
el(h3, t`Final notes`),
|
||||
el("p", t`The library also provides a method to dispatch the events.`),
|
||||
el(example, { src: fileURL("./components/examples/events/compareDispatch.js"), page_id }),
|
||||
|
||||
el(mnemonic)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { T, t } from "./utils/index.js";
|
||||
export const info= {
|
||||
title: "Signals and reactivity",
|
||||
description: "Handling reactivity in UI via signals.",
|
||||
title: t`Signals and reactivity`,
|
||||
description: t`Handling reactivity in UI via signals.`,
|
||||
};
|
||||
|
||||
import { el } from "deka-dom-el";
|
||||
@ -11,95 +12,115 @@ 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: t`Wikipedia: Event-driven programming`,
|
||||
href: "https://en.wikipedia.org/wiki/Event-driven_programming",
|
||||
},
|
||||
/** Publish–subscribe pattern */
|
||||
wiki_pubsub: {
|
||||
title: t`Wikipedia: Publish–subscribe pattern`,
|
||||
href: "https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern",
|
||||
},
|
||||
/** NPM package: fpubsub */
|
||||
fpubsub: {
|
||||
title: t`NPM package: fpubsub`,
|
||||
href: "https://www.npmjs.com/package/fpubsub",
|
||||
},
|
||||
/** JS Primitives | MDN */
|
||||
mdn_primitive: {
|
||||
title: t`Primitive | MDN`,
|
||||
href: "https://developer.mozilla.org/en-US/docs/Glossary/Primitive",
|
||||
},
|
||||
/** useReducer */
|
||||
mdn_use_reducer: {
|
||||
title: t`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;
|
||||
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("h2", t`Using signals to manage reactivity`),
|
||||
el("p").append(...T`
|
||||
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(code, { 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(h3, t`Introducing signals`),
|
||||
el("p").append(...T`
|
||||
Let’s re-introduce
|
||||
${el("a", { textContent: t`3PS principle`, href: "./#h-event-driven-programming--parts-separation--ps" })}.
|
||||
`),
|
||||
el("p").append(...T`
|
||||
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(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" }),
|
||||
" 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" }), ").",
|
||||
" 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. In example, you also found the way for representing",
|
||||
" “live” piece of code computation pattern (derived signal):"
|
||||
),
|
||||
el("p").append(...T`
|
||||
All this is just an example of
|
||||
${el("a", { textContent: t`Event-driven programming`, ...references.wiki_event_driven })} and
|
||||
${el("a", { textContent: t`Publish–subscribe pattern`, ...references.wiki_pubsub })} (compare for example
|
||||
with ${el("a", { textContent: t`fpubsub library`, ...references.fpubsub })}). All three parts can be in
|
||||
some manner independent and still connected to the same reactive entity.
|
||||
`),
|
||||
el("p").append(...T`
|
||||
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('${t`a new value`}')`)}. For listenning the signal value changes, use
|
||||
${el("code", "S.on(signal, console.log)")}.
|
||||
`),
|
||||
el("p").append(...T`
|
||||
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. In
|
||||
example, you also found the way for representing “live” piece of code computation pattern (derived signal):
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/signals/computations-abort.js"), page_id }),
|
||||
|
||||
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" }), ".",
|
||||
" ",
|
||||
"However, it may also be necessary to use reactive arrays, objects, or other complex reactive structures."
|
||||
),
|
||||
el(h3, t`Signals and actions`),
|
||||
el("p").append(...T`
|
||||
${el("code", `S(/* ${t`primitive`} */)`)} allows you to declare simple reactive variables, typically, around
|
||||
${el("em", t`immutable`)} ${el("a", { textContent: t`primitive types`, ...references.mdn_primitive })}.
|
||||
However, it may also be necessary to use reactive arrays, objects, or other complex reactive structures.
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/signals/actions-demo.js"), page_id }),
|
||||
el("p", "…but typical user-case is object/array (maps, sets and other mutable objects):"),
|
||||
el("p", t`…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" }),
|
||||
" hook from React. So, the ", el("code", "S(<data>, <actions>)"), " pattern creates",
|
||||
" a store “machine”. We can then invoke (dispatch) registered action by calling",
|
||||
" ", el("code", "S.action(<signal>, <name>, ...<args>)"), " after the action call",
|
||||
" the signal 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("p").append(...T`
|
||||
In some way, you can compare it with ${el("a", { textContent: "useReducer", ...references.mdn_use_reducer })}
|
||||
hook from React. So, the ${el("code", "S(<data>, <actions>)")} pattern creates a store “machine”. We can
|
||||
then invoke (dispatch) registered action by calling ${el("code", "S.action(<signal>, <name>, ...<args>)")}
|
||||
after the action call the signal 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(h3, t`Reactive DOM attributes and elements`),
|
||||
el("p", t`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("li", t`to change some attribute(s) of existing element(s)`),
|
||||
el("li", t`to generate elements itself dynamically – this covers conditions and loops`)
|
||||
),
|
||||
el(example, { src: fileURL("./components/examples/signals/dom-attrs.js"), page_id }),
|
||||
el("p").append(
|
||||
"To derived attribute based on value of signal variable just use the signal as",
|
||||
" a value of the attribute (", el("code", "assign(element, { attribute: S('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 signal” (see above) like ", el("code", "assign(element, { textContent: S(()=> 'Hello '+WorldSignal()) })"), ".",
|
||||
" ",
|
||||
"This is read-only signal its value is computed based on given function and updated when any signal used in the function changes."
|
||||
),
|
||||
el("p").append(
|
||||
"To represent part of the template filled dynamically based on the signal value use ", el("code", "S.el(signal, DOMgenerator)"), ".",
|
||||
" This was already used in the todo example above or see:"
|
||||
),
|
||||
el("p").append(...T`
|
||||
To derived attribute based on value of signal variable just use the signal as a value of the attribute
|
||||
(${el("code", "assign(element, { attribute: S('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(...T`
|
||||
For computation, you can use the “derived signal” (see above) like
|
||||
${el("code", "assign(element, { textContent: S(()=> 'Hello '+WorldSignal()) })")}. This is read-only signal
|
||||
its value is computed based on given function and updated when any signal used in the function changes.
|
||||
`),
|
||||
el("p").append(...T`
|
||||
To represent part of the template filled dynamically based on the signal value use
|
||||
${el("code", "S.el(signal, DOMgenerator)")}. This was already used in the todo example above or see:
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/signals/dom-el.js"), page_id }),
|
||||
|
||||
el(mnemonic)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { T, t } from "./utils/index.js";
|
||||
export const info= {
|
||||
title: "Scopes and components",
|
||||
description: "Organizing UI into components",
|
||||
title: t`Scopes and components`,
|
||||
description: t`Organizing UI into components`,
|
||||
};
|
||||
|
||||
import { el } from "deka-dom-el";
|
||||
@ -11,67 +12,74 @@ 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: t`MDN documentation page for Garbage collection`,
|
||||
href: "https://developer.mozilla.org/en-US/docs/Glossary/Garbage_collection",
|
||||
},
|
||||
/** Signals */
|
||||
signals: {
|
||||
title: t`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;
|
||||
return el(simplePage, { info, pkg }).append(
|
||||
el("h2", "Using functions as UI components"),
|
||||
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" }), "."
|
||||
),
|
||||
el("h2", t`Using functions as UI components`),
|
||||
el("p").append(...T`
|
||||
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: t`Garbage collection`, ...references.garbage_collection })}.
|
||||
`),
|
||||
el(code, { src: fileURL("./components/examples/scopes/intro.js"), page_id }),
|
||||
el("p").append(
|
||||
"The library therefore use ", el("em", "scopes"), " to provide these functionalities.",
|
||||
),
|
||||
el("p").append(...T`The library therefore use ${el("em", t`scopes`)} to provide these functionalities.`),
|
||||
|
||||
el(h3, "Scopes and hosts"),
|
||||
el("p").append(
|
||||
"The ", el("strong", "host"), " is the name for the element representing the component.",
|
||||
" This is typically element returned by function. To get reference, you can use ",
|
||||
el("code", "scope.host()"), " to applly addons just use ", el("code", "scope.host(...<addons>)"), "."
|
||||
),
|
||||
el(h3, t`Scopes and hosts`),
|
||||
el("p").append(...T`
|
||||
The ${el("strong", "host")} is the name for the element representing the component. This is typically
|
||||
element returned by function. To get reference, you can use ${el("code", "scope.host()")} to applly addons
|
||||
just use ${el("code", "scope.host(...<addons>)")}.
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/scopes/scopes-and-hosts.js"), page_id }),
|
||||
el("p").append(
|
||||
"To better understanding we implement function ", el("code", "elClass"), " helping to create",
|
||||
" component as class instances."
|
||||
),
|
||||
el("p").append(...T`
|
||||
To better understanding we implement function ${el("code", "elClass")} helping to create component as
|
||||
class instances.
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/scopes/class-component.js"), page_id }),
|
||||
el("p").append(
|
||||
"As you can see, the ", el("code", "scope.host()"), " is stored temporarily and synchronously.",
|
||||
" Therefore, at least in the beginning of using library, it is the good practise to store",
|
||||
" ", el("code", "host"), " in the root of your component. As it may be changed, typically when",
|
||||
" there is asynchronous code in the component."
|
||||
),
|
||||
el("p").append(...T`
|
||||
As you can see, the ${el("code", "scope.host()")} is stored temporarily and synchronously. Therefore, at
|
||||
least in the beginning of using library, it is the good practise to store ${el("code", "host")} in the root
|
||||
of your component. As it may be changed, typically when there is asynchronous code in the component.
|
||||
`),
|
||||
el(code, { src: fileURL("./components/examples/scopes/good-practise.js"), page_id }),
|
||||
|
||||
el(h3, "Scopes, signals and cleaning magic"),
|
||||
el("p").append(
|
||||
"The ", el("code", "host"), " is internally used to register the cleaning procedure,",
|
||||
" when the component (", el("code", "host"), " element) is removed from the DOM."
|
||||
),
|
||||
el(h3, t`Scopes, signals and cleaning magic`),
|
||||
el("p").append(...T`
|
||||
The ${el("code", "host")} is internally used to register the cleaning procedure, when the component
|
||||
(${el("code", "host")} element) is removed from the DOM.
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/scopes/cleaning.js"), page_id }),
|
||||
el("p").append(
|
||||
"The text content of the paragraph is changing when the value of the signal ", el("code", "textContent"),
|
||||
" is changed. Internally, there is association between ", el("code", "textContent"), " and the paragraph",
|
||||
" similar to using ", el("code", "S.on(textContent, /* update the paragraph */)"), "."
|
||||
),
|
||||
el("p").append(
|
||||
"This listener must be removed when the component is removed from the DOM. To do it, the library",
|
||||
" assign internally ", el("code", "on.disconnected(/* remove the listener */)(host())"), " to the host element."
|
||||
),
|
||||
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" }), "."
|
||||
),
|
||||
el("p").append(...T`
|
||||
The text content of the paragraph is changing when the value of the signal ${el("code", "textContent")}
|
||||
is changed. Internally, there is association between ${el("code", "textContent")} and the paragraph,
|
||||
similar to using ${el("code", `S.on(textContent, /* ${t`update the paragraph`} */)`)}.
|
||||
`),
|
||||
el("p").append(...T`
|
||||
This listener must be removed when the component is removed from the DOM. To do it, the library assign
|
||||
internally ${el("code", `on.disconnected(/* ${t`remove the listener`} */)(host())`)} to the host element.
|
||||
`),
|
||||
el("p", { className: "notice" }).append(...T`
|
||||
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", ...references.signals })}.
|
||||
`),
|
||||
el(code, { src: fileURL("./components/examples/scopes/declarative.js"), page_id }),
|
||||
el("p").append(
|
||||
"Strictly speaking, the imperative way of using the library is not prohibited.",
|
||||
" Just be careful (rather avoid) mixing declarative approach (using signals)",
|
||||
" and imperative manipulation of elements.",
|
||||
),
|
||||
el("p").append(...T`
|
||||
Strictly speaking, the imperative way of using the library is not prohibited. Just be careful (rather avoid)
|
||||
mixing declarative approach (using signals) and imperative manipulation of elements.
|
||||
`),
|
||||
el(code, { src: fileURL("./components/examples/scopes/imperative.js"), page_id }),
|
||||
|
||||
el(mnemonic)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { T, t } from "./utils/index.js";
|
||||
export const info= {
|
||||
title: "Custom elements",
|
||||
description: "Using custom elements in combinantion with DDE",
|
||||
title: t`Web Components`,
|
||||
description: t`Using custom elements in combinantion with DDE`,
|
||||
};
|
||||
|
||||
import { el } from "deka-dom-el";
|
||||
@ -11,24 +12,131 @@ 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= {
|
||||
/** Web Components on MDN */
|
||||
mdn_web_components: {
|
||||
title: t`MDN documentation page for Web Components`,
|
||||
href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components",
|
||||
},
|
||||
/** observedAttributes on MDN */
|
||||
mdn_observedAttributes: {
|
||||
title: t`MDN documentation page for observedAttributes`,
|
||||
href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#responding_to_attribute_changes",
|
||||
},
|
||||
/** Custom Elements on MDN */
|
||||
mdn_custom_elements: {
|
||||
title: t`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: t`Ideas and tips from WebReflection`,
|
||||
href: "https://gist.github.com/WebReflection/ec9f6687842aa385477c4afca625bbf4",
|
||||
},
|
||||
/** Shallow DOM on mdn */
|
||||
mdn_shadow_dom_depth: {
|
||||
title: t`MDN documentation page for Shadow DOM`,
|
||||
href: "https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM",
|
||||
},
|
||||
/** Shallow DOM on mdn */
|
||||
mdn_shadow_dom_slot: {
|
||||
title: t`MDN documentation page for <slot>`,
|
||||
href: "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot",
|
||||
},
|
||||
/** Shallow DOM */
|
||||
shadow_dom_depth: {
|
||||
title: t`Everything you need to know about Shadow DOM (github repo praveenpuglia/shadow-dom-in-depth)`,
|
||||
href: "https://github.com/praveenpuglia/shadow-dom-in-depth",
|
||||
},
|
||||
};
|
||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
|
||||
export function page({ pkg, info }){
|
||||
const page_id= info.id;
|
||||
return el(simplePage, { info, pkg }).append(
|
||||
el("h2", "Using custom elements in combinantion with DDE"),
|
||||
el("p").append(
|
||||
|
||||
),
|
||||
el("h2", t`Using web components in combinantion with DDE`),
|
||||
el("p").append(...T`
|
||||
The DDE library allows for use within ${el("a", references.mdn_web_components).append( el("strong", t`Web Components`) )}
|
||||
for dom-tree generation. However, in order to be able to use signals (possibly mapping to registered
|
||||
${el("a", references.mdn_observedAttributes).append( el("code", "observedAttributes") )}) and additional
|
||||
functionality is (unfortunately) required to use helpers provided by the library.
|
||||
`),
|
||||
el(code, { src: fileURL("./components/examples/customElement/intro.js"), page_id }),
|
||||
|
||||
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(h3, t`Custom Elements Introduction`),
|
||||
el("p").append(...T`
|
||||
Web Components, specifically Custom Elements, are a set of web platform APIs that allow you to create
|
||||
new HTML tags with custom functionality encapsulated within them. This allows for the creation of reusable
|
||||
components that can be used across web applications.
|
||||
`),
|
||||
el("p").append(...T`
|
||||
To start with, let’s see how to use native Custom Elements. As starting point please read
|
||||
${el("a", references.mdn_custom_elements).append( el("strong", t`Using Custom Elements`), t` on MDN` )}.
|
||||
To sum up and for mnemonic see following code overview:
|
||||
`),
|
||||
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("p").append(...T`
|
||||
For more advanced use of Custom Elements, the summary ${el("a", references.custom_elements_tips)
|
||||
.append( el("strong", t`Handy Custom Elements Patterns`) )} may be useful. Especially pay attention to
|
||||
linking HTML attributes and defining setters/getters, this is very helpful to use in combination with
|
||||
the library (${el("code", `el(HTMLCustomElement.tagName, { customAttribute: "${t`new-value`}" });`)}).
|
||||
`),
|
||||
el("p").append(...T`
|
||||
Also see the Life Cycle Events sections, very similarly we would like to use
|
||||
${el("a", { textContent: t`DDE events`, href: "./p03-events.html", title: t`See events part of the library documentation` })}.
|
||||
To do it, the library provides function ${el("code", "customElementWithDDE")}…
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/customElement/customElementWithDDE.js"), page_id }),
|
||||
|
||||
el("h3", t`Custom Elements with DDE`),
|
||||
el("p").append(...T`
|
||||
The ${el("code", "customElementWithDDE")} function is only (small) part of the inregration of the library.
|
||||
More important for coexistence is render component function as a body of the Custom Element. For that, you
|
||||
can use ${el("code", "customElementRender")} with arguments instance reference, target for connection,
|
||||
render function and optional properties (will be passed to the render function) see later…
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/customElement/dde.js"), page_id }),
|
||||
el("p").append(...T`
|
||||
…as you can see, you can use components created based on the documentation previously introduced. To unlock
|
||||
full potential, use with combination ${el("code", "customElementWithDDE")} (allows to use livecycle events)
|
||||
and ${el("code", "observedAttributes")} (converts attributes to render function arguments —
|
||||
${el("em", "default")}) or ${el("code", "S.observedAttributes")} (converts attributes to signals).
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/customElement/observedAttributes.js"), page_id }),
|
||||
|
||||
|
||||
el(h3, t`Shadow DOM`),
|
||||
el("p").append(...T`
|
||||
Shadow DOM is a web platform feature that allows for the encapsulation of a component’s internal DOM tree
|
||||
from the rest of the document. This means that styles and scripts applied to the document will not affect
|
||||
the component’s internal DOM, and vice versa.
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/customElement/shadowRoot.js"), page_id }),
|
||||
el("p").append(...T`
|
||||
Regarding to ${el("code", "this.attachShadow({ mode: 'open' })")} see quick overview
|
||||
${el("a", { textContent: t`Using Shadow DOM`, ...references.mdn_shadow_dom_depth })}. An another source of
|
||||
information can be ${el("a", { textContent: t`Shadow DOM in Depth`, ...references.shadow_dom_depth })}.
|
||||
`),
|
||||
el("p").append(...T`
|
||||
Besides the encapsulation, the Shadow DOM allows for using the ${el("a", references.mdn_shadow_dom_slot).append(
|
||||
el("strong", t`<slot>`), t` element(s)`)}. You can simulate this feature using ${el("code", "simulateSlots")}:
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/customElement/simulateSlots.js"), page_id }),
|
||||
el("p").append(...T`
|
||||
To sum up:
|
||||
`),
|
||||
el("ul").append(
|
||||
el("li").append(...T`
|
||||
The use of shadow DOM to encapsulate the internal structure of the custom element, which affects how
|
||||
the custom element can be styled and modified using JavaScript and CSS.
|
||||
`),
|
||||
el("li").append(...T`
|
||||
The ability to access and modify the internal structure of the custom element using JavaScript, which
|
||||
is affected by the use of shadow DOM and the mode of the shadow DOM.
|
||||
`),
|
||||
el("li").append(...T`
|
||||
The use of slots to allow for the insertion of content from the parent document into the custom
|
||||
element, which is affected by the use of shadow DOM and the mode of the shadow DOM.
|
||||
`),
|
||||
),
|
||||
|
||||
el(mnemonic)
|
||||
|
@ -1,3 +1,4 @@
|
||||
export { t } from "./utils/index.js";
|
||||
export const path_target= {
|
||||
root: "docs/",
|
||||
css: "docs/"
|
||||
|
52
docs_src/utils/index.js
Normal file
52
docs_src/utils/index.js
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* This is helper to write texts in code more readable
|
||||
* and doesn’t inpact the finally generated text in HTML.
|
||||
*
|
||||
* ```js
|
||||
* t`
|
||||
* Hello ${el("b", "world")}!
|
||||
* How are you?
|
||||
* ` === "Hello <b>world</b>! How are you?"
|
||||
* ```
|
||||
*
|
||||
* In future, this can be expanded to allow translations.
|
||||
*
|
||||
* ```js
|
||||
* t(key)`text`; // for example
|
||||
* ```
|
||||
*
|
||||
* @param {TemplateStringsArray} strings
|
||||
* @param {...(string|Node)} values
|
||||
* @returns {(string|Node)[]}
|
||||
* */
|
||||
export function T(strings, ...values){
|
||||
const out= [];
|
||||
tT(s=> out.push(s), strings, ...values);
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* Similarly to {@link T}, but for only strings.
|
||||
* @param {TemplateStringsArray} strings
|
||||
* @param {...string} values
|
||||
* @returns {string}
|
||||
* */
|
||||
export function t(strings, ...values){
|
||||
let out= "";
|
||||
tT(s=> out+= s, strings, ...values);
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(input: string|Node)=> void} callback
|
||||
* @param {TemplateStringsArray} strings
|
||||
* @param {...(string|Node)} values
|
||||
* */
|
||||
function tT(callback, strings, ...values){
|
||||
const { length }= strings;
|
||||
const last= length-1;
|
||||
for(let i= 0; i<length; i++){
|
||||
const out= strings[i].replace(/\n\s+/g, " ");
|
||||
callback(!i ? out.trimStart() : i===last ? out.trimEnd() : out);
|
||||
if(i<values.length) callback(values[i]);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user