mirror of
https://github.com/jaandrle/deka-dom-el
synced 2025-04-03 20:35:53 +02:00
🔤 updates texts
This commit is contained in:
parent
209fa49dee
commit
e2df9705d1
@ -195,7 +195,7 @@ export function code({ id, src, content, language= "js", className= host.slice(1
|
||||
dataJS= "todo";
|
||||
}
|
||||
return el("div", { id, className, dataJS }).append(
|
||||
el("code", { className: "language-"+language, textContent: content })
|
||||
el("code", { className: "language-"+language, textContent: content.trim() })
|
||||
);
|
||||
}
|
||||
let is_registered= {};
|
||||
|
@ -1,35 +1,26 @@
|
||||
/* PSEUDO-CODE!!! */
|
||||
import { el } from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
function component(){
|
||||
/* prepare changeable data */
|
||||
const dataA= S("data");
|
||||
const dataB= S("data");
|
||||
/* define data flow (can be asynchronous) */
|
||||
fetchAPI().then(data_new=> dataA(data_new));
|
||||
setTimeout(()=> dataB("DATA"));
|
||||
/* declarative UI */
|
||||
return el().append(
|
||||
el("h1", {
|
||||
textContent: "Example",
|
||||
/* declarative attribute(s) */
|
||||
classList: { declarative: dataB }
|
||||
}),
|
||||
el("ul").append(
|
||||
/* declarative element(s) */
|
||||
S.el(dataA, data=> data.map(d=> el("li", d)))
|
||||
),
|
||||
el("ul").append(
|
||||
/* declarative component(s) */
|
||||
S.el(dataA, data=> data.map(d=> el(subcomponent, d)))
|
||||
)
|
||||
function Counter() {
|
||||
// Define state
|
||||
const count = S(0);
|
||||
|
||||
// Define behavior
|
||||
const increment = () => count.set(count.get() + 1);
|
||||
|
||||
// Define data flow
|
||||
setTimeout(increment, 1000);
|
||||
// or fetchAPI().then(increment);
|
||||
|
||||
// Declarative UI (how to render data/`count`)
|
||||
// …automatically updates when changes
|
||||
return el("div").append(
|
||||
// declarative element(s)
|
||||
el("p", S(() => "Count: " + count.get())),
|
||||
el("button", {
|
||||
onclick: increment,
|
||||
textContent: "Increment",
|
||||
// declarative attribute(s)
|
||||
disabled: S(() => count.get() >= 10)
|
||||
})
|
||||
);
|
||||
}
|
||||
function subcomponent({ id }){
|
||||
/* prepare changeable data */
|
||||
const textContent= S("…");
|
||||
/* define data flow (can be asynchronous) */
|
||||
fetchAPI(id).then(text=> textContent(text));
|
||||
/* declarative UI */
|
||||
return el("li", { textContent, dataId: id });
|
||||
}
|
||||
|
@ -1,31 +1,25 @@
|
||||
/* PSEUDO-CODE!!! */
|
||||
import { el, on, scope } from "deka-dom-el";
|
||||
function component(){
|
||||
const { host }= scope;
|
||||
const ul= el("ul");
|
||||
const ac= new AbortController();
|
||||
fetchAPI({ signal: ac.signal }).then(data=> {
|
||||
data.forEach(d=> ul.append(el("li", d)));
|
||||
});
|
||||
host(
|
||||
/* element was remove before data fetched */
|
||||
on.disconnected(()=> ac.abort())
|
||||
import { el, scope } from "deka-dom-el";
|
||||
function Counter() {
|
||||
const { host } = scope;
|
||||
|
||||
let count = 0;
|
||||
const counterText = el("p", "Count: 0");
|
||||
|
||||
// Manually update DOM element
|
||||
const increment = () => {
|
||||
count++;
|
||||
counterText.textContent = "Count: " + count;
|
||||
host().querySelector("button").disabled = count >= 10;
|
||||
};
|
||||
setTimeout(increment, 1000);
|
||||
// or fetchAPI().then(increment);
|
||||
|
||||
return el("div").append(
|
||||
counterText,
|
||||
el("button", {
|
||||
onclick: increment,
|
||||
textContent: "Increment"
|
||||
})
|
||||
);
|
||||
return ul;
|
||||
/**
|
||||
* NEVER EVER!!
|
||||
* let data;
|
||||
* fetchAPI().then(d=> data= O(d));
|
||||
*
|
||||
* OR NEVER EVER!!
|
||||
* const ul= el("ul");
|
||||
* fetchAPI().then(d=> {
|
||||
* const data= O("data");
|
||||
* ul.append(el("li", data));
|
||||
* });
|
||||
*
|
||||
* // THE HOST IS PROBABLY DIFFERENT THAN
|
||||
* // YOU EXPECT AND OBSERVABLES MAY BE
|
||||
* // UNEXPECTEDLY REMOVED!!!
|
||||
* */
|
||||
}
|
||||
|
34
docs/components/examples/scopes/mixed.js
Normal file
34
docs/components/examples/scopes/mixed.js
Normal file
@ -0,0 +1,34 @@
|
||||
/* PSEUDO-CODE!!! */
|
||||
import { el, scope } from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
function Counter() {
|
||||
const { host } = scope;
|
||||
|
||||
let count = S(0);
|
||||
const counterText = el("p", "Count: 0");
|
||||
S.on(count, c=> counterText.textContent= "Count: " + c);
|
||||
|
||||
// Manually update DOM element
|
||||
const increment = () => {
|
||||
count.set(count.get() + 1);
|
||||
// NEVER EVER
|
||||
// count = S(count.get() + 1);
|
||||
host().querySelector("button").disabled = count.get() >= 10;
|
||||
};
|
||||
// NEVER EVER
|
||||
// setTimeout(()=> {
|
||||
// const wrong= S(0);
|
||||
// // THE HOST IS PROBABLY DIFFERENT THAN
|
||||
// // YOU EXPECT AND OBSERVABLES MAY BE
|
||||
// // UNEXPECTEDLY REMOVED!!!
|
||||
// counterText.textContent= "Count: " + wrong.get();
|
||||
// }, 1000);
|
||||
|
||||
return el("div").append(
|
||||
counterText,
|
||||
el("button", {
|
||||
onclick: increment,
|
||||
textContent: "Increment"
|
||||
})
|
||||
);
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
import { S } from "deka-dom-el/signals";
|
||||
// α — `signal` represents a reactive value
|
||||
// A — `signal` represents a reactive value
|
||||
const signal= S(0);
|
||||
// β — just reacts on signal changes
|
||||
// B — just reacts on signal changes
|
||||
S.on(signal, console.log);
|
||||
// γ — just updates the value
|
||||
// C — just updates the value
|
||||
const update= ()=> signal.set(signal.get()+1);
|
||||
|
||||
update();
|
||||
const interval= 5*1000;
|
||||
setTimeout(clearInterval, 10*interval,
|
||||
setInterval(update, interval));
|
||||
setInterval(update, interval));
|
||||
|
@ -208,6 +208,12 @@ pre code {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
.illustration:not(:has( .comparison)) pre {
|
||||
background: none;
|
||||
border-style: dashed !important;
|
||||
width: fit-content;
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
@media (min-width: 768px) {
|
||||
|
@ -55,8 +55,7 @@ export function page({ pkg, info }){
|
||||
el("li", t`Automatic UI updates when data changes`),
|
||||
el("li", t`Clean separation between data, logic, and UI`),
|
||||
el("li", t`Small runtime with minimal overhead`),
|
||||
el("li", t`Works seamlessly with DDE's DOM creation`),
|
||||
el("li", t`No dependencies or framework lock-in`)
|
||||
el("li").append(...T`${el("strong", "In future")} no dependencies or framework lock-in`)
|
||||
)
|
||||
),
|
||||
el(code, { src: fileURL("./components/examples/signals/intro.js"), page_id }),
|
||||
@ -68,17 +67,17 @@ export function page({ pkg, info }){
|
||||
`),
|
||||
el("div", { class: "signal-diagram" }).append(
|
||||
el("div", { class: "signal-part" }).append(
|
||||
el("h4", t`α: Create Signal`),
|
||||
el("h4", t`A: Create Signal`),
|
||||
el(code, { content: "const count = S(0);", page_id }),
|
||||
el("p", t`Define a reactive value that can be observed and changed`)
|
||||
),
|
||||
el("div", { class: "signal-part" }).append(
|
||||
el("h4", t`β: React to Changes`),
|
||||
el("h4", t`B: React to Changes`),
|
||||
el(code, { content: "S.on(count, value => updateUI(value));", page_id }),
|
||||
el("p", t`Subscribe to signal changes with callbacks or effects`)
|
||||
),
|
||||
el("div", { class: "signal-part" }).append(
|
||||
el("h4", t`γ: Update Signal`),
|
||||
el("h4", t`C: Update Signal`),
|
||||
el(code, { content: "count.set(count.get() + 1);", page_id }),
|
||||
el("p", t`Modify the signal value, which automatically triggers updates`)
|
||||
)
|
||||
@ -90,7 +89,8 @@ export function page({ pkg, info }){
|
||||
Signals implement the ${el("a", { textContent: t`Publish–subscribe pattern`, ...references.wiki_pubsub })},
|
||||
a form of ${el("a", { textContent: t`Event-driven programming`, ...references.wiki_event_driven })}.
|
||||
This architecture allows different parts of your application to stay synchronized through a shared signal,
|
||||
without direct dependencies on each other.
|
||||
without direct dependencies on each other. Compare for example with ${el("a", { textContent:
|
||||
t`fpubsub library`, ...references.fpubsub })}.
|
||||
`)
|
||||
),
|
||||
|
||||
@ -110,7 +110,8 @@ export function page({ pkg, info }){
|
||||
el("dd", t`S.on(signal, callback) → runs callback whenever signal changes`),
|
||||
|
||||
el("dt", t`Unsubscribing`),
|
||||
el("dd", t`S.on(signal, callback, { signal: abortController.signal })`)
|
||||
el("dd").append(...T`S.on(signal, callback, { signal: abortController.signal }) → Similarly to the
|
||||
${el("code", "on")} function to register DOM events listener.`)
|
||||
)
|
||||
),
|
||||
el("p").append(...T`
|
||||
@ -139,15 +140,6 @@ export function page({ pkg, info }){
|
||||
el("div", { class: "illustration" }).append(
|
||||
el("h4", t`Actions vs. Direct Mutation`),
|
||||
el("div", { class: "comparison" }).append(
|
||||
el("div", { class: "bad-practice" }).append(
|
||||
el("h5", t`❌ Without Actions`),
|
||||
el(code, { content: `
|
||||
const todos = S([]);
|
||||
// Directly mutating the array
|
||||
const items = todos.get();
|
||||
items.push("New todo");
|
||||
// This WON'T trigger updates!`, page_id }))
|
||||
),
|
||||
el("div", { class: "good-practice" }).append(
|
||||
el("h5", t`✅ With Actions`),
|
||||
el(code, { content: `const todos = S([], {
|
||||
@ -159,8 +151,25 @@ items.push("New todo");
|
||||
|
||||
// Use the action
|
||||
S.action(todos, "add", "New todo");`, page_id })
|
||||
)
|
||||
),
|
||||
el("div", { class: "bad-practice" }).append(
|
||||
el("h5", t`❌ Without Actions`),
|
||||
el(code, { content: `
|
||||
const todos = S([]);
|
||||
// Directly mutating the array
|
||||
const items = todos.get();
|
||||
items.push("New todo");
|
||||
// This WON'T trigger updates!`, page_id }))
|
||||
),
|
||||
),
|
||||
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(example, { src: fileURL("./components/examples/signals/actions-demo.js"), page_id }),
|
||||
|
||||
el("p").append(...T`
|
||||
@ -182,9 +191,7 @@ S.action(todos, "add", "New todo");`, page_id })
|
||||
${el("strong", "Special Action Methods")}: Signal actions can implement special lifecycle hooks:
|
||||
`),
|
||||
el("ul").append(
|
||||
el("li", t`[S.symbols.onclear]() - Called when the signal is cleared`),
|
||||
el("li", t`[S.symbols.onget]() - Called when the signal value is read`),
|
||||
el("li", t`[S.symbols.onset]() - Called after the signal value is changed`)
|
||||
el("li", t`[S.symbols.onclear]() - Called when the signal is cleared. Use it to clean up resources.`),
|
||||
)
|
||||
),
|
||||
|
||||
@ -229,12 +236,12 @@ S.action(items, "push", "Dragonfruit"); // List updates automatically`, page_id
|
||||
)
|
||||
),
|
||||
|
||||
el(example, { src: fileURL("./components/examples/signals/dom-attrs.js"), page_id }),
|
||||
el("p").append(...T`
|
||||
The ${el("code", "assign")} and ${el("code", "el")} functions detect signals automatically and handle binding.
|
||||
You can use special properties like ${el("code", "dataset")}, ${el("code", "ariaset")}, and
|
||||
${el("code", "classList")} for fine-grained control over specific attribute types.
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/signals/dom-attrs.js"), page_id }),
|
||||
|
||||
el("p").append(...T`
|
||||
${el("code", "S.el()")} is especially powerful for conditional rendering and lists:
|
||||
|
@ -30,53 +30,44 @@ export function page({ pkg, info }){
|
||||
return el(simplePage, { info, pkg }).append(
|
||||
el("h2", t`Building Maintainable UIs with Scopes and Components`),
|
||||
el("p").append(...T`
|
||||
Scopes provide a structured way to organize your UI code into reusable components that properly
|
||||
manage their lifecycle, handle cleanup, and maintain clear boundaries between different parts of your application.
|
||||
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("div", { className: "callout" }).append(
|
||||
el("h4", t`Why Use Scopes?`),
|
||||
el("ul").append(
|
||||
el("li", t`Automatic resource cleanup when components are removed from DOM`),
|
||||
el("li", t`Clear component boundaries with explicit host elements`),
|
||||
el("li", t`Simplified event handling with proper "this" binding`),
|
||||
el("li", t`Seamless integration with signals for reactive components`),
|
||||
el("li", t`Better memory management with ${el("a", { textContent: t`GC`, ...references.garbage_collection })}`)
|
||||
)
|
||||
),
|
||||
el(code, { src: fileURL("./components/examples/scopes/intro.js"), page_id }),
|
||||
el("p").append(...T`The library therefore use ${el("em", t`scopes`)} to provide these functionalities.`),
|
||||
|
||||
el(h3, t`Understanding Host Elements and Scopes`),
|
||||
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("div", { className: "illustration" }).append(
|
||||
el("h4", t`Component Anatomy`),
|
||||
el("pre").append(el("code", `
|
||||
┌─────────────────────────────────┐
|
||||
│ // 1. Component scope created │
|
||||
│ el(MyComponent); │
|
||||
│ │
|
||||
│ function MyComponent() { │
|
||||
│ // 2. access the host element │
|
||||
│ const { host } = scope; │
|
||||
│ │
|
||||
│ // 3. Add behavior to host │
|
||||
│ host( │
|
||||
│ on.click(handleClick) │
|
||||
│ ); │
|
||||
│ │
|
||||
│ // 4. Return the host element │
|
||||
│ return el("div", { │
|
||||
│ className: "my-component" │
|
||||
│ }).append( │
|
||||
│ el("h2", "Title"), │
|
||||
│ el("p", "Content") │
|
||||
│ ); │
|
||||
│ } │
|
||||
└─────────────────────────────────┘
|
||||
`))
|
||||
// 1. Component scope created
|
||||
el(MyComponent);
|
||||
|
||||
function MyComponent() {
|
||||
// 2. access the host element
|
||||
const { host } = scope;
|
||||
|
||||
// 3. Add behavior to host
|
||||
host(
|
||||
on.click(handleClick)
|
||||
);
|
||||
|
||||
// 4. Return the host element
|
||||
return el("div", {
|
||||
className: "my-component"
|
||||
}).append(
|
||||
el("h2", "Title"),
|
||||
el("p", "Content")
|
||||
);
|
||||
}
|
||||
`.trim()))
|
||||
),
|
||||
el("p").append(...T`
|
||||
The ${el("strong", "host element")} is the root element of your component - typically the element returned
|
||||
by your component function. It serves as the identity of your component in the DOM.
|
||||
`),
|
||||
el("div", { className: "function-table" }).append(
|
||||
el("h4", t`scope.host()`),
|
||||
el("dl").append(
|
||||
@ -91,34 +82,24 @@ export function page({ pkg, info }){
|
||||
|
||||
el("div", { className: "tip" }).append(
|
||||
el("p").append(...T`
|
||||
${el("strong", "Best Practice:")} Always capture the host reference at the beginning of your component function
|
||||
using ${el("code", "const { host } = scope")} to avoid scope-related issues, especially with asynchronous code.
|
||||
`)
|
||||
),
|
||||
|
||||
el(h3, t`Class-Based Components`),
|
||||
el("p").append(...T`
|
||||
While functional components are the primary pattern in DDE, you can also create class-based components
|
||||
for more structured organization of component logic.
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/scopes/class-component.js"), page_id }),
|
||||
|
||||
el("p").append(...T`
|
||||
This pattern can be useful when:
|
||||
`),
|
||||
el("ul").append(
|
||||
el("li", t`You have complex component logic that benefits from object-oriented organization`),
|
||||
el("li", t`You need private methods and properties for your component`),
|
||||
el("li", t`You're transitioning from another class-based component system`)
|
||||
),
|
||||
el("div", { className: "tip" }).append(
|
||||
${el("strong", "Best Practice:")} Always capture the host reference at the beginning of your component
|
||||
function using ${el("code", "const { host } = scope")} to avoid scope-related issues, especially with
|
||||
asynchronous code.
|
||||
`),
|
||||
el("p").append(...T`
|
||||
${el("strong", "Note:")} Even with class-based components, follow the best practice of storing the host reference
|
||||
early in your component code. This ensures proper access to the host throughout the component's lifecycle.
|
||||
If you are interested in the implementation details, see Class-Based Components section.
|
||||
`)
|
||||
),
|
||||
el(code, { src: fileURL("./components/examples/scopes/good-practise.js"), page_id }),
|
||||
|
||||
el(h3, t`Class-Based Components`),
|
||||
el("p").append(...T`
|
||||
While functional components are the primary pattern in DDE, you can also create class-based components.
|
||||
For this, we implement function ${el("code", "elClass")} and use it to demonstrate implementation details
|
||||
for better understanding of the scope logic.
|
||||
`),
|
||||
el(example, { src: fileURL("./components/examples/scopes/class-component.js"), page_id }),
|
||||
|
||||
el(h3, t`Automatic Cleanup with Scopes`),
|
||||
el("p").append(...T`
|
||||
One of the most powerful features of scopes is automatic cleanup when components are removed from the DOM.
|
||||
@ -149,77 +130,38 @@ export function page({ pkg, info }){
|
||||
|
||||
el(h3, t`Declarative vs Imperative Components`),
|
||||
el("p").append(...T`
|
||||
Scopes work best with a declarative approach to UI building, especially when combined
|
||||
with ${el("a", { textContent: "signals", ...references.signals })} for state management.
|
||||
The library DOM API and signals works best 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("div", { className: "note" }).append(
|
||||
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("div", { className: "tabs" }).append(
|
||||
el("div", { className: "tab", "data-tab": "declarative" }).append(
|
||||
el("h4", t`✅ Declarative Approach`),
|
||||
el("p", t`Define what your UI should look like based on state:`),
|
||||
el("pre").append(el("code", `function Counter() {
|
||||
const { host } = scope;
|
||||
|
||||
// Define state
|
||||
const count = S(0);
|
||||
|
||||
// Define behavior
|
||||
const increment = () => count.set(count.get() + 1);
|
||||
|
||||
// UI automatically updates when count changes
|
||||
return el("div").append(
|
||||
el("p", S(() => "Count: " + count.get())),
|
||||
el("button", {
|
||||
onclick: increment,
|
||||
textContent: "Increment"
|
||||
})
|
||||
);
|
||||
}`))
|
||||
el(code, { src: fileURL("./components/examples/scopes/declarative.js"), page_id }),
|
||||
),
|
||||
el("div", { className: "tab", "data-tab": "imperative" }).append(
|
||||
el("h4", t`⚠️ Imperative Approach`),
|
||||
el("p", t`Manually update the DOM in response to events:`),
|
||||
el("pre").append(el("code", `function Counter() {
|
||||
const { host } = scope;
|
||||
|
||||
let count = 0;
|
||||
const counterText = el("p", "Count: 0");
|
||||
|
||||
// Manually update DOM element
|
||||
const increment = () => {
|
||||
count++;
|
||||
counterText.textContent = "Count: " + count;
|
||||
};
|
||||
|
||||
return el("div").append(
|
||||
counterText,
|
||||
el("button", {
|
||||
onclick: increment,
|
||||
textContent: "Increment"
|
||||
})
|
||||
);
|
||||
}`))
|
||||
)
|
||||
el(code, { src: fileURL("./components/examples/scopes/imperative.js"), page_id }),
|
||||
),
|
||||
el("div", { className: "tab", "data-tab": "imperative" }).append(
|
||||
el("h4", t`❌ Mixed Approach`),
|
||||
el("p", t`Just AVOID:`),
|
||||
el(code, { src: fileURL("./components/examples/scopes/mixed.js"), page_id }),
|
||||
),
|
||||
),
|
||||
|
||||
el(code, { src: fileURL("./components/examples/scopes/declarative.js"), page_id }),
|
||||
|
||||
el("div", { className: "note" }).append(
|
||||
el("p").append(...T`
|
||||
While DDE supports both declarative and imperative approaches, the declarative style is recommended
|
||||
as it leads to more maintainable code with fewer opportunities for bugs. Signals handle the complexity
|
||||
of keeping your UI in sync with your data.
|
||||
`)
|
||||
),
|
||||
el(code, { src: fileURL("./components/examples/scopes/imperative.js"), page_id }),
|
||||
|
||||
el(h3, t`Best Practices for Scopes and Components`),
|
||||
el("ol").append(
|
||||
el("li").append(...T`
|
||||
${el("strong", "Capture host early:")} Use ${el("code", "const { host } = scope")} at component start
|
||||
`),
|
||||
el("li").append(...T`
|
||||
${el("strong", "Return a single root element:")} Components should have one host element that contains all others
|
||||
`),
|
||||
el("li").append(...T`
|
||||
${el("strong", "Prefer declarative patterns:")} Use signals to drive UI updates rather than manual DOM manipulation
|
||||
`),
|
||||
|
@ -201,22 +201,16 @@ export function page({ pkg, info }){
|
||||
el("div", { className: "illustration" }).append(
|
||||
el("h4", t`Shadow DOM Encapsulation`),
|
||||
el("pre").append(el("code", `
|
||||
┌─────────────────────────────────┐
|
||||
│ <my-custom-element> │
|
||||
│ │
|
||||
│ ┌─────────────────────────┐ │
|
||||
│ │ #shadow-root │ │
|
||||
│ │ │ │
|
||||
│ │ Created with DDE: │ │
|
||||
│ │ ┌─────────────────┐ │ │
|
||||
│ │ │ <div> │ │ │
|
||||
│ │ │ <h2>Title</h2> │ │ │
|
||||
│ │ │ <p>Content</p> │ │ │
|
||||
│ │ └─────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────┘
|
||||
<my-custom-element>
|
||||
|
||||
┌─────────────────────────┐
|
||||
#shadow-root
|
||||
|
||||
Created with DDE:
|
||||
┌──────────────────┐
|
||||
<div>
|
||||
<h2>Title</h2>
|
||||
<p>Content</p>
|
||||
`))
|
||||
),
|
||||
el(example, { src: fileURL("./components/examples/customElement/shadowRoot.js"), page_id }),
|
||||
@ -229,9 +223,10 @@ export function page({ pkg, info }){
|
||||
|
||||
el(h3, t`Working with Slots`),
|
||||
el("p").append(...T`
|
||||
Slots allow users of your component to insert content inside it. When using DDE, you can simulate the
|
||||
slot mechanism with the ${el("code", "simulateSlots")} function:
|
||||
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("div", { className: "function-table" }).append(
|
||||
el("h4", t`simulateSlots`),
|
||||
el("dl").append(
|
||||
@ -241,15 +236,6 @@ export function page({ pkg, info }){
|
||||
el("dd", t`A mapping object of slot names to DOM elements`)
|
||||
)
|
||||
),
|
||||
el(example, { src: fileURL("./components/examples/customElement/simulateSlots.js"), page_id }),
|
||||
|
||||
el("div", { className: "tip" }).append(
|
||||
el("p").append(...T`
|
||||
${el("strong", "When to use simulateSlots:")} This approach is useful when you need to distribute
|
||||
content from the light DOM into specific locations in the shadow DOM, particularly in environments
|
||||
where native slots might not be fully supported.
|
||||
`)
|
||||
),
|
||||
|
||||
el(h3, t`Best Practices for Web Components with DDE`),
|
||||
el("p").append(...T`
|
||||
|
Loading…
x
Reference in New Issue
Block a user