2024-05-22 21:43:49 +02:00
|
|
|
export const info= {
|
|
|
|
title: "Scopes and components",
|
|
|
|
description: "Organizing UI into components",
|
|
|
|
};
|
2023-11-30 17:05:19 +01:00
|
|
|
|
|
|
|
import { el } from "deka-dom-el";
|
2024-05-22 21:43:49 +02:00
|
|
|
import { simplePage } from "./layout/simplePage.html.js";
|
2023-11-30 17:05:19 +01:00
|
|
|
import { example } from "./components/example.html.js";
|
|
|
|
import { h3 } from "./components/pageUtils.html.js";
|
|
|
|
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);
|
2024-05-31 14:14:48 +02:00
|
|
|
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",
|
|
|
|
}
|
|
|
|
};
|
2023-11-30 17:05:19 +01:00
|
|
|
/** @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",
|
2024-05-31 14:14:48 +02:00
|
|
|
" JavaScript the way to properly use the ", el("a", { textContent: "Garbage collection", ...references.garbage_collection }), "."
|
2023-11-30 17:05:19 +01:00
|
|
|
),
|
|
|
|
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(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(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(example, { src: fileURL("./components/examples/scopes/class-component.js"), page_id }),
|
2023-12-04 18:19:30 +01:00
|
|
|
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(code, { src: fileURL("./components/examples/scopes/good-practise.js"), page_id }),
|
|
|
|
|
2024-05-22 21:43:49 +02:00
|
|
|
el(h3, "Scopes, signals and cleaning magic"),
|
2023-12-04 18:19:30 +01:00
|
|
|
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(example, { src: fileURL("./components/examples/scopes/cleaning.js"), page_id }),
|
|
|
|
el("p").append(
|
2024-05-22 21:43:49 +02:00
|
|
|
"The text content of the paragraph is changing when the value of the signal ", el("code", "textContent"),
|
2023-12-04 18:19:30 +01:00
|
|
|
" 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",
|
2023-12-04 18:24:59 +01:00
|
|
|
" assign internally ", el("code", "on.disconnected(/* remove the listener */)(host())"), " to the host element."
|
2023-12-04 18:19:30 +01:00
|
|
|
),
|
|
|
|
el("p", { className: "notice" }).append(
|
2024-05-22 21:43:49 +02:00
|
|
|
"The library DOM API and signals works ideally when used declaratively.",
|
2024-05-31 14:14:48 +02:00
|
|
|
" It means, you split your app logic into three parts as it was itroduced in ", el("a", { textContent: "Signals", ...references.signals }), "."
|
2023-12-04 18:19:30 +01:00
|
|
|
),
|
|
|
|
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.",
|
2024-05-22 21:43:49 +02:00
|
|
|
" Just be careful (rather avoid) mixing declarative approach (using signals)",
|
2023-12-04 18:19:30 +01:00
|
|
|
" and imperative manipulation of elements.",
|
|
|
|
),
|
|
|
|
el(code, { src: fileURL("./components/examples/scopes/imperative.js"), page_id }),
|
2023-11-30 17:05:19 +01:00
|
|
|
|
|
|
|
el(mnemonic)
|
|
|
|
);
|
|
|
|
}
|