Actualizar src/sigpro.js

This commit is contained in:
2026-03-21 13:44:20 +01:00
parent 2ac553850c
commit 47917ac5c8

View File

@@ -1,21 +1,17 @@
/** /**
* SigPro 2.0 - Complete Reactive Engine * SigPro 2.0 - Integrated Reactive Engine
* @author Gemini & User
*/ */
(() => { (() => {
/** @type {Function|null} */ /** @type {Function|null} Dependency tracker */
let activeEffect = null; let activeEffect = null;
/** /**
* Creates a Signal (State) or a Computed (Derived state). * Reactive Engine ($)
* @template T * Creates Signals, Computeds, or Effects.
* @param {T|function():T} initial - Initial value or a computation function. * @param {any} initial - Value or reactive function.
* @returns {{(newValue?: T|function(T):T): T}} A getter/setter function.
*/ */
const $ = (initial) => { window.$ = (initial) => {
/** @type {Set<Function>} */
const subs = new Set(); const subs = new Set();
if (typeof initial === 'function') { if (typeof initial === 'function') {
let cached; let cached;
const runner = () => { const runner = () => {
@@ -25,51 +21,39 @@
subs.forEach(s => s()); subs.forEach(s => s());
}; };
runner(); runner();
return () => { return () => { (activeEffect && subs.add(activeEffect)); return cached; };
if (activeEffect) subs.add(activeEffect);
return cached;
};
} }
return (...args) => { return (...args) => {
if (args.length) { if (args.length) {
const next = typeof args[0] === 'function' ? args[0](initial) : args[0]; const next = typeof args[0] === 'function' ? args[0](initial) : args[0];
if (!Object.is(initial, next)) { if (!Object.is(initial, next)) { initial = next; subs.forEach(s => s()); }
initial = next;
subs.forEach(s => s());
}
} }
if (activeEffect) subs.add(activeEffect); (activeEffect && subs.add(activeEffect));
return initial; return initial;
}; };
}; };
/** /**
* Creates a reactive effect. * Hyperscript Engine ($$)
* @param {function():void} fn - The function to execute reactively. * @param {string} tag - HTML tag.
* @param {Object} [props] - Attributes/Events.
* @param {any} [children] - Content.
*/ */
const _$ = (fn) => { window.$$ = (tag, props = {}, children = []) => {
const effect = () => {
const prev = activeEffect;
activeEffect = effect;
try { fn(); } finally { activeEffect = prev; }
};
effect();
return effect;
};
/**
* Universal DOM Constructor (Hyperscript).
* @param {string} tag - HTML Tag name.
* @param {Object<string, any> | HTMLElement | Array | string} [props] - Attributes or children.
* @param {Array | HTMLElement | string | function} [children] - Element children.
* @returns {HTMLElement}
*/
const $$ = (tag, props = {}, children = []) => {
const el = document.createElement(tag); const el = document.createElement(tag);
if (typeof props !== 'object' || props instanceof Node || Array.isArray(props)) {
children = props; props = {}; // Logic: If props is NOT a plain object or is a Node/Array/Function,
// it's actually the children.
if (
typeof props !== 'object' ||
props instanceof Node ||
Array.isArray(props) ||
typeof props === 'function'
) {
children = props;
props = {};
} }
for (let [key, val] of Object.entries(props)) { for (let [key, val] of Object.entries(props)) {
if (key.startsWith('on')) { if (key.startsWith('on')) {
el.addEventListener(key.toLowerCase().slice(2), val); el.addEventListener(key.toLowerCase().slice(2), val);
@@ -79,22 +63,20 @@
const ev = attr === 'checked' ? 'change' : 'input'; const ev = attr === 'checked' ? 'change' : 'input';
el.addEventListener(ev, e => val(attr === 'checked' ? e.target.checked : e.target.value)); el.addEventListener(ev, e => val(attr === 'checked' ? e.target.checked : e.target.value));
} }
_$(() => { $(() => {
const v = typeof val === 'function' ? val() : val; const v = typeof val === 'function' ? val() : val;
if (attr === 'value' || attr === 'checked') el[attr] = v; if (attr === 'value' || attr === 'checked') el[attr] = v;
else if (typeof v === 'boolean') el.toggleAttribute(attr, v); else if (typeof v === 'boolean') el.toggleAttribute(attr, v);
else el.setAttribute(attr, v ?? ''); else el.setAttribute(attr, v ?? '');
}); });
} else { } else el.setAttribute(key, val);
el.setAttribute(key, val);
}
} }
const append = (c) => { const append = (c) => {
if (Array.isArray(c)) return c.forEach(append); if (Array.isArray(c)) return c.forEach(append);
if (typeof c === 'function') { if (typeof c === 'function') {
const node = document.createTextNode(''); const node = document.createTextNode('');
_$(() => { $(() => {
const res = c(); const res = c();
if (res instanceof Node) { if (node.parentNode) node.replaceWith(res); } if (res instanceof Node) { if (node.parentNode) node.replaceWith(res); }
else { node.textContent = res ?? ''; } else { node.textContent = res ?? ''; }
@@ -108,61 +90,33 @@
}; };
/** /**
* Renders the application into a target element. * System Utilities (_)
* @param {HTMLElement | function():HTMLElement} node
* @param {HTMLElement} [target]
*/ */
const $render = (node, target = document.body) => { window._render = (node, target = document.body) => {
target.innerHTML = ''; target.innerHTML = '';
target.appendChild(typeof node === 'function' ? node() : node); target.appendChild(typeof node === 'function' ? node() : node);
}; };
/** window._router = (routes) => {
* Hash-based Reactive Router.
* @param {Array<{path: string, component: function|HTMLElement}>} routes
* @returns {HTMLElement}
*/
const $router = (routes) => {
const sPath = $(window.location.hash.replace(/^#/, "") || "/"); const sPath = $(window.location.hash.replace(/^#/, "") || "/");
window.addEventListener("hashchange", () => sPath(window.location.hash.replace(/^#/, "") || "/")); window.addEventListener("hashchange", () => sPath(window.location.hash.replace(/^#/, "") || "/"));
return div({ class: "sigpro-router" }, [
return $$('div', { class: "router-view" }, [
() => { () => {
const current = sPath(); const current = sPath();
let params = {};
const route = routes.find(r => { const route = routes.find(r => {
const rP = r.path.split('/').filter(Boolean); const rP = r.path.split('/').filter(Boolean);
const cP = current.split('/').filter(Boolean); const cP = current.split('/').filter(Boolean);
if (rP.length !== cP.length) return false; if (rP.length !== cP.length) return false;
return rP.every((part, i) => { return rP.every((part, i) => part.startsWith(':') || part === cP[i]);
if (part.startsWith(':')) { params[part.slice(1)] = cP[i]; return true; }
return part === cP[i];
});
}) || routes.find(r => r.path === "*"); }) || routes.find(r => r.path === "*");
return route ? (typeof route.component === 'function' ? route.component() : route.component) : h1("404");
if (!route) return $$('h1', '404');
return typeof route.component === 'function' ? route.component(params) : route.component;
} }
]); ]);
}; };
/** // Auto-generate tags: div(), h1(), etc.
* Registers a plugin into the SigPro ecosystem. ['div', 'span', 'p', 'button', 'h1', 'h2', 'h3', 'ul', 'li', 'a', 'input', 'section', 'main'].forEach(t => {
* @param {string} name window[t] = (p, c) => $$(t, p, c);
* @param {Object<string, Function>} exports
*/
const $use = (name, exports) => {
Object.assign(window, exports);
console.log(`%c[SigPro] Plugin Loaded: ${name}`, "color: #00ff7f; font-weight: bold;");
};
// --- AUTO-INJECT STANDARD TAGS ---
const tags = ['div', 'span', 'p', 'button', 'h1', 'h2', 'h3', 'ul', 'li', 'a', 'label', 'section', 'nav', 'main', 'header', 'footer', 'input', 'img', 'form'];
const standardTags = {};
tags.forEach(tag => {
standardTags[tag] = (p, c) => $$(tag, p, c);
}); });
// Global Exports
Object.assign(window, { $, _$, $$, $render, $router, $use, ...standardTags });
})(); })();