Update sigpro2.js
This commit is contained in:
@@ -1,8 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* SigPro 2.0 - Core Engine
|
||||||
|
* @author Gemini & User
|
||||||
|
*/
|
||||||
(() => {
|
(() => {
|
||||||
|
/** @type {Function|null} */
|
||||||
let activeEffect = null;
|
let activeEffect = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crea una Señal (Estado) o una Computada (Derivado).
|
||||||
|
* @template T
|
||||||
|
* @param {T|function():T} initial - Valor inicial o función de cálculo.
|
||||||
|
* @returns {{(newValue?: T|function(T):T): T}} Getter/Setter de la señal.
|
||||||
|
*/
|
||||||
window.$ = (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 = () => {
|
||||||
@@ -17,6 +30,7 @@
|
|||||||
return cached;
|
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];
|
||||||
@@ -30,7 +44,12 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
window.$e = (fn) => {
|
/**
|
||||||
|
* Crea un Efecto secundario reactivo.
|
||||||
|
* @param {function():void} fn - Función a ejecutar cuando cambien sus dependencias.
|
||||||
|
* @returns {function():void} La función del efecto.
|
||||||
|
*/
|
||||||
|
window._$ = (fn) => {
|
||||||
const effect = () => {
|
const effect = () => {
|
||||||
const prev = activeEffect;
|
const prev = activeEffect;
|
||||||
activeEffect = effect;
|
activeEffect = effect;
|
||||||
@@ -40,8 +59,18 @@
|
|||||||
return effect;
|
return effect;
|
||||||
};
|
};
|
||||||
|
|
||||||
const h = (tag, props = {}, children = []) => {
|
/**
|
||||||
|
* Constructor Universal de Elementos DOM.
|
||||||
|
* @param {string} tag - Etiqueta HTML.
|
||||||
|
* @param {Object<string, any> | HTMLElement | Array | string} [props] - Propiedades o hijos.
|
||||||
|
* @param {Array | HTMLElement | string | function} [children] - Hijos del elemento.
|
||||||
|
* @returns {HTMLElement} El elemento DOM creado.
|
||||||
|
*/
|
||||||
|
window.$$ = (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 = {};
|
||||||
|
}
|
||||||
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);
|
||||||
@@ -51,27 +80,25 @@
|
|||||||
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));
|
||||||
}
|
}
|
||||||
$e(() => {
|
_$(() => {
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/** @param {any} c */
|
||||||
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('');
|
||||||
$e(() => {
|
_$(() => {
|
||||||
const res = c();
|
const res = c();
|
||||||
if (res instanceof Node) {
|
if (res instanceof Node) { if (node.parentNode) node.replaceWith(res); }
|
||||||
if (node.parentNode) node.replaceWith(res);
|
else { node.textContent = res ?? ''; }
|
||||||
} else {
|
|
||||||
node.textContent = res ?? '';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return el.appendChild(node);
|
return el.appendChild(node);
|
||||||
}
|
}
|
||||||
@@ -81,68 +108,13 @@
|
|||||||
return el;
|
return el;
|
||||||
};
|
};
|
||||||
|
|
||||||
const tags = ['div', 'span', 'p', 'button', 'input', 'h1', 'h2', 'label', 'section', 'ul', 'li', 'a', 'header', 'footer', 'nav', 'main'];
|
/**
|
||||||
tags.forEach(tag => {
|
* Renderiza la aplicación en un contenedor.
|
||||||
window[`_${tag}`] = (props, children) => {
|
* @param {HTMLElement | function():HTMLElement} node - Elemento raíz o función que lo retorna.
|
||||||
if (typeof props !== 'object' || props instanceof Node || Array.isArray(props)) {
|
* @param {HTMLElement} [target] - Contenedor destino (por defecto document.body).
|
||||||
return h(tag, {}, props);
|
*/
|
||||||
}
|
window.$render = (node, target = document.body) => {
|
||||||
return h(tag, props, children);
|
target.innerHTML = '';
|
||||||
};
|
target.appendChild(typeof node === 'function' ? node() : node);
|
||||||
});
|
|
||||||
|
|
||||||
window._storage = (key, initial, storage = localStorage) => {
|
|
||||||
const saved = storage.getItem(key);
|
|
||||||
const signal = $((saved !== null) ? JSON.parse(saved) : initial);
|
|
||||||
$e(() => storage.setItem(key, JSON.stringify(signal())));
|
|
||||||
return signal;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
window._router = (routes) => {
|
|
||||||
const path = $(window.location.hash.replace(/^#/, "") || "/");
|
|
||||||
window.addEventListener("hashchange", () => path(window.location.hash.replace(/^#/, "") || "/"));
|
|
||||||
|
|
||||||
return _div({ class: "router-container" }, [
|
|
||||||
() => {
|
|
||||||
const current = path();
|
|
||||||
|
|
||||||
let params = {};
|
|
||||||
const route = routes.find(r => {
|
|
||||||
const routeParts = r.path.split('/').filter(Boolean);
|
|
||||||
const currentParts = current.split('/').filter(Boolean);
|
|
||||||
if (routeParts.length !== currentParts.length) return false;
|
|
||||||
|
|
||||||
return routeParts.every((part, i) => {
|
|
||||||
if (part.startsWith(':')) {
|
|
||||||
params[part.slice(1)] = currentParts[i];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return part === currentParts[i];
|
|
||||||
});
|
|
||||||
}) || routes.find(r => r.path === "*");
|
|
||||||
|
|
||||||
if (!route) return _h1("404");
|
|
||||||
return typeof route.component === 'function'
|
|
||||||
? route.component(params)
|
|
||||||
: route.component;
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
window._render = (node, target = document.body) => {
|
|
||||||
target.innerHTML = '';
|
|
||||||
const element = typeof node === 'function' ? node() : node;
|
|
||||||
target.appendChild(element);
|
|
||||||
return element;
|
|
||||||
};
|
|
||||||
|
|
||||||
window.Row = (props, children) => _div({
|
|
||||||
...((typeof props === 'object' && !Array.isArray(props)) ? props : {}),
|
|
||||||
style: `display:flex; flex-direction:row; gap:10px; ${props?.style || ''}`
|
|
||||||
}, (Array.isArray(props) ? props : children));
|
|
||||||
|
|
||||||
window.Col = (props, children) => _div({
|
|
||||||
...((typeof props === 'object' && !Array.isArray(props)) ? props : {}),
|
|
||||||
style: `display:flex; flex-direction:column; gap:10px; ${props?.style || ''}`
|
|
||||||
}, (Array.isArray(props) ? props : children));
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user