1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2025-04-03 20:35:53 +02:00
This commit is contained in:
Jan Andrle 2025-03-06 10:59:31 +01:00
parent 59efa84494
commit 963ed53c84
8 changed files with 82 additions and 91 deletions

View File

@ -3,7 +3,7 @@ export const info= {
href: "./",
title: t`Introduction`,
fullTitle: t`Vanilla for flavouring — a full-fledged feast for large projects`,
description: t`A lightweight, reactive DOM library for creating dynamic UIs with a declarative syntax`,
description: t`A lightweight, reactive DOM library for creating dynamic UIs with a declarative syntax`,
};
import { el } from "deka-dom-el";
@ -28,15 +28,15 @@ export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(...T`
Welcome to Deka DOM Elements (DDE) a lightweight library for building dynamic UIs with a
declarative syntax that stays close to the native DOM API. DDE gives you powerful reactive
tools without the complexity and overhead of larger frameworks.
Welcome to Deka DOM Elements (DDE) a lightweight library for building dynamic UIs with a declarative
syntax that stays close to the native DOM API. DDE gives you powerful reactive tools without the complexity
and overhead of larger frameworks.
`),
el("div", { className: "callout" }).append(
el("h4", t`What Makes DDE Special`),
el("ul").append(
el("li", t`No build step required — use directly in the browser`),
el("li", t`Lightweight core (~10-15kB minified) with zero dependencies`),
el("li", t`Lightweight core (~1015kB minified) with zero dependencies`),
el("li", t`Natural DOM API — work with real DOM nodes, not abstractions`),
el("li", t`Built-in reactivity with powerful signals system`),
el("li", t`Clean code organization with the 3PS pattern`)
@ -47,7 +47,8 @@ export function page({ pkg, info }){
el(h3, { textContent: t`The 3PS Pattern: A Better Way to Build UIs`, id: "h-3ps" }),
el("p").append(...T`
At the heart of DDE is the 3PS (3-Part Separation) pattern. This simple yet powerful approach helps you
organize your UI code into three distinct areas, making your applications more maintainable and easier to reason about.
organize your UI code into three distinct areas, making your applications more maintainable and easier
to reason about.
`),
el("div", { className: "illustration" }).append(
el("div", { className: "tabs" }).append(
@ -77,30 +78,31 @@ export function page({ pkg, info }){
),
el("p").append(...T`
By separating these concerns, your code becomes more modular, testable, and easier to maintain. This approach
shares principles with more formal patterns like ${el("a", { textContent: "MVVM", ...references.w_mvv })} and
${el("a", { textContent: "MVC", ...references.w_mvc })}, but with less overhead and complexity.
By separating these concerns, your code becomes more modular, testable, and easier to maintain. This
approach shares principles with more formal patterns like ${el("a", { textContent: "MVVM",
...references.w_mvv })} and ${el("a", { textContent: "MVC", ...references.w_mvc })}, but with less
overhead and complexity.
`),
el("div", { className: "note" }).append(
el("p").append(...T`
The 3PS pattern becomes especially powerful when combined with components, allowing you to create
reusable pieces of UI with encapsulated state and behavior. You'll learn more about this in the
reusable pieces of UI with encapsulated state and behavior. You'll learn more about this in the
following sections.
`)
),
el(h3, t`How to Use This Documentation`),
el("p").append(...T`
This guide will take you through DDE's features step by step:
This guide will take you through DDE's features step by step:
`),
el("ol").append(
el("li").append(...T`${el("strong", "Elements")} — Creating and manipulating DOM elements`),
el("li").append(...T`${el("strong", "Events")} — Handling user interactions and lifecycle events`),
el("li").append(...T`${el("strong", "Elements")} — Creating and manipulating DOM elements`),
el("li").append(...T`${el("strong", "Events")} — Handling user interactions and lifecycle events`),
el("li").append(...T`${el("strong", "Signals")} — Adding reactivity to your UI`),
el("li").append(...T`${el("strong", "Scopes")} — Managing component lifecycles`),
el("li").append(...T`${el("strong", "Custom Elements")} — Building web components`),
el("li").append(...T`${el("strong", "Debugging")} — Tools to help you build and fix your apps`),
el("li").append(...T`${el("strong", "Debugging")} — Tools to help you build and fix your apps`),
el("li").append(...T`${el("strong", "SSR")} — Server-side rendering with DDE`)
),
el("p").append(...T`
@ -108,4 +110,4 @@ export function page({ pkg, info }){
Let's get started with the basics of creating elements!
`),
);
}
}

View File

@ -50,7 +50,7 @@ export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(...T`
Building user interfaces in JavaScript often involves creating and manipulating DOM elements.
Building user interfaces in JavaScript often involves creating and manipulating DOM elements.
DDE provides a simple yet powerful approach to element creation that is declarative, chainable,
and maintains a clean syntax close to HTML structure.
`),
@ -71,7 +71,7 @@ export function page({ pkg, info }){
el("p").append(...T`
In standard JavaScript, you create DOM elements using the
${el("a", references.mdn_create).append(el("code", "document.createElement()"))} method
and then set properties individually or with Object.assign():
and then set properties individually or with Object.assign():
`),
el("div", { class: "illustration" }).append(
el("div", { class: "comparison" }).append(
@ -85,16 +85,17 @@ export function page({ pkg, info }){
)
)
),
el(example, { src: fileURL("./components/examples/elements/dekaCreateElement.js"), page_id }),
el("p").append(...T`
The ${el("code", "el")} function provides a simple wrapper around ${el("code", "document.createElement")}
with enhanced property assignment.
`),
el(example, { src: fileURL("./components/examples/elements/dekaCreateElement.js"), page_id }),
el(h3, t`Advanced Property Assignment`),
el("p").append(...T`
The ${el("code", "assign")} function is the heart of DDE's element property handling. It provides
intelligent assignment of both properties (IDL) and attributes:
The ${el("code", "assign")} function is the heart of DDE's element property handling. It is internally used
to assign properties using the ${el("code", "el")} function. ${el("code", "assign")} provides intelligent
assignment of both properties (IDL) and attributes:
`),
el("div", { class: "function-table" }).append(
el("dl").append(
@ -145,21 +146,21 @@ export function page({ pkg, info }){
)
)
),
el(example, { src: fileURL("./components/examples/elements/dekaAppend.js"), page_id }),
el("p").append(...T`
This chainable approach results in code that more closely mirrors the structure of your HTML,
making it easier to understand and maintain.
This chainable pattern is much cleaner and easier to follow, especially for deeply nested elements.
It also makes it simple to add multiple children to a parent element in a single fluent expression.
`),
el(example, { src: fileURL("./components/examples/elements/dekaAppend.js"), page_id }),
el(h3, t`Building Reusable Components`),
el(h3, t`Using Components to Build UI Fragments`),
el("p").append(...T`
DDE makes it simple to create reusable element components through regular JavaScript functions.
The ${el("code", "el()")} function accepts a component function as its first argument:
The ${el("code", "el")} function is overloaded to support both tag names and function components.
This lets you refactor complex UI trees into reusable pieces:
`),
el(example, { src: fileURL("./components/examples/elements/dekaBasicComponent.js"), page_id }),
el("p").append(...T`
Component functions receive props as their argument and return element(s). This pattern
encourages code reuse and better organization of your UI code.
Component functions receive the properties object as their first argument, just like regular elements.
This makes it easy to pass data down to components and create reusable UI fragments.
`),
el("div", { class: "tip" }).append(
el("p").append(...T`
@ -180,41 +181,21 @@ export function page({ pkg, info }){
using the same consistent interface.
`),
el(h3, t`Best Practices`),
el(h3, t`Best Practices for Declarative DOM Creation`),
el("ol").append(
el("li").append(...T`
${el("strong", "Prefer composition over complexity")}: Create small component functions that can be
combined rather than large, complex templates
${el("strong", "Use component functions for reusable UI fragments:")} Extract common UI patterns
into reusable functions that return elements.
`),
el("li").append(...T`
${el("strong", "Use meaningful component names")}: Name your component functions after the elements or
patterns they create
${el("strong", "Leverage destructuring for cleaner component code:")} Use
${el("a", { textContent: "destructuring", ...references.mdn_destruct })} to extract properties
from the props object for cleaner component code.
`),
el("li").append(...T`
${el("strong", "Destructure for better readability")}: Use ${el("code", "const { div, p, button } = el")}
to create element-specific functions
${el("strong", "Leverage chainable methods for better performance:")} Use chainable methods like
${el("code", ".append()")} to build complex DOM trees for better performance and cleaner code.
`),
el("li").append(...T`
${el("strong", "Be consistent with property usage")}: Prefer using the same pattern (property vs attribute)
throughout your code
`)
),
el("div", { class: "troubleshooting" }).append(
el("h4", t`Common Element Creation Pitfalls`),
el("dl").append(
el("dt", t`Elements not showing up in DOM`),
el("dd", t`Remember to append elements to the document or a parent that's already in the document`),
el("dt", t`Properties not being applied correctly`),
el("dd", t`Check if you're mixing up property (IDL) names with attribute names (e.g., className vs class)`),
el("dt", t`Event listeners not working`),
el("dd", t`Ensure you're using the correct event binding approach (see Events section)`),
el("dt", t`SVG elements not rendering correctly`),
el("dd", t`Make sure you're using elNS with the correct SVG namespace for SVG elements`)
)
),
el(mnemonic)

View File

@ -47,8 +47,8 @@ export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(...T`
Events are at the core of interactive web applications. DDE provides a clean, declarative approach to
handling DOM events and extends this pattern with a powerful Addon system to incorporate additional
Events are at the core of interactive web applications. DDE provides a clean, declarative approach to
handling DOM events and extends this pattern with a powerful Addon system to incorporate additional
functionalities into your UI templates.
`),
el("div", { className: "callout" }).append(
@ -84,7 +84,7 @@ export function page({ pkg, info }){
),
el(example, { src: fileURL("./components/examples/events/compare.js"), page_id }),
el("p").append(...T`
The main benefit of DDE's approach is that it works as an Addon, making it easy to integrate
The main benefit of DDE's approach is that it works as an Addon, making it easy to integrate
directly into element declarations.
`),
@ -127,7 +127,7 @@ export function page({ pkg, info }){
el(h3, t`Understanding Addons`),
el("p").append(...T`
Addons are a powerful pattern in DDE that extends beyond just event handling.
An Addon is any function that accepts an HTML element as its first parameter.
An Addon is any function that accepts an HTML element as its first parameter.
`),
el("div", { className: "callout" }).append(
el("h4", t`What Can Addons Do?`),
@ -151,7 +151,7 @@ export function page({ pkg, info }){
el(h3, t`Lifecycle Events`),
el("p").append(...T`
Addons are called immediately when an element is created, even before it's connected to the live DOM.
Addons are called immediately when an element is created, even before it's connected to the live DOM.
You can think of an Addon as an "oncreate" event handler.
`),
el("p").append(...T`

View File

@ -45,15 +45,15 @@ export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(...T`
Signals provide a simple yet powerful way to create reactive applications with DDE. They handle the
fundamental challenge of keeping your UI in sync with changing data in a declarative, efficient way.
Signals provide a simple yet powerful way to create reactive applications with DDE. They handle the
fundamental challenge of keeping your UI in sync with changing data in a declarative, efficient way.
`),
el("div", { class: "callout" }).append(
el("h4", t`What Makes Signals Special?`),
el("ul").append(
el("li", t`Fine-grained reactivity without complex state management`),
el("li", t`Automatic UI updates when data changes`),
el("li", t`Clean separation between data, logic, and UI`),
el("li", t`Clean separation between data, logic, and UI`),
el("li", t`Small runtime with minimal overhead`),
el("li").append(...T`${el("strong", "In future")} no dependencies or framework lock-in`)
)
@ -98,13 +98,13 @@ export function page({ pkg, info }){
el("div", { class: "function-table" }).append(
el("dl").append(
el("dt", t`Creating a Signal`),
el("dd", t`S(initialValue) → creates a signal with the given value`),
el("dd", t`S(initialValue) → creates a signal with the given value`),
el("dt", t`Reading a Signal`),
el("dd", t`signal.get() → returns the current value`),
el("dt", t`Writing to a Signal`),
el("dd", t`signal.set(newValue) → updates the value and notifies subscribers`),
el("dd", t`signal.set(newValue) → updates the value and notifies subscribers`),
el("dt", t`Subscribing to Changes`),
el("dd", t`S.on(signal, callback) → runs callback whenever signal changes`),
@ -123,7 +123,7 @@ export function page({ pkg, info }){
el(h3, t`Derived Signals: Computed Values`),
el("p").append(...T`
Computed values (also called derived signals) automatically update when their dependencies change.
Create them by passing a function to S():
Create them by passing a function to S():
`),
el(example, { src: fileURL("./components/examples/signals/derived.js"), page_id }),
el("p").append(...T`
@ -254,7 +254,7 @@ S.action(items, "push", "Dragonfruit"); // List updates automatically`, page_id
`),
el("ol").append(
el("li").append(...T`
${el("strong", "Keep signals small and focused")}: Use many small signals rather than a few large ones
${el("strong", "Keep signals small and focused")}: Use many small signals rather than a few large ones
`),
el("li").append(...T`
${el("strong", "Use derived signals for computations")}: Don't recompute values in multiple places

View File

@ -30,22 +30,23 @@ export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
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
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's life-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(...T`The library therefore use ${el("em", t`scopes`)} to provide these functionalities.`),
el("p").append(...T`The library therefore uses ${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 apply addons
The ${el("strong", "host")} is the name for the element representing the component. This is typically the
element returned by a function. To get a reference, you can use ${el("code", "scope.host()")}. To apply addons,
just use ${el("code", "scope.host(...<addons>)")}.
`),
el("p").append(...T`
Scopes are primarily needed when signals are used in DOM templates (with ${el("code", "el")}, ${el("code", "assign")}, or ${el("code", "S.el")}).
They provide a way for automatically removing signal listeners and cleaning up unused signals when components are removed from the DOM.
Scopes are primarily needed when signals are used in DOM templates (with ${el("code", "el")}, ${el("code",
"assign")}, or ${el("code", "S.el")}). They provide a way for automatically removing signal listeners
and cleaning up unused signals when components are removed from the DOM.
`),
el("div", { className: "illustration" }).append(
el("h4", t`Component Anatomy`),
@ -134,13 +135,13 @@ function MyComponent() {
el(h3, t`Declarative vs Imperative Components`),
el("p").append(...T`
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 })}.
The library DOM API and signals work best when used declaratively. It means you split your app's logic
into three parts as introduced 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.
Strictly speaking, the imperative way of using the library is not prohibited. Just be careful to avoid
mixing the declarative approach (using signals) with imperative manipulation of elements.
`)
),
el("div", { className: "tabs" }).append(
@ -156,7 +157,7 @@ function MyComponent() {
),
el("div", { className: "tab", "data-tab": "mixed" }).append(
el("h4", t`❌ Mixed Approach`),
el("p", t`Just AVOID:`),
el("p", t`This approach should be avoided:`),
el(code, { src: fileURL("./components/examples/scopes/mixed.js"), page_id })
)
),
@ -196,4 +197,4 @@ function MyComponent() {
el(mnemonic)
);
}
}

View File

@ -2,7 +2,7 @@ import { T, t } from "./utils/index.js";
export const info= {
title: t`Web Components`,
fullTitle: t`Using Web Components with DDE: Better Together`,
description: t`Using custom elements in combinantion with DDE`,
description: t`Using custom elements in combination with DDE`,
};
import { el } from "deka-dom-el";

View File

@ -104,7 +104,7 @@ export function page({ pkg, info }){
el("p").append(...T`
When using ${el("code", "S.el()")}, deka-dom-el creates reactive elements in the DOM
that are automatically updated when signal values change. These elements are wrapped in special
comment nodes for debugging (to be true they are also used internaly, so please do not edit them by hand):
comment nodes for debugging (to be true they are also used internally, so please do not edit them by hand):
`),
el(code, { src: fileURL("./components/examples/debugging/dom-reactive-mark.js"), page_id }),
el("p").append(...T`
@ -150,16 +150,16 @@ export function page({ pkg, info }){
el("h4", t`Examining signal connections`),
el("p").append(...T`
You can inspect signal relationships and bindings in the DevTools console using ${el("code", "$0.__dde_reactive")}.
In console you will see list of ${el("code", "[ [ signal, listener ], element, property ]")}, where:
In the console you will see a list of ${el("code", "[ [ signal, listener ], element, property ]")}, where:
`),
el("ul").append(
el("li", "signal — the signal triggering the changes"),
el("li", "listener — the listener function (this is internal function for dde)"),
el("li", "listener — the listener function (this is an internal function for DDE)"),
el("li", "element — the DOM element that is bound to the signal"),
el("li", "property — the attribute or property name which is changing based on the signal"),
),
el("p").append(...T`
the structure of \`__dde_reactive\` use the behavior of the browser that packs the first field,
the structure of \`__dde_reactive\` utilizes the browser's behavior of packing the first field,
so you can see the element and property that changes in the console right away
`),

View File

@ -16,6 +16,13 @@ const fileURL= url=> new URL(url, import.meta.url);
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("div", { className: "warning" }).append(
el("p").append(...T`
This part of the documentation is primarily intended for technical enthusiasts and library authors.
For regular users, this capability will hopefully be covered by third-party libraries or frameworks
that provide simpler SSR integration using deka-dom-el.
`)
),
el("p").append(...T`
deka-dom-el isn't limited to browser environments. Thanks to its flexible architecture,
it can be used for server-side rendering (SSR) to generate static HTML files.
@ -29,11 +36,11 @@ export function page({ pkg, info }){
SSR offers several benefits:
`),
el("ul").append(
el("li", t`Improved SEO - Search engines can easily index fully rendered content`),
el("li", t`Faster initial page load - Users see content immediately without waiting for JavaScript to load`),
el("li", t`Better performance on low-powered devices - Less JavaScript processing on the client`),
el("li", t`Content available without JavaScript - Useful for users who have disabled JavaScript`),
el("li", t`Static site generation - Build files once, serve them many times`)
el("li", t`Improved SEO Search engines can easily index fully rendered content`),
el("li", t`Faster initial page load Users see content immediately without waiting for JavaScript to load`),
el("li", t`Better performance on low-powered devices Less JavaScript processing on the client`),
el("li", t`Content available without JavaScript Useful for users who have disabled JavaScript`),
el("li", t`Static site generation Build files once, serve them many times`)
),
el(h3, t`How jsdom Integration Works`),