This commit is contained in:
2026-04-08 00:33:20 +02:00
parent d864765359
commit 61635a23f7

View File

@@ -18,6 +18,7 @@ const tick = () => {
let activeEffect = null; let activeEffect = null;
let currentContext = null; let currentContext = null;
const nodeContexts = new WeakMap(); const nodeContexts = new WeakMap();
const reactiveCache = new WeakMap(); // <-- Añadido para que Reactive funcione
// --- Seguridad --- // --- Seguridad ---
const DANGEROUS = /^(javascript|data|vbscript):/i; const DANGEROUS = /^(javascript|data|vbscript):/i;
@@ -85,7 +86,7 @@ export const Reactive = (obj) => {
const subs = {}; const subs = {};
const proxy = new Proxy(obj, { const proxy = new Proxy(obj, {
get(t, k) { get(t, k) {
track(subs[k] ??= new Set()); // <--- Uso de track track(subs[k] ??= new Set());
const val = t[k]; const val = t[k];
return (val && typeof val === 'object') ? Reactive(val) : val; return (val && typeof val === 'object') ? Reactive(val) : val;
}, },
@@ -145,22 +146,16 @@ const append = (parent, child) => {
.filter(n => n != null) .filter(n => n != null)
.map(n => isNode(n) ? n : document.createTextNode(String(n))); .map(n => isNode(n) ? n : document.createTextNode(String(n)));
// Diffing Bidireccional (Estilo Sigwork) const nextSet = new Set(next);
const oldNodes = nodes.filter(n => { nodes.forEach(n => { if (!nextSet.has(n)) destroy(n); });
const keep = next.includes(n);
if (!keep) destroy(n);
return keep;
});
const oldIdxs = new Map(oldNodes.map((n, i) => [n, i])); const oldIdxs = new Map(nodes.filter(n => nextSet.has(n)).map((n, i) => [n, i]));
let p = oldNodes.length - 1; let p = oldIdxs.size - 1;
for (let i = next.length - 1; i >= 0; i--) { for (let i = next.length - 1; i >= 0; i--) {
const node = next[i]; const node = next[i];
const ref = next[i + 1] || anchor; const ref = next[i + 1] || anchor;
if (!oldIdxs.has(node)) { if (!oldIdxs.has(node) || nodes[p] !== node) {
parent.insertBefore(node, ref);
} else if (oldNodes[p] !== node) {
parent.insertBefore(node, ref); parent.insertBefore(node, ref);
} else { } else {
p--; p--;
@@ -176,11 +171,8 @@ const append = (parent, child) => {
export const h = (tag, props = {}, ...children) => { export const h = (tag, props = {}, ...children) => {
props = props || {}; props = props || {};
const flatChildren = children.flat(Infinity); const flatChildren = children.flat(Infinity);
// 4. Fragments (Tag nulo o vacío)
if (!tag) return () => flatChildren; if (!tag) return () => flatChildren;
// 1. Componentes (Funciones)
if (typeof tag === 'function') { if (typeof tag === 'function') {
const context = { mount: [], unmount: [], Share: {}, parent: currentContext, cleanups: new Set() }; const context = { mount: [], unmount: [], Share: {}, parent: currentContext, cleanups: new Set() };
const prevCtx = currentContext; const prevCtx = currentContext;
@@ -192,9 +184,8 @@ export const h = (tag, props = {}, ...children) => {
return el; return el;
} }
// 1. Soporte SVG
let el; let el;
const isSVG = tag === 'svg' || tag === 'path' || tag === 'circle' || tag === 'rect' || tag === 'g'; const isSVG = /^(svg|path|circle|rect|g)$/i.test(tag);
if (isSVG) { if (isSVG) {
el = document.createElementNS("http://www.w3.org/2000/svg", tag); el = document.createElementNS("http://www.w3.org/2000/svg", tag);
} else { } else {
@@ -206,7 +197,6 @@ export const h = (tag, props = {}, ...children) => {
if (key.startsWith('on')) { if (key.startsWith('on')) {
el.addEventListener(key.slice(2).toLowerCase(), val); el.addEventListener(key.slice(2).toLowerCase(), val);
} }
// 2. Soporte para Refs
else if (key === 'ref') { else if (key === 'ref') {
if (typeof val === 'function') val(el); if (typeof val === 'function') val(el);
else if (val && val._isSig) val.value = el; else if (val && val._isSig) val.value = el;
@@ -236,8 +226,7 @@ export const For = (list, key, render) => {
const nextCache = new Map(); const nextCache = new Map();
const nodes = items.map((item, i) => { const nodes = items.map((item, i) => {
const k = key ? key(item, i) : (item.id || i); const k = key ? key(item, i) : (item.id || i);
let node = cache.get(k); let node = cache.get(k) || render(item, i);
if (!node) node = render(item, i);
nextCache.set(k, node); nextCache.set(k, node);
return node; return node;
}); });
@@ -249,7 +238,7 @@ export const For = (list, key, render) => {
// --- Router --- // --- Router ---
export const Router = (routes) => { export const Router = (routes) => {
const path = Signal(window.location.hash.replace(/^#/, '') || '/'); const path = Signal(window.location.hash.replace(/^#/, '') || '/');
window.addEventListener('hashchange', () => path.value = window.location.hash.replace(/^#/, '') || '/'); window.onhashchange = () => path.value = window.location.hash.replace(/^#/, '') || '/';
const outlet = h('div', { class: 'router-outlet' }); const outlet = h('div', { class: 'router-outlet' });
let currentView = null; let currentView = null;
Effect(() => { Effect(() => {
@@ -264,9 +253,8 @@ export const Router = (routes) => {
}; };
export const Mount = (root, target) => { export const Mount = (root, target) => {
const container = typeof target === 'string' ? document.querySelector(target) : target;
const el = typeof root === 'function' ? root() : root; const el = typeof root === 'function' ? root() : root;
container.replaceChildren(el); (typeof target === 'string' ? document.querySelector(target) : target).replaceChildren(el);
return () => destroy(el); return () => destroy(el);
}; };
@@ -282,4 +270,4 @@ export const Use = (key, def) => {
return def; return def;
}; };
export default { Signal, Computed, Storage, Effect, h, If, For, Router, Mount, Share, Use, onMount, onUnmount }; export default { Signal, Reactive, Computed, Storage, Effect, h, If, For, Router, Mount, Share, Use, onMount, onUnmount };