mirror of
				https://github.com/jaandrle/deka-dom-el
				synced 2025-11-03 22:59:16 +01:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			3301a6600c
			...
			330f702409
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						330f702409
	
				 | 
					
					
						|||
| 
						
						
							
						
						2f57f115a0
	
				 | 
					
					
						|||
| 
						
						
							
						
						d1b017c1a1
	
				 | 
					
					
						|||
| 
						
						
							
						
						992370b4e3
	
				 | 
					
					
						
							
								
								
									
										43
									
								
								dist/esm-with-signals.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								dist/esm-with-signals.js
									
									
									
									
										vendored
									
									
								
							@@ -41,19 +41,6 @@ function observedAttributes(instance, observedAttribute2) {
 | 
			
		||||
function kebabToCamel(name) {
 | 
			
		||||
	return name.replace(/-./g, (x) => x[1].toUpperCase());
 | 
			
		||||
}
 | 
			
		||||
var Defined = class extends Error {
 | 
			
		||||
	constructor() {
 | 
			
		||||
		super();
 | 
			
		||||
		const [curr, ...rest] = this.stack.split("\n");
 | 
			
		||||
		const curr_file = curr.slice(curr.indexOf("@"), curr.indexOf(".js:") + 4);
 | 
			
		||||
		const curr_lib = curr_file.includes("src/helpers.js") ? "src/" : curr_file;
 | 
			
		||||
		this.stack = rest.find((l) => !l.includes(curr_lib)) || curr;
 | 
			
		||||
	}
 | 
			
		||||
	get compact() {
 | 
			
		||||
		const { stack } = this;
 | 
			
		||||
		return stack.slice(0, stack.indexOf("@") + 1) + "\u2026" + stack.slice(stack.lastIndexOf("/"));
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// src/dom-lib/common.js
 | 
			
		||||
var enviroment = {
 | 
			
		||||
@@ -303,7 +290,7 @@ var store_abort = /* @__PURE__ */ new WeakMap();
 | 
			
		||||
var scope = {
 | 
			
		||||
	/**
 | 
			
		||||
	* Gets the current scope
 | 
			
		||||
	* @returns {Object} Current scope context
 | 
			
		||||
	* @returns {typeof scopes[number]} Current scope context
 | 
			
		||||
	*/
 | 
			
		||||
	get current() {
 | 
			
		||||
		return scopes[scopes.length - 1];
 | 
			
		||||
@@ -663,9 +650,9 @@ function memo(key, generator) {
 | 
			
		||||
memo.isScope = function(obj) {
 | 
			
		||||
	return obj[memoMark];
 | 
			
		||||
};
 | 
			
		||||
memo.scope = function memoScope(fun, { signal: signal2, onlyLast } = {}) {
 | 
			
		||||
memo.scope = function memoScopeCreate(fun, { signal: signal2, onlyLast } = {}) {
 | 
			
		||||
	let cache = oCreate();
 | 
			
		||||
	function memoScope2(...args) {
 | 
			
		||||
	function memoScope(...args) {
 | 
			
		||||
		if (signal2 && signal2.aborted)
 | 
			
		||||
			return fun.apply(this, args);
 | 
			
		||||
		let cache_local = onlyLast ? cache : oCreate();
 | 
			
		||||
@@ -680,10 +667,10 @@ memo.scope = function memoScope(fun, { signal: signal2, onlyLast } = {}) {
 | 
			
		||||
		cache = cache_local;
 | 
			
		||||
		return out;
 | 
			
		||||
	}
 | 
			
		||||
	memoScope2[memoMark] = true;
 | 
			
		||||
	memoScope2.clear = () => cache = oCreate();
 | 
			
		||||
	if (signal2) signal2.addEventListener("abort", memoScope2.clear);
 | 
			
		||||
	return memoScope2;
 | 
			
		||||
	memoScope[memoMark] = true;
 | 
			
		||||
	memoScope.clear = () => cache = oCreate();
 | 
			
		||||
	if (signal2) signal2.addEventListener("abort", memoScope.clear);
 | 
			
		||||
	return memoScope;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// src/signals-lib/helpers.js
 | 
			
		||||
@@ -800,17 +787,17 @@ signal.clear = function(...signals2) {
 | 
			
		||||
};
 | 
			
		||||
var key_reactive = "__dde_reactive";
 | 
			
		||||
signal.el = function(s, map) {
 | 
			
		||||
	map = memo.isScope(map) ? map : memo.scope(map, { onlyLast: true });
 | 
			
		||||
	const mark_start = createElement.mark({ type: "reactive", source: new Defined().compact }, true);
 | 
			
		||||
	const mapScoped = memo.isScope(map) ? map : memo.scope(map, { onlyLast: true });
 | 
			
		||||
	const { current } = scope, { scope: sc } = current;
 | 
			
		||||
	const mark_start = createElement.mark({ type: "reactive", component: sc && sc.name || "" }, true);
 | 
			
		||||
	const mark_end = mark_start.end;
 | 
			
		||||
	const out = enviroment.D.createDocumentFragment();
 | 
			
		||||
	out.append(mark_start, mark_end);
 | 
			
		||||
	const { current } = scope;
 | 
			
		||||
	const reRenderReactiveElement = (v) => {
 | 
			
		||||
		if (!mark_start.parentNode || !mark_end.parentNode)
 | 
			
		||||
			return removeSignalListener(s, reRenderReactiveElement);
 | 
			
		||||
		scope.push(current);
 | 
			
		||||
		let els = map(v);
 | 
			
		||||
		let els = mapScoped(v);
 | 
			
		||||
		scope.pop();
 | 
			
		||||
		if (!Array.isArray(els))
 | 
			
		||||
			els = [els];
 | 
			
		||||
@@ -830,7 +817,7 @@ signal.el = function(s, map) {
 | 
			
		||||
	current.host(on.disconnected(
 | 
			
		||||
		() => (
 | 
			
		||||
			/*! Clears cached elements for reactive element `S.el` */
 | 
			
		||||
			map.clear()
 | 
			
		||||
			mapScoped.clear()
 | 
			
		||||
		)
 | 
			
		||||
	));
 | 
			
		||||
	return out;
 | 
			
		||||
@@ -902,9 +889,8 @@ var signals_config = {
 | 
			
		||||
function removeSignalsFromElements(s, listener, ...notes) {
 | 
			
		||||
	const { current } = scope;
 | 
			
		||||
	current.host(function(element) {
 | 
			
		||||
		if (element[key_reactive])
 | 
			
		||||
			return element[key_reactive].push([[s, listener], ...notes]);
 | 
			
		||||
		element[key_reactive] = [];
 | 
			
		||||
		if (!element[key_reactive]) element[key_reactive] = [];
 | 
			
		||||
		element[key_reactive].push([[s, listener], ...notes]);
 | 
			
		||||
		if (current.prevent) return;
 | 
			
		||||
		on.disconnected(
 | 
			
		||||
			() => (
 | 
			
		||||
@@ -949,7 +935,6 @@ function toSignal(s, value, actions, readonly = false) {
 | 
			
		||||
			onclear,
 | 
			
		||||
			host,
 | 
			
		||||
			listeners: /* @__PURE__ */ new Set(),
 | 
			
		||||
			defined: new Defined().stack,
 | 
			
		||||
			readonly
 | 
			
		||||
		}),
 | 
			
		||||
		enumerable: false,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								dist/esm-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								dist/esm-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										14
									
								
								dist/esm.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								dist/esm.js
									
									
									
									
										vendored
									
									
								
							@@ -274,7 +274,7 @@ var store_abort = /* @__PURE__ */ new WeakMap();
 | 
			
		||||
var scope = {
 | 
			
		||||
	/**
 | 
			
		||||
	* Gets the current scope
 | 
			
		||||
	* @returns {Object} Current scope context
 | 
			
		||||
	* @returns {typeof scopes[number]} Current scope context
 | 
			
		||||
	*/
 | 
			
		||||
	get current() {
 | 
			
		||||
		return scopes[scopes.length - 1];
 | 
			
		||||
@@ -634,9 +634,9 @@ function memo(key, generator) {
 | 
			
		||||
memo.isScope = function(obj) {
 | 
			
		||||
	return obj[memoMark];
 | 
			
		||||
};
 | 
			
		||||
memo.scope = function memoScope(fun, { signal, onlyLast } = {}) {
 | 
			
		||||
memo.scope = function memoScopeCreate(fun, { signal, onlyLast } = {}) {
 | 
			
		||||
	let cache = oCreate();
 | 
			
		||||
	function memoScope2(...args) {
 | 
			
		||||
	function memoScope(...args) {
 | 
			
		||||
		if (signal && signal.aborted)
 | 
			
		||||
			return fun.apply(this, args);
 | 
			
		||||
		let cache_local = onlyLast ? cache : oCreate();
 | 
			
		||||
@@ -651,10 +651,10 @@ memo.scope = function memoScope(fun, { signal, onlyLast } = {}) {
 | 
			
		||||
		cache = cache_local;
 | 
			
		||||
		return out;
 | 
			
		||||
	}
 | 
			
		||||
	memoScope2[memoMark] = true;
 | 
			
		||||
	memoScope2.clear = () => cache = oCreate();
 | 
			
		||||
	if (signal) signal.addEventListener("abort", memoScope2.clear);
 | 
			
		||||
	return memoScope2;
 | 
			
		||||
	memoScope[memoMark] = true;
 | 
			
		||||
	memoScope.clear = () => cache = oCreate();
 | 
			
		||||
	if (signal) signal.addEventListener("abort", memoScope.clear);
 | 
			
		||||
	return memoScope;
 | 
			
		||||
};
 | 
			
		||||
export {
 | 
			
		||||
	assign,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								dist/esm.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/esm.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										43
									
								
								dist/iife-with-signals.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								dist/iife-with-signals.js
									
									
									
									
										vendored
									
									
								
							@@ -86,19 +86,6 @@ var DDE = (() => {
 | 
			
		||||
	function kebabToCamel(name) {
 | 
			
		||||
		return name.replace(/-./g, (x) => x[1].toUpperCase());
 | 
			
		||||
	}
 | 
			
		||||
	var Defined = class extends Error {
 | 
			
		||||
		constructor() {
 | 
			
		||||
			super();
 | 
			
		||||
			const [curr, ...rest] = this.stack.split("\n");
 | 
			
		||||
			const curr_file = curr.slice(curr.indexOf("@"), curr.indexOf(".js:") + 4);
 | 
			
		||||
			const curr_lib = curr_file.includes("src/helpers.js") ? "src/" : curr_file;
 | 
			
		||||
			this.stack = rest.find((l) => !l.includes(curr_lib)) || curr;
 | 
			
		||||
		}
 | 
			
		||||
		get compact() {
 | 
			
		||||
			const { stack } = this;
 | 
			
		||||
			return stack.slice(0, stack.indexOf("@") + 1) + "\u2026" + stack.slice(stack.lastIndexOf("/"));
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// src/dom-lib/common.js
 | 
			
		||||
	var enviroment = {
 | 
			
		||||
@@ -348,7 +335,7 @@ var DDE = (() => {
 | 
			
		||||
	var scope = {
 | 
			
		||||
		/**
 | 
			
		||||
		* Gets the current scope
 | 
			
		||||
		* @returns {Object} Current scope context
 | 
			
		||||
		* @returns {typeof scopes[number]} Current scope context
 | 
			
		||||
		*/
 | 
			
		||||
		get current() {
 | 
			
		||||
			return scopes[scopes.length - 1];
 | 
			
		||||
@@ -708,9 +695,9 @@ var DDE = (() => {
 | 
			
		||||
	memo.isScope = function(obj) {
 | 
			
		||||
		return obj[memoMark];
 | 
			
		||||
	};
 | 
			
		||||
	memo.scope = function memoScope(fun, { signal: signal2, onlyLast } = {}) {
 | 
			
		||||
	memo.scope = function memoScopeCreate(fun, { signal: signal2, onlyLast } = {}) {
 | 
			
		||||
		let cache = oCreate();
 | 
			
		||||
		function memoScope2(...args) {
 | 
			
		||||
		function memoScope(...args) {
 | 
			
		||||
			if (signal2 && signal2.aborted)
 | 
			
		||||
				return fun.apply(this, args);
 | 
			
		||||
			let cache_local = onlyLast ? cache : oCreate();
 | 
			
		||||
@@ -725,10 +712,10 @@ var DDE = (() => {
 | 
			
		||||
			cache = cache_local;
 | 
			
		||||
			return out;
 | 
			
		||||
		}
 | 
			
		||||
		memoScope2[memoMark] = true;
 | 
			
		||||
		memoScope2.clear = () => cache = oCreate();
 | 
			
		||||
		if (signal2) signal2.addEventListener("abort", memoScope2.clear);
 | 
			
		||||
		return memoScope2;
 | 
			
		||||
		memoScope[memoMark] = true;
 | 
			
		||||
		memoScope.clear = () => cache = oCreate();
 | 
			
		||||
		if (signal2) signal2.addEventListener("abort", memoScope.clear);
 | 
			
		||||
		return memoScope;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// src/signals-lib/helpers.js
 | 
			
		||||
@@ -845,17 +832,17 @@ var DDE = (() => {
 | 
			
		||||
	};
 | 
			
		||||
	var key_reactive = "__dde_reactive";
 | 
			
		||||
	signal.el = function(s, map) {
 | 
			
		||||
		map = memo.isScope(map) ? map : memo.scope(map, { onlyLast: true });
 | 
			
		||||
		const mark_start = createElement.mark({ type: "reactive", source: new Defined().compact }, true);
 | 
			
		||||
		const mapScoped = memo.isScope(map) ? map : memo.scope(map, { onlyLast: true });
 | 
			
		||||
		const { current } = scope, { scope: sc } = current;
 | 
			
		||||
		const mark_start = createElement.mark({ type: "reactive", component: sc && sc.name || "" }, true);
 | 
			
		||||
		const mark_end = mark_start.end;
 | 
			
		||||
		const out = enviroment.D.createDocumentFragment();
 | 
			
		||||
		out.append(mark_start, mark_end);
 | 
			
		||||
		const { current } = scope;
 | 
			
		||||
		const reRenderReactiveElement = (v) => {
 | 
			
		||||
			if (!mark_start.parentNode || !mark_end.parentNode)
 | 
			
		||||
				return removeSignalListener(s, reRenderReactiveElement);
 | 
			
		||||
			scope.push(current);
 | 
			
		||||
			let els = map(v);
 | 
			
		||||
			let els = mapScoped(v);
 | 
			
		||||
			scope.pop();
 | 
			
		||||
			if (!Array.isArray(els))
 | 
			
		||||
				els = [els];
 | 
			
		||||
@@ -875,7 +862,7 @@ var DDE = (() => {
 | 
			
		||||
		current.host(on.disconnected(
 | 
			
		||||
			() => (
 | 
			
		||||
				/*! Clears cached elements for reactive element `S.el` */
 | 
			
		||||
				map.clear()
 | 
			
		||||
				mapScoped.clear()
 | 
			
		||||
			)
 | 
			
		||||
		));
 | 
			
		||||
		return out;
 | 
			
		||||
@@ -947,9 +934,8 @@ var DDE = (() => {
 | 
			
		||||
	function removeSignalsFromElements(s, listener, ...notes) {
 | 
			
		||||
		const { current } = scope;
 | 
			
		||||
		current.host(function(element) {
 | 
			
		||||
			if (element[key_reactive])
 | 
			
		||||
				return element[key_reactive].push([[s, listener], ...notes]);
 | 
			
		||||
			element[key_reactive] = [];
 | 
			
		||||
			if (!element[key_reactive]) element[key_reactive] = [];
 | 
			
		||||
			element[key_reactive].push([[s, listener], ...notes]);
 | 
			
		||||
			if (current.prevent) return;
 | 
			
		||||
			on.disconnected(
 | 
			
		||||
				() => (
 | 
			
		||||
@@ -994,7 +980,6 @@ var DDE = (() => {
 | 
			
		||||
				onclear,
 | 
			
		||||
				host,
 | 
			
		||||
				listeners: /* @__PURE__ */ new Set(),
 | 
			
		||||
				defined: new Defined().stack,
 | 
			
		||||
				readonly
 | 
			
		||||
			}),
 | 
			
		||||
			enumerable: false,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								dist/iife-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								dist/iife-with-signals.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										14
									
								
								dist/iife.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								dist/iife.js
									
									
									
									
										vendored
									
									
								
							@@ -316,7 +316,7 @@ var DDE = (() => {
 | 
			
		||||
	var scope = {
 | 
			
		||||
		/**
 | 
			
		||||
		* Gets the current scope
 | 
			
		||||
		* @returns {Object} Current scope context
 | 
			
		||||
		* @returns {typeof scopes[number]} Current scope context
 | 
			
		||||
		*/
 | 
			
		||||
		get current() {
 | 
			
		||||
			return scopes[scopes.length - 1];
 | 
			
		||||
@@ -676,9 +676,9 @@ var DDE = (() => {
 | 
			
		||||
	memo.isScope = function(obj) {
 | 
			
		||||
		return obj[memoMark];
 | 
			
		||||
	};
 | 
			
		||||
	memo.scope = function memoScope(fun, { signal, onlyLast } = {}) {
 | 
			
		||||
	memo.scope = function memoScopeCreate(fun, { signal, onlyLast } = {}) {
 | 
			
		||||
		let cache = oCreate();
 | 
			
		||||
		function memoScope2(...args) {
 | 
			
		||||
		function memoScope(...args) {
 | 
			
		||||
			if (signal && signal.aborted)
 | 
			
		||||
				return fun.apply(this, args);
 | 
			
		||||
			let cache_local = onlyLast ? cache : oCreate();
 | 
			
		||||
@@ -693,10 +693,10 @@ var DDE = (() => {
 | 
			
		||||
			cache = cache_local;
 | 
			
		||||
			return out;
 | 
			
		||||
		}
 | 
			
		||||
		memoScope2[memoMark] = true;
 | 
			
		||||
		memoScope2.clear = () => cache = oCreate();
 | 
			
		||||
		if (signal) signal.addEventListener("abort", memoScope2.clear);
 | 
			
		||||
		return memoScope2;
 | 
			
		||||
		memoScope[memoMark] = true;
 | 
			
		||||
		memoScope.clear = () => cache = oCreate();
 | 
			
		||||
		if (signal) signal.addEventListener("abort", memoScope.clear);
 | 
			
		||||
		return memoScope;
 | 
			
		||||
	};
 | 
			
		||||
	return __toCommonJS(index_exports);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								dist/iife.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/iife.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -23,14 +23,6 @@ export function DataDashboard() {
 | 
			
		||||
		conversion: [2.9, 3.5, 3.7, 2.6, 3.4, 3.5, 2.8, 2.8, 2.8, 3.1, 3.0, 2.7],
 | 
			
		||||
		months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Application state
 | 
			
		||||
	const selectedYear = S(2024);
 | 
			
		||||
	const selectedDataType = S(/** @type {'sales' | 'visitors' | 'conversion'} */ ('sales'));
 | 
			
		||||
	const isLoading = S(false);
 | 
			
		||||
	const error = S(null);
 | 
			
		||||
 | 
			
		||||
	// Filter options
 | 
			
		||||
	const years = [2022, 2023, 2024];
 | 
			
		||||
	const dataTypes = [
 | 
			
		||||
		{ id: 'sales', label: 'Sales', unit: 'K' },
 | 
			
		||||
@@ -38,42 +30,32 @@ export function DataDashboard() {
 | 
			
		||||
		{ id: 'conversion', label: 'Conversion Rate', unit: '%' }
 | 
			
		||||
	];
 | 
			
		||||
 | 
			
		||||
	// Computed values
 | 
			
		||||
	const selectedData = S(() => {
 | 
			
		||||
		return DATA[selectedDataType.get()];
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	const currentDataType = S(() => {
 | 
			
		||||
		return dataTypes.find(type => type.id === selectedDataType.get());
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	const totalValue = S(() => {
 | 
			
		||||
		const data = selectedData.get();
 | 
			
		||||
		return data.reduce((sum, value) => sum + value, 0);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	const averageValue = S(() => {
 | 
			
		||||
		const data = selectedData.get();
 | 
			
		||||
		return data.reduce((sum, value) => sum + value, 0) / data.length;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	const highestValue = S(() => {
 | 
			
		||||
		return Math.max(...selectedData.get());
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Event handlers
 | 
			
		||||
	// Filter options
 | 
			
		||||
	const selectedYear = S(2024);
 | 
			
		||||
	const onYearChange = on("change", e => {
 | 
			
		||||
		selectedYear.set(parseInt(/** @type {HTMLSelectElement} */(e.target).value));
 | 
			
		||||
		loadData();
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	const selectedDataType = S(/** @type {'sales' | 'visitors' | 'conversion'} */ ('sales'));
 | 
			
		||||
	const onDataTypeChange = on("click", e => {
 | 
			
		||||
		const type = /** @type {'sales' | 'visitors' | 'conversion'} */(
 | 
			
		||||
			/** @type {HTMLButtonElement} */(e.currentTarget).dataset.type);
 | 
			
		||||
		selectedDataType.set(type);
 | 
			
		||||
	});
 | 
			
		||||
	const currentDataType = S(() => dataTypes.find(type => type.id === selectedDataType.get()));
 | 
			
		||||
	const selectedData = S(() => DATA[selectedDataType.get()]);
 | 
			
		||||
 | 
			
		||||
	// Values based on filters
 | 
			
		||||
	const totalValue = S(() => selectedData.get().reduce((sum, value) => sum + value, 0));
 | 
			
		||||
	const averageValue = S(() => {
 | 
			
		||||
		const data = selectedData.get();
 | 
			
		||||
		return data.reduce((sum, value) => sum + value, 0) / data.length;
 | 
			
		||||
	});
 | 
			
		||||
	const highestValue = S(() => Math.max(...selectedData.get()));
 | 
			
		||||
 | 
			
		||||
	// Simulate data loading
 | 
			
		||||
	const isLoading = S(false);
 | 
			
		||||
	const error = S(null);
 | 
			
		||||
	function loadData() {
 | 
			
		||||
		isLoading.set(true);
 | 
			
		||||
		error.set(null);
 | 
			
		||||
@@ -114,7 +96,7 @@ export function DataDashboard() {
 | 
			
		||||
			// Draw grid labels
 | 
			
		||||
			ctx.fillStyle = '#999';
 | 
			
		||||
			ctx.font = '12px Arial';
 | 
			
		||||
			ctx.fillText(Math.round(maxValue * (i / 5)), 20, y + 5);
 | 
			
		||||
			ctx.fillText(Math.round(maxValue * (i / 5)).toString(), 20, y + 5);
 | 
			
		||||
		}
 | 
			
		||||
		ctx.stroke();
 | 
			
		||||
 | 
			
		||||
@@ -154,7 +136,6 @@ export function DataDashboard() {
 | 
			
		||||
			)
 | 
			
		||||
		),
 | 
			
		||||
 | 
			
		||||
		// Error message (only shown when there's an error)
 | 
			
		||||
		S.el(error, errorMsg => !errorMsg
 | 
			
		||||
			? el()
 | 
			
		||||
			: el("div", { className: "error-message" }).append(
 | 
			
		||||
@@ -163,7 +144,6 @@ export function DataDashboard() {
 | 
			
		||||
				),
 | 
			
		||||
		),
 | 
			
		||||
 | 
			
		||||
		// Loading indicator
 | 
			
		||||
		S.el(isLoading, loading => !loading
 | 
			
		||||
			? el()
 | 
			
		||||
			: el("div", { className: "loading-spinner" })
 | 
			
		||||
 
 | 
			
		||||
@@ -27,25 +27,23 @@ const imagesSample = (url=> [
 | 
			
		||||
 * @returns {HTMLElement} Gallery element
 | 
			
		||||
 */
 | 
			
		||||
export function ImageGallery(images= imagesSample) {
 | 
			
		||||
 | 
			
		||||
	// Application state
 | 
			
		||||
	const selectedImageId = S(null);
 | 
			
		||||
	const filterTag = S('all');
 | 
			
		||||
	const imagesToDisplay = S(() => {
 | 
			
		||||
		const tag = filterTag.get();
 | 
			
		||||
		if (tag === 'all') return images;
 | 
			
		||||
		else return images.filter(img => img.alt.toLowerCase() === tag);
 | 
			
		||||
	})
 | 
			
		||||
	const onFilterChange = tag => on("click", () => {
 | 
			
		||||
		filterTag.set(tag);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Derived state
 | 
			
		||||
	// Lightbox
 | 
			
		||||
	const selectedImageId = S(null);
 | 
			
		||||
	const selectedImage = S(() => {
 | 
			
		||||
		const id = selectedImageId.get();
 | 
			
		||||
		return id ? images.find(img => img.id === id) : null;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	const isLightboxOpen = S(() => selectedImage.get() !== null);
 | 
			
		||||
 | 
			
		||||
	// Event handlers
 | 
			
		||||
	const onImageClick = id => on("click", () => {
 | 
			
		||||
		selectedImageId.set(id);
 | 
			
		||||
		document.body.style.overflow = 'hidden'; // Prevent scrolling when lightbox is open
 | 
			
		||||
@@ -76,9 +74,6 @@ export function ImageGallery(images= imagesSample) {
 | 
			
		||||
		const nextIndex = (currentIndex + 1) % images.length;
 | 
			
		||||
		selectedImageId.set(images[nextIndex].id);
 | 
			
		||||
	};
 | 
			
		||||
	const onFilterChange = tag => on("click", () => {
 | 
			
		||||
		filterTag.set(tag);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Keyboard navigation handler
 | 
			
		||||
	function handleKeyDown(e) {
 | 
			
		||||
 
 | 
			
		||||
@@ -73,8 +73,17 @@ export function Form({ initial }) {
 | 
			
		||||
			this.value[key] = value;
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	/**
 | 
			
		||||
	 * Event handler for input events
 | 
			
		||||
	 * @param {"value"|"checked"} prop
 | 
			
		||||
	 * @returns {(ev: Event) => void}
 | 
			
		||||
	 * */
 | 
			
		||||
	const onChange= prop => ev => {
 | 
			
		||||
		const input = /** @type {HTMLInputElement} */(ev.target);
 | 
			
		||||
		S.action(formState, "update", /** @type {keyof FormState} */(input.id), input[prop]);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Derived signals for validation
 | 
			
		||||
	// Form validate state
 | 
			
		||||
	const nameValid = S(() => formState.get().name.length >= 3);
 | 
			
		||||
	const emailValid = S(() => {
 | 
			
		||||
		const email = formState.get().email;
 | 
			
		||||
@@ -89,8 +98,6 @@ export function Form({ initial }) {
 | 
			
		||||
		return password === confirmPassword && confirmPassword !== '';
 | 
			
		||||
	});
 | 
			
		||||
	const termsAgreed = S(() => formState.get().agreedToTerms);
 | 
			
		||||
 | 
			
		||||
	// Overall form validity
 | 
			
		||||
	const formValid = S(() =>
 | 
			
		||||
		nameValid.get() &&
 | 
			
		||||
		emailValid.get() &&
 | 
			
		||||
@@ -99,16 +106,6 @@ export function Form({ initial }) {
 | 
			
		||||
		termsAgreed.get()
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	// Event handlers
 | 
			
		||||
	/**
 | 
			
		||||
	 * Event handler for input events
 | 
			
		||||
	 * @param {"value"|"checked"} prop
 | 
			
		||||
	 * @returns {(ev: Event) => void}
 | 
			
		||||
	 * */
 | 
			
		||||
	const onChange= prop => ev => {
 | 
			
		||||
		const input = /** @type {HTMLInputElement} */(ev.target);
 | 
			
		||||
		S.action(formState, "update", /** @type {keyof FormState} */(input.id), input[prop]);
 | 
			
		||||
	};
 | 
			
		||||
	const dispatcSubmit = dispatchEvent("form:submit", host);
 | 
			
		||||
	const onSubmit = on("submit", e => {
 | 
			
		||||
		e.preventDefault();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
// Example of reactive element marker
 | 
			
		||||
<!--<dde:mark type="reactive" source="...">-->
 | 
			
		||||
<!--<dde:mark type="reactive" component="<component-name>">-->
 | 
			
		||||
<!-- content that updates when signal changes -->
 | 
			
		||||
<!--</dde:mark>-->
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,6 @@ export function page({ pkg, info }){
 | 
			
		||||
			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`host: Reference to the host element/scope in which the signal was created`),
 | 
			
		||||
			el("li", t`defined: Stack trace information for debugging`),
 | 
			
		||||
			el("li", t`readonly: Boolean flag indicating if the signal is read-only`)
 | 
			
		||||
		),
 | 
			
		||||
		el("p").append(T`
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1,12 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
	"name": "deka-dom-el",
 | 
			
		||||
	"version": "0.9.2-alpha",
 | 
			
		||||
	"version": "0.9.3-alpha",
 | 
			
		||||
	"lockfileVersion": 3,
 | 
			
		||||
	"requires": true,
 | 
			
		||||
	"packages": {
 | 
			
		||||
		"": {
 | 
			
		||||
			"name": "deka-dom-el",
 | 
			
		||||
			"version": "0.9.2-alpha",
 | 
			
		||||
			"version": "0.9.3-alpha",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"devDependencies": {
 | 
			
		||||
				"@size-limit/preset-small-lib": "~11.2",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
	"name": "deka-dom-el",
 | 
			
		||||
	"version": "0.9.2-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.",
 | 
			
		||||
	"author": "Jan Andrle <andrle.jan@centrum.cz>",
 | 
			
		||||
	"license": "MIT",
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ const store_abort= new WeakMap();
 | 
			
		||||
export const scope= {
 | 
			
		||||
	/**
 | 
			
		||||
	 * Gets the current scope
 | 
			
		||||
	 * @returns {Object} Current scope context
 | 
			
		||||
	 * @returns {typeof scopes[number]} Current scope context
 | 
			
		||||
	 */
 | 
			
		||||
	get current(){ return scopes[scopes.length-1]; },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -68,21 +68,3 @@ export function observedAttributes(instance, observedAttribute){
 | 
			
		||||
 * @returns {string} The camelCase string
 | 
			
		||||
 */
 | 
			
		||||
function kebabToCamel(name){ return name.replace(/-./g, x=> x[1].toUpperCase()); }
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Error class for definition tracking
 | 
			
		||||
 * Shows the correct stack trace for debugging (signal) creation
 | 
			
		||||
 */
 | 
			
		||||
export class Defined extends Error{
 | 
			
		||||
	constructor(){
 | 
			
		||||
		super();
 | 
			
		||||
		const [ curr, ...rest ]= this.stack.split("\n");
 | 
			
		||||
		const curr_file= curr.slice(curr.indexOf("@"), curr.indexOf(".js:")+4);
 | 
			
		||||
		const curr_lib= curr_file.includes("src/helpers.js") ? "src/" : curr_file;
 | 
			
		||||
		this.stack= rest.find(l=> !l.includes(curr_lib)) || curr;
 | 
			
		||||
	}
 | 
			
		||||
	get compact(){
 | 
			
		||||
		const { stack }= this;
 | 
			
		||||
		return stack.slice(0, stack.indexOf("@")+1)+"…"+stack.slice(stack.lastIndexOf("/"));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ memo.isScope= function(obj){ return obj[memoMark]; };
 | 
			
		||||
 * @param {AbortSignal} options.signal
 | 
			
		||||
 * @param {boolean} [options.onlyLast=false]
 | 
			
		||||
 * */
 | 
			
		||||
memo.scope= function memoScope(fun, { signal, onlyLast }= {}){
 | 
			
		||||
memo.scope= function memoScopeCreate(fun, { signal, onlyLast }= {}){
 | 
			
		||||
	let cache= oCreate();
 | 
			
		||||
	function memoScope(...args){
 | 
			
		||||
		if(signal && signal.aborted)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { queueSignalWrite, mark } from "./helpers.js";
 | 
			
		||||
export { mark };
 | 
			
		||||
import { hasOwn, Defined, oCreate, oAssign } from "../helpers.js";
 | 
			
		||||
import { hasOwn, oCreate, oAssign } from "../helpers.js";
 | 
			
		||||
 | 
			
		||||
const Signal = oCreate(null, {
 | 
			
		||||
	get: { value(){ return read(this); } },
 | 
			
		||||
@@ -169,21 +169,21 @@ import { memo } from "../memo.js";
 | 
			
		||||
 * Creates a reactive DOM element that re-renders when signal changes
 | 
			
		||||
 *
 | 
			
		||||
 * @param {Object} s - Signal object to watch
 | 
			
		||||
 * @param {Function} map - Function mapping signal value to DOM elements
 | 
			
		||||
 * @param {Function} mapScoped - Function mapping signal value to DOM elements
 | 
			
		||||
 * @returns {DocumentFragment} Fragment containing reactive elements
 | 
			
		||||
 */
 | 
			
		||||
signal.el= function(s, map){
 | 
			
		||||
	map= memo.isScope(map) ? map : memo.scope(map, { onlyLast: true });
 | 
			
		||||
	const mark_start= el.mark({ type: "reactive", source: new Defined().compact }, true);
 | 
			
		||||
	const mapScoped= memo.isScope(map) ? map : memo.scope(map, { onlyLast: true });
 | 
			
		||||
	const { current }= scope, { scope: sc }= current;
 | 
			
		||||
	const mark_start= el.mark({ type: "reactive", component: sc && sc.name || "" }, true);
 | 
			
		||||
	const mark_end= mark_start.end;
 | 
			
		||||
	const out= env.D.createDocumentFragment();
 | 
			
		||||
	out.append(mark_start, mark_end);
 | 
			
		||||
	const { current }= scope;
 | 
			
		||||
	const reRenderReactiveElement= v=> {
 | 
			
		||||
		if(!mark_start.parentNode || !mark_end.parentNode) // === `isConnected` or wasn’t yet rendered
 | 
			
		||||
			return removeSignalListener(s, reRenderReactiveElement);
 | 
			
		||||
		scope.push(current);
 | 
			
		||||
		let els= map(v);
 | 
			
		||||
		let els= mapScoped(v);
 | 
			
		||||
		scope.pop();
 | 
			
		||||
		if(!Array.isArray(els))
 | 
			
		||||
			els= [ els ];
 | 
			
		||||
@@ -202,7 +202,7 @@ signal.el= function(s, map){
 | 
			
		||||
	reRenderReactiveElement(s.get());
 | 
			
		||||
	current.host(on.disconnected(()=>
 | 
			
		||||
		/*! Clears cached elements for reactive element `S.el` */
 | 
			
		||||
		map.clear()
 | 
			
		||||
		mapScoped.clear()
 | 
			
		||||
	));
 | 
			
		||||
	return out;
 | 
			
		||||
};
 | 
			
		||||
@@ -314,9 +314,8 @@ export const signals_config= {
 | 
			
		||||
function removeSignalsFromElements(s, listener, ...notes){
 | 
			
		||||
	const { current }= scope;
 | 
			
		||||
	current.host(function(element){
 | 
			
		||||
		if(element[key_reactive])
 | 
			
		||||
			return element[key_reactive].push([ [ s, listener ], ...notes ]);
 | 
			
		||||
		element[key_reactive]= [];
 | 
			
		||||
		if(!element[key_reactive]) element[key_reactive]= [];
 | 
			
		||||
		element[key_reactive].push([ [ s, listener ], ...notes ]);
 | 
			
		||||
		if(current.prevent) return; // typically document.body, doenst need auto-remove as it should happen on page leave
 | 
			
		||||
		on.disconnected(()=>
 | 
			
		||||
			/*! Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?).
 | 
			
		||||
@@ -386,7 +385,6 @@ function toSignal(s, value, actions, readonly= false){
 | 
			
		||||
		value: oAssign(oCreate(protoSigal), {
 | 
			
		||||
			value, actions, onclear, host,
 | 
			
		||||
			listeners: new Set(),
 | 
			
		||||
			defined: new Defined().stack,
 | 
			
		||||
			readonly
 | 
			
		||||
		}),
 | 
			
		||||
		enumerable: false,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user