mirror of
				https://github.com/jaandrle/deka-dom-el
				synced 2025-11-04 07:09:15 +01:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			v0.9.3-alp
			...
			v0.9.4-alp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4c450ae763 | |||
| 
						
						
							
						
						04f93345f8
	
				 | 
					
					
						
@@ -75,11 +75,14 @@ where everyone feels comfortable participating.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
…see [BS folder](./bs/README.md) for more info.
 | 
					…see [BS folder](./bs/README.md) for more info.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Categorizing [](https://robinpokorny.github.io/git3moji/) <!-- editorconfig-checker-disable-line -->
 | 
				
			||||||
 | 
					We use [git3moji](https://git3moji.netlify.app/) for commit messages, issue titles, pull request titles and in other
 | 
				
			||||||
 | 
					areas. To make categorizing quick and consistent.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Commit Guidelines
 | 
					## Commit Guidelines
 | 
				
			||||||
 | 
					
 | 
				
			||||||
We use
 | 
					We use [git3moji](https://git3moji.netlify.app/) for commit messages. This helps keep the commit history clear and
 | 
				
			||||||
[](https://robinpokorny.github.io/git3moji/) <!-- editorconfig-checker-disable-line -->
 | 
					consistent.
 | 
				
			||||||
for commit messages. This helps keep the commit history clear and consistent.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
:emoji: Short summary of the change
 | 
					:emoji: Short summary of the change
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										61
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								README.md
									
									
									
									
									
								
							@@ -1,18 +1,11 @@
 | 
				
			|||||||
**Alpha**
 | 
					**Alpha**
 | 
				
			||||||
| [source code on GitHub](https://github.com/jaandrle/deka-dom-el)
 | 
					| [Docs](https://jaandrle.github.io/deka-dom-el "Official documentation and guide site")
 | 
				
			||||||
| [*mirrored* on Gitea](https://gitea.jaandrle.cz/jaandrle/deka-dom-el)
 | 
					| [NPM](https://www.npmjs.com/package/deka-dom-el "Official NPM package page")
 | 
				
			||||||
| [](https://robinpokorny.github.io/git3moji/) <!-- editorconfig-checker-disable-line -->
 | 
					| [GitHub](https://github.com/jaandrle/deka-dom-el "Official GitHub repository")
 | 
				
			||||||
 | 
					([*Gitea*](https://gitea.jaandrle.cz/jaandrle/deka-dom-el "GitHub repository mirror on my own Gitea instance"))
 | 
				
			||||||
<p align="center">
 | 
					 | 
				
			||||||
	<img src="docs/assets/logo.svg" alt="Deka DOM Elements Logo" width="180" height="180">
 | 
					 | 
				
			||||||
</p>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Deka DOM Elements (dd\<el\> or DDE)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
***Vanilla for flavouring — a full-fledged feast for large projects***
 | 
					***Vanilla for flavouring — a full-fledged feast for large projects***
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*…use simple DOM API by default and library tools and logic when you need them*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```javascript
 | 
					```javascript
 | 
				
			||||||
// 🌟 Reactive component with clear separation of concerns
 | 
					// 🌟 Reactive component with clear separation of concerns
 | 
				
			||||||
document.body.append(
 | 
					document.body.append(
 | 
				
			||||||
@@ -20,28 +13,23 @@ document.body.append(
 | 
				
			|||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function EmojiCounter({ initial }) {
 | 
					function EmojiCounter({ initial }) {
 | 
				
			||||||
	// ✨ State - Define reactive data
 | 
						// ✨ - Define reactive data
 | 
				
			||||||
	const count = S(0);
 | 
						const count = S(0);
 | 
				
			||||||
	const emoji = S(initial);
 | 
						const emoji = S(initial);
 | 
				
			||||||
 | 
						const textContent = S(() => `Hello World ${emoji.get().repeat(count.get())}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** @param {HTMLOptionElement} el */
 | 
						// 🔄 - UI updates automatically when signals change
 | 
				
			||||||
	const isSelected= el=> (el.selected= el.value===initial);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 🔄 View - UI updates automatically when signals change
 | 
					 | 
				
			||||||
	return el().append(
 | 
						return el().append(
 | 
				
			||||||
		el("p", {
 | 
							el("p", { textContent, className: "output" }),
 | 
				
			||||||
			className: "output",
 | 
					 | 
				
			||||||
			textContent: S(() =>
 | 
					 | 
				
			||||||
				`Hello World ${emoji.get().repeat(count.get())}`),
 | 
					 | 
				
			||||||
		}),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 🎮 Controls - Update state on events
 | 
							// 🎮 - Update state on events
 | 
				
			||||||
		el("button", { textContent: "Add Emoji" },
 | 
							el("button", { textContent: "Add Emoji" },
 | 
				
			||||||
			on("click", () => count.set(count.get() + 1))
 | 
								on("click", () => count.set(count.get() + 1)),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("select", null, on.defer(el=> el.value= initial),
 | 
							el("select", null,
 | 
				
			||||||
			on("change", e => emoji.set(e.target.value))
 | 
								on.defer(el=> el.value= initial),
 | 
				
			||||||
 | 
								on("change", e => emoji.set(e.target.value)),
 | 
				
			||||||
		).append(
 | 
							).append(
 | 
				
			||||||
			el(Option, "🎉"),
 | 
								el(Option, "🎉"),
 | 
				
			||||||
			el(Option, "🚀"),
 | 
								el(Option, "🚀"),
 | 
				
			||||||
@@ -53,6 +41,13 @@ function Option({ textContent }){
 | 
				
			|||||||
	return el("option", { value: textContent, textContent });
 | 
						return el("option", { value: textContent, textContent });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					*…use simple DOM API by default and library tools and logic when you need them*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<p align="center">
 | 
				
			||||||
 | 
						<img src="docs/assets/logo.svg" alt="Deka DOM Elements Logo" width="180" height="180">
 | 
				
			||||||
 | 
					</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Deka DOM Elements (dd\<el\> or DDE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Creating reactive elements, components, and Web Components using the native
 | 
					Creating reactive elements, components, and Web Components using the native
 | 
				
			||||||
[IDL](https://developer.mozilla.org/en-US/docs/Glossary/IDL)/JavaScript DOM API enhanced with
 | 
					[IDL](https://developer.mozilla.org/en-US/docs/Glossary/IDL)/JavaScript DOM API enhanced with
 | 
				
			||||||
@@ -64,19 +59,21 @@ Creating reactive elements, components, and Web Components using the native
 | 
				
			|||||||
- ☑️ **Lightweight** — ~10-15kB minified (original goal 10kB) with **zero**/minimal dependencies
 | 
					- ☑️ **Lightweight** — ~10-15kB minified (original goal 10kB) with **zero**/minimal dependencies
 | 
				
			||||||
- ✅ **Declarative & functional approach** for clean, maintainable code
 | 
					- ✅ **Declarative & functional approach** for clean, maintainable code
 | 
				
			||||||
- ✅ **Signals and events** for reactive UI
 | 
					- ✅ **Signals and events** for reactive UI
 | 
				
			||||||
 | 
					- ✅ **Auto-releasing resources** for memory management but nice development experience
 | 
				
			||||||
- ✅ **Memoization for performance** — optimize rendering with intelligent caching
 | 
					- ✅ **Memoization for performance** — optimize rendering with intelligent caching
 | 
				
			||||||
- ✅ **Optional build-in signals** with support for custom reactive implementations (#39)
 | 
					- ☑️ **Optional build-in signals** with support for custom reactive implementations (#39)
 | 
				
			||||||
- ✅ **Server-side rendering** support via [jsdom](https://github.com/jsdom/jsdom)
 | 
					- ☑️ **Server-side rendering** support via [jsdom](https://github.com/jsdom/jsdom)
 | 
				
			||||||
- ✅ **TypeScript support**
 | 
					- ✅ **TypeScript support**
 | 
				
			||||||
- ☑️ **Support for debugging with browser DevTools** without extensions
 | 
					- ✅ **Support for debugging with browser DevTools** without extensions
 | 
				
			||||||
- ☑️ **Enhanced Web Components** support
 | 
					- ☑️ **Enhanced Web Components** support
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Getting Started
 | 
					## Getting Started
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Documentation
 | 
					### Quick Links
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [**Documentation and Guide**](https://jaandrle.github.io/deka-dom-el)
 | 
					- [**Documentation and Guide**](https://jaandrle.github.io/deka-dom-el)
 | 
				
			||||||
- [**Examples**](https://jaandrle.github.io/deka-dom-el/p15-examples.html)
 | 
					- [**Examples**](https://jaandrle.github.io/deka-dom-el/p15-examples.html)
 | 
				
			||||||
 | 
					- [**Changelog**](https://github.com/jaandrle/deka-dom-el/releases)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Installation
 | 
					### Installation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -87,7 +84,7 @@ npm install deka-dom-el --save
 | 
				
			|||||||
…or via CDN / Direct Script:
 | 
					…or via CDN / Direct Script:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
For CDN links and various build formats (ESM/IIFE, with/without signals, minified/unminified), see the [interactive
 | 
					For CDN links and various build formats (ESM/IIFE, with/without signals, minified/unminified), see the [interactive
 | 
				
			||||||
format selector](https://jaandrle.github.io/deka-dom-el/) on the documentation site.
 | 
					format selector](https://jaandrle.github.io/deka-dom-el/#h-getting-started) on the documentation site.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```html
 | 
					```html
 | 
				
			||||||
<!-- Example with IIFE build (creates a global DDE object) -->
 | 
					<!-- Example with IIFE build (creates a global DDE object) -->
 | 
				
			||||||
@@ -141,9 +138,9 @@ get started, coding standards, commit guidelines, and the pull request process.
 | 
				
			|||||||
	interfaces or HTML code.
 | 
						interfaces or HTML code.
 | 
				
			||||||
- [pota](https://pota.quack.uy/) — small and pluggable Reactive Web Renderer. It's compiler-less, includes an html
 | 
					- [pota](https://pota.quack.uy/) — small and pluggable Reactive Web Renderer. It's compiler-less, includes an html
 | 
				
			||||||
	function, and a optimized babel preset in case you fancy JSX.
 | 
						function, and a optimized babel preset in case you fancy JSX.
 | 
				
			||||||
- [jaandrle/dollar_dom_component](https://github.com/jaandrle/dollar_dom_component) —
 | 
					 | 
				
			||||||
	Functional DOM components without JSX/virtual DOM
 | 
					 | 
				
			||||||
- [TarekRaafat/eleva](https://github.com/TarekRaafat/eleva) — A minimalist, lightweight, pure vanilla JavaScript
 | 
					- [TarekRaafat/eleva](https://github.com/TarekRaafat/eleva) — A minimalist, lightweight, pure vanilla JavaScript
 | 
				
			||||||
	frontend runtime framework.
 | 
						frontend runtime framework.
 | 
				
			||||||
- [didi/mpx](https://github.com/didi/mpx) — Mpx,一款具有优秀开发体验和深度性能优化的增强型跨端小程序框架
 | 
					- [didi/mpx](https://github.com/didi/mpx) — Mpx,一款具有优秀开发体验和深度性能优化的增强型跨端小程序框架
 | 
				
			||||||
- [mxjp/rvx](https://github.com/mxjp/rvx) — A signal based frontend framework
 | 
					- [mxjp/rvx](https://github.com/mxjp/rvx) — A signal based frontend framework
 | 
				
			||||||
 | 
					- [jaandrle/dollar_dom_component](https://github.com/jaandrle/dollar_dom_component) —
 | 
				
			||||||
 | 
						Functional DOM components without JSX/virtual DOM (my old library)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */// editorconfig-checker-disable-line
 | 
					/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */// editorconfig-checker-disable-line
 | 
				
			||||||
echo("Building static documentation files…");
 | 
					echo("Building static documentation files…");
 | 
				
			||||||
echo("Preparing…");
 | 
					echo("Preparing…");
 | 
				
			||||||
import { path_target, pages as pages_registered, styles, dispatchEvent, t } from "../docs/ssr.js";
 | 
					import { path_target, pages as pages_registered, styles, currentPageId, dispatchEvent, t } from "../docs/ssr.js";
 | 
				
			||||||
import { createHTMl } from "./docs/jsdom.js";
 | 
					import { createHTMl } from "./docs/jsdom.js";
 | 
				
			||||||
import { register, queue } from "../jsdom.js";
 | 
					import { register, queue } from "../jsdom.js";
 | 
				
			||||||
const pkg= s.cat("package.json").xargs(JSON.parse);
 | 
					const pkg= s.cat("package.json").xargs(JSON.parse);
 | 
				
			||||||
@@ -28,6 +28,7 @@ for(const { id, info } of pages){
 | 
				
			|||||||
	);
 | 
						);
 | 
				
			||||||
	const { el }= await register(serverDOM.dom);
 | 
						const { el }= await register(serverDOM.dom);
 | 
				
			||||||
	const { page }= await import(`../docs/${id}.html.js`);
 | 
						const { page }= await import(`../docs/${id}.html.js`);
 | 
				
			||||||
 | 
						currentPageId(id)
 | 
				
			||||||
	serverDOM.document.body.append(
 | 
						serverDOM.document.body.append(
 | 
				
			||||||
		el(page, { pkg, info }),
 | 
							el(page, { pkg, info }),
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										35
									
								
								dist/esm-with-signals.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								dist/esm-with-signals.js
									
									
									
									
										vendored
									
									
								
							@@ -41,6 +41,11 @@ function observedAttributes(instance, observedAttribute2) {
 | 
				
			|||||||
function kebabToCamel(name) {
 | 
					function kebabToCamel(name) {
 | 
				
			||||||
	return name.replace(/-./g, (x) => x[1].toUpperCase());
 | 
						return name.replace(/-./g, (x) => x[1].toUpperCase());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					function requestIdle() {
 | 
				
			||||||
 | 
						return new Promise(function(resolve) {
 | 
				
			||||||
 | 
							(globalThis.requestIdleCallback || requestAnimationFrame)(resolve);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// src/dom-lib/common.js
 | 
					// src/dom-lib/common.js
 | 
				
			||||||
var enviroment = {
 | 
					var enviroment = {
 | 
				
			||||||
@@ -179,11 +184,6 @@ function connectionsChangesObserverConstructor() {
 | 
				
			|||||||
		is_observing = false;
 | 
							is_observing = false;
 | 
				
			||||||
		observer.disconnect();
 | 
							observer.disconnect();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	function requestIdle() {
 | 
					 | 
				
			||||||
		return new Promise(function(resolve) {
 | 
					 | 
				
			||||||
			(requestIdleCallback || requestAnimationFrame)(resolve);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	async function collectChildren(element) {
 | 
						async function collectChildren(element) {
 | 
				
			||||||
		if (store.size > 30)
 | 
							if (store.size > 30)
 | 
				
			||||||
			await requestIdle();
 | 
								await requestIdle();
 | 
				
			||||||
@@ -696,13 +696,10 @@ var queueSignalWrite = /* @__PURE__ */ (() => {
 | 
				
			|||||||
})();
 | 
					})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// src/signals-lib/signals-lib.js
 | 
					// src/signals-lib/signals-lib.js
 | 
				
			||||||
var Signal = oCreate(null, {
 | 
					var SignalReadOnly = oCreate(null, {
 | 
				
			||||||
	get: { value() {
 | 
						get: { value() {
 | 
				
			||||||
		return read(this);
 | 
							return read(this);
 | 
				
			||||||
	} },
 | 
						} },
 | 
				
			||||||
	set: { value(...v) {
 | 
					 | 
				
			||||||
		return write(this, ...v);
 | 
					 | 
				
			||||||
	} },
 | 
					 | 
				
			||||||
	toJSON: { value() {
 | 
						toJSON: { value() {
 | 
				
			||||||
		return read(this);
 | 
							return read(this);
 | 
				
			||||||
	} },
 | 
						} },
 | 
				
			||||||
@@ -710,9 +707,9 @@ var Signal = oCreate(null, {
 | 
				
			|||||||
		return this[mark] && this[mark].value;
 | 
							return this[mark] && this[mark].value;
 | 
				
			||||||
	} }
 | 
						} }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
var SignalReadOnly = oCreate(Signal, {
 | 
					var Signal = oCreate(SignalReadOnly, {
 | 
				
			||||||
	set: { value() {
 | 
						set: { value(...v) {
 | 
				
			||||||
		return;
 | 
							return write(this, ...v);
 | 
				
			||||||
	} }
 | 
						} }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
function isSignal(candidate) {
 | 
					function isSignal(candidate) {
 | 
				
			||||||
@@ -824,7 +821,7 @@ signal.el = function(s, map) {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
function requestCleanUpReactives(host) {
 | 
					function requestCleanUpReactives(host) {
 | 
				
			||||||
	if (!host || !host[key_reactive]) return;
 | 
						if (!host || !host[key_reactive]) return;
 | 
				
			||||||
	(requestIdleCallback || setTimeout)(function() {
 | 
						requestIdle().then(function() {
 | 
				
			||||||
		host[key_reactive] = host[key_reactive].filter(([s, el]) => el.isConnected ? true : (removeSignalListener(...s), false));
 | 
							host[key_reactive] = host[key_reactive].filter(([s, el]) => el.isConnected ? true : (removeSignalListener(...s), false));
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -889,9 +886,10 @@ var signals_config = {
 | 
				
			|||||||
function removeSignalsFromElements(s, listener, ...notes) {
 | 
					function removeSignalsFromElements(s, listener, ...notes) {
 | 
				
			||||||
	const { current } = scope;
 | 
						const { current } = scope;
 | 
				
			||||||
	current.host(function(element) {
 | 
						current.host(function(element) {
 | 
				
			||||||
		if (!element[key_reactive]) element[key_reactive] = [];
 | 
							const is_first = !element[key_reactive];
 | 
				
			||||||
 | 
							if (is_first) element[key_reactive] = [];
 | 
				
			||||||
		element[key_reactive].push([[s, listener], ...notes]);
 | 
							element[key_reactive].push([[s, listener], ...notes]);
 | 
				
			||||||
		if (current.prevent) return;
 | 
							if (!is_first || current.prevent) return;
 | 
				
			||||||
		on.disconnected(
 | 
							on.disconnected(
 | 
				
			||||||
			() => (
 | 
								() => (
 | 
				
			||||||
				/*! Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?).
 | 
									/*! Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?).
 | 
				
			||||||
@@ -906,7 +904,7 @@ var cleanUpRegistry = new FinalizationRegistry(function(s) {
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
function create(is_readonly, value, actions) {
 | 
					function create(is_readonly, value, actions) {
 | 
				
			||||||
	const varS = oCreate(is_readonly ? SignalReadOnly : Signal);
 | 
						const varS = oCreate(is_readonly ? SignalReadOnly : Signal);
 | 
				
			||||||
	const SI = toSignal(varS, value, actions, is_readonly);
 | 
						const SI = toSignal(varS, value, actions);
 | 
				
			||||||
	cleanUpRegistry.register(SI, SI[mark]);
 | 
						cleanUpRegistry.register(SI, SI[mark]);
 | 
				
			||||||
	return SI;
 | 
						return SI;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -918,7 +916,7 @@ var protoSigal = oAssign(oCreate(), {
 | 
				
			|||||||
		this.skip = true;
 | 
							this.skip = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
function toSignal(s, value, actions, readonly = false) {
 | 
					function toSignal(s, value, actions) {
 | 
				
			||||||
	const onclear = [];
 | 
						const onclear = [];
 | 
				
			||||||
	if (typeOf(actions) !== "[object Object]")
 | 
						if (typeOf(actions) !== "[object Object]")
 | 
				
			||||||
		actions = {};
 | 
							actions = {};
 | 
				
			||||||
@@ -934,8 +932,7 @@ function toSignal(s, value, actions, readonly = false) {
 | 
				
			|||||||
			actions,
 | 
								actions,
 | 
				
			||||||
			onclear,
 | 
								onclear,
 | 
				
			||||||
			host,
 | 
								host,
 | 
				
			||||||
			listeners: /* @__PURE__ */ new Set(),
 | 
								listeners: /* @__PURE__ */ new Set()
 | 
				
			||||||
			readonly
 | 
					 | 
				
			||||||
		}),
 | 
							}),
 | 
				
			||||||
		enumerable: false,
 | 
							enumerable: false,
 | 
				
			||||||
		writable: false,
 | 
							writable: false,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								dist/esm-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								dist/esm-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										10
									
								
								dist/esm.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								dist/esm.js
									
									
									
									
										vendored
									
									
								
							@@ -25,6 +25,11 @@ function onAbort(signal, listener) {
 | 
				
			|||||||
		signal.removeEventListener("abort", listener);
 | 
							signal.removeEventListener("abort", listener);
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					function requestIdle() {
 | 
				
			||||||
 | 
						return new Promise(function(resolve) {
 | 
				
			||||||
 | 
							(globalThis.requestIdleCallback || requestAnimationFrame)(resolve);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// src/dom-lib/common.js
 | 
					// src/dom-lib/common.js
 | 
				
			||||||
var enviroment = {
 | 
					var enviroment = {
 | 
				
			||||||
@@ -163,11 +168,6 @@ function connectionsChangesObserverConstructor() {
 | 
				
			|||||||
		is_observing = false;
 | 
							is_observing = false;
 | 
				
			||||||
		observer.disconnect();
 | 
							observer.disconnect();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	function requestIdle() {
 | 
					 | 
				
			||||||
		return new Promise(function(resolve) {
 | 
					 | 
				
			||||||
			(requestIdleCallback || requestAnimationFrame)(resolve);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	async function collectChildren(element) {
 | 
						async function collectChildren(element) {
 | 
				
			||||||
		if (store.size > 30)
 | 
							if (store.size > 30)
 | 
				
			||||||
			await requestIdle();
 | 
								await requestIdle();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								dist/esm.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/esm.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										35
									
								
								dist/iife-with-signals.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								dist/iife-with-signals.js
									
									
									
									
										vendored
									
									
								
							@@ -86,6 +86,11 @@ var DDE = (() => {
 | 
				
			|||||||
	function kebabToCamel(name) {
 | 
						function kebabToCamel(name) {
 | 
				
			||||||
		return name.replace(/-./g, (x) => x[1].toUpperCase());
 | 
							return name.replace(/-./g, (x) => x[1].toUpperCase());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						function requestIdle() {
 | 
				
			||||||
 | 
							return new Promise(function(resolve) {
 | 
				
			||||||
 | 
								(globalThis.requestIdleCallback || requestAnimationFrame)(resolve);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// src/dom-lib/common.js
 | 
						// src/dom-lib/common.js
 | 
				
			||||||
	var enviroment = {
 | 
						var enviroment = {
 | 
				
			||||||
@@ -224,11 +229,6 @@ var DDE = (() => {
 | 
				
			|||||||
			is_observing = false;
 | 
								is_observing = false;
 | 
				
			||||||
			observer.disconnect();
 | 
								observer.disconnect();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		function requestIdle() {
 | 
					 | 
				
			||||||
			return new Promise(function(resolve) {
 | 
					 | 
				
			||||||
				(requestIdleCallback || requestAnimationFrame)(resolve);
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		async function collectChildren(element) {
 | 
							async function collectChildren(element) {
 | 
				
			||||||
			if (store.size > 30)
 | 
								if (store.size > 30)
 | 
				
			||||||
				await requestIdle();
 | 
									await requestIdle();
 | 
				
			||||||
@@ -741,13 +741,10 @@ var DDE = (() => {
 | 
				
			|||||||
	})();
 | 
						})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// src/signals-lib/signals-lib.js
 | 
						// src/signals-lib/signals-lib.js
 | 
				
			||||||
	var Signal = oCreate(null, {
 | 
						var SignalReadOnly = oCreate(null, {
 | 
				
			||||||
		get: { value() {
 | 
							get: { value() {
 | 
				
			||||||
			return read(this);
 | 
								return read(this);
 | 
				
			||||||
		} },
 | 
							} },
 | 
				
			||||||
		set: { value(...v) {
 | 
					 | 
				
			||||||
			return write(this, ...v);
 | 
					 | 
				
			||||||
		} },
 | 
					 | 
				
			||||||
		toJSON: { value() {
 | 
							toJSON: { value() {
 | 
				
			||||||
			return read(this);
 | 
								return read(this);
 | 
				
			||||||
		} },
 | 
							} },
 | 
				
			||||||
@@ -755,9 +752,9 @@ var DDE = (() => {
 | 
				
			|||||||
			return this[mark] && this[mark].value;
 | 
								return this[mark] && this[mark].value;
 | 
				
			||||||
		} }
 | 
							} }
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	var SignalReadOnly = oCreate(Signal, {
 | 
						var Signal = oCreate(SignalReadOnly, {
 | 
				
			||||||
		set: { value() {
 | 
							set: { value(...v) {
 | 
				
			||||||
			return;
 | 
								return write(this, ...v);
 | 
				
			||||||
		} }
 | 
							} }
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	function isSignal(candidate) {
 | 
						function isSignal(candidate) {
 | 
				
			||||||
@@ -869,7 +866,7 @@ var DDE = (() => {
 | 
				
			|||||||
	};
 | 
						};
 | 
				
			||||||
	function requestCleanUpReactives(host) {
 | 
						function requestCleanUpReactives(host) {
 | 
				
			||||||
		if (!host || !host[key_reactive]) return;
 | 
							if (!host || !host[key_reactive]) return;
 | 
				
			||||||
		(requestIdleCallback || setTimeout)(function() {
 | 
							requestIdle().then(function() {
 | 
				
			||||||
			host[key_reactive] = host[key_reactive].filter(([s, el]) => el.isConnected ? true : (removeSignalListener(...s), false));
 | 
								host[key_reactive] = host[key_reactive].filter(([s, el]) => el.isConnected ? true : (removeSignalListener(...s), false));
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -934,9 +931,10 @@ var DDE = (() => {
 | 
				
			|||||||
	function removeSignalsFromElements(s, listener, ...notes) {
 | 
						function removeSignalsFromElements(s, listener, ...notes) {
 | 
				
			||||||
		const { current } = scope;
 | 
							const { current } = scope;
 | 
				
			||||||
		current.host(function(element) {
 | 
							current.host(function(element) {
 | 
				
			||||||
			if (!element[key_reactive]) element[key_reactive] = [];
 | 
								const is_first = !element[key_reactive];
 | 
				
			||||||
 | 
								if (is_first) element[key_reactive] = [];
 | 
				
			||||||
			element[key_reactive].push([[s, listener], ...notes]);
 | 
								element[key_reactive].push([[s, listener], ...notes]);
 | 
				
			||||||
			if (current.prevent) return;
 | 
								if (!is_first || current.prevent) return;
 | 
				
			||||||
			on.disconnected(
 | 
								on.disconnected(
 | 
				
			||||||
				() => (
 | 
									() => (
 | 
				
			||||||
					/*! Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?).
 | 
										/*! Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?).
 | 
				
			||||||
@@ -951,7 +949,7 @@ var DDE = (() => {
 | 
				
			|||||||
	});
 | 
						});
 | 
				
			||||||
	function create(is_readonly, value, actions) {
 | 
						function create(is_readonly, value, actions) {
 | 
				
			||||||
		const varS = oCreate(is_readonly ? SignalReadOnly : Signal);
 | 
							const varS = oCreate(is_readonly ? SignalReadOnly : Signal);
 | 
				
			||||||
		const SI = toSignal(varS, value, actions, is_readonly);
 | 
							const SI = toSignal(varS, value, actions);
 | 
				
			||||||
		cleanUpRegistry.register(SI, SI[mark]);
 | 
							cleanUpRegistry.register(SI, SI[mark]);
 | 
				
			||||||
		return SI;
 | 
							return SI;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -963,7 +961,7 @@ var DDE = (() => {
 | 
				
			|||||||
			this.skip = true;
 | 
								this.skip = true;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	function toSignal(s, value, actions, readonly = false) {
 | 
						function toSignal(s, value, actions) {
 | 
				
			||||||
		const onclear = [];
 | 
							const onclear = [];
 | 
				
			||||||
		if (typeOf(actions) !== "[object Object]")
 | 
							if (typeOf(actions) !== "[object Object]")
 | 
				
			||||||
			actions = {};
 | 
								actions = {};
 | 
				
			||||||
@@ -979,8 +977,7 @@ var DDE = (() => {
 | 
				
			|||||||
				actions,
 | 
									actions,
 | 
				
			||||||
				onclear,
 | 
									onclear,
 | 
				
			||||||
				host,
 | 
									host,
 | 
				
			||||||
				listeners: /* @__PURE__ */ new Set(),
 | 
									listeners: /* @__PURE__ */ new Set()
 | 
				
			||||||
				readonly
 | 
					 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
			enumerable: false,
 | 
								enumerable: false,
 | 
				
			||||||
			writable: false,
 | 
								writable: false,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								dist/iife-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								dist/iife-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										10
									
								
								dist/iife.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								dist/iife.js
									
									
									
									
										vendored
									
									
								
							@@ -67,6 +67,11 @@ var DDE = (() => {
 | 
				
			|||||||
			signal.removeEventListener("abort", listener);
 | 
								signal.removeEventListener("abort", listener);
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						function requestIdle() {
 | 
				
			||||||
 | 
							return new Promise(function(resolve) {
 | 
				
			||||||
 | 
								(globalThis.requestIdleCallback || requestAnimationFrame)(resolve);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// src/dom-lib/common.js
 | 
						// src/dom-lib/common.js
 | 
				
			||||||
	var enviroment = {
 | 
						var enviroment = {
 | 
				
			||||||
@@ -205,11 +210,6 @@ var DDE = (() => {
 | 
				
			|||||||
			is_observing = false;
 | 
								is_observing = false;
 | 
				
			||||||
			observer.disconnect();
 | 
								observer.disconnect();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		function requestIdle() {
 | 
					 | 
				
			||||||
			return new Promise(function(resolve) {
 | 
					 | 
				
			||||||
				(requestIdleCallback || requestAnimationFrame)(resolve);
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		async function collectChildren(element) {
 | 
							async function collectChildren(element) {
 | 
				
			||||||
			if (store.size > 30)
 | 
								if (store.size > 30)
 | 
				
			||||||
				await requestIdle();
 | 
									await requestIdle();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								dist/iife.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/iife.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1,4 +1,4 @@
 | 
				
			|||||||
import { registerClientFile, styles } from "../ssr.js";
 | 
					import { page_id, registerClientFile, styles } from "../ssr.js";
 | 
				
			||||||
const host= "."+code.name;
 | 
					const host= "."+code.name;
 | 
				
			||||||
styles.css`
 | 
					styles.css`
 | 
				
			||||||
/* Code block styling */
 | 
					/* Code block styling */
 | 
				
			||||||
@@ -177,6 +177,9 @@ ${host}:hover .copy-button {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
import { el } from "deka-dom-el";
 | 
					import { el } from "deka-dom-el";
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @typedef {"js"|"ts"|"html"|"css"|"shell"|"-"} Language
 | 
				
			||||||
 | 
					 * */
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Prints code to the page and registers flems to make it interactive.
 | 
					 * Prints code to the page and registers flems to make it interactive.
 | 
				
			||||||
 * @param {object} attrs
 | 
					 * @param {object} attrs
 | 
				
			||||||
@@ -184,15 +187,17 @@ import { el } from "deka-dom-el";
 | 
				
			|||||||
 * @param {string} [attrs.className]
 | 
					 * @param {string} [attrs.className]
 | 
				
			||||||
 * @param {URL} [attrs.src] Example code file path
 | 
					 * @param {URL} [attrs.src] Example code file path
 | 
				
			||||||
 * @param {string} [attrs.content] Example code
 | 
					 * @param {string} [attrs.content] Example code
 | 
				
			||||||
 * @param {"js"|"ts"|"html"|"css"|"shell"} [attrs.language="js"] Language of the code
 | 
					 * @param {Language} [attrs.language="-s"] Language of the code
 | 
				
			||||||
 * @param {string} [attrs.page_id] ID of the page, if setted it registers shiki
 | 
					 | 
				
			||||||
 * */
 | 
					 * */
 | 
				
			||||||
export function code({ id, src, content, language= "js", className= host.slice(1), page_id }){
 | 
					export function code({ id, src, content, language= "-", className= host.slice(1) }){
 | 
				
			||||||
	if(src) content= s.cat(src);
 | 
						if(src){
 | 
				
			||||||
 | 
							content= s.cat(src);
 | 
				
			||||||
 | 
							if(language=== "-") language= /** @type {Language} */(src.pathname.split(".").pop());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	content= normalizeIndentation(content);
 | 
						content= normalizeIndentation(content);
 | 
				
			||||||
	let dataJS;
 | 
						let dataJS;
 | 
				
			||||||
	if(page_id){
 | 
						if(language!== "-"){
 | 
				
			||||||
		registerClientPart(page_id);
 | 
							registerClientPart();
 | 
				
			||||||
		dataJS= "todo";
 | 
							dataJS= "todo";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return el("div", { id, className, dataJS, tabIndex: 0 }).append(
 | 
						return el("div", { id, className, dataJS, tabIndex: 0 }).append(
 | 
				
			||||||
@@ -204,8 +209,7 @@ export function pre({ content }){
 | 
				
			|||||||
	return el("pre").append(el("code", content.trim()));
 | 
						return el("pre").append(el("code", content.trim()));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
let is_registered= {};
 | 
					let is_registered= {};
 | 
				
			||||||
/** @param {string} page_id */
 | 
					function registerClientPart(){
 | 
				
			||||||
function registerClientPart(page_id){
 | 
					 | 
				
			||||||
	if(is_registered[page_id]) return;
 | 
						if(is_registered[page_id]) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Add Shiki with a more reliable loading method
 | 
						// Add Shiki with a more reliable loading method
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import { styles } from "../ssr.js";
 | 
					import { page_id, styles } from "../ssr.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
styles.css`
 | 
					styles.css`
 | 
				
			||||||
#html-to-dde-converter {
 | 
					#html-to-dde-converter {
 | 
				
			||||||
@@ -149,12 +149,11 @@ import { ireland } from "./ireland.html.js";
 | 
				
			|||||||
import { el } from "deka-dom-el";
 | 
					import { el } from "deka-dom-el";
 | 
				
			||||||
const fileURL= url=> new URL(url, import.meta.url);
 | 
					const fileURL= url=> new URL(url, import.meta.url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function converter({ page_id }){
 | 
					export function converter(){
 | 
				
			||||||
	registerClientPart(page_id);
 | 
						registerClientPart(page_id);
 | 
				
			||||||
	return el(ireland, {
 | 
						return el(ireland, {
 | 
				
			||||||
		src: fileURL("./converter.js.js"),
 | 
							src: fileURL("./converter.js.js"),
 | 
				
			||||||
		exportName: "converter",
 | 
							exportName: "converter",
 | 
				
			||||||
		page_id,
 | 
					 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import { styles } from "../ssr.js";
 | 
					import { page_id, styles } from "../ssr.js";
 | 
				
			||||||
const host= "."+example.name;
 | 
					const host= "."+example.name;
 | 
				
			||||||
styles.css`
 | 
					styles.css`
 | 
				
			||||||
${host} {
 | 
					${host} {
 | 
				
			||||||
@@ -119,9 +119,8 @@ import { relative } from "node:path";
 | 
				
			|||||||
 * @param {URL} attrs.src Example code file path
 | 
					 * @param {URL} attrs.src Example code file path
 | 
				
			||||||
 * @param {"js"|"ts"|"html"|"css"} [attrs.language="js"] Language of the code
 | 
					 * @param {"js"|"ts"|"html"|"css"} [attrs.language="js"] Language of the code
 | 
				
			||||||
 * @param {"normal"|"big"} [attrs.variant="normal"] Size of the example
 | 
					 * @param {"normal"|"big"} [attrs.variant="normal"] Size of the example
 | 
				
			||||||
 * @param {string} attrs.page_id ID of the page
 | 
					 | 
				
			||||||
 * */
 | 
					 * */
 | 
				
			||||||
export function example({ src, language= "js", variant= "normal", page_id }){
 | 
					export function example({ src, language= "js", variant= "normal" }){
 | 
				
			||||||
	registerClientPart(page_id);
 | 
						registerClientPart(page_id);
 | 
				
			||||||
	const content= s.cat(src).toString()
 | 
						const content= s.cat(src).toString()
 | 
				
			||||||
		.replaceAll(/ from "deka-dom-el(\/signals)?";/g, ' from "./esm-with-signals.js";');
 | 
							.replaceAll(/ from "deka-dom-el(\/signals)?";/g, ' from "./esm-with-signals.js";');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -303,6 +303,7 @@ document.body.append(
 | 
				
			|||||||
				padding: 1rem;
 | 
									padding: 1rem;
 | 
				
			||||||
				margin-bottom: 1.5rem;
 | 
									margin-bottom: 1.5rem;
 | 
				
			||||||
				box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
 | 
									box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
 | 
				
			||||||
 | 
									overflow: auto;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			.loading-spinner {
 | 
								.loading-spinner {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										501
									
								
								docs/components/examples/case-studies/products.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										501
									
								
								docs/components/examples/case-studies/products.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,501 @@
 | 
				
			|||||||
 | 
					import { el, on } from "deka-dom-el";
 | 
				
			||||||
 | 
					import { S } from "deka-dom-el/signals";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function ProductCatalog() {
 | 
				
			||||||
 | 
						const itemsPerPage = 5;
 | 
				
			||||||
 | 
						const products = asyncSignal(S, fetchProducts, { initial: [], keepLast: true });
 | 
				
			||||||
 | 
						const searchTerm = S("");
 | 
				
			||||||
 | 
						const handleSearch = (e) => searchTerm.set(e.target.value);
 | 
				
			||||||
 | 
						const sortOrder = S("default");
 | 
				
			||||||
 | 
						const handleSort = (e) => sortOrder.set(e.target.value);
 | 
				
			||||||
 | 
						const page = S(1);
 | 
				
			||||||
 | 
						const handlePageChange = (newPage) => page.set(newPage);
 | 
				
			||||||
 | 
						const resetFilters = () => {
 | 
				
			||||||
 | 
							searchTerm.set("");
 | 
				
			||||||
 | 
							sortOrder.set("default");
 | 
				
			||||||
 | 
							page.set(1);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const filteredProducts = S(() => {
 | 
				
			||||||
 | 
							if (products.status.get() !== "resolved") return [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const results = products.result.get().filter(product =>
 | 
				
			||||||
 | 
								product.title.toLowerCase().includes(searchTerm.get().toLowerCase()) ||
 | 
				
			||||||
 | 
								product.description.toLowerCase().includes(searchTerm.get().toLowerCase())
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return [...results].sort((a, b) => {
 | 
				
			||||||
 | 
								const order = sortOrder.get();
 | 
				
			||||||
 | 
								if (order === "price-asc") return a.price - b.price;
 | 
				
			||||||
 | 
								if (order === "price-desc") return b.price - a.price;
 | 
				
			||||||
 | 
								if (order === "rating") return b.rating - a.rating;
 | 
				
			||||||
 | 
								return 0; // default: no sorting
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						const totalPages = S(() => Math.ceil(filteredProducts.get().length / itemsPerPage));
 | 
				
			||||||
 | 
						const paginatedProducts = S(() => {
 | 
				
			||||||
 | 
							const currentPage = page.get();
 | 
				
			||||||
 | 
							const filtered = filteredProducts.get();
 | 
				
			||||||
 | 
							const start = (currentPage - 1) * itemsPerPage;
 | 
				
			||||||
 | 
							return filtered.slice(start, start + itemsPerPage);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Component structure
 | 
				
			||||||
 | 
						return el("div", { className: "product-catalog" }).append(
 | 
				
			||||||
 | 
							el("header", { className: "catalog-header" }).append(
 | 
				
			||||||
 | 
								el("h2", "Product Catalog"),
 | 
				
			||||||
 | 
								el("div", { className: "toolbar" }).append(
 | 
				
			||||||
 | 
									el("button", {
 | 
				
			||||||
 | 
										className: "refresh-btn",
 | 
				
			||||||
 | 
										textContent: "Refresh Products",
 | 
				
			||||||
 | 
										type: "button",
 | 
				
			||||||
 | 
										onclick: () => products.invoke(),
 | 
				
			||||||
 | 
									}),
 | 
				
			||||||
 | 
									el("button", {
 | 
				
			||||||
 | 
										className: "reset-btn",
 | 
				
			||||||
 | 
										textContent: "Reset Filters",
 | 
				
			||||||
 | 
										type: "button",
 | 
				
			||||||
 | 
										onclick: resetFilters,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Search and filter controls
 | 
				
			||||||
 | 
							el("div", { className: "controls" }).append(
 | 
				
			||||||
 | 
								el("div", { className: "search-box" }).append(
 | 
				
			||||||
 | 
									el("input", {
 | 
				
			||||||
 | 
										type: "search",
 | 
				
			||||||
 | 
										placeholder: "Search products...",
 | 
				
			||||||
 | 
										value: searchTerm,
 | 
				
			||||||
 | 
										oninput: handleSearch,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
								el("div", { className: "sort-options" }).append(
 | 
				
			||||||
 | 
									el("label", "Sort by: "),
 | 
				
			||||||
 | 
									el("select", { onchange: handleSort }, on.defer(el => el.value = sortOrder.get())).append(
 | 
				
			||||||
 | 
										el("option", { value: "default", textContent: "Default" }),
 | 
				
			||||||
 | 
										el("option", { value: "price-asc", textContent: "Price: Low to High" }),
 | 
				
			||||||
 | 
										el("option", { value: "price-desc", textContent: "Price: High to Low" }),
 | 
				
			||||||
 | 
										el("option", { value: "rating", textContent: "Top Rated" })
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Status indicators
 | 
				
			||||||
 | 
							el("div", { className: "status-container" }).append(
 | 
				
			||||||
 | 
								S.el(products.status, status =>
 | 
				
			||||||
 | 
									status === "pending" ?
 | 
				
			||||||
 | 
										el("div", { className: "loader" }).append(
 | 
				
			||||||
 | 
											el("div", { className: "spinner" }),
 | 
				
			||||||
 | 
											el("p", "Loading products...")
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									: status === "rejected" ?
 | 
				
			||||||
 | 
										el("div", { className: "error-message" }).append(
 | 
				
			||||||
 | 
											el("p", products.error.get().message),
 | 
				
			||||||
 | 
											el("button", {
 | 
				
			||||||
 | 
												textContent: "Try Again",
 | 
				
			||||||
 | 
												onclick: () => products.invoke()
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									: el()
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Results count
 | 
				
			||||||
 | 
							S.el(S(()=> [filteredProducts.get(), searchTerm.get()]), ([filtered, term]) =>
 | 
				
			||||||
 | 
								products.status.get() === "resolved"
 | 
				
			||||||
 | 
									? el("div", {
 | 
				
			||||||
 | 
										className: "results-info",
 | 
				
			||||||
 | 
										textContent: term ?
 | 
				
			||||||
 | 
												`Found ${filtered.length} products matching "${term}"`
 | 
				
			||||||
 | 
												: `Showing all ${filtered.length} products`
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									: el()
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Products grid
 | 
				
			||||||
 | 
							el("div", { className: "products-grid" }).append(
 | 
				
			||||||
 | 
								S.el(paginatedProducts, paginatedItems =>
 | 
				
			||||||
 | 
									products.status.get() === "resolved" && paginatedItems.length > 0 ?
 | 
				
			||||||
 | 
										paginatedItems.map(product => el(ProductCard, { product }))
 | 
				
			||||||
 | 
										: products.status.get() === "resolved" && paginatedItems.length === 0 ?
 | 
				
			||||||
 | 
											el("p", { className: "no-results", textContent: "No products found matching your criteria." })
 | 
				
			||||||
 | 
											: el()
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Pagination
 | 
				
			||||||
 | 
							S.el(S(()=> [totalPages.get(), page.get()]), ([total, current]) =>
 | 
				
			||||||
 | 
								products.status.get() === "resolved" && total > 1 ?
 | 
				
			||||||
 | 
									el("div", { className: "pagination" }).append(
 | 
				
			||||||
 | 
										el("button", {
 | 
				
			||||||
 | 
											textContent: "Previous",
 | 
				
			||||||
 | 
											disabled: current === 1,
 | 
				
			||||||
 | 
											onclick: () => handlePageChange(current - 1)
 | 
				
			||||||
 | 
										}),
 | 
				
			||||||
 | 
										...Array.from({ length: total }, (_, i) => i + 1).map(num =>
 | 
				
			||||||
 | 
											el("button", {
 | 
				
			||||||
 | 
												className: num === current ? "current-page" : "",
 | 
				
			||||||
 | 
												textContent: num,
 | 
				
			||||||
 | 
												onclick: () => handlePageChange(num)
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
										),
 | 
				
			||||||
 | 
										el("button", {
 | 
				
			||||||
 | 
											textContent: "Next",
 | 
				
			||||||
 | 
											disabled: current === total,
 | 
				
			||||||
 | 
											onclick: () => handlePageChange(current + 1)
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
									: el()
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Product card component
 | 
				
			||||||
 | 
					function ProductCard({ product }) {
 | 
				
			||||||
 | 
						const showDetails = S(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return el("div", { className: "product-card" }).append(
 | 
				
			||||||
 | 
							el("div", { className: "product-image" }).append(
 | 
				
			||||||
 | 
								el("img", { src: product.thumbnail, alt: product.title })
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
							el("div", { className: "product-info" }).append(
 | 
				
			||||||
 | 
								el("h3", { className: "product-title", textContent: product.title }),
 | 
				
			||||||
 | 
								el("div", { className: "product-price-rating" }).append(
 | 
				
			||||||
 | 
									el("span", { className: "product-price", textContent: `$${product.price.toFixed(2)}` }),
 | 
				
			||||||
 | 
									el("span", { className: "product-rating" }).append(
 | 
				
			||||||
 | 
										el("span", { className: "stars", textContent: "★".repeat(Math.round(product.rating)) }),
 | 
				
			||||||
 | 
										el("span", { className: "rating-value", textContent: `(${product.rating})` }),
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
								el("p", { className: "product-category", textContent: `Category: ${product.category}` }),
 | 
				
			||||||
 | 
								S.el(showDetails, details =>
 | 
				
			||||||
 | 
									details ?
 | 
				
			||||||
 | 
										el("div", { className: "product-details" }).append(
 | 
				
			||||||
 | 
											el("p", { className: "product-description", textContent: product.description }),
 | 
				
			||||||
 | 
											el("div", { className: "product-meta" }).append(
 | 
				
			||||||
 | 
												el("p", `Brand: ${product.brand}`),
 | 
				
			||||||
 | 
												el("p", `Stock: ${product.stock} units`),
 | 
				
			||||||
 | 
												el("p", `Discount: ${product.discountPercentage}%`)
 | 
				
			||||||
 | 
											)
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
										: el()
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
								el("div", { className: "product-actions" }).append(
 | 
				
			||||||
 | 
									el("button", {
 | 
				
			||||||
 | 
										className: "details-btn",
 | 
				
			||||||
 | 
										textContent: S(() => showDetails.get() ? "Hide Details" : "Show Details"),
 | 
				
			||||||
 | 
										onclick: () => showDetails.set(!showDetails.get())
 | 
				
			||||||
 | 
									}),
 | 
				
			||||||
 | 
									el("button", {
 | 
				
			||||||
 | 
										className: "add-to-cart-btn",
 | 
				
			||||||
 | 
										textContent: "Add to Cart"
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Data fetching function
 | 
				
			||||||
 | 
					async function fetchProducts({ signal }) {
 | 
				
			||||||
 | 
						await simulateNetworkDelay();
 | 
				
			||||||
 | 
						// Simulate random errors for demonstration
 | 
				
			||||||
 | 
						if (Math.random() > 0.9) throw new Error("Failed to load products. Network error.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const response = await fetch("https://dummyjson.com/products", { signal });
 | 
				
			||||||
 | 
						if (!response.ok) throw new Error(`API error: ${response.status}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const data = await response.json();
 | 
				
			||||||
 | 
						return data.products.slice(0, 20); // Limit to 20 products for the demo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Utility for simulating network latency
 | 
				
			||||||
 | 
					function simulateNetworkDelay(min = 300, max = 1200) {
 | 
				
			||||||
 | 
						const delay = Math.floor(Math.random() * (max - min + 1)) + min;
 | 
				
			||||||
 | 
						return new Promise(resolve => setTimeout(resolve, delay));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Custom hook for async data fetching with signals
 | 
				
			||||||
 | 
					 * @template T
 | 
				
			||||||
 | 
					 * @param {typeof S} S - Signal constructor
 | 
				
			||||||
 | 
					 * @param {(params: { signal: AbortSignal }) => Promise<T>} invoker - Async function to execute
 | 
				
			||||||
 | 
					 * @param {{ initial?: T, keepLast?: boolean }} options - Configuration options
 | 
				
			||||||
 | 
					 * @returns {Object} Status signals and control methods
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function asyncSignal(S, invoker, { initial, keepLast } = {}) {
 | 
				
			||||||
 | 
						// Status tracking signals
 | 
				
			||||||
 | 
						const status = S("pending");
 | 
				
			||||||
 | 
						const result = S(initial);
 | 
				
			||||||
 | 
						const error = S(null);
 | 
				
			||||||
 | 
						let controller = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Function to trigger data fetching
 | 
				
			||||||
 | 
						async function invoke() {
 | 
				
			||||||
 | 
							// Cancel any in-flight request
 | 
				
			||||||
 | 
							if (controller) controller.abort();
 | 
				
			||||||
 | 
							controller = new AbortController();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							status.set("pending");
 | 
				
			||||||
 | 
							error.set(null);
 | 
				
			||||||
 | 
							if (!keepLast) result.set(initial);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								const data = await invoker({
 | 
				
			||||||
 | 
									signal: controller.signal,
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								if (!controller.signal.aborted) {
 | 
				
			||||||
 | 
									status.set("resolved");
 | 
				
			||||||
 | 
									result.set(data);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} catch (e) {
 | 
				
			||||||
 | 
								if (e.name !== "AbortError") {
 | 
				
			||||||
 | 
									error.set(e);
 | 
				
			||||||
 | 
									status.set("rejected");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Initial data fetch
 | 
				
			||||||
 | 
						invoke();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return { status, result, error, invoke };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initialize the component
 | 
				
			||||||
 | 
					document.body.append(
 | 
				
			||||||
 | 
						el(ProductCatalog),
 | 
				
			||||||
 | 
						el("style", `
 | 
				
			||||||
 | 
							.product-catalog {
 | 
				
			||||||
 | 
								font-family: system-ui, -apple-system, sans-serif;
 | 
				
			||||||
 | 
								max-width: 1200px;
 | 
				
			||||||
 | 
								margin: 0 auto;
 | 
				
			||||||
 | 
								padding: 20px;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.catalog-header {
 | 
				
			||||||
 | 
								display: flex;
 | 
				
			||||||
 | 
								justify-content: space-between;
 | 
				
			||||||
 | 
								align-items: center;
 | 
				
			||||||
 | 
								margin-bottom: 20px;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.toolbar button {
 | 
				
			||||||
 | 
								margin-left: 10px;
 | 
				
			||||||
 | 
								padding: 8px 12px;
 | 
				
			||||||
 | 
								border-radius: 4px;
 | 
				
			||||||
 | 
								border: none;
 | 
				
			||||||
 | 
								background: #4a6cf7;
 | 
				
			||||||
 | 
								color: white;
 | 
				
			||||||
 | 
								cursor: pointer;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.controls {
 | 
				
			||||||
 | 
								display: flex;
 | 
				
			||||||
 | 
								justify-content: space-between;
 | 
				
			||||||
 | 
								margin-bottom: 20px;
 | 
				
			||||||
 | 
								gap: 15px;
 | 
				
			||||||
 | 
								flex-wrap: wrap;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.search-box input {
 | 
				
			||||||
 | 
								padding: 8px 12px;
 | 
				
			||||||
 | 
								border: 1px solid #ddd;
 | 
				
			||||||
 | 
								border-radius: 4px;
 | 
				
			||||||
 | 
								width: 300px;
 | 
				
			||||||
 | 
								max-width: 100%;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.sort-options select {
 | 
				
			||||||
 | 
								padding: 8px 12px;
 | 
				
			||||||
 | 
								border: 1px solid #ddd;
 | 
				
			||||||
 | 
								border-radius: 4px;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.loader {
 | 
				
			||||||
 | 
								text-align: center;
 | 
				
			||||||
 | 
								padding: 40px 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.spinner {
 | 
				
			||||||
 | 
								display: inline-block;
 | 
				
			||||||
 | 
								width: 40px;
 | 
				
			||||||
 | 
								height: 40px;
 | 
				
			||||||
 | 
								border: 4px solid rgba(0, 0, 0, 0.1);
 | 
				
			||||||
 | 
								border-left-color: #4a6cf7;
 | 
				
			||||||
 | 
								border-radius: 50%;
 | 
				
			||||||
 | 
								animation: spin 1s linear infinite;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@keyframes spin {
 | 
				
			||||||
 | 
								to { transform: rotate(360deg); }
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.error-message {
 | 
				
			||||||
 | 
								background: #ffebee;
 | 
				
			||||||
 | 
								color: #c62828;
 | 
				
			||||||
 | 
								padding: 15px;
 | 
				
			||||||
 | 
								border-radius: 4px;
 | 
				
			||||||
 | 
								margin: 20px 0;
 | 
				
			||||||
 | 
								text-align: center;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.results-info {
 | 
				
			||||||
 | 
								margin-bottom: 15px;
 | 
				
			||||||
 | 
								color: #666;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.products-grid {
 | 
				
			||||||
 | 
								display: grid;
 | 
				
			||||||
 | 
								grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
 | 
				
			||||||
 | 
								gap: 20px;
 | 
				
			||||||
 | 
								margin-bottom: 30px;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-card {
 | 
				
			||||||
 | 
								border: 1px solid #eee;
 | 
				
			||||||
 | 
								border-radius: 8px;
 | 
				
			||||||
 | 
								overflow: hidden;
 | 
				
			||||||
 | 
								transition: transform 0.2s, box-shadow 0.2s;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-card:hover {
 | 
				
			||||||
 | 
								transform: translateY(-5px);
 | 
				
			||||||
 | 
								box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-image img {
 | 
				
			||||||
 | 
								width: 100%;
 | 
				
			||||||
 | 
								height: 180px;
 | 
				
			||||||
 | 
								object-fit: cover;
 | 
				
			||||||
 | 
								display: block;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-info {
 | 
				
			||||||
 | 
								padding: 15px;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-title {
 | 
				
			||||||
 | 
								margin: 0 0 10px;
 | 
				
			||||||
 | 
								font-size: 1.1rem;
 | 
				
			||||||
 | 
								height: 2.4rem;
 | 
				
			||||||
 | 
								overflow: hidden;
 | 
				
			||||||
 | 
								display: -webkit-box;
 | 
				
			||||||
 | 
								-webkit-line-clamp: 2;
 | 
				
			||||||
 | 
								-webkit-box-orient: vertical;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-price-rating {
 | 
				
			||||||
 | 
								display: flex;
 | 
				
			||||||
 | 
								justify-content: space-between;
 | 
				
			||||||
 | 
								margin-bottom: 10px;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-price {
 | 
				
			||||||
 | 
								font-weight: bold;
 | 
				
			||||||
 | 
								color: #4a6cf7;
 | 
				
			||||||
 | 
								font-size: 1.2rem;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.stars {
 | 
				
			||||||
 | 
								color: gold;
 | 
				
			||||||
 | 
								margin-right: 5px;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-category {
 | 
				
			||||||
 | 
								color: #666;
 | 
				
			||||||
 | 
								font-size: 0.9rem;
 | 
				
			||||||
 | 
								margin-bottom: 15px;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-details {
 | 
				
			||||||
 | 
								margin: 15px 0;
 | 
				
			||||||
 | 
								font-size: 0.9rem;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-description {
 | 
				
			||||||
 | 
								line-height: 1.5;
 | 
				
			||||||
 | 
								margin-bottom: 10px;
 | 
				
			||||||
 | 
								color: #444;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-meta {
 | 
				
			||||||
 | 
								display: flex;
 | 
				
			||||||
 | 
								flex-wrap: wrap;
 | 
				
			||||||
 | 
								gap: 10px;
 | 
				
			||||||
 | 
								color: #666;
 | 
				
			||||||
 | 
								font-size: 0.85rem;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-actions {
 | 
				
			||||||
 | 
								display: flex;
 | 
				
			||||||
 | 
								gap: 10px;
 | 
				
			||||||
 | 
								margin-top: 15px;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.product-actions button {
 | 
				
			||||||
 | 
								flex: 1;
 | 
				
			||||||
 | 
								padding: 8px 0;
 | 
				
			||||||
 | 
								border: none;
 | 
				
			||||||
 | 
								border-radius: 4px;
 | 
				
			||||||
 | 
								cursor: pointer;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.details-btn {
 | 
				
			||||||
 | 
								background: #eee;
 | 
				
			||||||
 | 
								color: #333;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.add-to-cart-btn {
 | 
				
			||||||
 | 
								background: #4a6cf7;
 | 
				
			||||||
 | 
								color: white;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.pagination {
 | 
				
			||||||
 | 
								display: flex;
 | 
				
			||||||
 | 
								justify-content: center;
 | 
				
			||||||
 | 
								gap: 5px;
 | 
				
			||||||
 | 
								margin-top: 30px;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.pagination button {
 | 
				
			||||||
 | 
								padding: 8px 12px;
 | 
				
			||||||
 | 
								border: 1px solid #ddd;
 | 
				
			||||||
 | 
								background: white;
 | 
				
			||||||
 | 
								border-radius: 4px;
 | 
				
			||||||
 | 
								cursor: pointer;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.pagination button.current-page {
 | 
				
			||||||
 | 
								background: #4a6cf7;
 | 
				
			||||||
 | 
								color: white;
 | 
				
			||||||
 | 
								border-color: #4a6cf7;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.pagination button:disabled {
 | 
				
			||||||
 | 
								opacity: 0.5;
 | 
				
			||||||
 | 
								cursor: not-allowed;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.no-results {
 | 
				
			||||||
 | 
								grid-column: 1 / -1;
 | 
				
			||||||
 | 
								text-align: center;
 | 
				
			||||||
 | 
								padding: 40px;
 | 
				
			||||||
 | 
								color: #666;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@media (max-width: 768px) {
 | 
				
			||||||
 | 
								.controls {
 | 
				
			||||||
 | 
									flex-direction: column;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								.search-box input {
 | 
				
			||||||
 | 
									width: 100%;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								.products-grid {
 | 
				
			||||||
 | 
									grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						`),
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
@@ -483,7 +483,7 @@ document.body.append(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		.task-board {
 | 
							.task-board {
 | 
				
			||||||
			display: grid;
 | 
								display: grid;
 | 
				
			||||||
			grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
 | 
								grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
 | 
				
			||||||
			gap: 1.5rem;
 | 
								gap: 1.5rem;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import { el } from "deka-dom-el";
 | 
				
			|||||||
const button = el("button", {
 | 
					const button = el("button", {
 | 
				
			||||||
	textContent: "Click me",
 | 
						textContent: "Click me",
 | 
				
			||||||
	className: "primary",
 | 
						className: "primary",
 | 
				
			||||||
	disabled: true
 | 
						disabled: true,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Shorter and more expressive
 | 
					// Shorter and more expressive
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,6 @@ import { el } from "deka-dom-el";
 | 
				
			|||||||
document.body.append(
 | 
					document.body.append(
 | 
				
			||||||
	el("div").append(
 | 
						el("div").append(
 | 
				
			||||||
		el("h1", "Title"),
 | 
							el("h1", "Title"),
 | 
				
			||||||
		el("p", "Paragraph")
 | 
							el("p", "Paragraph"),
 | 
				
			||||||
	)
 | 
						),
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,11 +81,11 @@ function Todos(){
 | 
				
			|||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		S.el(todosS, todos => !todos.length
 | 
							S.el(todosS, ({ length }) => !length
 | 
				
			||||||
			? el()
 | 
								? el()
 | 
				
			||||||
			: el("footer", { className: "footer" }).append(
 | 
								: el("footer", { className: "footer" }).append(
 | 
				
			||||||
				el("span", { className: "todo-count" }).append(
 | 
									el("span", { className: "todo-count" }).append(
 | 
				
			||||||
					noOfLeft()
 | 
										el("strong", length + " " + (length === 1 ? "item" : "items")),
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				memo("filters", ()=>
 | 
									memo("filters", ()=>
 | 
				
			||||||
					el("ul", { className: "filters" }).append(
 | 
										el("ul", { className: "filters" }).append(
 | 
				
			||||||
@@ -100,7 +100,7 @@ function Todos(){
 | 
				
			|||||||
						)
 | 
											)
 | 
				
			||||||
					),
 | 
										),
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				todos.length - todosRemainingS.get() === 0
 | 
									length - todosRemainingS.get() === 0
 | 
				
			||||||
					? el()
 | 
										? el()
 | 
				
			||||||
					: memo("delete", () =>
 | 
										: memo("delete", () =>
 | 
				
			||||||
						el("button",
 | 
											el("button",
 | 
				
			||||||
@@ -110,13 +110,6 @@ function Todos(){
 | 
				
			|||||||
			)
 | 
								)
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
	function noOfLeft(){
 | 
					 | 
				
			||||||
		const length = todosRemainingS.get();
 | 
					 | 
				
			||||||
		return el("strong").append(
 | 
					 | 
				
			||||||
			length + " ",
 | 
					 | 
				
			||||||
			length === 1 ? "item left" : "items left"
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import { styles } from "../ssr.js";
 | 
					import { styles, page_id } from "../ssr.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
styles.css`
 | 
					styles.css`
 | 
				
			||||||
#library-url-form {
 | 
					#library-url-form {
 | 
				
			||||||
@@ -74,7 +74,7 @@ styles.css`
 | 
				
			|||||||
import { el } from "deka-dom-el";
 | 
					import { el } from "deka-dom-el";
 | 
				
			||||||
import { ireland } from "./ireland.html.js";
 | 
					import { ireland } from "./ireland.html.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getLibraryUrl({ page_id }){
 | 
					export function getLibraryUrl(){
 | 
				
			||||||
	return el(ireland, {
 | 
						return el(ireland, {
 | 
				
			||||||
		src: new URL("./getLibraryUrl.js.js", import.meta.url),
 | 
							src: new URL("./getLibraryUrl.js.js", import.meta.url),
 | 
				
			||||||
		exportName: "getLibraryUrl",
 | 
							exportName: "getLibraryUrl",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,6 @@ const componentsRegistry = new Map();
 | 
				
			|||||||
 * @param {object} attrs
 | 
					 * @param {object} attrs
 | 
				
			||||||
 * @param {URL} attrs.src - Path to the file containing the component
 | 
					 * @param {URL} attrs.src - Path to the file containing the component
 | 
				
			||||||
 * @param {string} [attrs.exportName="default"] - Name of the export to use
 | 
					 * @param {string} [attrs.exportName="default"] - Name of the export to use
 | 
				
			||||||
 * @param {string} attrs.page_id - ID of the current page
 | 
					 | 
				
			||||||
 * @param {object} [attrs.props={}] - Props to pass to the component
 | 
					 * @param {object} [attrs.props={}] - Props to pass to the component
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function ireland({ src, exportName = "default", props = {} }) {
 | 
					export function ireland({ src, exportName = "default", props = {} }) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,10 @@ export function mnemonic(){
 | 
				
			|||||||
			" — corresponds to custom elemnets callbacks ", el("code", "<live-cycle>Callback(...){...}"),
 | 
								" — corresponds to custom elemnets callbacks ", el("code", "<live-cycle>Callback(...){...}"),
 | 
				
			||||||
			". To connect to custom element see following page, else it is simulated by MutationObserver."
 | 
								". To connect to custom element see following page, else it is simulated by MutationObserver."
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
							el("li").append(
 | 
				
			||||||
 | 
								el("code", "on.defer(<identity>=> <identity>)(<identity>)"),
 | 
				
			||||||
 | 
								" — calls callback later",
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
		el("li").append(
 | 
							el("li").append(
 | 
				
			||||||
			el("code", "dispatchEvent(<event>[, <options>])(element)"),
 | 
								el("code", "dispatchEvent(<event>[, <options>])(element)"),
 | 
				
			||||||
			" — just ", el("code", "<element>.dispatchEvent(new Event(<event>[, <options>]))")
 | 
								" — just ", el("code", "<element>.dispatchEvent(new Event(<event>[, <options>]))")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,8 +3,8 @@ import { t, T } from "./utils/index.js";
 | 
				
			|||||||
export const info= {
 | 
					export const info= {
 | 
				
			||||||
	href: "./",
 | 
						href: "./",
 | 
				
			||||||
	title: t`Introduction`,
 | 
						title: t`Introduction`,
 | 
				
			||||||
	fullTitle: t`Vanilla for flavouring — a full-fledged feast for large projects`,
 | 
						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`Reactive DOM library for creating dynamic UIs with a declarative syntax`,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { el } from "deka-dom-el";
 | 
					import { el } from "deka-dom-el";
 | 
				
			||||||
@@ -16,6 +16,10 @@ import { getLibraryUrl } from "./components/getLibraryUrl.html.js";
 | 
				
			|||||||
/** @param {string} url */
 | 
					/** @param {string} url */
 | 
				
			||||||
const fileURL= url=> new URL(url, import.meta.url);
 | 
					const fileURL= url=> new URL(url, import.meta.url);
 | 
				
			||||||
const references= {
 | 
					const references= {
 | 
				
			||||||
 | 
						npm: {
 | 
				
			||||||
 | 
							title: t`NPM package page for dd<el>`,
 | 
				
			||||||
 | 
							href: "https://www.npmjs.com/package/deka-dom-el",
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	w_mvv: {
 | 
						w_mvv: {
 | 
				
			||||||
		title: t`Wikipedia: Model–view–viewmodel`,
 | 
							title: t`Wikipedia: Model–view–viewmodel`,
 | 
				
			||||||
		href: "https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel",
 | 
							href: "https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel",
 | 
				
			||||||
@@ -27,10 +31,9 @@ const references= {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Welcome to Deka DOM Elements (dd<el> or DDE) — a lightweight library for building dynamic UIs with
 | 
								Welcome to Deka DOM Elements (dd<el> or DDE) — a library for building dynamic UIs with
 | 
				
			||||||
			a declarative syntax that stays close to the native DOM API. dd<el> gives you powerful reactive tools
 | 
								a declarative syntax that stays close to the native DOM API. dd<el> gives you powerful reactive tools
 | 
				
			||||||
			without the complexity and overhead of larger frameworks.
 | 
								without the complexity and overhead of larger frameworks.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
@@ -40,11 +43,11 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				el("li", t`No build step required — use directly in the browser`),
 | 
									el("li", t`No build step required — use directly in the browser`),
 | 
				
			||||||
				el("li", t`Lightweight core (~10–15kB minified) without unnecessary dependencies (0 at now 😇)`),
 | 
									el("li", t`Lightweight core (~10–15kB minified) without unnecessary dependencies (0 at now 😇)`),
 | 
				
			||||||
				el("li", t`Natural DOM API — work with real DOM nodes, not abstractions`),
 | 
									el("li", t`Natural DOM API — work with real DOM nodes, not abstractions`),
 | 
				
			||||||
				el("li", t`Built-in reactivity with simplified but powerful signals system`),
 | 
									el("li", t`Built-in (but optional) reactivity with simplified but powerful signals system`),
 | 
				
			||||||
				el("li", t`Clean code organization with the 3PS pattern`)
 | 
									el("li", t`Clean code organization with the 3PS pattern`)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/introducing/helloWorld.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/introducing/helloWorld.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, { textContent: t`The 3PS Pattern: Simplified architecture pattern`, id: "h-3ps" }),
 | 
							el(h3, { textContent: t`The 3PS Pattern: Simplified architecture pattern`, id: "h-3ps" }),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -56,11 +59,11 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			el("div", { className: "tabs" }).append(
 | 
								el("div", { className: "tabs" }).append(
 | 
				
			||||||
				el("div", { className: "tab" }).append(
 | 
									el("div", { className: "tab" }).append(
 | 
				
			||||||
					el("h5", t`Traditional DOM Manipulation`),
 | 
										el("h5", t`Traditional DOM Manipulation`),
 | 
				
			||||||
					el(code, { src: fileURL("./components/examples/introducing/3ps-before.js"), page_id }),
 | 
										el(code, { src: fileURL("./components/examples/introducing/3ps-before.js") }),
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				el("div", { className: "tab" }).append(
 | 
									el("div", { className: "tab" }).append(
 | 
				
			||||||
					el("h5", t`dd<el>'s 3PS Pattern`),
 | 
										el("h5", t`dd<el>'s 3PS Pattern`),
 | 
				
			||||||
					el(code, { src: fileURL("./components/examples/introducing/3ps.js"), page_id }),
 | 
										el(code, { src: fileURL("./components/examples/introducing/3ps.js") }),
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -101,16 +104,20 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Getting Started`),
 | 
							el(h3, t`Getting Started`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			There are multiple ways to include dd<el> in your project. You can use npm for a full development setup,
 | 
								There are multiple ways to include dd<el> in your project. You can use npm for a full development setup,
 | 
				
			||||||
			or directly include it from a CDN for quick prototyping.
 | 
								or directly include it from a CDN for quick prototyping.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el("h4", "npm installation"),
 | 
							el("h4", "npm installation"),
 | 
				
			||||||
		el(code, { content: "npm install deka-dom-el --save", language: "shell", page_id }),
 | 
							el(code, { content: "npm install deka-dom-el --save", language: "shell" }),
 | 
				
			||||||
 | 
							el("p").append(T`
 | 
				
			||||||
 | 
								…see ${el("a", { textContent: "package page", ...references.npm, target: "_blank" })}.
 | 
				
			||||||
 | 
							`),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", "CDN / Direct Script Usage"),
 | 
							el("h4", "CDN / Direct Script Usage"),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Use the interactive selector below to choose your preferred format:
 | 
								Use the interactive selector below to choose your preferred format:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(getLibraryUrl, { page_id }),
 | 
							el(getLibraryUrl),
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
				Based on your selection, you can use dd<el> in your project like this:
 | 
									Based on your selection, you can use dd<el> in your project like this:
 | 
				
			||||||
@@ -119,10 +126,10 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				// ESM format (modern JavaScript with import/export)
 | 
									// ESM format (modern JavaScript with import/export)
 | 
				
			||||||
				import { el, on } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.min.js";
 | 
									import { el, on } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.min.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Or with IIFE format (creates a global DDE object)
 | 
									// Or with IIFE format (creates a global DDE object)
 | 
				
			||||||
				// <script src="https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/iife-with-signals.min.js"></script>
 | 
									// <script src="https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/iife-with-signals.min.js"></script>
 | 
				
			||||||
				const { el, on } = DDE;
 | 
									const { el, on } = DDE;
 | 
				
			||||||
			`, language: "js", page_id }),
 | 
								`, language: "js" }),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`How to Use This Documentation`),
 | 
							el(h3, t`How to Use This Documentation`),
 | 
				
			||||||
@@ -146,7 +153,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				Integrating third-party functionalities`),
 | 
									Integrating third-party functionalities`),
 | 
				
			||||||
			el("li").append(T`${el("a", { href: "p09-optimization.html" })
 | 
								el("li").append(T`${el("a", { href: "p09-optimization.html" })
 | 
				
			||||||
					.append(el("strong", "Performance Optimization"))} — Techniques for optimizing your applications`),
 | 
										.append(el("strong", "Performance Optimization"))} — Techniques for optimizing your applications`),
 | 
				
			||||||
			el("li").append(T`${el("a", { href: "p10-todomvc.html" }).append(el("strong", "TodoMVC"))} — A real-world
 | 
								el("li").append(T`${el("a", { href: "p10-todomvc.html" }).append(el("strong", "TodoMVC"))} — A real-world
 | 
				
			||||||
				application implementation`),
 | 
									application implementation`),
 | 
				
			||||||
			el("li").append(T`${el("a", { href: "p11-ssr.html" }).append(el("strong", "SSR"))} — Server-side
 | 
								el("li").append(T`${el("a", { href: "p11-ssr.html" }).append(el("strong", "SSR"))} — Server-side
 | 
				
			||||||
				rendering with dd<el>`),
 | 
									rendering with dd<el>`),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,12 +47,11 @@ const references= {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							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.
 | 
				
			||||||
			dd<el> provides a simple yet powerful approach to element creation that is declarative, chainable,
 | 
								dd<el> provides a simple yet powerful approach to element creation that is declarative, chainable,
 | 
				
			||||||
			and maintains a clean syntax close to HTML structure.
 | 
								and maintains a clean syntax close to HTML structure.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el("div", { className: "callout" }).append(
 | 
							el("div", { className: "callout" }).append(
 | 
				
			||||||
			el("h4", t`dd<el> Elements: Key Benefits`),
 | 
								el("h4", t`dd<el> Elements: Key Benefits`),
 | 
				
			||||||
@@ -65,7 +64,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/elements/intro.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/elements/intro.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Creating Elements: Native vs dd<el>`),
 | 
							el(h3, t`Creating Elements: Native vs dd<el>`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -77,11 +76,11 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			el("div", { className: "comparison" }).append(
 | 
								el("div", { className: "comparison" }).append(
 | 
				
			||||||
				el("div").append(
 | 
									el("div").append(
 | 
				
			||||||
					el("h5", t`Native DOM API`),
 | 
										el("h5", t`Native DOM API`),
 | 
				
			||||||
					el(code, { src: fileURL("./components/examples/elements/native-dom-create.js"), page_id })
 | 
										el(code, { src: fileURL("./components/examples/elements/native-dom-create.js") })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				el("div").append(
 | 
									el("div").append(
 | 
				
			||||||
					el("h5", t`dd<el> Approach`),
 | 
										el("h5", t`dd<el> Approach`),
 | 
				
			||||||
					el(code, { src: fileURL("./components/examples/elements/dde-dom-create.js"), page_id })
 | 
										el(code, { src: fileURL("./components/examples/elements/dde-dom-create.js") })
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -89,7 +88,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			The ${el("code", "el")} function provides a simple wrapper around ${el("code", "document.createElement")}
 | 
								The ${el("code", "el")} function provides a simple wrapper around ${el("code", "document.createElement")}
 | 
				
			||||||
			with enhanced property assignment.
 | 
								with enhanced property assignment.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/elements/dekaCreateElement.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/elements/dekaCreateElement.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Advanced Property Assignment`),
 | 
							el(h3, t`Advanced Property Assignment`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -122,7 +121,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				el("dd").append(T`Pass ${el("code", "undefined")} to remove a property or attribute`)
 | 
									el("dd").append(T`Pass ${el("code", "undefined")} to remove a property or attribute`)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/elements/dekaAssign.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/elements/dekaAssign.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -142,11 +141,11 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			el("div", { className: "comparison" }).append(
 | 
								el("div", { className: "comparison" }).append(
 | 
				
			||||||
				el("div", { className: "bad-practice" }).append(
 | 
									el("div", { className: "bad-practice" }).append(
 | 
				
			||||||
					el("h5", t`❌ Native DOM API`),
 | 
										el("h5", t`❌ Native DOM API`),
 | 
				
			||||||
					el(code, { src: fileURL("./components/examples/elements/native-dom-tree.js"), page_id })
 | 
										el(code, { src: fileURL("./components/examples/elements/native-dom-tree.js") })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				el("div", { className: "good-practice" }).append(
 | 
									el("div", { className: "good-practice" }).append(
 | 
				
			||||||
					el("h5", t`✅ dd<el> Approach`),
 | 
										el("h5", t`✅ dd<el> Approach`),
 | 
				
			||||||
					el(code, { src: fileURL("./components/examples/elements/dde-dom-tree.js"), page_id })
 | 
										el(code, { src: fileURL("./components/examples/elements/dde-dom-tree.js") })
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -154,14 +153,14 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			This chainable pattern is much cleaner and easier to follow, especially for deeply nested elements.
 | 
								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.
 | 
								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(example, { src: fileURL("./components/examples/elements/dekaAppend.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Using Components to Build UI Fragments`),
 | 
							el(h3, t`Using Components to Build UI Fragments`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			The ${el("code", "el")} function is overloaded to support both tag names and function components.
 | 
								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:
 | 
								This lets you refactor complex UI trees into reusable pieces:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/elements/dekaBasicComponent.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/elements/dekaBasicComponent.js") }),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Component functions receive the properties object as their first argument, just like regular elements.
 | 
								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.
 | 
								This makes it easy to pass data down to components and create reusable UI fragments.
 | 
				
			||||||
@@ -185,9 +184,9 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			function which corresponds to the native ${el("a", references.mdn_ns).append(el("code",
 | 
								function which corresponds to the native ${el("a", references.mdn_ns).append(el("code",
 | 
				
			||||||
				"document.createElementNS"))}:
 | 
									"document.createElementNS"))}:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/elements/dekaElNS.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/elements/dekaElNS.js") }),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			This function returns a namespace-specific element creator, allowing you to work with any element type
 | 
								This function returns a namespace-specific element creator, allowing you to work with any element type
 | 
				
			||||||
			using the same consistent interface.
 | 
								using the same consistent interface.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,6 @@ const references= {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Events are at the core of interactive web applications. dd<el> provides a clean, declarative approach to
 | 
								Events are at the core of interactive web applications. dd<el> provides a clean, declarative approach to
 | 
				
			||||||
@@ -57,7 +56,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/events/intro.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/events/intro.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Events and Listeners: Two Approaches`),
 | 
							el(h3, t`Events and Listeners: Two Approaches`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -70,11 +69,11 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			el("div", { className: "tabs" }).append(
 | 
								el("div", { className: "tabs" }).append(
 | 
				
			||||||
				el("div", { className: "tab" }).append(
 | 
									el("div", { className: "tab" }).append(
 | 
				
			||||||
					el("h5", t`Native DOM API`),
 | 
										el("h5", t`Native DOM API`),
 | 
				
			||||||
					el(code, { content: `element.addEventListener("click", callback, options);`, page_id })
 | 
										el(code, { content: `element.addEventListener("click", callback, options);`, language: "js" })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				el("div", { className: "tab" }).append(
 | 
									el("div", { className: "tab" }).append(
 | 
				
			||||||
					el("h5", t`dd<el> Approach`),
 | 
										el("h5", t`dd<el> Approach`),
 | 
				
			||||||
					el(code, { content: `on("click", callback, options)(element);`, page_id })
 | 
										el(code, { content: `on("click", callback, options)(element);`, language: "js" })
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -82,7 +81,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			The main benefit of dd<el>’s approach is that it works as an Addon (see below), making it easy to integrate
 | 
								The main benefit of dd<el>’s approach is that it works as an Addon (see below), making it easy to integrate
 | 
				
			||||||
			directly into element declarations.
 | 
								directly into element declarations.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/events/compare.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/events/compare.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Removing Event Listeners`),
 | 
							el(h3, t`Removing Event Listeners`),
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
@@ -91,7 +90,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				${el("a", { textContent: "AbortSignal", ...references.mdn_abortListener })} for declarative removal:
 | 
									${el("a", { textContent: "AbortSignal", ...references.mdn_abortListener })} for declarative removal:
 | 
				
			||||||
			`)
 | 
								`)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/events/abortSignal.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/events/abortSignal.js") }),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			This is the same for signals (see next section) and works well with scopes and library extendability (
 | 
								This is the same for signals (see next section) and works well with scopes and library extendability (
 | 
				
			||||||
			see scopes and extensions section — mainly ${el("code", "scope.signal")}).
 | 
								see scopes and extensions section — mainly ${el("code", "scope.signal")}).
 | 
				
			||||||
@@ -101,26 +100,26 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		el("div", { className: "tabs" }).append(
 | 
							el("div", { className: "tabs" }).append(
 | 
				
			||||||
			el("div", { className: "tab", dataTab: "html-attr" }).append(
 | 
								el("div", { className: "tab", dataTab: "html-attr" }).append(
 | 
				
			||||||
				el("h4", t`HTML Attribute Style`),
 | 
									el("h4", t`HTML Attribute Style`),
 | 
				
			||||||
				el(code, { src: fileURL("./components/examples/events/attribute-event.js"), page_id }),
 | 
									el(code, { src: fileURL("./components/examples/events/attribute-event.js") }),
 | 
				
			||||||
				el("p").append(T`
 | 
									el("p").append(T`
 | 
				
			||||||
					Forces usage as an HTML attribute. Corresponds to
 | 
										Forces usage as an HTML attribute. Corresponds to
 | 
				
			||||||
					${el("code", `<button onclick="console.log(event)">click me</button>`)}. This can be particularly
 | 
										${el("code", `<button onclick="console.log(event)">click me</button>`)}. This can be particularly
 | 
				
			||||||
					useful for SSR scenarios.
 | 
										useful for SSR scenarios.
 | 
				
			||||||
				`)
 | 
									`)
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			el("div", { className: "tab", dataTab: "property" }).append(
 | 
								el("div", { className: "tab", dataTab: "property" }).append(
 | 
				
			||||||
				el("h4", t`Property Assignment`),
 | 
									el("h4", t`Property Assignment`),
 | 
				
			||||||
				el(code, { src: fileURL("./components/examples/events/property-event.js"), page_id }),
 | 
									el(code, { src: fileURL("./components/examples/events/property-event.js") }),
 | 
				
			||||||
				el("p", t`Assigns the event handler directly to the element’s property.`)
 | 
									el("p", t`Assigns the event handler directly to the element’s property.`)
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			el("div", { className: "tab", dataTab: "addon" }).append(
 | 
								el("div", { className: "tab", dataTab: "addon" }).append(
 | 
				
			||||||
				el("h4", t`Addon Approach`),
 | 
									el("h4", t`Addon Approach`),
 | 
				
			||||||
				el(code, { src: fileURL("./components/examples/events/chain-event.js"), page_id }),
 | 
									el(code, { src: fileURL("./components/examples/events/chain-event.js") }),
 | 
				
			||||||
				el("p", t`Uses the addon pattern (so adds the event listener to the element), see above.`)
 | 
									el("p", t`Uses the addon pattern (so adds the event listener to the element), see above.`)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			For a deeper comparison of these approaches, see
 | 
								For a deeper comparison of these approaches, see
 | 
				
			||||||
			${el("a", { textContent: "WebReflection’s detailed analysis", ...references.web_events })}.
 | 
								${el("a", { textContent: "WebReflection’s detailed analysis", ...references.web_events })}.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -143,7 +142,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			You can use Addons as ≥3rd argument of the ${el("code", "el")} function, making it possible to
 | 
								You can use Addons as ≥3rd argument of the ${el("code", "el")} function, making it possible to
 | 
				
			||||||
			extend your templates with additional functionality:
 | 
								extend your templates with additional functionality:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/events/templateWithListeners.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/events/templateWithListeners.js") }),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			As the example shows, you can provide types in JSDoc+TypeScript using the global type
 | 
								As the example shows, you can provide types in JSDoc+TypeScript using the global type
 | 
				
			||||||
			${el("code", "ddeElementAddon")}. Notice how Addons can also be used to get element references.
 | 
								${el("code", "ddeElementAddon")}. Notice how Addons can also be used to get element references.
 | 
				
			||||||
@@ -167,7 +166,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				el("dd", t`Fires when the element is removed from the DOM`),
 | 
									el("dd", t`Fires when the element is removed from the DOM`),
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/events/live-cycle.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/events/live-cycle.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -217,8 +216,8 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			This makes it easy to implement component communication through events, following standard web platform
 | 
								This makes it easy to implement component communication through events, following standard web platform
 | 
				
			||||||
			patterns. The curried approach allows for easy reuse of event dispatchers throughout your application.
 | 
								patterns. The curried approach allows for easy reuse of event dispatchers throughout your application.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/events/compareDispatch.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/events/compareDispatch.js") }),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/events/dispatch.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/events/dispatch.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Best Practices`),
 | 
							el(h3, t`Best Practices`),
 | 
				
			||||||
		el("ol").append(
 | 
							el("ol").append(
 | 
				
			||||||
@@ -251,6 +250,6 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(mnemonic)
 | 
							el(mnemonic),
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,7 +42,6 @@ const references= {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Signals provide a simple yet powerful way to create reactive applications with dd<el>. They handle the
 | 
								Signals provide a simple yet powerful way to create reactive applications with dd<el>. They handle the
 | 
				
			||||||
@@ -58,7 +57,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				el("li").append(T`${el("strong", "In future")} 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 }),
 | 
							el(code, { src: fileURL("./components/examples/signals/intro.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`The 3-Part Structure of Signals`),
 | 
							el(h3, t`The 3-Part Structure of Signals`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -68,21 +67,21 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		el("div", { className: "signal-diagram" }).append(
 | 
							el("div", { className: "signal-diagram" }).append(
 | 
				
			||||||
			el("div", { className: "signal-part" }).append(
 | 
								el("div", { className: "signal-part" }).append(
 | 
				
			||||||
				el("h4", t`PART 1: Create Signal`),
 | 
									el("h4", t`PART 1: Create Signal`),
 | 
				
			||||||
				el(code, { content: "const count = S(0);", page_id }),
 | 
									el(code, { content: "const count = S(0);", language: "js" }),
 | 
				
			||||||
				el("p", t`Define a reactive value that can be observed and changed`)
 | 
									el("p", t`Define a reactive value that can be observed and changed`)
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			el("div", { className: "signal-part" }).append(
 | 
								el("div", { className: "signal-part" }).append(
 | 
				
			||||||
				el("h4", t`PART 2: React to Changes`),
 | 
									el("h4", t`PART 2: React to Changes`),
 | 
				
			||||||
				el(code, { content: "S.on(count, value => updateUI(value));", page_id }),
 | 
									el(code, { content: "S.on(count, value => updateUI(value));", language: "js" }),
 | 
				
			||||||
				el("p", t`Subscribe to signal changes with callbacks or effects`)
 | 
									el("p", t`Subscribe to signal changes with callbacks or effects`)
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			el("div", { className: "signal-part" }).append(
 | 
								el("div", { className: "signal-part" }).append(
 | 
				
			||||||
				el("h4", t`PART 3: Update Signal`),
 | 
									el("h4", t`PART 3: Update Signal`),
 | 
				
			||||||
				el(code, { content: "count.set(count.get() + 1);", page_id }),
 | 
									el(code, { content: "count.set(count.get() + 1);", language: "js" }),
 | 
				
			||||||
				el("p", t`Modify the signal value, which automatically triggers updates`)
 | 
									el("p", t`Modify the signal value, which automatically triggers updates`)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/signals/signals.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/signals/signals.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -125,12 +124,12 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			Computed values (also called derived signals) automatically update when their dependencies change.
 | 
								Computed values (also called derived signals) automatically update when their dependencies change.
 | 
				
			||||||
			Create them by passing ${el("strong", "a function")} to ${el("code", "S()")}:
 | 
								Create them by passing ${el("strong", "a function")} to ${el("code", "S()")}:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/signals/derived.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/signals/derived.js") }),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Derived signals are read-only - you can’t call ${el("code", ".set()")} on them. Their value is always
 | 
								Derived signals are read-only - you can’t call ${el("code", ".set()")} on them. Their value is always
 | 
				
			||||||
			computed from their dependencies. They’re perfect for transforming or combining data from other signals.
 | 
								computed from their dependencies. They’re perfect for transforming or combining data from other signals.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/signals/computations-abort.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/signals/computations-abort.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Signal Actions: For Complex State`),
 | 
							el(h3, t`Signal Actions: For Complex State`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -151,7 +150,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
						});
 | 
											});
 | 
				
			||||||
						// Use the action
 | 
											// Use the action
 | 
				
			||||||
						S.action(todos, "add", "New todo");
 | 
											S.action(todos, "add", "New todo");
 | 
				
			||||||
					`, page_id })
 | 
										`, language: "js" })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				el("div", { className: "bad-practice" }).append(
 | 
									el("div", { className: "bad-practice" }).append(
 | 
				
			||||||
					el("h5", t`❌ Without Actions`),
 | 
										el("h5", t`❌ Without Actions`),
 | 
				
			||||||
@@ -161,7 +160,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
						const items = todos.get();
 | 
											const items = todos.get();
 | 
				
			||||||
						items.push("New todo");
 | 
											items.push("New todo");
 | 
				
			||||||
						// This WON’T trigger updates!
 | 
											// This WON’T trigger updates!
 | 
				
			||||||
					`, page_id }))
 | 
										`, language: "js" }))
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -172,7 +171,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			${el("code", "this.stopPropagation()")} in the method representing the given action. As it can be seen in
 | 
								${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")}).
 | 
								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(example, { src: fileURL("./components/examples/signals/actions-demo.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Actions provide these benefits:
 | 
								Actions provide these benefits:
 | 
				
			||||||
@@ -186,7 +185,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Here’s a more complete example of a todo list using signal actions:
 | 
								Here’s a more complete example of a todo list using signal actions:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/signals/actions-todos.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/signals/actions-todos.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -223,7 +222,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
					// Later:
 | 
										// Later:
 | 
				
			||||||
					color.set("red"); // UI updates automatically
 | 
										color.set("red"); // UI updates automatically
 | 
				
			||||||
				`, page_id }),
 | 
									`, language: "js" }),
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			el("div", { className: "tab", dataTab: "elements" }).append(
 | 
								el("div", { className: "tab", dataTab: "elements" }).append(
 | 
				
			||||||
				el("h4", t`Reactive Elements`),
 | 
									el("h4", t`Reactive Elements`),
 | 
				
			||||||
@@ -241,7 +240,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
					// Later:
 | 
										// Later:
 | 
				
			||||||
					S.action(items, "push", "Dragonfruit"); // List updates automatically
 | 
										S.action(items, "push", "Dragonfruit"); // List updates automatically
 | 
				
			||||||
				`, page_id }),
 | 
									`, language: "js" }),
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -250,12 +249,12 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			You can use special properties like ${el("code", "dataset")}, ${el("code", "ariaset")}, and
 | 
								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("code", "classList")} for fine-grained control over specific attribute types.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/signals/dom-attrs.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/signals/dom-attrs.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			${el("code", "S.el()")} is especially powerful for conditional rendering and lists:
 | 
								${el("code", "S.el()")} is especially powerful for conditional rendering and lists:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/signals/dom-el.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/signals/dom-el.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Best Practices for Signals`),
 | 
							el(h3, t`Best Practices for Signals`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -289,7 +288,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				el("h4", t`We can process form events without signals`),
 | 
									el("h4", t`We can process form events without signals`),
 | 
				
			||||||
				el("p", t`This can be used when the form data doesn’t need to be reactive and we just waiting for
 | 
									el("p", t`This can be used when the form data doesn’t need to be reactive and we just waiting for
 | 
				
			||||||
					results.`),
 | 
										results.`),
 | 
				
			||||||
				el(code, { page_id, content: `
 | 
									el(code, { content: `
 | 
				
			||||||
					const onFormSubmit = on("submit", e => {
 | 
										const onFormSubmit = on("submit", e => {
 | 
				
			||||||
						e.preventDefault();
 | 
											e.preventDefault();
 | 
				
			||||||
						const formData = new FormData(e.currentTarget);
 | 
											const formData = new FormData(e.currentTarget);
 | 
				
			||||||
@@ -301,13 +300,13 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					return el("form", null, onFormSubmit).append(
 | 
										return el("form", null, onFormSubmit).append(
 | 
				
			||||||
						// …
 | 
											// …
 | 
				
			||||||
					);
 | 
										);
 | 
				
			||||||
				` })
 | 
									`, language: "js" })
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			el("div", { className: "tab", dataTab: "variables" }).append(
 | 
								el("div", { className: "tab", dataTab: "variables" }).append(
 | 
				
			||||||
				el("h4", t`We can use variables without signals`),
 | 
									el("h4", t`We can use variables without signals`),
 | 
				
			||||||
				el("p", t`We use this when we dont’t need to reflect changes in the elsewhere (UI).`),
 | 
									el("p", t`We use this when we dont’t need to reflect changes in the elsewhere (UI).`),
 | 
				
			||||||
				el(code, { page_id, content: `
 | 
									el(code, { content: `
 | 
				
			||||||
					let canSubmit = false;
 | 
										let canSubmit = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					const onFormSubmit = on("submit", e => {
 | 
										const onFormSubmit = on("submit", e => {
 | 
				
			||||||
@@ -318,14 +317,14 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					const onAllowSubmit = on("click", e => {
 | 
										const onAllowSubmit = on("click", e => {
 | 
				
			||||||
						canSubmit = true;
 | 
											canSubmit = true;
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
				`}),
 | 
									`, language: "js" }),
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			el("div", { className: "tab", dataTab: "state" }).append(
 | 
								el("div", { className: "tab", dataTab: "state" }).append(
 | 
				
			||||||
				el("h4", t`Using signals`),
 | 
									el("h4", t`Using signals`),
 | 
				
			||||||
				el("p", t`We use this when we need to reflect changes for example in the UI (e.g. enable/disable
 | 
									el("p", t`We use this when we need to reflect changes for example in the UI (e.g. enable/disable
 | 
				
			||||||
					buttons).`),
 | 
										buttons).`),
 | 
				
			||||||
				el(code, { page_id, content: `
 | 
									el(code, { content: `
 | 
				
			||||||
					const canSubmit = S(false);
 | 
										const canSubmit = S(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					const onFormSubmit = on("submit", e => {
 | 
										const onFormSubmit = on("submit", e => {
 | 
				
			||||||
@@ -341,7 +340,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
						el("button", { textContent: "Allow Submit", type: "button" }, onAllowSubmit),
 | 
											el("button", { textContent: "Allow Submit", type: "button" }, onAllowSubmit),
 | 
				
			||||||
						el("button", { disabled: S(()=> !canSubmit), textContent: "Submit" })
 | 
											el("button", { disabled: S(()=> !canSubmit), textContent: "Submit" })
 | 
				
			||||||
					);
 | 
										);
 | 
				
			||||||
				`}),
 | 
									`, language: "js" }),
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,14 +27,13 @@ const references= {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			For state-less components we can use functions as UI components (see “Elements” page). But in real life,
 | 
								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
 | 
								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 })}.
 | 
								the ${el("a", { textContent: t`Garbage collection`, ...references.garbage_collection })}.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/scopes/intro.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/scopes/intro.js") }),
 | 
				
			||||||
		el("p").append(T`The library therefore uses ${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(h3, t`Understanding Host Elements and Scopes`),
 | 
				
			||||||
@@ -83,7 +82,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				el("dd", t`Applies the addons to the host element (and returns the host element)`)
 | 
									el("dd", t`Applies the addons to the host element (and returns the host element)`)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/scopes/scopes-and-hosts.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/scopes/scopes-and-hosts.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -95,7 +94,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				If you are interested in the implementation details, see Class-Based Components section.
 | 
									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(code, { src: fileURL("./components/examples/scopes/good-practise.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Class-Based Components`),
 | 
							el(h3, t`Class-Based Components`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -103,7 +102,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			For this, we implement function ${el("code", "elClass")} and use it to demonstrate implementation details
 | 
								For this, we implement function ${el("code", "elClass")} and use it to demonstrate implementation details
 | 
				
			||||||
			for better understanding of the scope logic.
 | 
								for better understanding of the scope logic.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/scopes/class-component.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/scopes/class-component.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Automatic Cleanup with Scopes`),
 | 
							el(h3, t`Automatic Cleanup with Scopes`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -123,7 +122,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					- Custom cleanup code (dd<el> and user)
 | 
										- Custom cleanup code (dd<el> and user)
 | 
				
			||||||
			` })
 | 
								` })
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/scopes/cleaning.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/scopes/cleaning.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -149,17 +148,17 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			el("div", { className: "tab", dataTab: "declarative" }).append(
 | 
								el("div", { className: "tab", dataTab: "declarative" }).append(
 | 
				
			||||||
				el("h4", t`✅ Declarative Approach`),
 | 
									el("h4", t`✅ Declarative Approach`),
 | 
				
			||||||
				el("p", t`Define what your UI should look like based on state:`),
 | 
									el("p", t`Define what your UI should look like based on state:`),
 | 
				
			||||||
				el(code, { src: fileURL("./components/examples/scopes/declarative.js"), page_id })
 | 
									el(code, { src: fileURL("./components/examples/scopes/declarative.js") })
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			el("div", { className: "tab", dataTab: "imperative" }).append(
 | 
								el("div", { className: "tab", dataTab: "imperative" }).append(
 | 
				
			||||||
				el("h4", t`⚠️ Imperative Approach`),
 | 
									el("h4", t`⚠️ Imperative Approach`),
 | 
				
			||||||
				el("p", t`Manually update the DOM in response to events:`),
 | 
									el("p", t`Manually update the DOM in response to events:`),
 | 
				
			||||||
				el(code, { src: fileURL("./components/examples/scopes/imperative.js"), page_id })
 | 
									el(code, { src: fileURL("./components/examples/scopes/imperative.js") })
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			el("div", { className: "tab", dataTab: "mixed" }).append(
 | 
								el("div", { className: "tab", dataTab: "mixed" }).append(
 | 
				
			||||||
				el("h4", t`❌ Mixed Approach`),
 | 
									el("h4", t`❌ Mixed Approach`),
 | 
				
			||||||
				el("p", t`This approach should be avoided:`),
 | 
									el("p", t`This approach should be avoided:`),
 | 
				
			||||||
				el(code, { src: fileURL("./components/examples/scopes/mixed.js"), page_id })
 | 
									el(code, { src: fileURL("./components/examples/scopes/mixed.js") })
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,7 +57,6 @@ const references= {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			dd<el> pairs powerfully with ${el("a", references.mdn_web_components).append(el("strong", t`Web
 | 
								dd<el> pairs powerfully with ${el("a", references.mdn_web_components).append(el("strong", t`Web
 | 
				
			||||||
@@ -73,7 +72,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				el("li", t`Clean component lifecycle management`),
 | 
									el("li", t`Clean component lifecycle management`),
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/customElement/intro.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/customElement/intro.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Getting Started: Web Components Basics`),
 | 
							el(h3, t`Getting Started: Web Components Basics`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -95,7 +94,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Let’s start with a basic Custom Element example without dd<el> to establish the foundation:
 | 
								Let’s start with a basic Custom Element example without dd<el> to establish the foundation:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/customElement/native-basic.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/customElement/native-basic.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -124,7 +123,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				el("dd", t`Allows using on.connected(), on.disconnected() or S.observedAttributes().`)
 | 
									el("dd", t`Allows using on.connected(), on.disconnected() or S.observedAttributes().`)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/customElement/customElementWithDDE.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/customElement/customElementWithDDE.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -156,7 +155,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				el("dd", t`The rendered DOM tree`)
 | 
									el("dd", t`The rendered DOM tree`)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/customElement/dde.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/customElement/dde.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -188,7 +187,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			Using the ${el("code", "S.observedAttributes")} creates a reactive connection between your element’s
 | 
								Using the ${el("code", "S.observedAttributes")} creates a reactive connection between your element’s
 | 
				
			||||||
			attributes and its internal rendering. When attributes change, your component automatically updates!
 | 
								attributes and its internal rendering. When attributes change, your component automatically updates!
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/customElement/observedAttributes.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/customElement/observedAttributes.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "callout" }).append(
 | 
							el("div", { className: "callout" }).append(
 | 
				
			||||||
			el("h4", t`How S.observedAttributes Works`),
 | 
								el("h4", t`How S.observedAttributes Works`),
 | 
				
			||||||
@@ -221,7 +220,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
								<p>Content</p>
 | 
													<p>Content</p>
 | 
				
			||||||
			` })
 | 
								` })
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/customElement/shadowRoot.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/customElement/shadowRoot.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			For more information on Shadow DOM, see
 | 
								For more information on Shadow DOM, see
 | 
				
			||||||
@@ -234,7 +233,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			Besides the encapsulation, the Shadow DOM allows for using the ${el("a", references.mdn_shadow_dom_slot).append(
 | 
								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("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(example, { src: fileURL("./components/examples/customElement/simulateSlots.js") }),
 | 
				
			||||||
		el("div", { className: "function-table" }).append(
 | 
							el("div", { className: "function-table" }).append(
 | 
				
			||||||
			el("h4", t`simulateSlots`),
 | 
								el("h4", t`simulateSlots`),
 | 
				
			||||||
			el("dl").append(
 | 
								el("dl").append(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,6 @@ const fileURL= url=> new URL(url, import.meta.url);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Debugging is an essential part of application development. This guide provides techniques
 | 
								Debugging is an essential part of application development. This guide provides techniques
 | 
				
			||||||
@@ -36,7 +35,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
			const signal = S(0);
 | 
								const signal = S(0);
 | 
				
			||||||
			console.log('Current value:', signal.valueOf());
 | 
								console.log('Current value:', signal.valueOf());
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
		el("div", { className: "warning" }).append(
 | 
							el("div", { className: "warning" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
				${el("code", "signal.get")} is OK, but in some situations may lead to unexpected results:
 | 
									${el("code", "signal.get")} is OK, but in some situations may lead to unexpected results:
 | 
				
			||||||
@@ -49,7 +48,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					// but typically this is fine ↓
 | 
										// but typically this is fine ↓
 | 
				
			||||||
					return signal.get() + 1;
 | 
										return signal.get() + 1;
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
			` })
 | 
								`, language: "js" })
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			You can also monitor signal changes by adding a listener:
 | 
								You can also monitor signal changes by adding a listener:
 | 
				
			||||||
@@ -57,7 +56,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
			// Log every time the signal changes
 | 
								// Log every time the signal changes
 | 
				
			||||||
			S.on(signal, value => console.log('Signal changed:', value));
 | 
								S.on(signal, value => console.log('Signal changed:', value));
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", t`Debugging derived signals`),
 | 
							el("h4", t`Debugging derived signals`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -69,7 +68,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			el("li", t`Add logging/debugger inside the computation function to see when it runs`),
 | 
								el("li", t`Add logging/debugger inside the computation function to see when it runs`),
 | 
				
			||||||
			el("li", t`Verify that the computation function actually accesses the signal values with .get()`)
 | 
								el("li", t`Verify that the computation function actually accesses the signal values with .get()`)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/debugging/consoleLog.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/debugging/consoleLog.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", t`Examining signal via DevTools`),
 | 
							el("h4", t`Examining signal via DevTools`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -77,11 +76,11 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			signal objects. It contains the following information:
 | 
								signal objects. It contains the following information:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el("ul").append(
 | 
							el("ul").append(
 | 
				
			||||||
 | 
								// TODO: value?
 | 
				
			||||||
			el("li", t`listeners: A Set of functions called when the signal value changes`),
 | 
								el("li", t`listeners: A Set of functions called when the signal value changes`),
 | 
				
			||||||
			el("li", t`actions: Custom actions that can be performed on the signal`),
 | 
								el("li", t`actions: Custom actions that can be performed on the signal`),
 | 
				
			||||||
			el("li", t`onclear: Functions to run when the signal is cleared`),
 | 
								el("li", t`onclear: Functions to run when the signal is cleared`),
 | 
				
			||||||
			el("li", t`host: Reference to the host element/scope in which the signal was created`),
 | 
								el("li", t`host: Reference to the host element/scope in which the signal was created`),
 | 
				
			||||||
			el("li", t`readonly: Boolean flag indicating if the signal is read-only`)
 | 
					 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			…to determine the current value of the signal, call ${el("code", "signal.valueOf()")}. Don’t hesitate to
 | 
								…to determine the current value of the signal, call ${el("code", "signal.valueOf()")}. Don’t hesitate to
 | 
				
			||||||
@@ -121,7 +120,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				"S.el(S(()=> count.get() % 2), odd=> …)")}).
 | 
									"S.el(S(()=> count.get() % 2), odd=> …)")}).
 | 
				
			||||||
			`),
 | 
								`),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/debugging/mutations.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/debugging/mutations.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", t`Memory leaks with signal listeners`),
 | 
							el("h4", t`Memory leaks with signal listeners`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -137,7 +136,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			el("li", t`Make sure derived signals don’t perform expensive calculations unnecessarily`),
 | 
								el("li", t`Make sure derived signals don’t perform expensive calculations unnecessarily`),
 | 
				
			||||||
			el("li", t`Keep signal computations focused and minimal`)
 | 
								el("li", t`Keep signal computations focused and minimal`)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/debugging/debouncing.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/debugging/debouncing.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Browser DevTools tips for components and reactivity`),
 | 
							el(h3, t`Browser DevTools tips for components and reactivity`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -150,7 +149,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			that are automatically updated when signal values change. These elements are wrapped in special
 | 
								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 internally, 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.html"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/debugging/dom-reactive-mark.html") }),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			This is particularly useful when debugging why a reactive section isn’t updating as expected.
 | 
								This is particularly useful when debugging why a reactive section isn’t updating as expected.
 | 
				
			||||||
			You can inspect the elements between the comment nodes to see their current state and the
 | 
								You can inspect the elements between the comment nodes to see their current state and the
 | 
				
			||||||
@@ -187,7 +186,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			so you can see the element and property that changes in the console right away. These properties make it
 | 
								so you can see the element and property that changes in the console right away. These properties make it
 | 
				
			||||||
			easier to understand the reactive structure of your application when inspecting elements.
 | 
								easier to understand the reactive structure of your application when inspecting elements.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/signals/debugging-dom.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/signals/debugging-dom.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p", { className: "note" }).append(T`
 | 
							el("p", { className: "note" }).append(T`
 | 
				
			||||||
			${el("code", "<element>.__dde_reactive")} - An array property on DOM elements that tracks signal-to-element
 | 
								${el("code", "<element>.__dde_reactive")} - An array property on DOM elements that tracks signal-to-element
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@ const fileURL= url=> new URL(url, import.meta.url);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/** @param {import("./types.js").PageAttrs} attrs */
 | 
					/** @param {import("./types.js").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			dd<el> is designed with extensibility in mind. This page covers how to separate
 | 
								dd<el> is designed with extensibility in mind. This page covers how to separate
 | 
				
			||||||
@@ -49,7 +48,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			// Using an addon
 | 
								// Using an addon
 | 
				
			||||||
			el("div", { id: "example" }, myAddon({ option: "value" }));
 | 
								el("div", { id: "example" }, myAddon({ option: "value" }));
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Resource Cleanup with Abort Signals`),
 | 
							el(h3, t`Resource Cleanup with Abort Signals`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -83,7 +82,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				const { signal }= scope;
 | 
									const { signal }= scope;
 | 
				
			||||||
				return el("div", null, externalLibraryAddon({ option: "value" }, signal));
 | 
									return el("div", null, externalLibraryAddon({ option: "value" }, signal));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Building Library-Independent Extensions`),
 | 
							el(h3, t`Building Library-Independent Extensions`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -104,7 +103,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
								});
 | 
													});
 | 
				
			||||||
							};
 | 
												};
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					`, page_id })
 | 
										`, language: "js" })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				el("div", { className: "tab" }).append(
 | 
									el("div", { className: "tab" }).append(
 | 
				
			||||||
					el("h5", t`⚠️ Library-Dependent`),
 | 
										el("h5", t`⚠️ Library-Dependent`),
 | 
				
			||||||
@@ -118,7 +117,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
								})(element);
 | 
													})(element);
 | 
				
			||||||
							};
 | 
												};
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					`, page_id })
 | 
										`, language: "js" })
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -177,7 +176,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					textContent: "All"
 | 
										textContent: "All"
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "callout" }).append(
 | 
							el("div", { className: "callout" }).append(
 | 
				
			||||||
			el("h4", t`Benefits of Signal Factories`),
 | 
								el("h4", t`Benefits of Signal Factories`),
 | 
				
			||||||
@@ -217,7 +216,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			const counter = createEnhancedSignal(0);
 | 
								const counter = createEnhancedSignal(0);
 | 
				
			||||||
			el("button", { textContent: "Increment", onclick: () => counter.increment() });
 | 
								el("button", { textContent: "Increment", onclick: () => counter.increment() });
 | 
				
			||||||
			el("div", S.text\`Count: \${counter}\`);
 | 
								el("div", S.text\`Count: \${counter}\`);
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -259,7 +258,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			// Update signal value
 | 
								// Update signal value
 | 
				
			||||||
			count.set(5); // Logs: 5
 | 
								count.set(5); // Logs: 5
 | 
				
			||||||
			console.log(doubled.get()); // 10
 | 
								console.log(doubled.get()); // 10
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			The independent signals API includes all core functionality (${el("code", "S()")}, ${el("code", "S.on()")},
 | 
								The independent signals API includes all core functionality (${el("code", "S()")}, ${el("code", "S.on()")},
 | 
				
			||||||
			${el("code", "S.action()")}).
 | 
								${el("code", "S.action()")}).
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,6 @@ const references= {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			As your applications grow, performance becomes increasingly important. dd<el> provides several
 | 
								As your applications grow, performance becomes increasingly important. dd<el> provides several
 | 
				
			||||||
@@ -60,7 +59,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				el("li", t`Simple debugging for performance bottlenecks`)
 | 
									el("li", t`Simple debugging for performance bottlenecks`)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/optimization/intro.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/optimization/intro.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Memoization with memo: Native vs dd<el>`),
 | 
							el(h3, t`Memoization with memo: Native vs dd<el>`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -84,7 +83,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
								))
 | 
													))
 | 
				
			||||||
							);
 | 
												);
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					`, page_id })
 | 
										`, language: "js" })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				el("div").append(
 | 
									el("div").append(
 | 
				
			||||||
					el("h5", t`With dd<el>'s memo`),
 | 
										el("h5", t`With dd<el>'s memo`),
 | 
				
			||||||
@@ -102,7 +101,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
								)))
 | 
													)))
 | 
				
			||||||
							);
 | 
												);
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					`, page_id })
 | 
										`, language: "js" })
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -134,7 +133,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
						memo(todo.id, () =>
 | 
											memo(todo.id, () =>
 | 
				
			||||||
							el(TodoItem, todo)
 | 
												el(TodoItem, todo)
 | 
				
			||||||
			))))
 | 
								))))
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			The ${el("code", "memo")} function in this context:
 | 
								The ${el("code", "memo")} function in this context:
 | 
				
			||||||
@@ -146,7 +145,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			el("li", t`Only calls the generator function when rendering an item with a new key`)
 | 
								el("li", t`Only calls the generator function when rendering an item with a new key`)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/optimization/memo.js"), page_id }),
 | 
							el(example, { src: fileURL("./components/examples/optimization/memo.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Creating Memoization Scopes`),
 | 
							el(h3, t`Creating Memoization Scopes`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -171,7 +170,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			const container = el("div").append(
 | 
								const container = el("div").append(
 | 
				
			||||||
				...items.map(item => renderItem(item))
 | 
									...items.map(item => renderItem(item))
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			The scope function accepts options to customize its behavior:
 | 
								The scope function accepts options to customize its behavior:
 | 
				
			||||||
@@ -188,7 +187,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				// Clear cache when signal is aborted
 | 
									// Clear cache when signal is aborted
 | 
				
			||||||
				signal: controller.signal
 | 
									signal: controller.signal
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			You can use custom memo scope as function in (e. g. ${el("code", "S.el(signal, renderList)")}) and as
 | 
								You can use custom memo scope as function in (e. g. ${el("code", "S.el(signal, renderList)")}) and as
 | 
				
			||||||
			(Abort) signal use ${el("code", "scope.signal")}.
 | 
								(Abort) signal use ${el("code", "scope.signal")}.
 | 
				
			||||||
@@ -317,7 +316,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			// On subsequent renders, the cached fragment is empty!
 | 
								// On subsequent renders, the cached fragment is empty!
 | 
				
			||||||
			container.append(memoizedFragment); // Nothing gets appended
 | 
								container.append(memoizedFragment); // Nothing gets appended
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			This happens because a DocumentFragment is emptied when it's appended to the DOM. When the fragment
 | 
								This happens because a DocumentFragment is emptied when it's appended to the DOM. When the fragment
 | 
				
			||||||
@@ -338,7 +337,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
						S.el(itemsSignal, items => items.map(item => el("div", item)))
 | 
											S.el(itemsSignal, items => items.map(item => el("div", item)))
 | 
				
			||||||
					)
 | 
										)
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
			`, page_id })
 | 
								`, language: "js" })
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,6 @@ const references= {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			${el("a", references.todomvc).append("TodoMVC")} is a project that helps developers compare different
 | 
								${el("a", references.todomvc).append("TodoMVC")} is a project that helps developers compare different
 | 
				
			||||||
@@ -69,7 +68,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			challenges in a clean, maintainable way.
 | 
								challenges in a clean, maintainable way.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/reallife/todomvc.js"), variant: "big", page_id }),
 | 
							el(example, { src: fileURL("./components/examples/reallife/todomvc.js"), variant: "big" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Application Architecture Overview`),
 | 
							el(h3, t`Application Architecture Overview`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -118,7 +117,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				});
 | 
									});
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
			const todosRemainingS = S(()=> todosS.get().filter(todo => !todo.completed).length);
 | 
								const todosRemainingS = S(()=> todosS.get().filter(todo => !todo.completed).length);
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			The ${el("code", "todosSignal")} function creates a custom signal with actions for manipulating the todos:
 | 
								The ${el("code", "todosSignal")} function creates a custom signal with actions for manipulating the todos:
 | 
				
			||||||
@@ -207,7 +206,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				});
 | 
									});
 | 
				
			||||||
				return out;
 | 
									return out;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -241,7 +240,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					memo(todo.id, ()=> el(TodoItem, todo, onDelete, onEdit)))
 | 
										memo(todo.id, ()=> el(TodoItem, todo, onDelete, onEdit)))
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			The derived signal automatically recalculates whenever either the todos list or the current filter changes,
 | 
								The derived signal automatically recalculates whenever either the todos list or the current filter changes,
 | 
				
			||||||
@@ -263,7 +262,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				type: "checkbox"
 | 
									type: "checkbox"
 | 
				
			||||||
			}, onToggleAll),
 | 
								}, onToggleAll),
 | 
				
			||||||
			el("label", { htmlFor: "toggle-all", title: "Mark all as complete" }),
 | 
								el("label", { htmlFor: "toggle-all", title: "Mark all as complete" }),
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			The "toggle all" checkbox allows users to mark all todos as completed or active. When the checkbox
 | 
								The "toggle all" checkbox allows users to mark all todos as completed or active. When the checkbox
 | 
				
			||||||
@@ -300,7 +299,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					// Component content...
 | 
										// Component content...
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			The TodoItem component maintains its own local UI state with signals, providing immediate
 | 
								The TodoItem component maintains its own local UI state with signals, providing immediate
 | 
				
			||||||
@@ -311,16 +310,16 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
			// Dynamic class attributes
 | 
								// Dynamic class attributes
 | 
				
			||||||
			el("a", {
 | 
								el("a", {
 | 
				
			||||||
				textContent: "All",
 | 
									textContent,
 | 
				
			||||||
				className: S(()=> pageS.get() === "all" ? "selected" : ""),
 | 
									classList: { selected: S(()=> pageS.get() === textContent.toLowerCase()) },
 | 
				
			||||||
				href: "#"
 | 
									href: \`#\${textContent.toLowerCase()}\`
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Reactive classList
 | 
								// Reactive classList
 | 
				
			||||||
			el("li", {
 | 
								el("li", {
 | 
				
			||||||
				classList: { completed: isCompleted, editing: isEditing }
 | 
									classList: { completed: isCompleted, editing: isEditing }
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -341,7 +340,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					memo(todo.id, ()=> el(TodoItem, todo, onDelete, onEdit)))
 | 
										memo(todo.id, ()=> el(TodoItem, todo, onDelete, onEdit)))
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			This approach ensures that:
 | 
								This approach ensures that:
 | 
				
			||||||
@@ -355,18 +354,25 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		el("h4", t`Memoizing UI Sections`),
 | 
							el("h4", t`Memoizing UI Sections`),
 | 
				
			||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
			S.el(todosS, todos => memo(todos.length, length=> length
 | 
								S.el(todosS, ({ length }) => !length
 | 
				
			||||||
				? el("footer", { className: "footer" }).append(
 | 
									? el()
 | 
				
			||||||
					// Footer content...
 | 
									: el("footer", { className: "footer" }).append(
 | 
				
			||||||
 | 
										// …
 | 
				
			||||||
 | 
										memo("filters", ()=>
 | 
				
			||||||
 | 
											// …
 | 
				
			||||||
 | 
													el("a", {
 | 
				
			||||||
 | 
														textContent,
 | 
				
			||||||
 | 
														classList: { selected: S(()=> pageS.get() === textContent.toLowerCase()) },
 | 
				
			||||||
 | 
														href: \`#\${textContent.toLowerCase()}\`
 | 
				
			||||||
 | 
													})
 | 
				
			||||||
 | 
										// …
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
				: el()
 | 
					 | 
				
			||||||
			))
 | 
								))
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			By memoizing based on the todos length, the entire footer component is only re-rendered
 | 
								We memoize the UI section and uses derived signal for the classList. Re-rendering this part is therefore
 | 
				
			||||||
			when todos are added or removed, not when their properties change. This improves performance
 | 
								unnecessary when the number of todos changes.
 | 
				
			||||||
			by avoiding unnecessary DOM operations.
 | 
					 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
@@ -389,9 +395,11 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
			// Event handlers in the main component
 | 
								// Event handlers in the main component
 | 
				
			||||||
			const onDelete = on("todo:delete", ev => S.action(todosS, "delete", ev.detail));
 | 
								const onDelete = on("todo:delete", ev =>
 | 
				
			||||||
			const onEdit = on("todo:edit", ev => S.action(todosS, "edit", ev.detail));
 | 
									S.action(todosS, "delete", /** @type {{ detail: Todo["id"] }} */(ev).detail));
 | 
				
			||||||
		`, page_id }),
 | 
								const onEdit = on("todo:edit", ev =>
 | 
				
			||||||
 | 
									S.action(todosS, "edit", /** @type {{ detail: Partial<Todo> & { id: Todo["id"] } }} */(ev).detail));
 | 
				
			||||||
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", t`2. The TodoItem Component with Scopes and Local State`),
 | 
							el("h4", t`2. The TodoItem Component with Scopes and Local State`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -434,7 +442,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				// Component implementation...
 | 
									// Component implementation...
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -455,7 +463,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			}).append(
 | 
								}).append(
 | 
				
			||||||
				// Component content...
 | 
									// Component content...
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Benefits of using ${el("code", "classList")}:
 | 
								Benefits of using ${el("code", "classList")}:
 | 
				
			||||||
@@ -494,7 +502,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				value: title,
 | 
									value: title,
 | 
				
			||||||
				"data-id": id
 | 
									"data-id": id
 | 
				
			||||||
			}, onBlurEdit, onKeyDown, addFocus)
 | 
								}, onBlurEdit, onKeyDown, addFocus)
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			This approach offers several advantages:
 | 
								This approach offers several advantages:
 | 
				
			||||||
@@ -522,27 +530,26 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		el("h4", t`Conditional Todo List`),
 | 
							el("h4", t`Conditional Todo List`),
 | 
				
			||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
			S.el(todosS, todos => todos.length
 | 
								S.el(todosS, todos => todos.length
 | 
				
			||||||
				? el("main", { className: "main" }).append(
 | 
									? el()
 | 
				
			||||||
 | 
									: el("main", { className: "main" }).append(
 | 
				
			||||||
					// Main content with toggle all and todo list
 | 
										// Main content with toggle all and todo list
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
				: el()
 | 
					 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", t`Conditional Edit Form`),
 | 
							el("h4", t`Conditional Edit Form`),
 | 
				
			||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
			S.el(isEditing, editing => editing
 | 
								S.el(isEditing, editing => !editing
 | 
				
			||||||
				? el("form", null, onSubmitEdit).append(
 | 
									? el()
 | 
				
			||||||
 | 
									: el("form", null, onSubmitEdit).append(
 | 
				
			||||||
					el("input", {
 | 
										el("input", {
 | 
				
			||||||
						className: "edit",
 | 
											className: "edit",
 | 
				
			||||||
						name: "edit",
 | 
											name: formEdit,
 | 
				
			||||||
						value: title,
 | 
											value: title,
 | 
				
			||||||
						"data-id": id
 | 
					 | 
				
			||||||
					}, onBlurEdit, onKeyDown, addFocus)
 | 
										}, onBlurEdit, onKeyDown, addFocus)
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
				: el()
 | 
					 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", t`Conditional Clear Completed Button`),
 | 
							el("h4", t`Conditional Clear Completed Button`),
 | 
				
			||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
@@ -553,7 +560,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
						{ textContent: "Clear completed", className: "clear-completed" },
 | 
											{ textContent: "Clear completed", className: "clear-completed" },
 | 
				
			||||||
						onClearCompleted)
 | 
											onClearCompleted)
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -599,7 +606,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				if (event.key !== "Escape") return;
 | 
									if (event.key !== "Escape") return;
 | 
				
			||||||
				isEditing.set(false);
 | 
									isEditing.set(false);
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -630,7 +637,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				${el("strong", "Declarative Class Management:")} Using the classList property for cleaner class handling
 | 
									${el("strong", "Declarative Class Management:")} Using the classList property for cleaner class handling
 | 
				
			||||||
			`),
 | 
								`),
 | 
				
			||||||
			el("li").append(T`
 | 
								el("li").append(T`
 | 
				
			||||||
				${el("strong", "Focus Management:")} Reliable input focus with setTimeout
 | 
									${el("strong", "Focus Management:")} Reliable input focus with requestAnimationFrame
 | 
				
			||||||
			`),
 | 
								`),
 | 
				
			||||||
			el("li").append(T`
 | 
								el("li").append(T`
 | 
				
			||||||
				${el("strong", "Persistent Storage:")} Automatically saving application state with signal listeners
 | 
									${el("strong", "Persistent Storage:")} Automatically saving application state with signal listeners
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@ const fileURL= url=> new URL(url, import.meta.url);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/** @param {import("./types.js").PageAttrs} attrs */
 | 
					/** @param {import("./types.js").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("div", { className: "warning" }).append(
 | 
							el("div", { className: "warning" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -45,7 +44,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				than jsdom
 | 
									than jsdom
 | 
				
			||||||
			`),
 | 
								`),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/ssr/intro.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/intro.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Why Server-Side Rendering?`),
 | 
							el(h3, t`Why Server-Side Rendering?`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -71,27 +70,27 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			el("li", t`Provides a promise queue system for managing async operations during rendering`),
 | 
								el("li", t`Provides a promise queue system for managing async operations during rendering`),
 | 
				
			||||||
			el("li", t`Handles DOM property/attribute mapping differences between browsers and jsdom`)
 | 
								el("li", t`Handles DOM property/attribute mapping differences between browsers and jsdom`)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/ssr/start.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/start.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Basic SSR Example`),
 | 
							el(h3, t`Basic SSR Example`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Here’s a simple example of how to use dd<el> for server-side rendering in a Node.js script:
 | 
								Here’s a simple example of how to use dd<el> for server-side rendering in a Node.js script:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/ssr/basic-example.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/basic-example.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Building a Static Site Generator`),
 | 
							el(h3, t`Building a Static Site Generator`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			You can build a complete static site generator with dd<el>. In fact, this documentation site
 | 
								You can build a complete static site generator with dd<el>. In fact, this documentation site
 | 
				
			||||||
			is built using dd<el> for server-side rendering! Here’s how the documentation build process works:
 | 
								is built using dd<el> for server-side rendering! Here’s how the documentation build process works:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Working with Async Content in SSR`),
 | 
							el(h3, t`Working with Async Content in SSR`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			The jsdom export includes a queue system to handle asynchronous operations during rendering.
 | 
								The jsdom export includes a queue system to handle asynchronous operations during rendering.
 | 
				
			||||||
			This is crucial for components that fetch data or perform other async tasks.
 | 
								This is crucial for components that fetch data or perform other async tasks.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/ssr/async-data.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/async-data.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Working with Dynamic Imports for SSR`),
 | 
							el(h3, t`Working with Dynamic Imports for SSR`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -119,7 +118,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Follow this pattern when creating server-side rendered pages:
 | 
								Follow this pattern when creating server-side rendered pages:
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/ssr/pages.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/pages.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`SSR Considerations and Limitations`),
 | 
							el(h3, t`SSR Considerations and Limitations`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -141,7 +140,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			This documentation site itself is built using dd<el>’s SSR capabilities.
 | 
								This documentation site itself is built using dd<el>’s SSR capabilities.
 | 
				
			||||||
			The build process collects all page components, renders them with jsdom, and outputs static HTML files.
 | 
								The build process collects all page components, renders them with jsdom, and outputs static HTML files.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js"), page_id }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js") }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			The resulting static files can be deployed to any static hosting service,
 | 
								The resulting static files can be deployed to any static hosting service,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,8 @@
 | 
				
			|||||||
import { T, t } from "./utils/index.js";
 | 
					import { T, t } from "./utils/index.js";
 | 
				
			||||||
export const info= {
 | 
					export const info= {
 | 
				
			||||||
	title: t`Ireland Components`,
 | 
						title: t`Ireland Components`,
 | 
				
			||||||
	fullTitle: t`Interactive Demo Components with Server-Side Pre-Rendering`,
 | 
						fullTitle: t`Server-Side Pre-Rendering and Client-Side Rehydration`,
 | 
				
			||||||
	description: t`Creating live, interactive component examples in documentation with server-side
 | 
						description: t`Using Ireland components for server-side pre-rendering and client-side rehydration`,
 | 
				
			||||||
		rendering and client-side hydration.`,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { el } from "deka-dom-el";
 | 
					import { el } from "deka-dom-el";
 | 
				
			||||||
@@ -16,7 +15,6 @@ const fileURL= url=> new URL(url, import.meta.url);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/** @param {import("./types.js").PageAttrs} attrs */
 | 
					/** @param {import("./types.js").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("div", { className: "warning" }).append(
 | 
							el("div", { className: "warning" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -67,7 +65,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				src: fileURL("./components/examples/path/to/component.js"),
 | 
									src: fileURL("./components/examples/path/to/component.js"),
 | 
				
			||||||
				exportName: "NamedExport", // optional, defaults to "default",
 | 
									exportName: "NamedExport", // optional, defaults to "default",
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			During the build process (${el("code", "bs/docs.js")}), the following happens:
 | 
								During the build process (${el("code", "bs/docs.js")}), the following happens:
 | 
				
			||||||
@@ -119,7 +117,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			// Final build step - trigger SSR end event
 | 
								// Final build step - trigger SSR end event
 | 
				
			||||||
			dispatchEvent("onssrend");
 | 
								dispatchEvent("onssrend");
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
		el("h4", t`File Registration`),
 | 
							el("h4", t`File Registration`),
 | 
				
			||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
			// From docs/ssr.js - File registration system
 | 
								// From docs/ssr.js - File registration system
 | 
				
			||||||
@@ -145,7 +143,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				head[head instanceof HTMLScriptElement ? "src" : "href"] = file_name;
 | 
									head[head instanceof HTMLScriptElement ? "src" : "href"] = file_name;
 | 
				
			||||||
				document.head.append(head);
 | 
									document.head.append(head);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
		el("h4", t`Server-Side Rendering`),
 | 
							el("h4", t`Server-Side Rendering`),
 | 
				
			||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
			// From docs/components/ireland.html.js - Server-side component implementation
 | 
								// From docs/components/ireland.html.js - Server-side component implementation
 | 
				
			||||||
@@ -226,7 +224,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					\`.trim())
 | 
										\`.trim())
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
		el("h4", t`Client-Side Hydration`),
 | 
							el("h4", t`Client-Side Hydration`),
 | 
				
			||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
			// From docs/components/ireland.js.js - Client-side hydration
 | 
								// From docs/components/ireland.js.js - Client-side hydration
 | 
				
			||||||
@@ -250,7 +248,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				});
 | 
									});
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Live Example`),
 | 
							el(h3, t`Live Example`),
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -259,14 +257,10 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				rendered with the Ireland component system:
 | 
									rendered with the Ireland component system:
 | 
				
			||||||
			`),
 | 
								`),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			el(code, {
 | 
								el(code, { src: fileURL("./components/examples/ireland-test/counter.js") }),
 | 
				
			||||||
				src: fileURL("./components/examples/ireland-test/counter.js"),
 | 
					 | 
				
			||||||
				page_id
 | 
					 | 
				
			||||||
			}),
 | 
					 | 
				
			||||||
			el(ireland, {
 | 
								el(ireland, {
 | 
				
			||||||
				src: fileURL("./components/examples/ireland-test/counter.js"),
 | 
									src: fileURL("./components/examples/ireland-test/counter.js"),
 | 
				
			||||||
				exportName: "CounterStandard",
 | 
									exportName: "CounterStandard",
 | 
				
			||||||
				page_id
 | 
					 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,6 @@ const references= {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			This reference guide provides a comprehensive summary of dd<el>’s key concepts, best practices,
 | 
								This reference guide provides a comprehensive summary of dd<el>’s key concepts, best practices,
 | 
				
			||||||
@@ -166,7 +165,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					className: S(() => countS.get() > 10 ? 'warning' : '')
 | 
										className: S(() => countS.get() > 10 ? 'warning' : '')
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		`, page_id }),
 | 
							`, language: "js" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Key Concepts Reference`),
 | 
							el(h3, t`Key Concepts Reference`),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,6 @@ import { converter } from "./components/converter.html.js";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Transitioning from HTML to dd<el> is simple with our interactive converter. This tool helps you quickly
 | 
								Transitioning from HTML to dd<el> is simple with our interactive converter. This tool helps you quickly
 | 
				
			||||||
@@ -28,7 +27,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// The actual converter component
 | 
							// The actual converter component
 | 
				
			||||||
		el(converter, { page_id }),
 | 
							el(converter),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Next Steps`),
 | 
							el(h3, t`Next Steps`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@ const fileURL= url=> new URL(url, import.meta.url);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
					/** @param {import("./types.d.ts").PageAttrs} attrs */
 | 
				
			||||||
export function page({ pkg, info }){
 | 
					export function page({ pkg, info }){
 | 
				
			||||||
	const page_id= info.id;
 | 
					 | 
				
			||||||
	return el(simplePage, { info, pkg }).append(
 | 
						return el(simplePage, { info, pkg }).append(
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Real-world application examples showcasing how to build complete, production-ready interfaces with dd<el>:
 | 
								Real-world application examples showcasing how to build complete, production-ready interfaces with dd<el>:
 | 
				
			||||||
@@ -25,23 +24,21 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			third-party charting library, data fetching and state management, responsive layout design, and multiple
 | 
								third-party charting library, data fetching and state management, responsive layout design, and multiple
 | 
				
			||||||
			interactive components working together.
 | 
								interactive components working together.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/case-studies/data-dashboard.js"), variant: "big", page_id }),
 | 
							el(example, { src: fileURL("./components/examples/case-studies/data-dashboard.js"), variant: "big" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Interactive Form`),
 | 
							el(h3, t`Interactive Form`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Complete form with real-time validation, conditional rendering, and responsive design. Form handling with
 | 
								Complete form with real-time validation, conditional rendering, and responsive design. Form handling with
 | 
				
			||||||
			real-time validation, reactive UI updates, complex form state management, and clean separation of concerns.
 | 
								real-time validation, reactive UI updates, complex form state management, and clean separation of concerns.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/case-studies/interactive-form.js"), variant: "big", page_id }),
 | 
							el(example, { src: fileURL("./components/examples/case-studies/interactive-form.js"), variant: "big" }),
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Interactive Image Gallery`),
 | 
							el(h3, t`Interactive Image Gallery`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Responsive image gallery with lightbox, keyboard navigation, and filtering. Dynamic loading of content,
 | 
								Responsive image gallery with lightbox, keyboard navigation, and filtering. Dynamic loading of content,
 | 
				
			||||||
			lightbox functionality, animation handling, and keyboard and gesture navigation support.
 | 
								lightbox functionality, animation handling, and keyboard and gesture navigation support.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/case-studies/image-gallery.js"), variant: "big", page_id }),
 | 
							el(example, { src: fileURL("./components/examples/case-studies/image-gallery.js"), variant: "big" }),
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Task Manager`),
 | 
							el(h3, t`Task Manager`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -49,8 +46,29 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			with signals, drag and drop functionality, local storage persistence, and responsive design for different
 | 
								with signals, drag and drop functionality, local storage persistence, and responsive design for different
 | 
				
			||||||
			devices.
 | 
								devices.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/case-studies/task-manager.js"), variant: "big", page_id }),
 | 
							el(example, { src: fileURL("./components/examples/case-studies/task-manager.js"), variant: "big" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							el(h3, t`Product Catalog with asyncSignal`),
 | 
				
			||||||
 | 
							el("p").append(T`
 | 
				
			||||||
 | 
								Interactive product catalog with search, sorting, and pagination. Features include dynamic product filtering,
 | 
				
			||||||
 | 
								responsive UI with detailed view toggles, error handling with retry capability, and proper resource cleanup.
 | 
				
			||||||
 | 
								Demonstrates advanced signal usage, including derived signals, abortable async data fetching, and optimized
 | 
				
			||||||
 | 
								rendering patterns.
 | 
				
			||||||
 | 
							`),
 | 
				
			||||||
 | 
							el("div", { className: "callout" }).append(
 | 
				
			||||||
 | 
								el("h4", t`asyncSignal Utility`),
 | 
				
			||||||
 | 
								el("p").append(T`
 | 
				
			||||||
 | 
									This example showcases the asyncSignal utility, which is a powerful abstraction for handling async data
 | 
				
			||||||
 | 
									fetching with proper state management. It provides:
 | 
				
			||||||
 | 
								`),
 | 
				
			||||||
 | 
								el("ul").append(
 | 
				
			||||||
 | 
									el("li", t`Automatic tracking of loading, success, and error states`),
 | 
				
			||||||
 | 
									el("li", t`AbortController integration for request cancellation`),
 | 
				
			||||||
 | 
									el("li", t`Error handling and recovery`),
 | 
				
			||||||
 | 
									el("li", t`Options for caching previous data during loading states`)
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
							el(example, { src: fileURL("./components/examples/case-studies/products.js"), variant: "big" }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`TodoMVC`),
 | 
							el(h3, t`TodoMVC`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,8 @@
 | 
				
			|||||||
export { t } from "./utils/index.js";
 | 
					export { t } from "./utils/index.js";
 | 
				
			||||||
 | 
					/** @type {string} */
 | 
				
			||||||
 | 
					export let page_id;
 | 
				
			||||||
 | 
					/** @param {string} id */
 | 
				
			||||||
 | 
					export function currentPageId(id){ page_id= id; }
 | 
				
			||||||
export const path_target= {
 | 
					export const path_target= {
 | 
				
			||||||
	root: "dist/docs/",
 | 
						root: "dist/docs/",
 | 
				
			||||||
	css: "dist/docs/",
 | 
						css: "dist/docs/",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1,12 +1,12 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"name": "deka-dom-el",
 | 
						"name": "deka-dom-el",
 | 
				
			||||||
	"version": "0.9.3-alpha",
 | 
						"version": "0.9.4-alpha",
 | 
				
			||||||
	"lockfileVersion": 3,
 | 
						"lockfileVersion": 3,
 | 
				
			||||||
	"requires": true,
 | 
						"requires": true,
 | 
				
			||||||
	"packages": {
 | 
						"packages": {
 | 
				
			||||||
		"": {
 | 
							"": {
 | 
				
			||||||
			"name": "deka-dom-el",
 | 
								"name": "deka-dom-el",
 | 
				
			||||||
			"version": "0.9.3-alpha",
 | 
								"version": "0.9.4-alpha",
 | 
				
			||||||
			"license": "MIT",
 | 
								"license": "MIT",
 | 
				
			||||||
			"devDependencies": {
 | 
								"devDependencies": {
 | 
				
			||||||
				"@size-limit/preset-small-lib": "~11.2",
 | 
									"@size-limit/preset-small-lib": "~11.2",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										15
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								package.json
									
									
									
									
									
								
							@@ -1,13 +1,13 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"name": "deka-dom-el",
 | 
						"name": "deka-dom-el",
 | 
				
			||||||
	"version": "0.9.3-alpha",
 | 
						"version": "0.9.4-alpha",
 | 
				
			||||||
	"description": "A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks.",
 | 
						"description": "A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks.",
 | 
				
			||||||
	"author": "Jan Andrle <andrle.jan@centrum.cz>",
 | 
						"author": "Jan Andrle <andrle.jan@centrum.cz>",
 | 
				
			||||||
	"license": "MIT",
 | 
						"license": "MIT",
 | 
				
			||||||
	"homepage": "https://github.com/jaandrle/deka-dom-el",
 | 
						"homepage": "https://jaandrle.github.io/deka-dom-el/",
 | 
				
			||||||
	"repository": {
 | 
						"repository": {
 | 
				
			||||||
		"type": "git",
 | 
							"type": "git",
 | 
				
			||||||
		"url": "git@github.com:jaandrle/deka-dom-el.git"
 | 
							"url": "git+ssh://git@github.com/jaandrle/deka-dom-el.git"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"bugs": {
 | 
						"bugs": {
 | 
				
			||||||
		"url": "https://github.com/jaandrle/deka-dom-el/issues"
 | 
							"url": "https://github.com/jaandrle/deka-dom-el/issues"
 | 
				
			||||||
@@ -52,7 +52,6 @@
 | 
				
			|||||||
		"maxdepth": 3,
 | 
							"maxdepth": 3,
 | 
				
			||||||
		"maxcomplexity": 14,
 | 
							"maxcomplexity": 14,
 | 
				
			||||||
		"globals": {
 | 
							"globals": {
 | 
				
			||||||
			"requestIdleCallback": false,
 | 
					 | 
				
			||||||
			"AbortController": false,
 | 
								"AbortController": false,
 | 
				
			||||||
			"AbortSignal": false,
 | 
								"AbortSignal": false,
 | 
				
			||||||
			"FinalizationRegistry": false
 | 
								"FinalizationRegistry": false
 | 
				
			||||||
@@ -61,25 +60,25 @@
 | 
				
			|||||||
	"size-limit": [
 | 
						"size-limit": [
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"path": "./index.js",
 | 
								"path": "./index.js",
 | 
				
			||||||
			"limit": "10.5 kB",
 | 
								"limit": "10 kB",
 | 
				
			||||||
			"gzip": false,
 | 
								"gzip": false,
 | 
				
			||||||
			"brotli": false
 | 
								"brotli": false
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"path": "./signals.js",
 | 
								"path": "./signals.js",
 | 
				
			||||||
			"limit": "12.5 kB",
 | 
								"limit": "12.2 kB",
 | 
				
			||||||
			"gzip": false,
 | 
								"gzip": false,
 | 
				
			||||||
			"brotli": false
 | 
								"brotli": false
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"path": "./index-with-signals.js",
 | 
								"path": "./index-with-signals.js",
 | 
				
			||||||
			"limit": "15 kB",
 | 
								"limit": "14.75 kB",
 | 
				
			||||||
			"gzip": false,
 | 
								"gzip": false,
 | 
				
			||||||
			"brotli": false
 | 
								"brotli": false
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"path": "./index-with-signals.js",
 | 
								"path": "./index-with-signals.js",
 | 
				
			||||||
			"limit": "5.5 kB"
 | 
								"limit": "5.25 kB"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	],
 | 
						],
 | 
				
			||||||
	"modifyEsbuildConfig": {
 | 
						"modifyEsbuildConfig": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import { enviroment as env, evc, evd } from './common.js';
 | 
					import { enviroment as env, evc, evd } from './common.js';
 | 
				
			||||||
import { isInstance } from "../helpers.js";
 | 
					import { isInstance, requestIdle } from "../helpers.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Connection changes observer for tracking element connection/disconnection
 | 
					 * Connection changes observer for tracking element connection/disconnection
 | 
				
			||||||
@@ -149,15 +149,6 @@ function connectionsChangesObserverConstructor(){
 | 
				
			|||||||
		observer.disconnect();
 | 
							observer.disconnect();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//TODO: remount support?
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Schedule a task during browser idle time
 | 
					 | 
				
			||||||
	 * @returns {Promise<void>} Promise that resolves when browser is idle
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	function requestIdle(){ return new Promise(function(resolve){
 | 
					 | 
				
			||||||
		(requestIdleCallback || requestAnimationFrame)(resolve);
 | 
					 | 
				
			||||||
	}); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Collects child elements from the store that are contained by the given element
 | 
						 * Collects child elements from the store that are contained by the given element
 | 
				
			||||||
	 * @param {Element} element - Parent element
 | 
						 * @param {Element} element - Parent element
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,3 +68,12 @@ export function observedAttributes(instance, observedAttribute){
 | 
				
			|||||||
 * @returns {string} The camelCase string
 | 
					 * @returns {string} The camelCase string
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function kebabToCamel(name){ return name.replace(/-./g, x=> x[1].toUpperCase()); }
 | 
					function kebabToCamel(name){ return name.replace(/-./g, x=> x[1].toUpperCase()); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Schedule a task during browser idle time
 | 
				
			||||||
 | 
					 * @returns {Promise<void>} Promise that resolves when browser is idle
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function requestIdle(){ return new Promise(function(resolve){
 | 
				
			||||||
 | 
							(globalThis.requestIdleCallback || requestAnimationFrame)(resolve);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,14 @@
 | 
				
			|||||||
import { queueSignalWrite, mark } from "./helpers.js";
 | 
					import { queueSignalWrite, mark } from "./helpers.js";
 | 
				
			||||||
export { mark };
 | 
					export { mark };
 | 
				
			||||||
import { hasOwn, oCreate, oAssign } from "../helpers.js";
 | 
					import { hasOwn, oCreate, oAssign, requestIdle } from "../helpers.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Signal = oCreate(null, {
 | 
					const SignalReadOnly= oCreate(null, {
 | 
				
			||||||
	get: { value(){ return read(this); } },
 | 
						get: { value(){ return read(this); } },
 | 
				
			||||||
	set: { value(...v){ return write(this, ...v); } },
 | 
					 | 
				
			||||||
	toJSON: { value(){ return read(this); } },
 | 
						toJSON: { value(){ return read(this); } },
 | 
				
			||||||
	valueOf: { value(){ return this[mark] && this[mark].value; } }
 | 
						valueOf: { value(){ return this[mark] && this[mark].value; } }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const SignalReadOnly= oCreate(Signal, {
 | 
					const Signal = oCreate(SignalReadOnly, {
 | 
				
			||||||
	set: { value(){ return; } },
 | 
						set: { value(...v){ return write(this, ...v); } },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Checks if a value is a signal
 | 
					 * Checks if a value is a signal
 | 
				
			||||||
@@ -214,7 +213,7 @@ signal.el= function(s, map){
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
function requestCleanUpReactives(host){
 | 
					function requestCleanUpReactives(host){
 | 
				
			||||||
	if(!host || !host[key_reactive]) return;
 | 
						if(!host || !host[key_reactive]) return;
 | 
				
			||||||
	(requestIdleCallback || setTimeout)(function(){
 | 
						requestIdle().then(function(){
 | 
				
			||||||
		host[key_reactive]= host[key_reactive]
 | 
							host[key_reactive]= host[key_reactive]
 | 
				
			||||||
			.filter(([ s, el ])=> el.isConnected ? true : (removeSignalListener(...s), false));
 | 
								.filter(([ s, el ])=> el.isConnected ? true : (removeSignalListener(...s), false));
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
@@ -314,9 +313,14 @@ export const signals_config= {
 | 
				
			|||||||
function removeSignalsFromElements(s, listener, ...notes){
 | 
					function removeSignalsFromElements(s, listener, ...notes){
 | 
				
			||||||
	const { current }= scope;
 | 
						const { current }= scope;
 | 
				
			||||||
	current.host(function(element){
 | 
						current.host(function(element){
 | 
				
			||||||
		if(!element[key_reactive]) element[key_reactive]= [];
 | 
							const is_first= !element[key_reactive];
 | 
				
			||||||
 | 
							if(is_first) element[key_reactive]= [];
 | 
				
			||||||
		element[key_reactive].push([ [ s, listener ], ...notes ]);
 | 
							element[key_reactive].push([ [ s, listener ], ...notes ]);
 | 
				
			||||||
		if(current.prevent) return; // typically document.body, doenst need auto-remove as it should happen on page leave
 | 
							if(
 | 
				
			||||||
 | 
								!is_first
 | 
				
			||||||
 | 
								// typically document.body, doenst need auto-remove as it should happen on page leave
 | 
				
			||||||
 | 
								|| current.prevent
 | 
				
			||||||
 | 
							) return;
 | 
				
			||||||
		on.disconnected(()=>
 | 
							on.disconnected(()=>
 | 
				
			||||||
			/*! Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?).
 | 
								/*! Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?).
 | 
				
			||||||
				You can investigate the `__dde_reactive` key of the element. */
 | 
									You can investigate the `__dde_reactive` key of the element. */
 | 
				
			||||||
@@ -344,7 +348,7 @@ const cleanUpRegistry = new FinalizationRegistry(function(s){
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
function create(is_readonly, value, actions){
 | 
					function create(is_readonly, value, actions){
 | 
				
			||||||
	const varS = oCreate(is_readonly ? SignalReadOnly : Signal);
 | 
						const varS = oCreate(is_readonly ? SignalReadOnly : Signal);
 | 
				
			||||||
	const SI= toSignal(varS, value, actions, is_readonly);
 | 
						const SI= toSignal(varS, value, actions);
 | 
				
			||||||
	cleanUpRegistry.register(SI, SI[mark]);
 | 
						cleanUpRegistry.register(SI, SI[mark]);
 | 
				
			||||||
	return SI;
 | 
						return SI;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -367,11 +371,10 @@ const protoSigal= oAssign(oCreate(), {
 | 
				
			|||||||
 * @param {Object} s - Object to transform
 | 
					 * @param {Object} s - Object to transform
 | 
				
			||||||
 * @param {any} value - Initial value
 | 
					 * @param {any} value - Initial value
 | 
				
			||||||
 * @param {Object} actions - Custom actions
 | 
					 * @param {Object} actions - Custom actions
 | 
				
			||||||
 * @param {boolean} [readonly=false] - Whether the signal is readonly
 | 
					 | 
				
			||||||
 * @returns {Object} Signal object with get() and set() methods
 | 
					 * @returns {Object} Signal object with get() and set() methods
 | 
				
			||||||
 * @private
 | 
					 * @private
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function toSignal(s, value, actions, readonly= false){
 | 
					function toSignal(s, value, actions){
 | 
				
			||||||
	const onclear= [];
 | 
						const onclear= [];
 | 
				
			||||||
	if(typeOf(actions)!=="[object Object]")
 | 
						if(typeOf(actions)!=="[object Object]")
 | 
				
			||||||
		actions= {};
 | 
							actions= {};
 | 
				
			||||||
@@ -385,7 +388,6 @@ function toSignal(s, value, actions, readonly= false){
 | 
				
			|||||||
		value: oAssign(oCreate(protoSigal), {
 | 
							value: oAssign(oCreate(protoSigal), {
 | 
				
			||||||
			value, actions, onclear, host,
 | 
								value, actions, onclear, host,
 | 
				
			||||||
			listeners: new Set(),
 | 
								listeners: new Set(),
 | 
				
			||||||
			readonly
 | 
					 | 
				
			||||||
		}),
 | 
							}),
 | 
				
			||||||
		enumerable: false,
 | 
							enumerable: false,
 | 
				
			||||||
		writable: false,
 | 
							writable: false,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user