mirror of
				https://github.com/jaandrle/deka-dom-el
				synced 2025-11-03 22:59:16 +01:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			v0.9.4-alp
			...
			v0.9.5-alp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f2c85ec983 | 
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
				
			|||||||
**Alpha**
 | 
					**Alpha**
 | 
				
			||||||
| [Docs](https://jaandrle.github.io/deka-dom-el "Official documentation and guide site")
 | 
					| [Docs&Examples](https://jaandrle.github.io/deka-dom-el "Official documentation and guide site")
 | 
				
			||||||
| [NPM](https://www.npmjs.com/package/deka-dom-el "Official NPM package page")
 | 
					| [NPM](https://www.npmjs.com/package/deka-dom-el "Official NPM package page")
 | 
				
			||||||
| [GitHub](https://github.com/jaandrle/deka-dom-el "Official GitHub repository")
 | 
					| [GitHub](https://github.com/jaandrle/deka-dom-el "Official GitHub repository")
 | 
				
			||||||
([*Gitea*](https://gitea.jaandrle.cz/jaandrle/deka-dom-el "GitHub repository mirror on my own Gitea instance"))
 | 
					([*Gitea*](https://gitea.jaandrle.cz/jaandrle/deka-dom-el "GitHub repository mirror on my own Gitea instance"))
 | 
				
			||||||
@@ -9,7 +9,7 @@
 | 
				
			|||||||
```javascript
 | 
					```javascript
 | 
				
			||||||
// 🌟 Reactive component with clear separation of concerns
 | 
					// 🌟 Reactive component with clear separation of concerns
 | 
				
			||||||
document.body.append(
 | 
					document.body.append(
 | 
				
			||||||
	el(EmojiCounter, { initial: "🚀" })
 | 
						el(EmojiCounter, { initial: "🚀" }),
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function EmojiCounter({ initial }) {
 | 
					function EmojiCounter({ initial }) {
 | 
				
			||||||
@@ -34,7 +34,7 @@ function EmojiCounter({ initial }) {
 | 
				
			|||||||
			el(Option, "🎉"),
 | 
								el(Option, "🎉"),
 | 
				
			||||||
			el(Option, "🚀"),
 | 
								el(Option, "🚀"),
 | 
				
			||||||
			el(Option, "💖"),
 | 
								el(Option, "💖"),
 | 
				
			||||||
		)
 | 
							),
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function Option({ textContent }){
 | 
					function Option({ textContent }){
 | 
				
			||||||
@@ -56,10 +56,10 @@ 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
 | 
				
			||||||
- ☑️ **Lightweight** — ~10-15kB minified (original goal 10kB) with **zero**/minimal dependencies
 | 
					- ✅ **Minimalized footprint** — ~10-15kB minified bundle (original goal 10kB), **zero**/minimal dependencies and
 | 
				
			||||||
- ✅ **Declarative & functional approach** for clean, maintainable code
 | 
						small in-memory size (auto-releasing resources as much as possible)
 | 
				
			||||||
 | 
					- ✅ **Declarative & functional approach support** for clean, maintainable code
 | 
				
			||||||
- ✅ **Signals and events** for reactive UI
 | 
					- ✅ **Signals and events** for reactive UI
 | 
				
			||||||
- ✅ **Auto-releasing resources** for memory management but nice development experience
 | 
					 | 
				
			||||||
- ✅ **Memoization for performance** — optimize rendering with intelligent caching
 | 
					- ✅ **Memoization for performance** — optimize rendering with intelligent caching
 | 
				
			||||||
- ☑️ **Optional build-in signals** with support for custom reactive implementations (#39)
 | 
					- ☑️ **Optional build-in signals** with support for custom reactive implementations (#39)
 | 
				
			||||||
- ☑️ **Server-side rendering** support via [jsdom](https://github.com/jsdom/jsdom)
 | 
					- ☑️ **Server-side rendering** support via [jsdom](https://github.com/jsdom/jsdom)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,3 +9,4 @@ 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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								dist/esm-with-signals.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								dist/esm-with-signals.js
									
									
									
									
										vendored
									
									
								
							@@ -430,9 +430,10 @@ 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 = tag(attributes || void 0);
 | 
								el = /** @type {Element} */
 | 
				
			||||||
			const is_fragment = isInstance(el, enviroment.F);
 | 
								tag(attributes || void 0);
 | 
				
			||||||
			if (el.nodeName === "#comment") break;
 | 
								if (el.nodeName === "#comment") break;
 | 
				
			||||||
 | 
								const is_fragment = isInstance(el, enviroment.F);
 | 
				
			||||||
			const el_mark = createElement.mark({
 | 
								const el_mark = createElement.mark({
 | 
				
			||||||
				type: "component",
 | 
									type: "component",
 | 
				
			||||||
				name: tag.name,
 | 
									name: tag.name,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								dist/esm-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								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,9 +414,10 @@ 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 = tag(attributes || void 0);
 | 
								el = /** @type {Element} */
 | 
				
			||||||
			const is_fragment = isInstance(el, enviroment.F);
 | 
								tag(attributes || void 0);
 | 
				
			||||||
			if (el.nodeName === "#comment") break;
 | 
								if (el.nodeName === "#comment") break;
 | 
				
			||||||
 | 
								const is_fragment = isInstance(el, enviroment.F);
 | 
				
			||||||
			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
											
										
									
								
							
							
								
								
									
										5
									
								
								dist/iife-with-signals.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								dist/iife-with-signals.js
									
									
									
									
										vendored
									
									
								
							@@ -475,9 +475,10 @@ 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 = tag(attributes || void 0);
 | 
									el = /** @type {Element} */
 | 
				
			||||||
				const is_fragment = isInstance(el, enviroment.F);
 | 
									tag(attributes || void 0);
 | 
				
			||||||
				if (el.nodeName === "#comment") break;
 | 
									if (el.nodeName === "#comment") break;
 | 
				
			||||||
 | 
									const is_fragment = isInstance(el, enviroment.F);
 | 
				
			||||||
				const el_mark = createElement.mark({
 | 
									const el_mark = createElement.mark({
 | 
				
			||||||
					type: "component",
 | 
										type: "component",
 | 
				
			||||||
					name: tag.name,
 | 
										name: tag.name,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								dist/iife-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								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,9 +456,10 @@ 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 = tag(attributes || void 0);
 | 
									el = /** @type {Element} */
 | 
				
			||||||
				const is_fragment = isInstance(el, enviroment.F);
 | 
									tag(attributes || void 0);
 | 
				
			||||||
				if (el.nodeName === "#comment") break;
 | 
									if (el.nodeName === "#comment") break;
 | 
				
			||||||
 | 
									const is_fragment = isInstance(el, enviroment.F);
 | 
				
			||||||
				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
											
										
									
								
							@@ -82,10 +82,10 @@ export function ImageGallery(images= imagesSample) {
 | 
				
			|||||||
				closeLightbox();
 | 
									closeLightbox();
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			case 'ArrowLeft':
 | 
								case 'ArrowLeft':
 | 
				
			||||||
				document.querySelector('.lightbox-prev-btn').click();
 | 
									onPrevImage(e);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			case 'ArrowRight':
 | 
								case 'ArrowRight':
 | 
				
			||||||
				document.querySelector('.lightbox-next-btn').click();
 | 
									onNextImage(e);
 | 
				
			||||||
				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()
 | 
				
			||||||
						}).append(
 | 
											}, onImageClick(image.id)).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)
 | 
				
			||||||
@@ -146,41 +146,41 @@ export function ImageGallery(images= imagesSample) {
 | 
				
			|||||||
		S.el(isLightboxOpen, open => !open
 | 
							S.el(isLightboxOpen, open => !open
 | 
				
			||||||
			? el()
 | 
								? el()
 | 
				
			||||||
			: el("div", { className: "lightbox-overlay" }, on("click", closeLightbox)).append(
 | 
								: el("div", { className: "lightbox-overlay" }, on("click", closeLightbox)).append(
 | 
				
			||||||
					el("div", {
 | 
									el("div", {
 | 
				
			||||||
						className: "lightbox-content",
 | 
										className: "lightbox-content",
 | 
				
			||||||
						onClick: e => e.stopPropagation() // Prevent closing when clicking inside
 | 
										onClick: e => e.stopPropagation() // Prevent closing when clicking inside
 | 
				
			||||||
					}).append(
 | 
									}).append(
 | 
				
			||||||
						el("button", {
 | 
										el("button", {
 | 
				
			||||||
							className: "lightbox-close-btn",
 | 
											className: "lightbox-close-btn",
 | 
				
			||||||
							"aria-label": "Close lightbox"
 | 
											ariaLabel: "Close lightbox",
 | 
				
			||||||
						}, on("click", closeLightbox)).append("×"),
 | 
										}, on("click", closeLightbox)).append("×"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						el("button", {
 | 
										el("button", {
 | 
				
			||||||
							className: "lightbox-prev-btn",
 | 
											className: "lightbox-prev-btn",
 | 
				
			||||||
							"aria-label": "Previous image"
 | 
											ariaLabel: "Previous image",
 | 
				
			||||||
						}, on("click", onPrevImage)).append("❮"),
 | 
										}, on("click", onPrevImage)).append("❮"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						el("button", {
 | 
										el("button", {
 | 
				
			||||||
							className: "lightbox-next-btn",
 | 
											className: "lightbox-next-btn",
 | 
				
			||||||
							"aria-label": "Next image"
 | 
											ariaLabel: "Next image",
 | 
				
			||||||
						}, on("click", onNextImage)).append("❯"),
 | 
										}, on("click", onNextImage)).append("❯"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						S.el(selectedImage, img => !img
 | 
										S.el(selectedImage, img => !img
 | 
				
			||||||
							? el()
 | 
											? el()
 | 
				
			||||||
							: el("div", { className: "lightbox-image-container" }).append(
 | 
											: el("div", { className: "lightbox-image-container" }).append(
 | 
				
			||||||
								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,9 +1,13 @@
 | 
				
			|||||||
import { el, on } from "deka-dom-el";
 | 
					import { el, on, scope } from "deka-dom-el";
 | 
				
			||||||
import { S } from "deka-dom-el/signals";
 | 
					import { S } from "deka-dom-el/signals";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function ProductCatalog() {
 | 
					export function ProductCatalog() {
 | 
				
			||||||
 | 
						const { signal }= scope;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const itemsPerPage = 5;
 | 
						const itemsPerPage = 5;
 | 
				
			||||||
	const products = asyncSignal(S, fetchProducts, { initial: [], keepLast: true });
 | 
						const products = asyncSignal(S,
 | 
				
			||||||
 | 
							fetchProducts,
 | 
				
			||||||
 | 
							{ initial: [], keepLast: true, signal });
 | 
				
			||||||
	const searchTerm = S("");
 | 
						const searchTerm = S("");
 | 
				
			||||||
	const handleSearch = (e) => searchTerm.set(e.target.value);
 | 
						const handleSearch = (e) => searchTerm.set(e.target.value);
 | 
				
			||||||
	const sortOrder = S("default");
 | 
						const sortOrder = S("default");
 | 
				
			||||||
@@ -220,10 +224,14 @@ function simulateNetworkDelay(min = 300, max = 1200) {
 | 
				
			|||||||
 * @template T
 | 
					 * @template T
 | 
				
			||||||
 * @param {typeof S} S - Signal constructor
 | 
					 * @param {typeof S} S - Signal constructor
 | 
				
			||||||
 * @param {(params: { signal: AbortSignal }) => Promise<T>} invoker - Async function to execute
 | 
					 * @param {(params: { signal: AbortSignal }) => Promise<T>} invoker - Async function to execute
 | 
				
			||||||
 * @param {{ initial?: T, keepLast?: boolean }} options - Configuration options
 | 
					 * @param {{ initial?: T, keepLast?: boolean, signal?: AbortSignal }} options - Configuration options
 | 
				
			||||||
 * @returns {Object} Status signals and control methods
 | 
					 * @returns {Object} Status signals and control methods
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function asyncSignal(S, invoker, { initial, keepLast } = {}) {
 | 
					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
 | 
						// Status tracking signals
 | 
				
			||||||
	const status = S("pending");
 | 
						const status = S("pending");
 | 
				
			||||||
	const result = S(initial);
 | 
						const result = S(initial);
 | 
				
			||||||
@@ -242,7 +250,7 @@ export function asyncSignal(S, invoker, { initial, keepLast } = {}) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			const data = await invoker({
 | 
								const data = await invoker({
 | 
				
			||||||
				signal: controller.signal,
 | 
									signal: anySignal(controller.signal),
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
			if (!controller.signal.aborted) {
 | 
								if (!controller.signal.aborted) {
 | 
				
			||||||
				status.set("resolved");
 | 
									status.set("resolved");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,13 +74,11 @@ export function h3({ textContent, id }){
 | 
				
			|||||||
		if(!id) id= "h-"+textContent.toLowerCase().replaceAll(/\s/g, "-").replaceAll(/[^a-z-]/g, "");
 | 
							if(!id) id= "h-"+textContent.toLowerCase().replaceAll(/\s/g, "-").replaceAll(/[^a-z-]/g, "");
 | 
				
			||||||
		return el("h3", { id }).append(
 | 
							return el("h3", { id }).append(
 | 
				
			||||||
				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,
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,12 @@ 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`Lightweight core (~10–15kB minified) without unnecessary dependencies (0 at now 😇)`),
 | 
									el("li", t`Minimalized footprint:`),
 | 
				
			||||||
 | 
									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 (but optional) reactivity with simplified but powerful signals system`),
 | 
				
			||||||
				el("li", t`Clean code organization with the 3PS pattern`)
 | 
									el("li", t`Clean code organization with the 3PS pattern`)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -151,7 +151,7 @@ export function header({ info: { href, title, description }, pkg }){
 | 
				
			|||||||
				),
 | 
									),
 | 
				
			||||||
				el("span", {
 | 
									el("span", {
 | 
				
			||||||
					className: "version-badge",
 | 
										className: "version-badge",
 | 
				
			||||||
					"aria-label": "Version",
 | 
										ariaLabel: "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",
 | 
				
			||||||
		"aria-label": "Main navigation",
 | 
							ariaLabel: "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",
 | 
				
			||||||
			"aria-label": "View on GitHub",
 | 
								ariaLabel: "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}`,
 | 
				
			||||||
				"aria-current": isCurrent ? "page" : null,
 | 
									ariaCurrent: isCurrent ? "page" : null,
 | 
				
			||||||
			}).append(
 | 
								}).append(
 | 
				
			||||||
				el("span", {
 | 
									el("span", {
 | 
				
			||||||
					className: "nav-number",
 | 
										className: "nav-number",
 | 
				
			||||||
					"aria-hidden": "true",
 | 
										ariaHidden: "true",
 | 
				
			||||||
					textContent: `${i+1}. `
 | 
										textContent: `${i+1}. `
 | 
				
			||||||
				}),
 | 
									}),
 | 
				
			||||||
				p.title
 | 
									p.title
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -168,6 +168,22 @@ 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`
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										65
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										65
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -15,7 +15,8 @@
 | 
				
			|||||||
				"esbuild": "~0.25",
 | 
									"esbuild": "~0.25",
 | 
				
			||||||
				"jsdom": "~26.0",
 | 
									"jsdom": "~26.0",
 | 
				
			||||||
				"jshint": "~2.13",
 | 
									"jshint": "~2.13",
 | 
				
			||||||
				"nodejsscript": "^1.0.2",
 | 
									"nodejsscript": "^1.0",
 | 
				
			||||||
 | 
									"publint": "^0.3",
 | 
				
			||||||
				"size-limit-node-esbuild": "~0.3"
 | 
									"size-limit-node-esbuild": "~0.3"
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			"engines": {
 | 
								"engines": {
 | 
				
			||||||
@@ -614,6 +615,19 @@
 | 
				
			|||||||
				"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",
 | 
				
			||||||
@@ -2229,6 +2243,16 @@
 | 
				
			|||||||
				"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",
 | 
				
			||||||
@@ -2306,6 +2330,28 @@
 | 
				
			|||||||
				"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",
 | 
				
			||||||
@@ -2316,6 +2362,23 @@
 | 
				
			|||||||
				"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",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								package.json
									
									
									
									
									
								
							@@ -1,10 +1,10 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"name": "deka-dom-el",
 | 
						"name": "deka-dom-el",
 | 
				
			||||||
	"version": "0.9.4-alpha",
 | 
						"version": "0.9.5-alpha",
 | 
				
			||||||
	"description": "A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks.",
 | 
						"description": "A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks.",
 | 
				
			||||||
	"author": "Jan Andrle <andrle.jan@centrum.cz>",
 | 
						"author": "Jan Andrle <andrle.jan@centrum.cz>",
 | 
				
			||||||
	"license": "MIT",
 | 
						"license": "MIT",
 | 
				
			||||||
	"homepage": "https://jaandrle.github.io/deka-dom-el/",
 | 
						"homepage": "https://github.com/jaandrle/deka-dom-el",
 | 
				
			||||||
	"repository": {
 | 
						"repository": {
 | 
				
			||||||
		"type": "git",
 | 
							"type": "git",
 | 
				
			||||||
		"url": "git+ssh://git@github.com/jaandrle/deka-dom-el.git"
 | 
							"url": "git+ssh://git@github.com/jaandrle/deka-dom-el.git"
 | 
				
			||||||
@@ -17,20 +17,20 @@
 | 
				
			|||||||
	"type": "module",
 | 
						"type": "module",
 | 
				
			||||||
	"exports": {
 | 
						"exports": {
 | 
				
			||||||
		".": {
 | 
							".": {
 | 
				
			||||||
			"import": "./index.js",
 | 
								"types": "./index.d.ts",
 | 
				
			||||||
			"types": "./index.d.ts"
 | 
								"import": "./index.js"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"./signals": {
 | 
							"./signals": {
 | 
				
			||||||
			"import": "./signals.js",
 | 
								"types": "./signals.d.ts",
 | 
				
			||||||
			"types": "./signals.d.ts"
 | 
								"import": "./signals.js"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"./jsdom": {
 | 
							"./jsdom": {
 | 
				
			||||||
			"import": "./jsdom.js",
 | 
								"types": "./jsdom.d.ts",
 | 
				
			||||||
			"types": "./jsdom.d.ts"
 | 
								"import": "./jsdom.js"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"./src/signals-lib": {
 | 
							"./src/signals-lib": {
 | 
				
			||||||
			"import": "./src/signals-lib/signals-lib.js",
 | 
								"types": "./src/signals-lib/signals-lib.d.ts",
 | 
				
			||||||
			"types": "./src/signals-lib/signals-lib.d.ts"
 | 
								"import": "./src/signals-lib/signals-lib.js"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"files": [
 | 
						"files": [
 | 
				
			||||||
@@ -103,7 +103,8 @@
 | 
				
			|||||||
		"esbuild": "~0.25",
 | 
							"esbuild": "~0.25",
 | 
				
			||||||
		"jsdom": "~26.0",
 | 
							"jsdom": "~26.0",
 | 
				
			||||||
		"jshint": "~2.13",
 | 
							"jshint": "~2.13",
 | 
				
			||||||
		"nodejsscript": "^1.0.2",
 | 
							"nodejsscript": "^1.0",
 | 
				
			||||||
 | 
							"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= tag(attributes || undefined);
 | 
								el= /** @type {Element} */(tag(attributes || undefined));
 | 
				
			||||||
			const is_fragment= isInstance(el, env.F);
 | 
					 | 
				
			||||||
			if(el.nodeName==="#comment") break;
 | 
								if(el.nodeName==="#comment") break;
 | 
				
			||||||
 | 
								const is_fragment= isInstance(el, env.F);
 | 
				
			||||||
			const el_mark= createElement.mark({
 | 
								const el_mark= createElement.mark({
 | 
				
			||||||
				type: "component",
 | 
									type: "component",
 | 
				
			||||||
				name: tag.name,
 | 
									name: tag.name,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user