mirror of
				https://github.com/jaandrle/deka-dom-el
				synced 2025-11-03 22:59:16 +01:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			v0.9.5-alp
			...
			8415f5cf6f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						8415f5cf6f
	
				 | 
					
					
						|||
| 
						
						
							
						
						7e3b54153d
	
				 | 
					
					
						
@@ -75,14 +75,11 @@ 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 [git3moji](https://git3moji.netlify.app/) for commit messages. This helps keep the commit history clear and
 | 
					We use
 | 
				
			||||||
consistent.
 | 
					[](https://robinpokorny.github.io/git3moji/) <!-- editorconfig-checker-disable-line -->
 | 
				
			||||||
 | 
					for commit messages. This helps keep the commit history clear and consistent.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
:emoji: Short summary of the change
 | 
					:emoji: Short summary of the change
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										111
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								README.md
									
									
									
									
									
								
							@@ -1,47 +1,7 @@
 | 
				
			|||||||
**Alpha**
 | 
					**Alpha**
 | 
				
			||||||
| [Docs&Examples](https://jaandrle.github.io/deka-dom-el "Official documentation and guide site")
 | 
					| [source code on GitHub](https://github.com/jaandrle/deka-dom-el)
 | 
				
			||||||
| [NPM](https://www.npmjs.com/package/deka-dom-el "Official NPM package page")
 | 
					| [*mirrored* on Gitea](https://gitea.jaandrle.cz/jaandrle/deka-dom-el)
 | 
				
			||||||
| [GitHub](https://github.com/jaandrle/deka-dom-el "Official GitHub repository")
 | 
					| [](https://robinpokorny.github.io/git3moji/) <!-- editorconfig-checker-disable-line -->
 | 
				
			||||||
([*Gitea*](https://gitea.jaandrle.cz/jaandrle/deka-dom-el "GitHub repository mirror on my own Gitea instance"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
***Vanilla for flavouring — a full-fledged feast for large projects***
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```javascript
 | 
					 | 
				
			||||||
// 🌟 Reactive component with clear separation of concerns
 | 
					 | 
				
			||||||
document.body.append(
 | 
					 | 
				
			||||||
	el(EmojiCounter, { initial: "🚀" }),
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function EmojiCounter({ initial }) {
 | 
					 | 
				
			||||||
	// ✨ - Define reactive data
 | 
					 | 
				
			||||||
	const count = S(0);
 | 
					 | 
				
			||||||
	const emoji = S(initial);
 | 
					 | 
				
			||||||
	const textContent = S(() => `Hello World ${emoji.get().repeat(count.get())}`);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 🔄 - UI updates automatically when signals change
 | 
					 | 
				
			||||||
	return el().append(
 | 
					 | 
				
			||||||
		el("p", { textContent, className: "output" }),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// 🎮 - Update state on events
 | 
					 | 
				
			||||||
		el("button", { textContent: "Add Emoji" },
 | 
					 | 
				
			||||||
			on("click", () => count.set(count.get() + 1)),
 | 
					 | 
				
			||||||
		),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		el("select", null,
 | 
					 | 
				
			||||||
			on.defer(el=> el.value= initial),
 | 
					 | 
				
			||||||
			on("change", e => emoji.set(e.target.value)),
 | 
					 | 
				
			||||||
		).append(
 | 
					 | 
				
			||||||
			el(Option, "🎉"),
 | 
					 | 
				
			||||||
			el(Option, "🚀"),
 | 
					 | 
				
			||||||
			el(Option, "💖"),
 | 
					 | 
				
			||||||
		),
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
function Option({ 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">
 | 
					<p align="center">
 | 
				
			||||||
	<img src="docs/assets/logo.svg" alt="Deka DOM Elements Logo" width="180" height="180">
 | 
						<img src="docs/assets/logo.svg" alt="Deka DOM Elements Logo" width="180" height="180">
 | 
				
			||||||
@@ -49,6 +9,51 @@ function Option({ textContent }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Deka DOM Elements (dd\<el\> or DDE)
 | 
					# Deka DOM Elements (dd\<el\> or DDE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					***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
 | 
				
			||||||
 | 
					// 🌟 Reactive component with clear separation of concerns
 | 
				
			||||||
 | 
					document.body.append(
 | 
				
			||||||
 | 
						el(EmojiCounter, { initial: "🚀" })
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function EmojiCounter({ initial }) {
 | 
				
			||||||
 | 
						// ✨ State - Define reactive data
 | 
				
			||||||
 | 
						const count = S(0);
 | 
				
			||||||
 | 
						const emoji = S(initial);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** @param {HTMLOptionElement} el */
 | 
				
			||||||
 | 
						const isSelected= el=> (el.selected= el.value===initial);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 🔄 View - UI updates automatically when signals change
 | 
				
			||||||
 | 
						return el().append(
 | 
				
			||||||
 | 
							el("p", {
 | 
				
			||||||
 | 
								className: "output",
 | 
				
			||||||
 | 
								textContent: S(() =>
 | 
				
			||||||
 | 
									`Hello World ${emoji.get().repeat(count.get())}`),
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 🎮 Controls - Update state on events
 | 
				
			||||||
 | 
							el("button", { textContent: "Add Emoji" },
 | 
				
			||||||
 | 
								on("click", () => count.set(count.get() + 1))
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							el("select", null, on.defer(el=> el.value= initial),
 | 
				
			||||||
 | 
								on("change", e => emoji.set(e.target.value))
 | 
				
			||||||
 | 
							).append(
 | 
				
			||||||
 | 
								el(Option, "🎉"),
 | 
				
			||||||
 | 
								el(Option, "🚀"),
 | 
				
			||||||
 | 
								el(Option, "💖"),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function Option({ textContent }){
 | 
				
			||||||
 | 
						return el("option", { value: textContent, textContent });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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
 | 
				
			||||||
[**signals/observables**](#understanding-signals).
 | 
					[**signals/observables**](#understanding-signals).
 | 
				
			||||||
@@ -56,24 +61,22 @@ Creating reactive elements, components, and Web Components using the native
 | 
				
			|||||||
## Features at a Glance
 | 
					## Features at a Glance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- ✅ **No build step required** — use directly in browsers or Node.js
 | 
					- ✅ **No build step required** — use directly in browsers or Node.js
 | 
				
			||||||
- ✅ **Minimalized footprint** — ~10-15kB minified bundle (original goal 10kB), **zero**/minimal dependencies and
 | 
					- ☑️ **Lightweight** — ~10-15kB minified (original goal 10kB) with **zero**/minimal dependencies
 | 
				
			||||||
	small in-memory size (auto-releasing resources as much as possible)
 | 
					- ✅ **Declarative & functional approach** for clean, maintainable code
 | 
				
			||||||
- ✅ **Declarative & functional approach support** for clean, maintainable code
 | 
					 | 
				
			||||||
- ✅ **Signals and events** for reactive UI
 | 
					- ✅ **Signals and events** for reactive UI
 | 
				
			||||||
- ✅ **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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Quick Links
 | 
					### Documentation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [**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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -84,7 +87,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/#h-getting-started) on the documentation site.
 | 
					format selector](https://jaandrle.github.io/deka-dom-el/) on the documentation site.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```html
 | 
					```html
 | 
				
			||||||
<!-- Example with IIFE build (creates a global DDE object) -->
 | 
					<!-- Example with IIFE build (creates a global DDE object) -->
 | 
				
			||||||
@@ -138,9 +141,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, currentPageId, dispatchEvent, t } from "../docs/ssr.js";
 | 
					import { path_target, pages as pages_registered, styles, 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,7 +28,6 @@ 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 }),
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,4 +9,3 @@ npx editorconfig-checker -format gcc ${additional}
 | 
				
			|||||||
npx jshint index.js src ${additional}
 | 
					npx jshint index.js src ${additional}
 | 
				
			||||||
[ "$one" = 'vim' ] && exit 0
 | 
					[ "$one" = 'vim' ] && exit 0
 | 
				
			||||||
npx size-limit
 | 
					npx size-limit
 | 
				
			||||||
npx publint
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								dist/esm-with-signals.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								dist/esm-with-signals.js
									
									
									
									
										vendored
									
									
								
							@@ -430,10 +430,9 @@ function createElement(tag, attributes, ...addons) {
 | 
				
			|||||||
			scoped = 1;
 | 
								scoped = 1;
 | 
				
			||||||
			const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
 | 
								const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
 | 
				
			||||||
			scope.push({ scope: tag, host });
 | 
								scope.push({ scope: tag, host });
 | 
				
			||||||
			el = /** @type {Element} */
 | 
								el = tag(attributes || void 0);
 | 
				
			||||||
			tag(attributes || void 0);
 | 
					 | 
				
			||||||
			if (el.nodeName === "#comment") break;
 | 
					 | 
				
			||||||
			const is_fragment = isInstance(el, enviroment.F);
 | 
								const is_fragment = isInstance(el, enviroment.F);
 | 
				
			||||||
 | 
								if (el.nodeName === "#comment") break;
 | 
				
			||||||
			const el_mark = createElement.mark({
 | 
								const el_mark = createElement.mark({
 | 
				
			||||||
				type: "component",
 | 
									type: "component",
 | 
				
			||||||
				name: tag.name,
 | 
									name: tag.name,
 | 
				
			||||||
@@ -697,10 +696,13 @@ var queueSignalWrite = /* @__PURE__ */ (() => {
 | 
				
			|||||||
})();
 | 
					})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// src/signals-lib/signals-lib.js
 | 
					// src/signals-lib/signals-lib.js
 | 
				
			||||||
var SignalReadOnly = oCreate(null, {
 | 
					var Signal = 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);
 | 
				
			||||||
	} },
 | 
						} },
 | 
				
			||||||
@@ -708,9 +710,9 @@ var SignalReadOnly = oCreate(null, {
 | 
				
			|||||||
		return this[mark] && this[mark].value;
 | 
							return this[mark] && this[mark].value;
 | 
				
			||||||
	} }
 | 
						} }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
var Signal = oCreate(SignalReadOnly, {
 | 
					var SignalReadOnly = oCreate(Signal, {
 | 
				
			||||||
	set: { value(...v) {
 | 
						set: { value() {
 | 
				
			||||||
		return write(this, ...v);
 | 
							return;
 | 
				
			||||||
	} }
 | 
						} }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
function isSignal(candidate) {
 | 
					function isSignal(candidate) {
 | 
				
			||||||
@@ -887,10 +889,9 @@ 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) {
 | 
				
			||||||
		const is_first = !element[key_reactive];
 | 
							if (!element[key_reactive]) element[key_reactive] = [];
 | 
				
			||||||
		if (is_first) element[key_reactive] = [];
 | 
					 | 
				
			||||||
		element[key_reactive].push([[s, listener], ...notes]);
 | 
							element[key_reactive].push([[s, listener], ...notes]);
 | 
				
			||||||
		if (!is_first || current.prevent) return;
 | 
							if (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`, …?).
 | 
				
			||||||
@@ -905,7 +906,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);
 | 
						const SI = toSignal(varS, value, actions, is_readonly);
 | 
				
			||||||
	cleanUpRegistry.register(SI, SI[mark]);
 | 
						cleanUpRegistry.register(SI, SI[mark]);
 | 
				
			||||||
	return SI;
 | 
						return SI;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -917,7 +918,7 @@ var protoSigal = oAssign(oCreate(), {
 | 
				
			|||||||
		this.skip = true;
 | 
							this.skip = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
function toSignal(s, value, actions) {
 | 
					function toSignal(s, value, actions, readonly = false) {
 | 
				
			||||||
	const onclear = [];
 | 
						const onclear = [];
 | 
				
			||||||
	if (typeOf(actions) !== "[object Object]")
 | 
						if (typeOf(actions) !== "[object Object]")
 | 
				
			||||||
		actions = {};
 | 
							actions = {};
 | 
				
			||||||
@@ -933,7 +934,8 @@ function toSignal(s, value, actions) {
 | 
				
			|||||||
			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
											
										
									
								
							
							
								
								
									
										5
									
								
								dist/esm.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								dist/esm.js
									
									
									
									
										vendored
									
									
								
							@@ -414,10 +414,9 @@ function createElement(tag, attributes, ...addons) {
 | 
				
			|||||||
			scoped = 1;
 | 
								scoped = 1;
 | 
				
			||||||
			const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
 | 
								const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
 | 
				
			||||||
			scope.push({ scope: tag, host });
 | 
								scope.push({ scope: tag, host });
 | 
				
			||||||
			el = /** @type {Element} */
 | 
								el = tag(attributes || void 0);
 | 
				
			||||||
			tag(attributes || void 0);
 | 
					 | 
				
			||||||
			if (el.nodeName === "#comment") break;
 | 
					 | 
				
			||||||
			const is_fragment = isInstance(el, enviroment.F);
 | 
								const is_fragment = isInstance(el, enviroment.F);
 | 
				
			||||||
 | 
								if (el.nodeName === "#comment") break;
 | 
				
			||||||
			const el_mark = createElement.mark({
 | 
								const el_mark = createElement.mark({
 | 
				
			||||||
				type: "component",
 | 
									type: "component",
 | 
				
			||||||
				name: tag.name,
 | 
									name: tag.name,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								dist/esm.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/esm.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										28
									
								
								dist/iife-with-signals.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								dist/iife-with-signals.js
									
									
									
									
										vendored
									
									
								
							@@ -475,10 +475,9 @@ var DDE = (() => {
 | 
				
			|||||||
				scoped = 1;
 | 
									scoped = 1;
 | 
				
			||||||
				const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
 | 
									const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
 | 
				
			||||||
				scope.push({ scope: tag, host });
 | 
									scope.push({ scope: tag, host });
 | 
				
			||||||
				el = /** @type {Element} */
 | 
									el = tag(attributes || void 0);
 | 
				
			||||||
				tag(attributes || void 0);
 | 
					 | 
				
			||||||
				if (el.nodeName === "#comment") break;
 | 
					 | 
				
			||||||
				const is_fragment = isInstance(el, enviroment.F);
 | 
									const is_fragment = isInstance(el, enviroment.F);
 | 
				
			||||||
 | 
									if (el.nodeName === "#comment") break;
 | 
				
			||||||
				const el_mark = createElement.mark({
 | 
									const el_mark = createElement.mark({
 | 
				
			||||||
					type: "component",
 | 
										type: "component",
 | 
				
			||||||
					name: tag.name,
 | 
										name: tag.name,
 | 
				
			||||||
@@ -742,10 +741,13 @@ var DDE = (() => {
 | 
				
			|||||||
	})();
 | 
						})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// src/signals-lib/signals-lib.js
 | 
						// src/signals-lib/signals-lib.js
 | 
				
			||||||
	var SignalReadOnly = oCreate(null, {
 | 
						var Signal = 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);
 | 
				
			||||||
		} },
 | 
							} },
 | 
				
			||||||
@@ -753,9 +755,9 @@ var DDE = (() => {
 | 
				
			|||||||
			return this[mark] && this[mark].value;
 | 
								return this[mark] && this[mark].value;
 | 
				
			||||||
		} }
 | 
							} }
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	var Signal = oCreate(SignalReadOnly, {
 | 
						var SignalReadOnly = oCreate(Signal, {
 | 
				
			||||||
		set: { value(...v) {
 | 
							set: { value() {
 | 
				
			||||||
			return write(this, ...v);
 | 
								return;
 | 
				
			||||||
		} }
 | 
							} }
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	function isSignal(candidate) {
 | 
						function isSignal(candidate) {
 | 
				
			||||||
@@ -932,10 +934,9 @@ 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) {
 | 
				
			||||||
			const is_first = !element[key_reactive];
 | 
								if (!element[key_reactive]) element[key_reactive] = [];
 | 
				
			||||||
			if (is_first) element[key_reactive] = [];
 | 
					 | 
				
			||||||
			element[key_reactive].push([[s, listener], ...notes]);
 | 
								element[key_reactive].push([[s, listener], ...notes]);
 | 
				
			||||||
			if (!is_first || current.prevent) return;
 | 
								if (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`, …?).
 | 
				
			||||||
@@ -950,7 +951,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);
 | 
							const SI = toSignal(varS, value, actions, is_readonly);
 | 
				
			||||||
		cleanUpRegistry.register(SI, SI[mark]);
 | 
							cleanUpRegistry.register(SI, SI[mark]);
 | 
				
			||||||
		return SI;
 | 
							return SI;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -962,7 +963,7 @@ var DDE = (() => {
 | 
				
			|||||||
			this.skip = true;
 | 
								this.skip = true;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	function toSignal(s, value, actions) {
 | 
						function toSignal(s, value, actions, readonly = false) {
 | 
				
			||||||
		const onclear = [];
 | 
							const onclear = [];
 | 
				
			||||||
		if (typeOf(actions) !== "[object Object]")
 | 
							if (typeOf(actions) !== "[object Object]")
 | 
				
			||||||
			actions = {};
 | 
								actions = {};
 | 
				
			||||||
@@ -978,7 +979,8 @@ 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,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								dist/iife-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								dist/iife-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										5
									
								
								dist/iife.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								dist/iife.js
									
									
									
									
										vendored
									
									
								
							@@ -456,10 +456,9 @@ var DDE = (() => {
 | 
				
			|||||||
				scoped = 1;
 | 
									scoped = 1;
 | 
				
			||||||
				const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
 | 
									const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
 | 
				
			||||||
				scope.push({ scope: tag, host });
 | 
									scope.push({ scope: tag, host });
 | 
				
			||||||
				el = /** @type {Element} */
 | 
									el = tag(attributes || void 0);
 | 
				
			||||||
				tag(attributes || void 0);
 | 
					 | 
				
			||||||
				if (el.nodeName === "#comment") break;
 | 
					 | 
				
			||||||
				const is_fragment = isInstance(el, enviroment.F);
 | 
									const is_fragment = isInstance(el, enviroment.F);
 | 
				
			||||||
 | 
									if (el.nodeName === "#comment") break;
 | 
				
			||||||
				const el_mark = createElement.mark({
 | 
									const el_mark = createElement.mark({
 | 
				
			||||||
					type: "component",
 | 
										type: "component",
 | 
				
			||||||
					name: tag.name,
 | 
										name: tag.name,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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 { page_id, registerClientFile, styles } from "../ssr.js";
 | 
					import { registerClientFile, styles } from "../ssr.js";
 | 
				
			||||||
const host= "."+code.name;
 | 
					const host= "."+code.name;
 | 
				
			||||||
styles.css`
 | 
					styles.css`
 | 
				
			||||||
/* Code block styling */
 | 
					/* Code block styling */
 | 
				
			||||||
@@ -177,9 +177,6 @@ ${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
 | 
				
			||||||
@@ -187,17 +184,15 @@ 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 {Language} [attrs.language="-s"] Language of the code
 | 
					 * @param {"js"|"ts"|"html"|"css"|"shell"} [attrs.language="js"] 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= "-", className= host.slice(1) }){
 | 
					export function code({ id, src, content, language= "js", className= host.slice(1), page_id }){
 | 
				
			||||||
	if(src){
 | 
						if(src) content= s.cat(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(language!== "-"){
 | 
						if(page_id){
 | 
				
			||||||
		registerClientPart();
 | 
							registerClientPart(page_id);
 | 
				
			||||||
		dataJS= "todo";
 | 
							dataJS= "todo";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return el("div", { id, className, dataJS, tabIndex: 0 }).append(
 | 
						return el("div", { id, className, dataJS, tabIndex: 0 }).append(
 | 
				
			||||||
@@ -209,7 +204,8 @@ 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= {};
 | 
				
			||||||
function registerClientPart(){
 | 
					/** @param {string} page_id */
 | 
				
			||||||
 | 
					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 { page_id, styles } from "../ssr.js";
 | 
					import { styles } from "../ssr.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
styles.css`
 | 
					styles.css`
 | 
				
			||||||
#html-to-dde-converter {
 | 
					#html-to-dde-converter {
 | 
				
			||||||
@@ -149,11 +149,12 @@ 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(){
 | 
					export function converter({ page_id }){
 | 
				
			||||||
	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 { page_id, styles } from "../ssr.js";
 | 
					import { styles } from "../ssr.js";
 | 
				
			||||||
const host= "."+example.name;
 | 
					const host= "."+example.name;
 | 
				
			||||||
styles.css`
 | 
					styles.css`
 | 
				
			||||||
${host} {
 | 
					${host} {
 | 
				
			||||||
@@ -119,8 +119,9 @@ 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" }){
 | 
					export function example({ src, language= "js", variant= "normal", page_id }){
 | 
				
			||||||
	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,7 +303,6 @@ 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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -82,10 +82,10 @@ export function ImageGallery(images= imagesSample) {
 | 
				
			|||||||
				closeLightbox();
 | 
									closeLightbox();
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			case 'ArrowLeft':
 | 
								case 'ArrowLeft':
 | 
				
			||||||
				onPrevImage(e);
 | 
									document.querySelector('.lightbox-prev-btn').click();
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			case 'ArrowRight':
 | 
								case 'ArrowRight':
 | 
				
			||||||
				onNextImage(e);
 | 
									document.querySelector('.lightbox-next-btn').click();
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -126,12 +126,12 @@ export function ImageGallery(images= imagesSample) {
 | 
				
			|||||||
						el("div", {
 | 
											el("div", {
 | 
				
			||||||
							className: "gallery-item",
 | 
												className: "gallery-item",
 | 
				
			||||||
							dataTag: image.alt.toLowerCase()
 | 
												dataTag: image.alt.toLowerCase()
 | 
				
			||||||
						}, onImageClick(image.id)).append(
 | 
											}).append(
 | 
				
			||||||
							el("img", {
 | 
												el("img", {
 | 
				
			||||||
								src: image.src,
 | 
													src: image.src,
 | 
				
			||||||
								alt: image.alt,
 | 
													alt: image.alt,
 | 
				
			||||||
								loading: "lazy"
 | 
													loading: "lazy"
 | 
				
			||||||
							}),
 | 
												}, onImageClick(image.id)),
 | 
				
			||||||
							el("div", { className: "gallery-item-caption" }).append(
 | 
												el("div", { className: "gallery-item-caption" }).append(
 | 
				
			||||||
								el("h3", image.title),
 | 
													el("h3", image.title),
 | 
				
			||||||
								el("p", image.alt)
 | 
													el("p", image.alt)
 | 
				
			||||||
@@ -152,17 +152,17 @@ export function ImageGallery(images= imagesSample) {
 | 
				
			|||||||
					}).append(
 | 
										}).append(
 | 
				
			||||||
						el("button", {
 | 
											el("button", {
 | 
				
			||||||
							className: "lightbox-close-btn",
 | 
												className: "lightbox-close-btn",
 | 
				
			||||||
						ariaLabel: "Close lightbox",
 | 
												"aria-label": "Close lightbox"
 | 
				
			||||||
						}, on("click", closeLightbox)).append("×"),
 | 
											}, on("click", closeLightbox)).append("×"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						el("button", {
 | 
											el("button", {
 | 
				
			||||||
							className: "lightbox-prev-btn",
 | 
												className: "lightbox-prev-btn",
 | 
				
			||||||
						ariaLabel: "Previous image",
 | 
												"aria-label": "Previous image"
 | 
				
			||||||
						}, on("click", onPrevImage)).append("❮"),
 | 
											}, on("click", onPrevImage)).append("❮"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						el("button", {
 | 
											el("button", {
 | 
				
			||||||
							className: "lightbox-next-btn",
 | 
												className: "lightbox-next-btn",
 | 
				
			||||||
						ariaLabel: "Next image",
 | 
												"aria-label": "Next image"
 | 
				
			||||||
						}, on("click", onNextImage)).append("❯"),
 | 
											}, on("click", onNextImage)).append("❯"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						S.el(selectedImage, img => !img
 | 
											S.el(selectedImage, img => !img
 | 
				
			||||||
@@ -171,16 +171,16 @@ export function ImageGallery(images= imagesSample) {
 | 
				
			|||||||
								el("img", {
 | 
													el("img", {
 | 
				
			||||||
									src: img.src,
 | 
														src: img.src,
 | 
				
			||||||
									alt: img.alt,
 | 
														alt: img.alt,
 | 
				
			||||||
								className: "lightbox-image",
 | 
														className: "lightbox-image"
 | 
				
			||||||
								}),
 | 
													}),
 | 
				
			||||||
								el("div", { className: "lightbox-caption" }).append(
 | 
													el("div", { className: "lightbox-caption" }).append(
 | 
				
			||||||
									el("h2", img.title),
 | 
														el("h2", img.title),
 | 
				
			||||||
								el("p", img.alt),
 | 
														el("p", img.alt)
 | 
				
			||||||
							),
 | 
													)
 | 
				
			||||||
 | 
												)
 | 
				
			||||||
 | 
											)
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
					),
 | 
					 | 
				
			||||||
				),
 | 
					 | 
				
			||||||
			),
 | 
					 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,509 +0,0 @@
 | 
				
			|||||||
import { el, on, scope } from "deka-dom-el";
 | 
					 | 
				
			||||||
import { S } from "deka-dom-el/signals";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function ProductCatalog() {
 | 
					 | 
				
			||||||
	const { signal }= scope;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const itemsPerPage = 5;
 | 
					 | 
				
			||||||
	const products = asyncSignal(S,
 | 
					 | 
				
			||||||
		fetchProducts,
 | 
					 | 
				
			||||||
		{ initial: [], keepLast: true, signal });
 | 
					 | 
				
			||||||
	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, signal?: AbortSignal }} options - Configuration options
 | 
					 | 
				
			||||||
 * @returns {Object} Status signals and control methods
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export function asyncSignal(S, invoker, { initial, keepLast, signal } = {}) {
 | 
					 | 
				
			||||||
	/** @type {(s: AbortSignal) => AbortSignal} */
 | 
					 | 
				
			||||||
	const anySignal = !signal || !AbortSignal.any // TODO: make better
 | 
					 | 
				
			||||||
		? s=> s
 | 
					 | 
				
			||||||
		: s=> AbortSignal.any([s, signal]);
 | 
					 | 
				
			||||||
	// 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: anySignal(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(250px, 1fr));
 | 
								grid-template-columns: repeat(auto-fit, minmax(300px, 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, ({ length }) => !length
 | 
							S.el(todosS, todos => !todos.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(
 | 
				
			||||||
					el("strong", length + " " + (length === 1 ? "item" : "items")),
 | 
										noOfLeft()
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				memo("filters", ()=>
 | 
									memo("filters", ()=>
 | 
				
			||||||
					el("ul", { className: "filters" }).append(
 | 
										el("ul", { className: "filters" }).append(
 | 
				
			||||||
@@ -100,7 +100,7 @@ function Todos(){
 | 
				
			|||||||
						)
 | 
											)
 | 
				
			||||||
					),
 | 
										),
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				length - todosRemainingS.get() === 0
 | 
									todos.length - todosRemainingS.get() === 0
 | 
				
			||||||
					? el()
 | 
										? el()
 | 
				
			||||||
					: memo("delete", () =>
 | 
										: memo("delete", () =>
 | 
				
			||||||
						el("button",
 | 
											el("button",
 | 
				
			||||||
@@ -110,6 +110,13 @@ 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, page_id } from "../ssr.js";
 | 
					import { styles } 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(){
 | 
					export function getLibraryUrl({ page_id }){
 | 
				
			||||||
	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,6 +43,7 @@ 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,10 +12,6 @@ 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>]))")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,9 +76,11 @@ export function h3({ textContent, id }){
 | 
				
			|||||||
				el("a", {
 | 
									el("a", {
 | 
				
			||||||
						className: "heading-anchor",
 | 
											className: "heading-anchor",
 | 
				
			||||||
						href: "#"+id,
 | 
											href: "#"+id,
 | 
				
			||||||
 | 
											textContent: "#",
 | 
				
			||||||
						title: `Link to this section: ${textContent}`,
 | 
											title: `Link to this section: ${textContent}`,
 | 
				
			||||||
 | 
											"aria-label": `Link to section ${textContent}`
 | 
				
			||||||
				}),
 | 
									}),
 | 
				
			||||||
				"# ",
 | 
									" ",
 | 
				
			||||||
				textContent,
 | 
									textContent,
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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`Reactive DOM library for creating dynamic UIs with a declarative syntax`,
 | 
						description: t`A lightweight, reactive DOM library for creating dynamic UIs with a declarative syntax`,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { el } from "deka-dom-el";
 | 
					import { el } from "deka-dom-el";
 | 
				
			||||||
@@ -16,11 +16,7 @@ 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: {
 | 
						w_mvv:{
 | 
				
			||||||
		title: t`NPM package page for dd<el>`,
 | 
					 | 
				
			||||||
		href: "https://www.npmjs.com/package/deka-dom-el",
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	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",
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
@@ -31,9 +27,10 @@ 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 library for building dynamic UIs with
 | 
								Welcome to Deka DOM Elements (dd<el> or DDE) — a lightweight 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.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
@@ -41,18 +38,13 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			el("h4", t`Key Benefits of dd<el>`),
 | 
								el("h4", t`Key Benefits of dd<el>`),
 | 
				
			||||||
			el("ul").append(
 | 
								el("ul").append(
 | 
				
			||||||
				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`Minimalized footprint:`),
 | 
									el("li", t`Lightweight core (~10–15kB minified) without unnecessary dependencies (0 at now 😇)`),
 | 
				
			||||||
				el("ul").append(
 | 
					 | 
				
			||||||
					el("li", t`lightweight core (~10–15kB minified)`),
 | 
					 | 
				
			||||||
					el("li", t`…without unnecessary dependencies (0 at now 😇)`),
 | 
					 | 
				
			||||||
					el("li", t`auto-releasing resources with focus on performance and development experience`),
 | 
					 | 
				
			||||||
				),
 | 
					 | 
				
			||||||
				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 (but optional) reactivity with simplified but powerful signals system`),
 | 
									el("li", t`Built-in 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") }),
 | 
							el(example, { src: fileURL("./components/examples/introducing/helloWorld.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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`
 | 
				
			||||||
@@ -64,11 +56,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") }),
 | 
										el(code, { src: fileURL("./components/examples/introducing/3ps-before.js"), page_id }),
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				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") }),
 | 
										el(code, { src: fileURL("./components/examples/introducing/3ps.js"), page_id }),
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -109,20 +101,16 @@ 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" }),
 | 
							el(code, { content: "npm install deka-dom-el --save", language: "shell", page_id }),
 | 
				
			||||||
		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),
 | 
							el(getLibraryUrl, { page_id }),
 | 
				
			||||||
		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:
 | 
				
			||||||
@@ -131,10 +119,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" }),
 | 
								`, language: "js", page_id }),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`How to Use This Documentation`),
 | 
							el(h3, t`How to Use This Documentation`),
 | 
				
			||||||
@@ -158,7 +146,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>`),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -151,7 +151,7 @@ export function header({ info: { href, title, description }, pkg }){
 | 
				
			|||||||
				),
 | 
									),
 | 
				
			||||||
				el("span", {
 | 
									el("span", {
 | 
				
			||||||
					className: "version-badge",
 | 
										className: "version-badge",
 | 
				
			||||||
					ariaLabel: "Version",
 | 
										"aria-label": "Version",
 | 
				
			||||||
					textContent: pkg.version || ""
 | 
										textContent: pkg.version || ""
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
@@ -165,13 +165,13 @@ export function header({ info: { href, title, description }, pkg }){
 | 
				
			|||||||
function nav({ href, pkg }){
 | 
					function nav({ href, pkg }){
 | 
				
			||||||
	return el("nav", {
 | 
						return el("nav", {
 | 
				
			||||||
		role: "navigation",
 | 
							role: "navigation",
 | 
				
			||||||
		ariaLabel: "Main navigation",
 | 
							"aria-label": "Main navigation",
 | 
				
			||||||
		className: nav.name
 | 
							className: nav.name
 | 
				
			||||||
	}).append(
 | 
						}).append(
 | 
				
			||||||
		el("a", {
 | 
							el("a", {
 | 
				
			||||||
			href: pkg.homepage,
 | 
								href: pkg.homepage,
 | 
				
			||||||
			className: "github-link",
 | 
								className: "github-link",
 | 
				
			||||||
			ariaLabel: "View on GitHub",
 | 
								"aria-label": "View on GitHub",
 | 
				
			||||||
			target: "_blank",
 | 
								target: "_blank",
 | 
				
			||||||
			rel: "noopener noreferrer",
 | 
								rel: "noopener noreferrer",
 | 
				
			||||||
		}).append(
 | 
							}).append(
 | 
				
			||||||
@@ -185,11 +185,11 @@ function nav({ href, pkg }){
 | 
				
			|||||||
			return el("a", {
 | 
								return el("a", {
 | 
				
			||||||
				href: isIndex ? "./" : p.href,
 | 
									href: isIndex ? "./" : p.href,
 | 
				
			||||||
				title: p.description || `Go to ${p.title}`,
 | 
									title: p.description || `Go to ${p.title}`,
 | 
				
			||||||
				ariaCurrent: isCurrent ? "page" : null,
 | 
									"aria-current": isCurrent ? "page" : null,
 | 
				
			||||||
			}).append(
 | 
								}).append(
 | 
				
			||||||
				el("span", {
 | 
									el("span", {
 | 
				
			||||||
					className: "nav-number",
 | 
										className: "nav-number",
 | 
				
			||||||
					ariaHidden: "true",
 | 
										"aria-hidden": "true",
 | 
				
			||||||
					textContent: `${i+1}. `
 | 
										textContent: `${i+1}. `
 | 
				
			||||||
				}),
 | 
									}),
 | 
				
			||||||
				p.title
 | 
									p.title
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,11 +47,12 @@ 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`),
 | 
				
			||||||
@@ -64,7 +65,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/elements/intro.js") }),
 | 
							el(code, { src: fileURL("./components/examples/elements/intro.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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`
 | 
				
			||||||
@@ -76,11 +77,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") })
 | 
										el(code, { src: fileURL("./components/examples/elements/native-dom-create.js"), page_id })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				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") })
 | 
										el(code, { src: fileURL("./components/examples/elements/dde-dom-create.js"), page_id })
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -88,7 +89,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") }),
 | 
							el(example, { src: fileURL("./components/examples/elements/dekaCreateElement.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Advanced Property Assignment`),
 | 
							el(h3, t`Advanced Property Assignment`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -121,7 +122,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") }),
 | 
							el(example, { src: fileURL("./components/examples/elements/dekaAssign.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -141,11 +142,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") })
 | 
										el(code, { src: fileURL("./components/examples/elements/native-dom-tree.js"), page_id })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				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") })
 | 
										el(code, { src: fileURL("./components/examples/elements/dde-dom-tree.js"), page_id })
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -153,14 +154,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") }),
 | 
							el(example, { src: fileURL("./components/examples/elements/dekaAppend.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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") }),
 | 
							el(example, { src: fileURL("./components/examples/elements/dekaBasicComponent.js"), page_id }),
 | 
				
			||||||
		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.
 | 
				
			||||||
@@ -184,9 +185,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") }),
 | 
							el(example, { src: fileURL("./components/examples/elements/dekaElNS.js"), page_id }),
 | 
				
			||||||
		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,6 +39,7 @@ 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
 | 
				
			||||||
@@ -56,7 +57,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/events/intro.js") }),
 | 
							el(code, { src: fileURL("./components/examples/events/intro.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Events and Listeners: Two Approaches`),
 | 
							el(h3, t`Events and Listeners: Two Approaches`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -69,11 +70,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);`, language: "js" })
 | 
										el(code, { content: `element.addEventListener("click", callback, options);`, page_id })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				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);`, language: "js" })
 | 
										el(code, { content: `on("click", callback, options)(element);`, page_id })
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -81,7 +82,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") }),
 | 
							el(example, { src: fileURL("./components/examples/events/compare.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Removing Event Listeners`),
 | 
							el(h3, t`Removing Event Listeners`),
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
@@ -90,7 +91,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") }),
 | 
							el(example, { src: fileURL("./components/examples/events/abortSignal.js"), page_id }),
 | 
				
			||||||
		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")}).
 | 
				
			||||||
@@ -100,26 +101,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") }),
 | 
									el(code, { src: fileURL("./components/examples/events/attribute-event.js"), page_id }),
 | 
				
			||||||
				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") }),
 | 
									el(code, { src: fileURL("./components/examples/events/property-event.js"), page_id }),
 | 
				
			||||||
				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") }),
 | 
									el(code, { src: fileURL("./components/examples/events/chain-event.js"), page_id }),
 | 
				
			||||||
				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 })}.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -142,7 +143,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") }),
 | 
							el(example, { src: fileURL("./components/examples/events/templateWithListeners.js"), page_id }),
 | 
				
			||||||
		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.
 | 
				
			||||||
@@ -166,7 +167,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") }),
 | 
							el(example, { src: fileURL("./components/examples/events/live-cycle.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -216,8 +217,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") }),
 | 
							el(example, { src: fileURL("./components/examples/events/compareDispatch.js"), page_id }),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/events/dispatch.js") }),
 | 
							el(code, { src: fileURL("./components/examples/events/dispatch.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Best Practices`),
 | 
							el(h3, t`Best Practices`),
 | 
				
			||||||
		el("ol").append(
 | 
							el("ol").append(
 | 
				
			||||||
@@ -250,6 +251,6 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(mnemonic),
 | 
							el(mnemonic)
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,6 +42,7 @@ 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
 | 
				
			||||||
@@ -57,7 +58,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") }),
 | 
							el(code, { src: fileURL("./components/examples/signals/intro.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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`
 | 
				
			||||||
@@ -67,21 +68,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);", language: "js" }),
 | 
									el(code, { content: "const count = S(0);", page_id }),
 | 
				
			||||||
				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));", language: "js" }),
 | 
									el(code, { content: "S.on(count, value => updateUI(value));", page_id }),
 | 
				
			||||||
				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);", language: "js" }),
 | 
									el(code, { content: "count.set(count.get() + 1);", page_id }),
 | 
				
			||||||
				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") }),
 | 
							el(example, { src: fileURL("./components/examples/signals/signals.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -124,12 +125,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") }),
 | 
							el(example, { src: fileURL("./components/examples/signals/derived.js"), page_id }),
 | 
				
			||||||
		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") }),
 | 
							el(example, { src: fileURL("./components/examples/signals/computations-abort.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Signal Actions: For Complex State`),
 | 
							el(h3, t`Signal Actions: For Complex State`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -150,7 +151,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
						});
 | 
											});
 | 
				
			||||||
						// Use the action
 | 
											// Use the action
 | 
				
			||||||
						S.action(todos, "add", "New todo");
 | 
											S.action(todos, "add", "New todo");
 | 
				
			||||||
					`, language: "js" })
 | 
										`, page_id })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				el("div", { className: "bad-practice" }).append(
 | 
									el("div", { className: "bad-practice" }).append(
 | 
				
			||||||
					el("h5", t`❌ Without Actions`),
 | 
										el("h5", t`❌ Without Actions`),
 | 
				
			||||||
@@ -160,7 +161,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!
 | 
				
			||||||
					`, language: "js" }))
 | 
										`, page_id }))
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -171,7 +172,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") }),
 | 
							el(example, { src: fileURL("./components/examples/signals/actions-demo.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Actions provide these benefits:
 | 
								Actions provide these benefits:
 | 
				
			||||||
@@ -185,7 +186,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") }),
 | 
							el(example, { src: fileURL("./components/examples/signals/actions-todos.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -222,7 +223,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
					// Later:
 | 
										// Later:
 | 
				
			||||||
					color.set("red"); // UI updates automatically
 | 
										color.set("red"); // UI updates automatically
 | 
				
			||||||
				`, language: "js" }),
 | 
									`, page_id }),
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			el("div", { className: "tab", dataTab: "elements" }).append(
 | 
								el("div", { className: "tab", dataTab: "elements" }).append(
 | 
				
			||||||
				el("h4", t`Reactive Elements`),
 | 
									el("h4", t`Reactive Elements`),
 | 
				
			||||||
@@ -240,7 +241,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
					// Later:
 | 
										// Later:
 | 
				
			||||||
					S.action(items, "push", "Dragonfruit"); // List updates automatically
 | 
										S.action(items, "push", "Dragonfruit"); // List updates automatically
 | 
				
			||||||
				`, language: "js" }),
 | 
									`, page_id }),
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -249,12 +250,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") }),
 | 
							el(example, { src: fileURL("./components/examples/signals/dom-attrs.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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") }),
 | 
							el(example, { src: fileURL("./components/examples/signals/dom-el.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Best Practices for Signals`),
 | 
							el(h3, t`Best Practices for Signals`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -288,7 +289,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, { content: `
 | 
									el(code, { page_id, 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);
 | 
				
			||||||
@@ -300,13 +301,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, { content: `
 | 
									el(code, { page_id, content: `
 | 
				
			||||||
					let canSubmit = false;
 | 
										let canSubmit = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					const onFormSubmit = on("submit", e => {
 | 
										const onFormSubmit = on("submit", e => {
 | 
				
			||||||
@@ -317,14 +318,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, { content: `
 | 
									el(code, { page_id, content: `
 | 
				
			||||||
					const canSubmit = S(false);
 | 
										const canSubmit = S(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					const onFormSubmit = on("submit", e => {
 | 
										const onFormSubmit = on("submit", e => {
 | 
				
			||||||
@@ -340,7 +341,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,13 +27,14 @@ 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") }),
 | 
							el(code, { src: fileURL("./components/examples/scopes/intro.js"), page_id }),
 | 
				
			||||||
		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`),
 | 
				
			||||||
@@ -82,7 +83,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") }),
 | 
							el(example, { src: fileURL("./components/examples/scopes/scopes-and-hosts.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -94,7 +95,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") }),
 | 
							el(code, { src: fileURL("./components/examples/scopes/good-practise.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Class-Based Components`),
 | 
							el(h3, t`Class-Based Components`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -102,7 +103,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") }),
 | 
							el(example, { src: fileURL("./components/examples/scopes/class-component.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Automatic Cleanup with Scopes`),
 | 
							el(h3, t`Automatic Cleanup with Scopes`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -122,7 +123,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") }),
 | 
							el(example, { src: fileURL("./components/examples/scopes/cleaning.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -148,17 +149,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") })
 | 
									el(code, { src: fileURL("./components/examples/scopes/declarative.js"), page_id })
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			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") })
 | 
									el(code, { src: fileURL("./components/examples/scopes/imperative.js"), page_id })
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
			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") })
 | 
									el(code, { src: fileURL("./components/examples/scopes/mixed.js"), page_id })
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,6 +57,7 @@ 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
 | 
				
			||||||
@@ -72,7 +73,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") }),
 | 
							el(code, { src: fileURL("./components/examples/customElement/intro.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Getting Started: Web Components Basics`),
 | 
							el(h3, t`Getting Started: Web Components Basics`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -94,7 +95,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") }),
 | 
							el(code, { src: fileURL("./components/examples/customElement/native-basic.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -123,7 +124,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") }),
 | 
							el(example, { src: fileURL("./components/examples/customElement/customElementWithDDE.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -155,7 +156,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") }),
 | 
							el(example, { src: fileURL("./components/examples/customElement/dde.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -187,7 +188,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") }),
 | 
							el(example, { src: fileURL("./components/examples/customElement/observedAttributes.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "callout" }).append(
 | 
							el("div", { className: "callout" }).append(
 | 
				
			||||||
			el("h4", t`How S.observedAttributes Works`),
 | 
								el("h4", t`How S.observedAttributes Works`),
 | 
				
			||||||
@@ -220,7 +221,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
								<p>Content</p>
 | 
													<p>Content</p>
 | 
				
			||||||
			` })
 | 
								` })
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(example, { src: fileURL("./components/examples/customElement/shadowRoot.js") }),
 | 
							el(example, { src: fileURL("./components/examples/customElement/shadowRoot.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			For more information on Shadow DOM, see
 | 
								For more information on Shadow DOM, see
 | 
				
			||||||
@@ -233,7 +234,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") }),
 | 
							el(example, { src: fileURL("./components/examples/customElement/simulateSlots.js"), page_id }),
 | 
				
			||||||
		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,6 +15,7 @@ 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
 | 
				
			||||||
@@ -35,7 +36,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());
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
		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:
 | 
				
			||||||
@@ -48,7 +49,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:
 | 
				
			||||||
@@ -56,7 +57,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));
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", t`Debugging derived signals`),
 | 
							el("h4", t`Debugging derived signals`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -68,7 +69,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") }),
 | 
							el(example, { src: fileURL("./components/examples/debugging/consoleLog.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", t`Examining signal via DevTools`),
 | 
							el("h4", t`Examining signal via DevTools`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -76,11 +77,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
 | 
				
			||||||
@@ -120,7 +121,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") }),
 | 
							el(code, { src: fileURL("./components/examples/debugging/mutations.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", t`Memory leaks with signal listeners`),
 | 
							el("h4", t`Memory leaks with signal listeners`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -136,7 +137,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") }),
 | 
							el(code, { src: fileURL("./components/examples/debugging/debouncing.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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`
 | 
				
			||||||
@@ -149,7 +150,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") }),
 | 
							el(code, { src: fileURL("./components/examples/debugging/dom-reactive-mark.html"), page_id }),
 | 
				
			||||||
		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
 | 
				
			||||||
@@ -168,22 +169,6 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			el("li", t`name - The name of the component function`),
 | 
								el("li", t`name - The name of the component function`),
 | 
				
			||||||
			el("li", t`host - Indicates whether the host is "this" (for DocumentFragments) or "parentElement"`),
 | 
								el("li", t`host - Indicates whether the host is "this" (for DocumentFragments) or "parentElement"`),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el("div", { className: "warning" }).append(
 | 
					 | 
				
			||||||
			el("p").append(T`
 | 
					 | 
				
			||||||
				There are edge case when the mark can be missing. For example, the (utility) components with reactive
 | 
					 | 
				
			||||||
				keys such as ${el("code", ".textContent")}, ${el("code", ".innerText")} or ${el("code", ".innerHTML")}.
 | 
					 | 
				
			||||||
				As they change the content of the host element.
 | 
					 | 
				
			||||||
			`),
 | 
					 | 
				
			||||||
			el(code, { content: `
 | 
					 | 
				
			||||||
				function Counter() {
 | 
					 | 
				
			||||||
					const count = S(0);
 | 
					 | 
				
			||||||
					return el("button",
 | 
					 | 
				
			||||||
						{ textContent: count, type: "button" },
 | 
					 | 
				
			||||||
						on("click", () => count.set(count.get() + 1)),
 | 
					 | 
				
			||||||
					);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			`, language: "js" }),
 | 
					 | 
				
			||||||
		),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", t`Identifying reactive elements in the DOM`),
 | 
							el("h4", t`Identifying reactive elements in the DOM`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -202,7 +187,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") }),
 | 
							el(example, { src: fileURL("./components/examples/signals/debugging-dom.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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,6 +14,7 @@ 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
 | 
				
			||||||
@@ -48,7 +49,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" }));
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Resource Cleanup with Abort Signals`),
 | 
							el(h3, t`Resource Cleanup with Abort Signals`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -82,7 +83,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));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Building Library-Independent Extensions`),
 | 
							el(h3, t`Building Library-Independent Extensions`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -103,7 +104,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
								});
 | 
													});
 | 
				
			||||||
							};
 | 
												};
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					`, language: "js" })
 | 
										`, page_id })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				el("div", { className: "tab" }).append(
 | 
									el("div", { className: "tab" }).append(
 | 
				
			||||||
					el("h5", t`⚠️ Library-Dependent`),
 | 
										el("h5", t`⚠️ Library-Dependent`),
 | 
				
			||||||
@@ -117,7 +118,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
								})(element);
 | 
													})(element);
 | 
				
			||||||
							};
 | 
												};
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					`, language: "js" })
 | 
										`, page_id })
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -176,7 +177,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					textContent: "All"
 | 
										textContent: "All"
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "callout" }).append(
 | 
							el("div", { className: "callout" }).append(
 | 
				
			||||||
			el("h4", t`Benefits of Signal Factories`),
 | 
								el("h4", t`Benefits of Signal Factories`),
 | 
				
			||||||
@@ -216,7 +217,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}\`);
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -258,7 +259,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
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
		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,6 +43,7 @@ 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
 | 
				
			||||||
@@ -59,7 +60,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") }),
 | 
							el(code, { src: fileURL("./components/examples/optimization/intro.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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`
 | 
				
			||||||
@@ -83,7 +84,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
								))
 | 
													))
 | 
				
			||||||
							);
 | 
												);
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					`, language: "js" })
 | 
										`, page_id })
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
				el("div").append(
 | 
									el("div").append(
 | 
				
			||||||
					el("h5", t`With dd<el>'s memo`),
 | 
										el("h5", t`With dd<el>'s memo`),
 | 
				
			||||||
@@ -101,7 +102,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
								)))
 | 
													)))
 | 
				
			||||||
							);
 | 
												);
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					`, language: "js" })
 | 
										`, page_id })
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
@@ -133,7 +134,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
						memo(todo.id, () =>
 | 
											memo(todo.id, () =>
 | 
				
			||||||
							el(TodoItem, todo)
 | 
												el(TodoItem, todo)
 | 
				
			||||||
			))))
 | 
								))))
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			The ${el("code", "memo")} function in this context:
 | 
								The ${el("code", "memo")} function in this context:
 | 
				
			||||||
@@ -145,7 +146,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") }),
 | 
							el(example, { src: fileURL("./components/examples/optimization/memo.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Creating Memoization Scopes`),
 | 
							el(h3, t`Creating Memoization Scopes`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -170,7 +171,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))
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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:
 | 
				
			||||||
@@ -187,7 +188,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				// Clear cache when signal is aborted
 | 
									// Clear cache when signal is aborted
 | 
				
			||||||
				signal: controller.signal
 | 
									signal: controller.signal
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
		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")}.
 | 
				
			||||||
@@ -316,7 +317,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
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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
 | 
				
			||||||
@@ -337,7 +338,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)))
 | 
				
			||||||
					)
 | 
										)
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
			`, language: "js" })
 | 
								`, page_id })
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,7 @@ 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
 | 
				
			||||||
@@ -68,7 +69,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" }),
 | 
							el(example, { src: fileURL("./components/examples/reallife/todomvc.js"), variant: "big", page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Application Architecture Overview`),
 | 
							el(h3, t`Application Architecture Overview`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -117,7 +118,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);
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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:
 | 
				
			||||||
@@ -206,7 +207,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				});
 | 
									});
 | 
				
			||||||
				return out;
 | 
									return out;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -240,7 +241,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					memo(todo.id, ()=> el(TodoItem, todo, onDelete, onEdit)))
 | 
										memo(todo.id, ()=> el(TodoItem, todo, onDelete, onEdit)))
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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,
 | 
				
			||||||
@@ -262,7 +263,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" }),
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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
 | 
				
			||||||
@@ -299,7 +300,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					// Component content...
 | 
										// Component content...
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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
 | 
				
			||||||
@@ -310,16 +311,16 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
			// Dynamic class attributes
 | 
								// Dynamic class attributes
 | 
				
			||||||
			el("a", {
 | 
								el("a", {
 | 
				
			||||||
				textContent,
 | 
									textContent: "All",
 | 
				
			||||||
				classList: { selected: S(()=> pageS.get() === textContent.toLowerCase()) },
 | 
									className: S(()=> pageS.get() === "all" ? "selected" : ""),
 | 
				
			||||||
				href: \`#\${textContent.toLowerCase()}\`
 | 
									href: "#"
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Reactive classList
 | 
								// Reactive classList
 | 
				
			||||||
			el("li", {
 | 
								el("li", {
 | 
				
			||||||
				classList: { completed: isCompleted, editing: isEditing }
 | 
									classList: { completed: isCompleted, editing: isEditing }
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -340,7 +341,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					memo(todo.id, ()=> el(TodoItem, todo, onDelete, onEdit)))
 | 
										memo(todo.id, ()=> el(TodoItem, todo, onDelete, onEdit)))
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			This approach ensures that:
 | 
								This approach ensures that:
 | 
				
			||||||
@@ -354,25 +355,18 @@ 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, ({ length }) => !length
 | 
								S.el(todosS, todos => memo(todos.length, length=> length
 | 
				
			||||||
				? el()
 | 
									? el("footer", { className: "footer" }).append(
 | 
				
			||||||
				: el("footer", { className: "footer" }).append(
 | 
										// Footer content...
 | 
				
			||||||
					// …
 | 
					 | 
				
			||||||
					memo("filters", ()=>
 | 
					 | 
				
			||||||
						// …
 | 
					 | 
				
			||||||
								el("a", {
 | 
					 | 
				
			||||||
									textContent,
 | 
					 | 
				
			||||||
									classList: { selected: S(()=> pageS.get() === textContent.toLowerCase()) },
 | 
					 | 
				
			||||||
									href: \`#\${textContent.toLowerCase()}\`
 | 
					 | 
				
			||||||
								})
 | 
					 | 
				
			||||||
					// …
 | 
					 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
 | 
									: el()
 | 
				
			||||||
			))
 | 
								))
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			We memoize the UI section and uses derived signal for the classList. Re-rendering this part is therefore
 | 
								By memoizing based on the todos length, the entire footer component is only re-rendered
 | 
				
			||||||
			unnecessary when the number of todos changes.
 | 
								when todos are added or removed, not when their properties change. This improves performance
 | 
				
			||||||
 | 
								by avoiding unnecessary DOM operations.
 | 
				
			||||||
		`),
 | 
							`),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
@@ -395,11 +389,9 @@ 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 =>
 | 
								const onDelete = on("todo:delete", ev => S.action(todosS, "delete", ev.detail));
 | 
				
			||||||
				S.action(todosS, "delete", /** @type {{ detail: Todo["id"] }} */(ev).detail));
 | 
								const onEdit = on("todo:edit", ev => S.action(todosS, "edit", ev.detail));
 | 
				
			||||||
			const onEdit = on("todo:edit", ev =>
 | 
							`, page_id }),
 | 
				
			||||||
				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`
 | 
				
			||||||
@@ -442,7 +434,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				// Component implementation...
 | 
									// Component implementation...
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -463,7 +455,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
			}).append(
 | 
								}).append(
 | 
				
			||||||
				// Component content...
 | 
									// Component content...
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			Benefits of using ${el("code", "classList")}:
 | 
								Benefits of using ${el("code", "classList")}:
 | 
				
			||||||
@@ -502,7 +494,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				value: title,
 | 
									value: title,
 | 
				
			||||||
				"data-id": id
 | 
									"data-id": id
 | 
				
			||||||
			}, onBlurEdit, onKeyDown, addFocus)
 | 
								}, onBlurEdit, onKeyDown, addFocus)
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
			This approach offers several advantages:
 | 
								This approach offers several advantages:
 | 
				
			||||||
@@ -530,26 +522,27 @@ 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()
 | 
									? el("main", { className: "main" }).append(
 | 
				
			||||||
				: el("main", { className: "main" }).append(
 | 
					 | 
				
			||||||
					// Main content with toggle all and todo list
 | 
										// Main content with toggle all and todo list
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
 | 
									: el()
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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()
 | 
									? el("form", null, onSubmitEdit).append(
 | 
				
			||||||
				: el("form", null, onSubmitEdit).append(
 | 
					 | 
				
			||||||
					el("input", {
 | 
										el("input", {
 | 
				
			||||||
						className: "edit",
 | 
											className: "edit",
 | 
				
			||||||
						name: formEdit,
 | 
											name: "edit",
 | 
				
			||||||
						value: title,
 | 
											value: title,
 | 
				
			||||||
 | 
											"data-id": id
 | 
				
			||||||
					}, onBlurEdit, onKeyDown, addFocus)
 | 
										}, onBlurEdit, onKeyDown, addFocus)
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
 | 
									: el()
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("h4", t`Conditional Clear Completed Button`),
 | 
							el("h4", t`Conditional Clear Completed Button`),
 | 
				
			||||||
		el(code, { content: `
 | 
							el(code, { content: `
 | 
				
			||||||
@@ -560,7 +553,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
						{ textContent: "Clear completed", className: "clear-completed" },
 | 
											{ textContent: "Clear completed", className: "clear-completed" },
 | 
				
			||||||
						onClearCompleted)
 | 
											onClearCompleted)
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "note" }).append(
 | 
							el("div", { className: "note" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -606,7 +599,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				if (event.key !== "Escape") return;
 | 
									if (event.key !== "Escape") return;
 | 
				
			||||||
				isEditing.set(false);
 | 
									isEditing.set(false);
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el("div", { className: "tip" }).append(
 | 
							el("div", { className: "tip" }).append(
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -637,7 +630,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 requestAnimationFrame
 | 
									${el("strong", "Focus Management:")} Reliable input focus with setTimeout
 | 
				
			||||||
			`),
 | 
								`),
 | 
				
			||||||
			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,6 +14,7 @@ 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`
 | 
				
			||||||
@@ -44,7 +45,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				than jsdom
 | 
									than jsdom
 | 
				
			||||||
			`),
 | 
								`),
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		el(code, { src: fileURL("./components/examples/ssr/intro.js") }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/intro.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Why Server-Side Rendering?`),
 | 
							el(h3, t`Why Server-Side Rendering?`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -70,27 +71,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") }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/start.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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") }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/basic-example.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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") }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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") }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/async-data.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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`
 | 
				
			||||||
@@ -118,7 +119,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") }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/pages.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`SSR Considerations and Limitations`),
 | 
							el(h3, t`SSR Considerations and Limitations`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -140,7 +141,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") }),
 | 
							el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js"), page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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,8 +1,9 @@
 | 
				
			|||||||
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`Server-Side Pre-Rendering and Client-Side Rehydration`,
 | 
						fullTitle: t`Interactive Demo Components with Server-Side Pre-Rendering`,
 | 
				
			||||||
	description: t`Using Ireland components for server-side pre-rendering and client-side rehydration`,
 | 
						description: t`Creating live, interactive component examples in documentation with server-side
 | 
				
			||||||
 | 
							rendering and client-side hydration.`,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { el } from "deka-dom-el";
 | 
					import { el } from "deka-dom-el";
 | 
				
			||||||
@@ -15,6 +16,7 @@ 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`
 | 
				
			||||||
@@ -65,7 +67,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",
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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:
 | 
				
			||||||
@@ -117,7 +119,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			// Final build step - trigger SSR end event
 | 
								// Final build step - trigger SSR end event
 | 
				
			||||||
			dispatchEvent("onssrend");
 | 
								dispatchEvent("onssrend");
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
		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
 | 
				
			||||||
@@ -143,7 +145,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);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
		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
 | 
				
			||||||
@@ -224,7 +226,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					\`.trim())
 | 
										\`.trim())
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
		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
 | 
				
			||||||
@@ -248,7 +250,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				});
 | 
									});
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Live Example`),
 | 
							el(h3, t`Live Example`),
 | 
				
			||||||
			el("p").append(T`
 | 
								el("p").append(T`
 | 
				
			||||||
@@ -257,10 +259,14 @@ export function page({ pkg, info }){
 | 
				
			|||||||
				rendered with the Ireland component system:
 | 
									rendered with the Ireland component system:
 | 
				
			||||||
			`),
 | 
								`),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			el(code, { src: fileURL("./components/examples/ireland-test/counter.js") }),
 | 
								el(code, {
 | 
				
			||||||
 | 
									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,6 +40,7 @@ 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,
 | 
				
			||||||
@@ -165,7 +166,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
					className: S(() => countS.get() > 10 ? 'warning' : '')
 | 
										className: S(() => countS.get() > 10 ? 'warning' : '')
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		`, language: "js" }),
 | 
							`, page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Key Concepts Reference`),
 | 
							el(h3, t`Key Concepts Reference`),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ 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
 | 
				
			||||||
@@ -27,7 +28,7 @@ export function page({ pkg, info }){
 | 
				
			|||||||
		),
 | 
							),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// The actual converter component
 | 
							// The actual converter component
 | 
				
			||||||
		el(converter),
 | 
							el(converter, { page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Next Steps`),
 | 
							el(h3, t`Next Steps`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ 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>:
 | 
				
			||||||
@@ -24,21 +25,23 @@ 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" }),
 | 
							el(example, { src: fileURL("./components/examples/case-studies/data-dashboard.js"), variant: "big", page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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" }),
 | 
							el(example, { src: fileURL("./components/examples/case-studies/interactive-form.js"), variant: "big", page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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" }),
 | 
							el(example, { src: fileURL("./components/examples/case-studies/image-gallery.js"), variant: "big", page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		el(h3, t`Task Manager`),
 | 
							el(h3, t`Task Manager`),
 | 
				
			||||||
		el("p").append(T`
 | 
							el("p").append(T`
 | 
				
			||||||
@@ -46,29 +49,8 @@ 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" }),
 | 
							el(example, { src: fileURL("./components/examples/case-studies/task-manager.js"), variant: "big", page_id }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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,8 +1,4 @@
 | 
				
			|||||||
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/",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										69
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										69
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1,12 +1,12 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"name": "deka-dom-el",
 | 
						"name": "deka-dom-el",
 | 
				
			||||||
	"version": "0.9.4-alpha",
 | 
						"version": "0.9.3-alpha",
 | 
				
			||||||
	"lockfileVersion": 3,
 | 
						"lockfileVersion": 3,
 | 
				
			||||||
	"requires": true,
 | 
						"requires": true,
 | 
				
			||||||
	"packages": {
 | 
						"packages": {
 | 
				
			||||||
		"": {
 | 
							"": {
 | 
				
			||||||
			"name": "deka-dom-el",
 | 
								"name": "deka-dom-el",
 | 
				
			||||||
			"version": "0.9.4-alpha",
 | 
								"version": "0.9.3-alpha",
 | 
				
			||||||
			"license": "MIT",
 | 
								"license": "MIT",
 | 
				
			||||||
			"devDependencies": {
 | 
								"devDependencies": {
 | 
				
			||||||
				"@size-limit/preset-small-lib": "~11.2",
 | 
									"@size-limit/preset-small-lib": "~11.2",
 | 
				
			||||||
@@ -15,8 +15,7 @@
 | 
				
			|||||||
				"esbuild": "~0.25",
 | 
									"esbuild": "~0.25",
 | 
				
			||||||
				"jsdom": "~26.0",
 | 
									"jsdom": "~26.0",
 | 
				
			||||||
				"jshint": "~2.13",
 | 
									"jshint": "~2.13",
 | 
				
			||||||
				"nodejsscript": "^1.0",
 | 
									"nodejsscript": "^1.0.2",
 | 
				
			||||||
				"publint": "^0.3",
 | 
					 | 
				
			||||||
				"size-limit-node-esbuild": "~0.3"
 | 
									"size-limit-node-esbuild": "~0.3"
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			"engines": {
 | 
								"engines": {
 | 
				
			||||||
@@ -615,19 +614,6 @@
 | 
				
			|||||||
				"node": ">= 8"
 | 
									"node": ">= 8"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"node_modules/@publint/pack": {
 | 
					 | 
				
			||||||
			"version": "0.1.2",
 | 
					 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/@publint/pack/-/pack-0.1.2.tgz",
 | 
					 | 
				
			||||||
			"integrity": "sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw==",
 | 
					 | 
				
			||||||
			"dev": true,
 | 
					 | 
				
			||||||
			"license": "MIT",
 | 
					 | 
				
			||||||
			"engines": {
 | 
					 | 
				
			||||||
				"node": ">=18"
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			"funding": {
 | 
					 | 
				
			||||||
				"url": "https://bjornlu.com/sponsor"
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"node_modules/@sindresorhus/merge-streams": {
 | 
							"node_modules/@sindresorhus/merge-streams": {
 | 
				
			||||||
			"version": "2.3.0",
 | 
								"version": "2.3.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
 | 
				
			||||||
@@ -2243,16 +2229,6 @@
 | 
				
			|||||||
				"node": ">=4"
 | 
									"node": ">=4"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"node_modules/package-manager-detector": {
 | 
					 | 
				
			||||||
			"version": "0.2.11",
 | 
					 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz",
 | 
					 | 
				
			||||||
			"integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==",
 | 
					 | 
				
			||||||
			"dev": true,
 | 
					 | 
				
			||||||
			"license": "MIT",
 | 
					 | 
				
			||||||
			"dependencies": {
 | 
					 | 
				
			||||||
				"quansync": "^0.2.7"
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"node_modules/parse5": {
 | 
							"node_modules/parse5": {
 | 
				
			||||||
			"version": "7.2.1",
 | 
								"version": "7.2.1",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
 | 
								"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
 | 
				
			||||||
@@ -2330,28 +2306,6 @@
 | 
				
			|||||||
				"url": "https://github.com/sponsors/jonschlinkert"
 | 
									"url": "https://github.com/sponsors/jonschlinkert"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"node_modules/publint": {
 | 
					 | 
				
			||||||
			"version": "0.3.9",
 | 
					 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/publint/-/publint-0.3.9.tgz",
 | 
					 | 
				
			||||||
			"integrity": "sha512-irTwfRfYW38vomkxxoiZQtFtUOQKpz5m0p9Z60z4xpXrl1KmvSrX1OMARvnnolB5usOXeNfvLj6d/W3rwXKfBQ==",
 | 
					 | 
				
			||||||
			"dev": true,
 | 
					 | 
				
			||||||
			"license": "MIT",
 | 
					 | 
				
			||||||
			"dependencies": {
 | 
					 | 
				
			||||||
				"@publint/pack": "^0.1.2",
 | 
					 | 
				
			||||||
				"package-manager-detector": "^0.2.9",
 | 
					 | 
				
			||||||
				"picocolors": "^1.1.1",
 | 
					 | 
				
			||||||
				"sade": "^1.8.1"
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			"bin": {
 | 
					 | 
				
			||||||
				"publint": "src/cli.js"
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			"engines": {
 | 
					 | 
				
			||||||
				"node": ">=18"
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			"funding": {
 | 
					 | 
				
			||||||
				"url": "https://bjornlu.com/sponsor"
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"node_modules/punycode": {
 | 
							"node_modules/punycode": {
 | 
				
			||||||
			"version": "2.3.1",
 | 
								"version": "2.3.1",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
 | 
								"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
 | 
				
			||||||
@@ -2362,23 +2316,6 @@
 | 
				
			|||||||
				"node": ">=6"
 | 
									"node": ">=6"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"node_modules/quansync": {
 | 
					 | 
				
			||||||
			"version": "0.2.10",
 | 
					 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz",
 | 
					 | 
				
			||||||
			"integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==",
 | 
					 | 
				
			||||||
			"dev": true,
 | 
					 | 
				
			||||||
			"funding": [
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					"type": "individual",
 | 
					 | 
				
			||||||
					"url": "https://github.com/sponsors/antfu"
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					"type": "individual",
 | 
					 | 
				
			||||||
					"url": "https://github.com/sponsors/sxzz"
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			],
 | 
					 | 
				
			||||||
			"license": "MIT"
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"node_modules/queue-microtask": {
 | 
							"node_modules/queue-microtask": {
 | 
				
			||||||
			"version": "1.2.3",
 | 
								"version": "1.2.3",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
 | 
								"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								package.json
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"name": "deka-dom-el",
 | 
						"name": "deka-dom-el",
 | 
				
			||||||
	"version": "0.9.5-alpha",
 | 
						"version": "0.9.3-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",
 | 
				
			||||||
@@ -17,20 +17,20 @@
 | 
				
			|||||||
	"type": "module",
 | 
						"type": "module",
 | 
				
			||||||
	"exports": {
 | 
						"exports": {
 | 
				
			||||||
		".": {
 | 
							".": {
 | 
				
			||||||
			"types": "./index.d.ts",
 | 
								"import": "./index.js",
 | 
				
			||||||
			"import": "./index.js"
 | 
								"types": "./index.d.ts"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"./signals": {
 | 
							"./signals": {
 | 
				
			||||||
			"types": "./signals.d.ts",
 | 
								"import": "./signals.js",
 | 
				
			||||||
			"import": "./signals.js"
 | 
								"types": "./signals.d.ts"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"./jsdom": {
 | 
							"./jsdom": {
 | 
				
			||||||
			"types": "./jsdom.d.ts",
 | 
								"import": "./jsdom.js",
 | 
				
			||||||
			"import": "./jsdom.js"
 | 
								"types": "./jsdom.d.ts"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"./src/signals-lib": {
 | 
							"./src/signals-lib": {
 | 
				
			||||||
			"types": "./src/signals-lib/signals-lib.d.ts",
 | 
								"import": "./src/signals-lib/signals-lib.js",
 | 
				
			||||||
			"import": "./src/signals-lib/signals-lib.js"
 | 
								"types": "./src/signals-lib/signals-lib.d.ts"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"files": [
 | 
						"files": [
 | 
				
			||||||
@@ -52,6 +52,7 @@
 | 
				
			|||||||
		"maxdepth": 3,
 | 
							"maxdepth": 3,
 | 
				
			||||||
		"maxcomplexity": 14,
 | 
							"maxcomplexity": 14,
 | 
				
			||||||
		"globals": {
 | 
							"globals": {
 | 
				
			||||||
 | 
								"requestIdleCallback": false,
 | 
				
			||||||
			"AbortController": false,
 | 
								"AbortController": false,
 | 
				
			||||||
			"AbortSignal": false,
 | 
								"AbortSignal": false,
 | 
				
			||||||
			"FinalizationRegistry": false
 | 
								"FinalizationRegistry": false
 | 
				
			||||||
@@ -103,8 +104,7 @@
 | 
				
			|||||||
		"esbuild": "~0.25",
 | 
							"esbuild": "~0.25",
 | 
				
			||||||
		"jsdom": "~26.0",
 | 
							"jsdom": "~26.0",
 | 
				
			||||||
		"jshint": "~2.13",
 | 
							"jshint": "~2.13",
 | 
				
			||||||
		"nodejsscript": "^1.0",
 | 
							"nodejsscript": "^1.0.2",
 | 
				
			||||||
		"publint": "^0.3",
 | 
					 | 
				
			||||||
		"size-limit-node-esbuild": "~0.3"
 | 
							"size-limit-node-esbuild": "~0.3"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,9 +51,9 @@ export function createElement(tag, attributes, ...addons){
 | 
				
			|||||||
			const host= (...c)=> !c.length ? el_host :
 | 
								const host= (...c)=> !c.length ? el_host :
 | 
				
			||||||
				(scoped===1 ? addons.unshift(...c) : c.forEach(c=> c(el_host)), undefined);
 | 
									(scoped===1 ? addons.unshift(...c) : c.forEach(c=> c(el_host)), undefined);
 | 
				
			||||||
			scope.push({ scope: tag, host });
 | 
								scope.push({ scope: tag, host });
 | 
				
			||||||
			el= /** @type {Element} */(tag(attributes || undefined));
 | 
								el= tag(attributes || undefined);
 | 
				
			||||||
			if(el.nodeName==="#comment") break;
 | 
					 | 
				
			||||||
			const is_fragment= isInstance(el, env.F);
 | 
								const is_fragment= isInstance(el, env.F);
 | 
				
			||||||
 | 
								if(el.nodeName==="#comment") break;
 | 
				
			||||||
			const el_mark= createElement.mark({
 | 
								const el_mark= createElement.mark({
 | 
				
			||||||
				type: "component",
 | 
									type: "component",
 | 
				
			||||||
				name: tag.name,
 | 
									name: tag.name,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,13 +2,14 @@ import { queueSignalWrite, mark } from "./helpers.js";
 | 
				
			|||||||
export { mark };
 | 
					export { mark };
 | 
				
			||||||
import { hasOwn, oCreate, oAssign, requestIdle } from "../helpers.js";
 | 
					import { hasOwn, oCreate, oAssign, requestIdle } from "../helpers.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SignalReadOnly= oCreate(null, {
 | 
					const Signal = 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 Signal = oCreate(SignalReadOnly, {
 | 
					const SignalReadOnly= oCreate(Signal, {
 | 
				
			||||||
	set: { value(...v){ return write(this, ...v); } },
 | 
						set: { value(){ return; } },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Checks if a value is a signal
 | 
					 * Checks if a value is a signal
 | 
				
			||||||
@@ -313,14 +314,9 @@ 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){
 | 
				
			||||||
		const is_first= !element[key_reactive];
 | 
							if(!element[key_reactive]) element[key_reactive]= [];
 | 
				
			||||||
		if(is_first) element[key_reactive]= [];
 | 
					 | 
				
			||||||
		element[key_reactive].push([ [ s, listener ], ...notes ]);
 | 
							element[key_reactive].push([ [ s, listener ], ...notes ]);
 | 
				
			||||||
		if(
 | 
							if(current.prevent) return; // typically document.body, doenst need auto-remove as it should happen on page leave
 | 
				
			||||||
			!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. */
 | 
				
			||||||
@@ -348,7 +344,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);
 | 
						const SI= toSignal(varS, value, actions, is_readonly);
 | 
				
			||||||
	cleanUpRegistry.register(SI, SI[mark]);
 | 
						cleanUpRegistry.register(SI, SI[mark]);
 | 
				
			||||||
	return SI;
 | 
						return SI;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -371,10 +367,11 @@ 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){
 | 
					function toSignal(s, value, actions, readonly= false){
 | 
				
			||||||
	const onclear= [];
 | 
						const onclear= [];
 | 
				
			||||||
	if(typeOf(actions)!=="[object Object]")
 | 
						if(typeOf(actions)!=="[object Object]")
 | 
				
			||||||
		actions= {};
 | 
							actions= {};
 | 
				
			||||||
@@ -388,6 +385,7 @@ function toSignal(s, value, actions){
 | 
				
			|||||||
		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