Fase2
This commit is contained in:
355
sigpro2.js
355
sigpro2.js
@@ -1,195 +1,133 @@
|
|||||||
/**
|
/**
|
||||||
* SigPro - Fine-Grained Reactive UI Framework
|
* SigPro v3.3 - Stable Integrated Engine
|
||||||
* Zero-Dependency Single-File Architecture
|
|
||||||
*/
|
*/
|
||||||
|
const SigPro = (() => {
|
||||||
// --- 1. CORE HELPERS (DRY) ---
|
|
||||||
const doc = typeof document !== "undefined" ? document : null;
|
const doc = typeof document !== "undefined" ? document : null;
|
||||||
const isArr = Array.isArray;
|
const isArr = Array.isArray, assign = Object.assign, isFunc = (f) => typeof f === "function", isObj = (o) => typeof o === "object" && o !== null;
|
||||||
const assign = Object.assign;
|
const ensureNode = (n) => n?._isRuntime ? n.container : (n instanceof Node ? n : doc.createTextNode(String(n ?? "")));
|
||||||
const isFunc = (f) => typeof f === "function";
|
|
||||||
const isObj = (o) => typeof o === "object" && o !== null;
|
|
||||||
|
|
||||||
const createEl = (t) => doc?.createElement(t);
|
// --- INTERNAL STATE & CLEANUP ---
|
||||||
const createText = (t) => doc?.createTextNode(String(t ?? ""));
|
let activeEffect = null, currentOwner = null, isFlushing = false;
|
||||||
const ensureNode = (n) => n?._isRuntime ? n.container : (n instanceof Node ? n : createText(n));
|
const effectQueue = new Set(), MOUNTED_NODES = new WeakMap();
|
||||||
|
|
||||||
// Funciones DRY para reducir repetición en memoria y reactividad
|
const runCleanups = (s) => { s?.forEach(f => f()); s?.clear(); };
|
||||||
const runCleanups = (set) => { set?.forEach(fn => fn()); set?.clear(); };
|
const clearDeps = (e) => { e._deps.forEach(d => d.delete(e)); e._deps.clear(); };
|
||||||
const clearDeps = (effect) => { effect._deps.forEach(d => d.delete(effect)); effect._deps.clear(); };
|
const onUnmount = (fn) => currentOwner && currentOwner.cleanups.add(fn);
|
||||||
|
|
||||||
// --- 2. INTERNAL STATE ---
|
|
||||||
let activeEffect = null;
|
|
||||||
let currentOwner = null;
|
|
||||||
const effectQueue = new Set();
|
|
||||||
let isFlushing = false;
|
|
||||||
const MOUNTED_NODES = new WeakMap();
|
|
||||||
|
|
||||||
// --- 3. SCHEDULER & MEMORY ---
|
|
||||||
const runWithContext = (effect, callback) => {
|
|
||||||
const prev = activeEffect;
|
|
||||||
activeEffect = effect;
|
|
||||||
try { return callback(); }
|
|
||||||
finally { activeEffect = prev; }
|
|
||||||
};
|
|
||||||
|
|
||||||
const cleanupNode = (node) => {
|
const cleanupNode = (node) => {
|
||||||
runCleanups(node._cleanups);
|
if (node._cleanups) runCleanups(node._cleanups);
|
||||||
node.childNodes?.forEach(cleanupNode);
|
node.childNodes?.forEach(cleanupNode);
|
||||||
};
|
};
|
||||||
|
|
||||||
const flushEffects = () => {
|
// --- SCHEDULER ---
|
||||||
if (isFlushing) return;
|
const runWithContext = (e, cb) => {
|
||||||
isFlushing = true;
|
const p = activeEffect; activeEffect = e;
|
||||||
|
try { return cb(); } finally { activeEffect = p; }
|
||||||
|
};
|
||||||
|
|
||||||
|
const flush = () => {
|
||||||
|
if (isFlushing) return; isFlushing = true;
|
||||||
const sorted = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0));
|
const sorted = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0));
|
||||||
effectQueue.clear();
|
effectQueue.clear();
|
||||||
sorted.forEach(effect => !effect._deleted && effect());
|
sorted.forEach(e => !e._deleted && e());
|
||||||
isFlushing = false;
|
isFlushing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const trackUpdate = (subscribers, isTrigger = false) => {
|
const trackUpdate = (subs, trigger = false) => {
|
||||||
if (!isTrigger && activeEffect && !activeEffect._deleted) {
|
if (!trigger && activeEffect && !activeEffect._deleted) {
|
||||||
subscribers.add(activeEffect);
|
subs.add(activeEffect); activeEffect._deps.add(subs);
|
||||||
activeEffect._deps.add(subscribers);
|
} else if (trigger) {
|
||||||
} else if (isTrigger) {
|
subs.forEach(e => {
|
||||||
subscribers.forEach((eff) => {
|
if (e === activeEffect || e._deleted) return;
|
||||||
if (eff === activeEffect || eff._deleted) return;
|
if (e._isComputed) { e.markDirty(); if (e._subs) trackUpdate(e._subs, true); }
|
||||||
if (eff._isComputed) { eff.markDirty(); if (eff._subs) trackUpdate(eff._subs, true); }
|
else effectQueue.add(e);
|
||||||
else effectQueue.add(eff);
|
|
||||||
});
|
});
|
||||||
if (!isFlushing) queueMicrotask(flushEffects);
|
if (!isFlushing) queueMicrotask(flush);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 4. REACTIVITY ---
|
// --- CORE API ---
|
||||||
const $ = (initialValue, storageKey = null) => {
|
const untrack = (fn) => {
|
||||||
const subscribers = new Set();
|
const p = activeEffect; activeEffect = null;
|
||||||
|
try { return fn(); } finally { activeEffect = p; }
|
||||||
|
};
|
||||||
|
|
||||||
if (isFunc(initialValue)) { // Computed
|
const $ = (val, key = null) => {
|
||||||
let cachedValue, isDirty = true;
|
const subs = new Set();
|
||||||
const effect = () => {
|
if (isFunc(val)) {
|
||||||
if (effect._deleted) return;
|
let cache, dirty = true;
|
||||||
clearDeps(effect);
|
const e = () => {
|
||||||
runWithContext(effect, () => {
|
if (e._deleted) return;
|
||||||
const newVal = initialValue();
|
clearDeps(e);
|
||||||
if (!Object.is(cachedValue, newVal) || isDirty) {
|
runWithContext(e, () => {
|
||||||
cachedValue = newVal; isDirty = false; trackUpdate(subscribers, true);
|
const next = val();
|
||||||
}
|
if (!Object.is(cache, next) || dirty) { cache = next; dirty = false; trackUpdate(subs, true); }
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
assign(effect, {
|
assign(e, { _deps: new Set(), _isComputed: true, _subs: subs, _deleted: false, markDirty: () => (dirty = true),
|
||||||
_deps: new Set(), _isComputed: true, _subs: subscribers, _deleted: false,
|
stop: () => { e._deleted = true; clearDeps(e); subs.clear(); } });
|
||||||
markDirty: () => (isDirty = true),
|
onUnmount(e.stop);
|
||||||
stop: () => { effect._deleted = true; clearDeps(effect); subscribers.clear(); }
|
return () => { if (dirty) e(); trackUpdate(subs); return cache; };
|
||||||
});
|
|
||||||
if (currentOwner) currentOwner.cleanups.add(effect.stop);
|
|
||||||
return () => { if (isDirty) effect(); trackUpdate(subscribers); return cachedValue; };
|
|
||||||
}
|
}
|
||||||
|
if (key) try { val = JSON.parse(localStorage.getItem(key)) ?? val; } catch(e){}
|
||||||
// Signal
|
|
||||||
let value = initialValue;
|
|
||||||
if (storageKey) try { value = JSON.parse(localStorage.getItem(storageKey) || "null") ?? value; } catch(e){}
|
|
||||||
|
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
if (args.length) {
|
if (args.length) {
|
||||||
const next = isFunc(args[0]) ? args[0](value) : args[0];
|
const next = isFunc(args[0]) ? args[0](val) : args[0];
|
||||||
if (!Object.is(value, next)) {
|
if (!Object.is(val, next)) {
|
||||||
value = next;
|
val = next; if (key) localStorage.setItem(key, JSON.stringify(val));
|
||||||
if (storageKey) localStorage.setItem(storageKey, JSON.stringify(value));
|
trackUpdate(subs, true);
|
||||||
trackUpdate(subscribers, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trackUpdate(subscribers);
|
trackUpdate(subs); return val;
|
||||||
return value;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const $$ = (object, cache = new WeakMap()) => {
|
const $$ = (obj, cache = new WeakMap()) => {
|
||||||
if (!isObj(object)) return object;
|
if (!isObj(obj)) return obj;
|
||||||
if (cache.has(object)) return cache.get(object);
|
if (cache.has(obj)) return cache.get(obj);
|
||||||
const subs = {};
|
const subs = {};
|
||||||
const proxy = new Proxy(object, {
|
const proxy = new Proxy(obj, {
|
||||||
get: (t, k) => { trackUpdate(subs[k] ??= new Set()); const v = t[k]; return isObj(v) ? $$(v, cache) : v; },
|
get: (t, k) => { trackUpdate(subs[k] ??= new Set()); return isObj(t[k]) ? $$(t[k], cache) : t[k]; },
|
||||||
set: (t, k, v) => { if (!Object.is(t[k], v)) { t[k] = v; if (subs[k]) trackUpdate(subs[k], true); } return true; }
|
set: (t, k, v) => { if (!Object.is(t[k], v)) { t[k] = v; if (subs[k]) trackUpdate(subs[k], true); } return true; }
|
||||||
});
|
});
|
||||||
cache.set(object, proxy); return proxy;
|
cache.set(obj, proxy); return proxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Watch = (target, callbackFn) => {
|
const Watch = (target, cb) => {
|
||||||
const isExplicit = isArr(target);
|
const explicit = isArr(target), runner = () => {
|
||||||
const callback = isExplicit ? callbackFn : target;
|
|
||||||
if (!isFunc(callback)) return () => {};
|
|
||||||
|
|
||||||
const owner = currentOwner;
|
|
||||||
const runner = () => {
|
|
||||||
if (runner._deleted) return;
|
if (runner._deleted) return;
|
||||||
clearDeps(runner); runCleanups(runner._cleanups);
|
clearDeps(runner); runCleanups(runner._cleanups);
|
||||||
runner.depth = activeEffect ? activeEffect.depth + 1 : 0;
|
runner.depth = activeEffect ? activeEffect.depth + 1 : 0;
|
||||||
|
|
||||||
runWithContext(runner, () => {
|
runWithContext(runner, () => {
|
||||||
const prevOwner = currentOwner;
|
const prev = currentOwner; currentOwner = { cleanups: runner._cleanups };
|
||||||
currentOwner = { cleanups: runner._cleanups };
|
explicit ? (untrack(cb), target.forEach(d => isFunc(d) && d())) : cb();
|
||||||
if (isExplicit) { runWithContext(null, callback); target.forEach(d => isFunc(d) && d()); }
|
currentOwner = prev;
|
||||||
else callback();
|
|
||||||
currentOwner = prevOwner;
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
assign(runner, { _deps: new Set(), _cleanups: new Set(), _deleted: false,
|
||||||
assign(runner, {
|
stop: () => { runner._deleted = true; clearDeps(runner); runCleanups(runner._cleanups); } });
|
||||||
_deps: new Set(), _cleanups: new Set(), _deleted: false,
|
onUnmount(runner.stop);
|
||||||
stop: () => {
|
|
||||||
if (runner._deleted) return;
|
|
||||||
runner._deleted = true; effectQueue.delete(runner);
|
|
||||||
clearDeps(runner); runCleanups(runner._cleanups);
|
|
||||||
if (owner) owner.cleanups.delete(runner.stop);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (owner) owner.cleanups.add(runner.stop);
|
|
||||||
runner(); return runner.stop;
|
runner(); return runner.stop;
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 5. DOM & COMPONENTS ---
|
|
||||||
const Render = (renderFn) => {
|
|
||||||
const cleanups = new Set(), prev = currentOwner, container = createEl("div");
|
|
||||||
container.style.display = "contents";
|
|
||||||
currentOwner = { cleanups };
|
|
||||||
|
|
||||||
const process = (res) => {
|
|
||||||
if (!res) return;
|
|
||||||
if (res._isRuntime) { cleanups.add(res.destroy); container.appendChild(res.container); }
|
|
||||||
else if (isArr(res)) res.forEach(process);
|
|
||||||
else container.appendChild(ensureNode(res));
|
|
||||||
};
|
|
||||||
|
|
||||||
try { process(renderFn({ onCleanup: (fn) => cleanups.add(fn) })); }
|
|
||||||
finally { currentOwner = prev; }
|
|
||||||
|
|
||||||
return { _isRuntime: true, container, destroy: () => { runCleanups(cleanups); cleanupNode(container); container.remove(); } };
|
|
||||||
};
|
|
||||||
|
|
||||||
const Tag = (tag, props = {}, children = []) => {
|
const Tag = (tag, props = {}, children = []) => {
|
||||||
if (props instanceof Node || isArr(props) || !isObj(props)) { children = props; props = {}; }
|
if (props instanceof Node || isArr(props) || !isObj(props)) { children = props; props = {}; }
|
||||||
|
|
||||||
const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag);
|
const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag);
|
||||||
const el = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : createEl(tag);
|
const el = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : doc.createElement(tag);
|
||||||
el._cleanups = new Set(); el.onUnmount = (fn) => el._cleanups.add(fn);
|
el._cleanups = new Set();
|
||||||
|
|
||||||
for (let [k, v] of Object.entries(props)) {
|
for (let [k, v] of Object.entries(props)) {
|
||||||
if (k === "ref") { isFunc(v) ? v(el) : (v.current = el); continue; }
|
if (k === "ref") { isFunc(v) ? v(el) : (v.current = el); continue; }
|
||||||
const isSig = isFunc(v);
|
|
||||||
|
|
||||||
if (k.startsWith("on")) {
|
if (k.startsWith("on")) {
|
||||||
const evt = k.slice(2).toLowerCase().split(".")[0];
|
const ev = k.slice(2).toLowerCase(); el.addEventListener(ev, v);
|
||||||
el.addEventListener(evt, v); el._cleanups.add(() => el.removeEventListener(evt, v));
|
el._cleanups.add(() => el.removeEventListener(ev, v));
|
||||||
} else if (isSig) {
|
} else if (isFunc(v)) {
|
||||||
el._cleanups.add(Watch(() => {
|
el._cleanups.add(Watch(() => {
|
||||||
const val = v(), clean = (k === 'src' || k === 'href') && String(val).toLowerCase().includes('javascript:') ? '#' : val;
|
const val = v(), safe = (k === 'src' || k === 'href') && String(val).includes('javascript:') ? '#' : val;
|
||||||
k === "class" ? (el.className = clean || "") : clean == null || clean === false ? el.removeAttribute(k) : el.setAttribute(k, clean === true ? "" : clean);
|
k === "class" ? (el.className = safe || "") : (safe == null || safe === false ? el.removeAttribute(k) : el.setAttribute(k, safe === true ? "" : safe));
|
||||||
}));
|
}));
|
||||||
if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) {
|
if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) {
|
||||||
const evt = k === "checked" ? "change" : "input", handler = (e) => v(e.target[k]);
|
el.addEventListener(k === "checked" ? "change" : "input", (e) => v(e.target[k]));
|
||||||
el.addEventListener(evt, handler); el._cleanups.add(() => el.removeEventListener(evt, handler));
|
|
||||||
}
|
}
|
||||||
} else el.setAttribute(k, v);
|
} else el.setAttribute(k, v);
|
||||||
}
|
}
|
||||||
@@ -197,128 +135,113 @@ const Tag = (tag, props = {}, children = []) => {
|
|||||||
const append = (c) => {
|
const append = (c) => {
|
||||||
if (isArr(c)) return c.forEach(append);
|
if (isArr(c)) return c.forEach(append);
|
||||||
if (isFunc(c)) {
|
if (isFunc(c)) {
|
||||||
const marker = createText(""); el.appendChild(marker);
|
const m = doc.createTextNode(""); el.appendChild(m); let curr = [];
|
||||||
let curr = [];
|
|
||||||
el._cleanups.add(Watch(() => {
|
el._cleanups.add(Watch(() => {
|
||||||
const res = c(), next = (isArr(res) ? res : [res]).map(ensureNode);
|
const res = c(), next = (isArr(res) ? res : [res]).map(ensureNode);
|
||||||
curr.forEach(n => { cleanupNode(n); n.remove(); });
|
curr.forEach(n => { if (n instanceof Node) { cleanupNode(n); n.remove(); } });
|
||||||
next.forEach(n => marker.parentNode?.insertBefore(n, marker));
|
next.forEach(n => m.parentNode?.insertBefore(n, m)); curr = next;
|
||||||
curr = next;
|
|
||||||
}));
|
}));
|
||||||
} else el.appendChild(ensureNode(c));
|
} else el.appendChild(ensureNode(c));
|
||||||
};
|
};
|
||||||
append(children); return el;
|
append(children); return el;
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 6. CONTROL FLOW ---
|
const Render = (fn) => {
|
||||||
const If = (condition, thenVal, otherwiseVal = null, transition = null) => {
|
const cleanups = new Set(), prev = currentOwner, container = doc.createElement("div");
|
||||||
const marker = createText(""), container = Tag("div", { style: "display:contents" }, [marker]);
|
container.style.display = "contents"; currentOwner = { cleanups };
|
||||||
let currentView = null, lastState = null;
|
const res = fn({ onCleanup: (f) => cleanups.add(f) });
|
||||||
|
(isArr(res) ? res : [res]).forEach(r => container.appendChild(ensureNode(r)));
|
||||||
Watch(() => {
|
currentOwner = prev;
|
||||||
const state = !!(isFunc(condition) ? condition() : condition);
|
return { _isRuntime: true, container, destroy: () => { runCleanups(cleanups); cleanupNode(container); container.remove(); } };
|
||||||
if (state === lastState) return;
|
|
||||||
lastState = state;
|
|
||||||
|
|
||||||
const dispose = () => { currentView?.destroy(); currentView = null; };
|
|
||||||
if (currentView && !state && transition?.out) transition.out(currentView.container, dispose); else dispose();
|
|
||||||
|
|
||||||
const branch = state ? thenVal : otherwiseVal;
|
|
||||||
if (branch) {
|
|
||||||
currentView = Render(() => isFunc(branch) ? branch() : branch);
|
|
||||||
container.insertBefore(currentView.container, marker);
|
|
||||||
if (state && transition?.in) transition.in(currentView.container);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return container;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const For = (source, renderFn, keyFn, tag = "div", props = { style: "display:contents" }) => {
|
const If = (cond, t, f = null, trans = null) => {
|
||||||
const marker = createText(""), container = Tag(tag, props, [marker]);
|
const m = doc.createTextNode(""), root = Tag("div", { style: "display:contents" }, [m]);
|
||||||
let viewCache = new Map();
|
let view = null, last = null;
|
||||||
|
|
||||||
Watch(() => {
|
Watch(() => {
|
||||||
const items = (isFunc(source) ? source() : source) || [], nextCache = new Map(), order = [];
|
const s = !!(isFunc(cond) ? cond() : cond);
|
||||||
|
if (s === last) return; last = s;
|
||||||
|
const dispose = () => { if(view) { view.destroy(); view = null; } };
|
||||||
|
if (view && !s && trans?.out) trans.out(view.container, dispose); else dispose();
|
||||||
|
const b = s ? t : f;
|
||||||
|
if (b) {
|
||||||
|
view = Render(() => isFunc(b) ? b() : b);
|
||||||
|
root.insertBefore(view.container, m);
|
||||||
|
if (trans?.in) trans.in(view.container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return root;
|
||||||
|
};
|
||||||
|
|
||||||
|
const For = (src, itemFn, keyFn) => {
|
||||||
|
const m = doc.createTextNode(""), root = Tag("div", { style: "display:contents" }, [m]);
|
||||||
|
let cache = new Map();
|
||||||
|
Watch(() => {
|
||||||
|
const items = (isFunc(src) ? src() : src) || [], next = new Map(), order = [];
|
||||||
items.forEach((item, i) => {
|
items.forEach((item, i) => {
|
||||||
const key = keyFn ? keyFn(item, i) : i;
|
const k = keyFn ? keyFn(item, i) : i;
|
||||||
let view = viewCache.get(key);
|
let v = cache.get(k) || Render(() => itemFn(item, i));
|
||||||
if (!view) {
|
cache.delete(k); next.set(k, v); order.push(k);
|
||||||
const res = renderFn(item, i);
|
|
||||||
view = res instanceof Node ? { container: res, destroy: () => { cleanupNode(res); res.remove(); } } : Render(() => res);
|
|
||||||
}
|
|
||||||
viewCache.delete(key); nextCache.set(key, view); order.push(key);
|
|
||||||
});
|
});
|
||||||
|
cache.forEach(v => v.destroy());
|
||||||
viewCache.forEach(v => v.destroy());
|
let anchor = m;
|
||||||
let anchor = marker;
|
|
||||||
for (let i = order.length - 1; i >= 0; i--) {
|
for (let i = order.length - 1; i >= 0; i--) {
|
||||||
const view = nextCache.get(order[i]);
|
const v = next.get(order[i]);
|
||||||
if (view.container.nextSibling !== anchor) container.insertBefore(view.container, anchor);
|
if (v.container.nextSibling !== anchor) root.insertBefore(v.container, anchor);
|
||||||
anchor = view.container;
|
anchor = v.container;
|
||||||
}
|
}
|
||||||
viewCache = nextCache;
|
cache = next;
|
||||||
});
|
});
|
||||||
return container;
|
return root;
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 7. ROUTER & MOUNT ---
|
// --- ROUTER SYSTEM ---
|
||||||
const Router = (routes) => {
|
const Router = (routes) => {
|
||||||
const path = $(window.location.hash.replace(/^#/, "") || "/");
|
const getHash = () => window.location.hash.replace(/^#/, "") || "/";
|
||||||
window.addEventListener("hashchange", () => path(window.location.hash.replace(/^#/, "") || "/"));
|
const path = $(getHash());
|
||||||
const outlet = Tag("div", { class: "router-transition" });
|
window.addEventListener("hashchange", () => path(getHash()));
|
||||||
|
const outlet = Tag("div", { class: "router-outlet" });
|
||||||
let currentView = null;
|
let currentView = null;
|
||||||
|
|
||||||
Watch([path], async () => {
|
Watch([path], async () => {
|
||||||
const current = path();
|
const cur = path(), route = routes.find(r => {
|
||||||
const route = routes.find(r => {
|
const p1 = r.path.split("/").filter(Boolean), p2 = cur.split("/").filter(Boolean);
|
||||||
const p1 = r.path.split("/").filter(Boolean), p2 = current.split("/").filter(Boolean);
|
return p1.length === p2.length && p1.every((p, i) => p.startsWith(":") || p === p2[i]);
|
||||||
return p1.length === p2.length && p1.every((part, i) => part.startsWith(":") || part === p2[i]);
|
|
||||||
}) || routes.find(r => r.path === "*");
|
}) || routes.find(r => r.path === "*");
|
||||||
|
|
||||||
if (route) {
|
if (route) {
|
||||||
let comp = route.component;
|
let comp = route.component;
|
||||||
if (isFunc(comp) && comp.toString().includes('import')) comp = (await comp()).default || await comp();
|
if (isFunc(comp) && comp.toString().includes('import')) comp = (await comp()).default;
|
||||||
|
|
||||||
const params = {};
|
const params = {};
|
||||||
route.path.split("/").filter(Boolean).forEach((p, i) => { if (p.startsWith(":")) params[p.slice(1)] = current.split("/").filter(Boolean)[i]; });
|
route.path.split("/").filter(Boolean).forEach((p, i) => { if (p.startsWith(":")) params[p.slice(1)] = cur.split("/").filter(Boolean)[i]; });
|
||||||
|
|
||||||
currentView?.destroy();
|
currentView?.destroy();
|
||||||
if (Router.params) Router.params(params);
|
Router.params(params);
|
||||||
|
currentView = Render(() => isFunc(comp) ? comp(params) : comp);
|
||||||
currentView = Render(() => { try { return isFunc(comp) ? comp(params) : comp; } catch (e) { return Tag("div", {}, "Error"); } });
|
|
||||||
outlet.appendChild(currentView.container);
|
outlet.appendChild(currentView.container);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return outlet;
|
return outlet;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// router utils
|
||||||
Router.params = $({});
|
Router.params = $({});
|
||||||
Router.to = (p) => window.location.hash = p.replace(/^#?\/?/, "#/");
|
Router.to = (p) => window.location.hash = p.replace(/^#?\/?/, "#/");
|
||||||
Router.back = () => window.history.back();
|
Router.back = () => window.history.back();
|
||||||
Router.path = () => window.location.hash.replace(/^#/, "") || "/";
|
Router.path = () => window.location.hash.replace(/^#/, "") || "/";
|
||||||
|
|
||||||
const Mount = (comp, target) => {
|
const Mount = (comp, target) => {
|
||||||
const el = typeof target === "string" ? doc.querySelector(target) : target;
|
const t = typeof target === "string" ? doc.querySelector(target) : target;
|
||||||
if (!el) return;
|
if (!t) return; if (MOUNTED_NODES.has(t)) MOUNTED_NODES.get(t).destroy();
|
||||||
if (MOUNTED_NODES.has(el)) MOUNTED_NODES.get(el).destroy();
|
const inst = Render(isFunc(comp) ? comp : () => comp);
|
||||||
const instance = Render(isFunc(comp) ? comp : () => comp);
|
t.replaceChildren(inst.container); MOUNTED_NODES.set(t, inst); return inst;
|
||||||
el.replaceChildren(instance.container);
|
|
||||||
MOUNTED_NODES.set(el, instance);
|
|
||||||
return instance;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const SigPro = { $, $$, Render, Watch, Tag, If, For, Router, Mount };
|
return { $, $$, Watch, Tag, Render, If, For, Router, Mount, untrack, onUnmount };
|
||||||
|
})();
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
assign(window, SigPro);
|
Object.assign(window, SigPro);
|
||||||
"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer address ul ol li dl dt dd a em strong small i b u mark time sub sup pre code blockquote details summary dialog form label input textarea select button option fieldset legend table thead tbody tfoot tr th td caption img video audio canvas svg iframe picture source progress meter"
|
"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg"
|
||||||
.split(" ").forEach(t => {
|
.split(" ").forEach(t => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
||||||
const h = t[0].toUpperCase() + t.slice(1);
|
|
||||||
if (!(h in window)) window[h] = (p, c) => Tag(t, p, c);
|
|
||||||
});
|
|
||||||
window.SigPro = Object.freeze(SigPro);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { $, $$, Render, Watch, Tag, If, For, Router, Mount };
|
|
||||||
export default SigPro;
|
export default SigPro;
|
||||||
Reference in New Issue
Block a user