1.2.12 Fix computed problem
This commit is contained in:
140
dist/sigpro.esm.js
vendored
140
dist/sigpro.esm.js
vendored
@@ -108,11 +108,11 @@ var trackUpdate = (subs, trigger = false) => {
|
|||||||
if (!trigger && activeEffect && !activeEffect._disposed) {
|
if (!trigger && activeEffect && !activeEffect._disposed) {
|
||||||
subs.add(activeEffect);
|
subs.add(activeEffect);
|
||||||
(activeEffect._deps ||= new Set).add(subs);
|
(activeEffect._deps ||= new Set).add(subs);
|
||||||
} else if (trigger) {
|
} else if (trigger && subs.size > 0) {
|
||||||
let hasQueue = false;
|
let hasQueue = false;
|
||||||
subs.forEach((e) => {
|
for (const e of subs) {
|
||||||
if (e === activeEffect || e._disposed)
|
if (e === activeEffect || e._disposed)
|
||||||
return;
|
continue;
|
||||||
if (e._isComputed) {
|
if (e._isComputed) {
|
||||||
e._dirty = true;
|
e._dirty = true;
|
||||||
if (e._subs)
|
if (e._subs)
|
||||||
@@ -121,29 +121,29 @@ var trackUpdate = (subs, trigger = false) => {
|
|||||||
effectQueue.add(e);
|
effectQueue.add(e);
|
||||||
hasQueue = true;
|
hasQueue = true;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
if (hasQueue && !isFlushing && batchDepth === 0)
|
if (hasQueue && !isFlushing && batchDepth === 0)
|
||||||
queueMicrotask(flush);
|
queueMicrotask(flush);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var $ = (val, key = null) => {
|
var $ = (val2, key = null) => {
|
||||||
const subs = new Set;
|
const subs = new Set;
|
||||||
if (isFunc(val)) {
|
if (isFunc(val2)) {
|
||||||
let cache, dirty = true;
|
let cache;
|
||||||
const computed = () => {
|
const computed = () => {
|
||||||
if (dirty) {
|
if (computed._dirty) {
|
||||||
const prev = activeEffect;
|
const prev = activeEffect;
|
||||||
activeEffect = computed;
|
activeEffect = computed;
|
||||||
try {
|
try {
|
||||||
const next = val();
|
const next = val2();
|
||||||
if (!Object.is(cache, next)) {
|
if (!Object.is(cache, next)) {
|
||||||
cache = next;
|
cache = next;
|
||||||
dirty = false;
|
|
||||||
trackUpdate(subs, true);
|
trackUpdate(subs, true);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
activeEffect = prev;
|
activeEffect = prev;
|
||||||
}
|
}
|
||||||
|
computed._dirty = false;
|
||||||
}
|
}
|
||||||
trackUpdate(subs);
|
trackUpdate(subs);
|
||||||
return cache;
|
return cache;
|
||||||
@@ -153,44 +153,35 @@ var $ = (val, key = null) => {
|
|||||||
computed._dirty = true;
|
computed._dirty = true;
|
||||||
computed._deps = null;
|
computed._deps = null;
|
||||||
computed._disposed = false;
|
computed._disposed = false;
|
||||||
computed.markDirty = () => {
|
computed.stop = () => {};
|
||||||
dirty = true;
|
|
||||||
};
|
|
||||||
computed.stop = () => {
|
|
||||||
computed._disposed = true;
|
|
||||||
if (computed._deps) {
|
|
||||||
computed._deps.forEach((depSet) => depSet.delete(computed));
|
|
||||||
computed._deps.clear();
|
|
||||||
}
|
|
||||||
subs.clear();
|
|
||||||
};
|
|
||||||
if (activeOwner)
|
if (activeOwner)
|
||||||
onUnmount(computed.stop);
|
onUnmount(computed.stop);
|
||||||
return computed;
|
return computed;
|
||||||
}
|
}
|
||||||
if (key)
|
if (key)
|
||||||
try {
|
try {
|
||||||
val = JSON.parse(localStorage.getItem(key)) ?? val;
|
val2 = JSON.parse(localStorage.getItem(key)) ?? val2;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
if (args.length) {
|
if (args.length) {
|
||||||
const next = isFunc(args[0]) ? args[0](val) : args[0];
|
const next = isFunc(args[0]) ? args[0](val2) : args[0];
|
||||||
if (!Object.is(val, next)) {
|
if (!Object.is(val2, next)) {
|
||||||
val = next;
|
val2 = next;
|
||||||
if (key)
|
if (key)
|
||||||
localStorage.setItem(key, JSON.stringify(val));
|
localStorage.setItem(key, JSON.stringify(val2));
|
||||||
trackUpdate(subs, true);
|
trackUpdate(subs, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trackUpdate(subs);
|
trackUpdate(subs);
|
||||||
return val;
|
return val2;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
var $$ = (target) => {
|
var $$ = (target) => {
|
||||||
if (!isObj(target))
|
if (!isObj(target))
|
||||||
return target;
|
return target;
|
||||||
if (proxyCache.has(target))
|
let proxy = proxyCache.get(target);
|
||||||
return proxyCache.get(target);
|
if (proxy)
|
||||||
|
return proxy;
|
||||||
const subsMap = new Map;
|
const subsMap = new Map;
|
||||||
const getSubs = (k) => {
|
const getSubs = (k) => {
|
||||||
let s = subsMap.get(k);
|
let s = subsMap.get(k);
|
||||||
@@ -198,28 +189,30 @@ var $$ = (target) => {
|
|||||||
subsMap.set(k, s = new Set);
|
subsMap.set(k, s = new Set);
|
||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
const proxy = new Proxy(target, {
|
proxy = new Proxy(target, {
|
||||||
get(t, k) {
|
get(t, k, receiver) {
|
||||||
trackUpdate(getSubs(k));
|
if (typeof k !== "symbol")
|
||||||
return $$(t[k]);
|
trackUpdate(getSubs(k));
|
||||||
|
return $$(Reflect.get(t, k, receiver));
|
||||||
},
|
},
|
||||||
set(t, k, v) {
|
set(t, k, v, receiver) {
|
||||||
const isNew = !(k in t);
|
const isNew = !Reflect.has(t, k);
|
||||||
if (!Object.is(t[k], v)) {
|
const oldV = Reflect.get(t, k, receiver);
|
||||||
t[k] = v;
|
const result = Reflect.set(t, k, v, receiver);
|
||||||
|
if (result && !Object.is(oldV, v)) {
|
||||||
trackUpdate(getSubs(k), true);
|
trackUpdate(getSubs(k), true);
|
||||||
if (isNew)
|
if (isNew)
|
||||||
trackUpdate(getSubs(ITER), true);
|
trackUpdate(getSubs(ITER), true);
|
||||||
}
|
}
|
||||||
return true;
|
return result;
|
||||||
},
|
},
|
||||||
deleteProperty(t, k) {
|
deleteProperty(t, k) {
|
||||||
const res = Reflect.deleteProperty(t, k);
|
const result = Reflect.deleteProperty(t, k);
|
||||||
if (res) {
|
if (result) {
|
||||||
trackUpdate(getSubs(k), true);
|
trackUpdate(getSubs(k), true);
|
||||||
trackUpdate(getSubs(ITER), true);
|
trackUpdate(getSubs(ITER), true);
|
||||||
}
|
}
|
||||||
return res;
|
return result;
|
||||||
},
|
},
|
||||||
ownKeys(t) {
|
ownKeys(t) {
|
||||||
trackUpdate(getSubs(ITER));
|
trackUpdate(getSubs(ITER));
|
||||||
@@ -254,17 +247,17 @@ var cleanupNode = (node) => {
|
|||||||
};
|
};
|
||||||
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
||||||
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
|
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
|
||||||
var validateAttr = (key, val) => {
|
var validateAttr = (key, val2) => {
|
||||||
if (val == null || val === false)
|
if (val2 == null || val2 === false)
|
||||||
return null;
|
return null;
|
||||||
if (isDangerousAttr(key)) {
|
if (isDangerousAttr(key)) {
|
||||||
const sVal = String(val);
|
const sVal = String(val2);
|
||||||
if (DANGEROUS_PROTOCOL.test(sVal)) {
|
if (DANGEROUS_PROTOCOL.test(sVal)) {
|
||||||
console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`);
|
console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`);
|
||||||
return "#";
|
return "#";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return val;
|
return val2;
|
||||||
};
|
};
|
||||||
var Tag = (tag, props = {}, children = []) => {
|
var Tag = (tag, props = {}, children = []) => {
|
||||||
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
||||||
@@ -296,7 +289,7 @@ var Tag = (tag, props = {}, children = []) => {
|
|||||||
isArr(node) ? node.forEach(attach) : attach(node);
|
isArr(node) ? node.forEach(attach) : attach(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag);
|
const isSVG = /^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(tag);
|
||||||
const el = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : doc.createElement(tag);
|
const el = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : doc.createElement(tag);
|
||||||
el._cleanups = new Set;
|
el._cleanups = new Set;
|
||||||
for (let k in props) {
|
for (let k in props) {
|
||||||
@@ -307,6 +300,11 @@ var Tag = (tag, props = {}, children = []) => {
|
|||||||
isFunc(v) ? v(el) : v.current = el;
|
isFunc(v) ? v(el) : v.current = el;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (isSVG && k.startsWith("xlink:")) {
|
||||||
|
const ns = "http://www.w3.org/1999/xlink";
|
||||||
|
val == null ? el.removeAttributeNS(ns, k.slice(6)) : el.setAttributeNS(ns, k.slice(6), val);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (k.startsWith("on")) {
|
if (k.startsWith("on")) {
|
||||||
const ev = k.slice(2).toLowerCase();
|
const ev = k.slice(2).toLowerCase();
|
||||||
el.addEventListener(ev, v);
|
el.addEventListener(ev, v);
|
||||||
@@ -315,15 +313,15 @@ var Tag = (tag, props = {}, children = []) => {
|
|||||||
onUnmount(off);
|
onUnmount(off);
|
||||||
} else if (isFunc(v)) {
|
} else if (isFunc(v)) {
|
||||||
const effect = createEffect(() => {
|
const effect = createEffect(() => {
|
||||||
const val = validateAttr(k, v());
|
const val2 = validateAttr(k, v());
|
||||||
if (k === "class")
|
if (k === "class")
|
||||||
el.className = val || "";
|
el.className = val2 || "";
|
||||||
else if (val == null)
|
else if (val2 == null)
|
||||||
el.removeAttribute(k);
|
el.removeAttribute(k);
|
||||||
else if (k in el && !isSVG)
|
else if (k in el && !isSVG)
|
||||||
el[k] = val;
|
el[k] = val2;
|
||||||
else
|
else
|
||||||
el.setAttribute(k, val === true ? "" : val);
|
el.setAttribute(k, val2 === true ? "" : val2);
|
||||||
});
|
});
|
||||||
effect();
|
effect();
|
||||||
el._cleanups.add(() => dispose(effect));
|
el._cleanups.add(() => dispose(effect));
|
||||||
@@ -333,12 +331,12 @@ var Tag = (tag, props = {}, children = []) => {
|
|||||||
el.addEventListener(evType, (ev) => v(ev.target[k]));
|
el.addEventListener(evType, (ev) => v(ev.target[k]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const val = validateAttr(k, v);
|
const val2 = validateAttr(k, v);
|
||||||
if (val != null) {
|
if (val2 != null) {
|
||||||
if (k in el && !isSVG)
|
if (k in el && !isSVG)
|
||||||
el[k] = val;
|
el[k] = val2;
|
||||||
else
|
else
|
||||||
el.setAttribute(k, val === true ? "" : val);
|
el.setAttribute(k, val2 === true ? "" : val2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -506,6 +504,35 @@ 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(/^#/, "") || "/";
|
||||||
|
var Anim = (show, render, { enter, leave } = {}) => {
|
||||||
|
const wrap = Tag("div", { style: "display:contents" });
|
||||||
|
let view = null;
|
||||||
|
const wait = (el, cb) => {
|
||||||
|
let done = false;
|
||||||
|
const finish = () => !done && (done = true, cb());
|
||||||
|
if (!el)
|
||||||
|
return finish();
|
||||||
|
"transitionend animationend".split(" ").map((e) => el.addEventListener(e, finish, { once: true }));
|
||||||
|
setTimeout(finish, 500);
|
||||||
|
};
|
||||||
|
Watch(show, (on) => {
|
||||||
|
if (on && !view) {
|
||||||
|
const el = (view = Render(render)).container.firstChild;
|
||||||
|
wrap.appendChild(view.container);
|
||||||
|
if (enter && el) {
|
||||||
|
el.classList.add(enter);
|
||||||
|
el.clientTop;
|
||||||
|
el.classList.add(enter + "-active");
|
||||||
|
wait(el, () => el.classList.remove(enter, enter + "-active"));
|
||||||
|
}
|
||||||
|
} else if (!on && view) {
|
||||||
|
const el = view.container.firstChild;
|
||||||
|
const del = () => (view?.destroy(), view = null);
|
||||||
|
leave && el ? (el.classList.add(leave), wait(el, del)) : del();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return onUnmount(() => view?.destroy()), wrap;
|
||||||
|
};
|
||||||
var Mount = (comp, target) => {
|
var Mount = (comp, target) => {
|
||||||
const t = typeof target === "string" ? doc.querySelector(target) : target;
|
const t = typeof target === "string" ? doc.querySelector(target) : target;
|
||||||
if (!t)
|
if (!t)
|
||||||
@@ -517,7 +544,7 @@ var Mount = (comp, target) => {
|
|||||||
MOUNTED_NODES.set(t, inst);
|
MOUNTED_NODES.set(t, inst);
|
||||||
return inst;
|
return inst;
|
||||||
};
|
};
|
||||||
var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Batch });
|
var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Anim, Batch });
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
Object.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 ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((t) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
"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) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
||||||
@@ -533,6 +560,7 @@ export {
|
|||||||
If,
|
If,
|
||||||
For,
|
For,
|
||||||
Batch,
|
Batch,
|
||||||
|
Anim,
|
||||||
$$,
|
$$,
|
||||||
$
|
$
|
||||||
};
|
};
|
||||||
|
|||||||
2
dist/sigpro.esm.min.js
vendored
2
dist/sigpro.esm.min.js
vendored
File diff suppressed because one or more lines are too long
140
dist/sigpro.js
vendored
140
dist/sigpro.js
vendored
@@ -40,6 +40,7 @@
|
|||||||
If: () => If,
|
If: () => If,
|
||||||
For: () => For,
|
For: () => For,
|
||||||
Batch: () => Batch,
|
Batch: () => Batch,
|
||||||
|
Anim: () => Anim,
|
||||||
$$: () => $$,
|
$$: () => $$,
|
||||||
$: () => $
|
$: () => $
|
||||||
});
|
});
|
||||||
@@ -154,11 +155,11 @@
|
|||||||
if (!trigger && activeEffect && !activeEffect._disposed) {
|
if (!trigger && activeEffect && !activeEffect._disposed) {
|
||||||
subs.add(activeEffect);
|
subs.add(activeEffect);
|
||||||
(activeEffect._deps ||= new Set).add(subs);
|
(activeEffect._deps ||= new Set).add(subs);
|
||||||
} else if (trigger) {
|
} else if (trigger && subs.size > 0) {
|
||||||
let hasQueue = false;
|
let hasQueue = false;
|
||||||
subs.forEach((e) => {
|
for (const e of subs) {
|
||||||
if (e === activeEffect || e._disposed)
|
if (e === activeEffect || e._disposed)
|
||||||
return;
|
continue;
|
||||||
if (e._isComputed) {
|
if (e._isComputed) {
|
||||||
e._dirty = true;
|
e._dirty = true;
|
||||||
if (e._subs)
|
if (e._subs)
|
||||||
@@ -167,29 +168,29 @@
|
|||||||
effectQueue.add(e);
|
effectQueue.add(e);
|
||||||
hasQueue = true;
|
hasQueue = true;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
if (hasQueue && !isFlushing && batchDepth === 0)
|
if (hasQueue && !isFlushing && batchDepth === 0)
|
||||||
queueMicrotask(flush);
|
queueMicrotask(flush);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var $ = (val, key = null) => {
|
var $ = (val2, key = null) => {
|
||||||
const subs = new Set;
|
const subs = new Set;
|
||||||
if (isFunc(val)) {
|
if (isFunc(val2)) {
|
||||||
let cache, dirty = true;
|
let cache;
|
||||||
const computed = () => {
|
const computed = () => {
|
||||||
if (dirty) {
|
if (computed._dirty) {
|
||||||
const prev = activeEffect;
|
const prev = activeEffect;
|
||||||
activeEffect = computed;
|
activeEffect = computed;
|
||||||
try {
|
try {
|
||||||
const next = val();
|
const next = val2();
|
||||||
if (!Object.is(cache, next)) {
|
if (!Object.is(cache, next)) {
|
||||||
cache = next;
|
cache = next;
|
||||||
dirty = false;
|
|
||||||
trackUpdate(subs, true);
|
trackUpdate(subs, true);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
activeEffect = prev;
|
activeEffect = prev;
|
||||||
}
|
}
|
||||||
|
computed._dirty = false;
|
||||||
}
|
}
|
||||||
trackUpdate(subs);
|
trackUpdate(subs);
|
||||||
return cache;
|
return cache;
|
||||||
@@ -199,44 +200,35 @@
|
|||||||
computed._dirty = true;
|
computed._dirty = true;
|
||||||
computed._deps = null;
|
computed._deps = null;
|
||||||
computed._disposed = false;
|
computed._disposed = false;
|
||||||
computed.markDirty = () => {
|
computed.stop = () => {};
|
||||||
dirty = true;
|
|
||||||
};
|
|
||||||
computed.stop = () => {
|
|
||||||
computed._disposed = true;
|
|
||||||
if (computed._deps) {
|
|
||||||
computed._deps.forEach((depSet) => depSet.delete(computed));
|
|
||||||
computed._deps.clear();
|
|
||||||
}
|
|
||||||
subs.clear();
|
|
||||||
};
|
|
||||||
if (activeOwner)
|
if (activeOwner)
|
||||||
onUnmount(computed.stop);
|
onUnmount(computed.stop);
|
||||||
return computed;
|
return computed;
|
||||||
}
|
}
|
||||||
if (key)
|
if (key)
|
||||||
try {
|
try {
|
||||||
val = JSON.parse(localStorage.getItem(key)) ?? val;
|
val2 = JSON.parse(localStorage.getItem(key)) ?? val2;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
if (args.length) {
|
if (args.length) {
|
||||||
const next = isFunc(args[0]) ? args[0](val) : args[0];
|
const next = isFunc(args[0]) ? args[0](val2) : args[0];
|
||||||
if (!Object.is(val, next)) {
|
if (!Object.is(val2, next)) {
|
||||||
val = next;
|
val2 = next;
|
||||||
if (key)
|
if (key)
|
||||||
localStorage.setItem(key, JSON.stringify(val));
|
localStorage.setItem(key, JSON.stringify(val2));
|
||||||
trackUpdate(subs, true);
|
trackUpdate(subs, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trackUpdate(subs);
|
trackUpdate(subs);
|
||||||
return val;
|
return val2;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
var $$ = (target) => {
|
var $$ = (target) => {
|
||||||
if (!isObj(target))
|
if (!isObj(target))
|
||||||
return target;
|
return target;
|
||||||
if (proxyCache.has(target))
|
let proxy = proxyCache.get(target);
|
||||||
return proxyCache.get(target);
|
if (proxy)
|
||||||
|
return proxy;
|
||||||
const subsMap = new Map;
|
const subsMap = new Map;
|
||||||
const getSubs = (k) => {
|
const getSubs = (k) => {
|
||||||
let s = subsMap.get(k);
|
let s = subsMap.get(k);
|
||||||
@@ -244,28 +236,30 @@
|
|||||||
subsMap.set(k, s = new Set);
|
subsMap.set(k, s = new Set);
|
||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
const proxy = new Proxy(target, {
|
proxy = new Proxy(target, {
|
||||||
get(t, k) {
|
get(t, k, receiver) {
|
||||||
trackUpdate(getSubs(k));
|
if (typeof k !== "symbol")
|
||||||
return $$(t[k]);
|
trackUpdate(getSubs(k));
|
||||||
|
return $$(Reflect.get(t, k, receiver));
|
||||||
},
|
},
|
||||||
set(t, k, v) {
|
set(t, k, v, receiver) {
|
||||||
const isNew = !(k in t);
|
const isNew = !Reflect.has(t, k);
|
||||||
if (!Object.is(t[k], v)) {
|
const oldV = Reflect.get(t, k, receiver);
|
||||||
t[k] = v;
|
const result = Reflect.set(t, k, v, receiver);
|
||||||
|
if (result && !Object.is(oldV, v)) {
|
||||||
trackUpdate(getSubs(k), true);
|
trackUpdate(getSubs(k), true);
|
||||||
if (isNew)
|
if (isNew)
|
||||||
trackUpdate(getSubs(ITER), true);
|
trackUpdate(getSubs(ITER), true);
|
||||||
}
|
}
|
||||||
return true;
|
return result;
|
||||||
},
|
},
|
||||||
deleteProperty(t, k) {
|
deleteProperty(t, k) {
|
||||||
const res = Reflect.deleteProperty(t, k);
|
const result = Reflect.deleteProperty(t, k);
|
||||||
if (res) {
|
if (result) {
|
||||||
trackUpdate(getSubs(k), true);
|
trackUpdate(getSubs(k), true);
|
||||||
trackUpdate(getSubs(ITER), true);
|
trackUpdate(getSubs(ITER), true);
|
||||||
}
|
}
|
||||||
return res;
|
return result;
|
||||||
},
|
},
|
||||||
ownKeys(t) {
|
ownKeys(t) {
|
||||||
trackUpdate(getSubs(ITER));
|
trackUpdate(getSubs(ITER));
|
||||||
@@ -300,17 +294,17 @@
|
|||||||
};
|
};
|
||||||
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
||||||
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
|
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
|
||||||
var validateAttr = (key, val) => {
|
var validateAttr = (key, val2) => {
|
||||||
if (val == null || val === false)
|
if (val2 == null || val2 === false)
|
||||||
return null;
|
return null;
|
||||||
if (isDangerousAttr(key)) {
|
if (isDangerousAttr(key)) {
|
||||||
const sVal = String(val);
|
const sVal = String(val2);
|
||||||
if (DANGEROUS_PROTOCOL.test(sVal)) {
|
if (DANGEROUS_PROTOCOL.test(sVal)) {
|
||||||
console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`);
|
console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`);
|
||||||
return "#";
|
return "#";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return val;
|
return val2;
|
||||||
};
|
};
|
||||||
var Tag = (tag, props = {}, children = []) => {
|
var Tag = (tag, props = {}, children = []) => {
|
||||||
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
||||||
@@ -342,7 +336,7 @@
|
|||||||
isArr(node) ? node.forEach(attach) : attach(node);
|
isArr(node) ? node.forEach(attach) : attach(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag);
|
const isSVG = /^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(tag);
|
||||||
const el = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : doc.createElement(tag);
|
const el = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : doc.createElement(tag);
|
||||||
el._cleanups = new Set;
|
el._cleanups = new Set;
|
||||||
for (let k in props) {
|
for (let k in props) {
|
||||||
@@ -353,6 +347,11 @@
|
|||||||
isFunc(v) ? v(el) : v.current = el;
|
isFunc(v) ? v(el) : v.current = el;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (isSVG && k.startsWith("xlink:")) {
|
||||||
|
const ns = "http://www.w3.org/1999/xlink";
|
||||||
|
val == null ? el.removeAttributeNS(ns, k.slice(6)) : el.setAttributeNS(ns, k.slice(6), val);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (k.startsWith("on")) {
|
if (k.startsWith("on")) {
|
||||||
const ev = k.slice(2).toLowerCase();
|
const ev = k.slice(2).toLowerCase();
|
||||||
el.addEventListener(ev, v);
|
el.addEventListener(ev, v);
|
||||||
@@ -361,15 +360,15 @@
|
|||||||
onUnmount(off);
|
onUnmount(off);
|
||||||
} else if (isFunc(v)) {
|
} else if (isFunc(v)) {
|
||||||
const effect = createEffect(() => {
|
const effect = createEffect(() => {
|
||||||
const val = validateAttr(k, v());
|
const val2 = validateAttr(k, v());
|
||||||
if (k === "class")
|
if (k === "class")
|
||||||
el.className = val || "";
|
el.className = val2 || "";
|
||||||
else if (val == null)
|
else if (val2 == null)
|
||||||
el.removeAttribute(k);
|
el.removeAttribute(k);
|
||||||
else if (k in el && !isSVG)
|
else if (k in el && !isSVG)
|
||||||
el[k] = val;
|
el[k] = val2;
|
||||||
else
|
else
|
||||||
el.setAttribute(k, val === true ? "" : val);
|
el.setAttribute(k, val2 === true ? "" : val2);
|
||||||
});
|
});
|
||||||
effect();
|
effect();
|
||||||
el._cleanups.add(() => dispose(effect));
|
el._cleanups.add(() => dispose(effect));
|
||||||
@@ -379,12 +378,12 @@
|
|||||||
el.addEventListener(evType, (ev) => v(ev.target[k]));
|
el.addEventListener(evType, (ev) => v(ev.target[k]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const val = validateAttr(k, v);
|
const val2 = validateAttr(k, v);
|
||||||
if (val != null) {
|
if (val2 != null) {
|
||||||
if (k in el && !isSVG)
|
if (k in el && !isSVG)
|
||||||
el[k] = val;
|
el[k] = val2;
|
||||||
else
|
else
|
||||||
el.setAttribute(k, val === true ? "" : val);
|
el.setAttribute(k, val2 === true ? "" : val2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -552,6 +551,35 @@
|
|||||||
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(/^#/, "") || "/";
|
||||||
|
var Anim = (show, render, { enter, leave } = {}) => {
|
||||||
|
const wrap = Tag("div", { style: "display:contents" });
|
||||||
|
let view = null;
|
||||||
|
const wait = (el, cb) => {
|
||||||
|
let done = false;
|
||||||
|
const finish = () => !done && (done = true, cb());
|
||||||
|
if (!el)
|
||||||
|
return finish();
|
||||||
|
"transitionend animationend".split(" ").map((e) => el.addEventListener(e, finish, { once: true }));
|
||||||
|
setTimeout(finish, 500);
|
||||||
|
};
|
||||||
|
Watch(show, (on) => {
|
||||||
|
if (on && !view) {
|
||||||
|
const el = (view = Render(render)).container.firstChild;
|
||||||
|
wrap.appendChild(view.container);
|
||||||
|
if (enter && el) {
|
||||||
|
el.classList.add(enter);
|
||||||
|
el.clientTop;
|
||||||
|
el.classList.add(enter + "-active");
|
||||||
|
wait(el, () => el.classList.remove(enter, enter + "-active"));
|
||||||
|
}
|
||||||
|
} else if (!on && view) {
|
||||||
|
const el = view.container.firstChild;
|
||||||
|
const del = () => (view?.destroy(), view = null);
|
||||||
|
leave && el ? (el.classList.add(leave), wait(el, del)) : del();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return onUnmount(() => view?.destroy()), wrap;
|
||||||
|
};
|
||||||
var Mount = (comp, target) => {
|
var Mount = (comp, target) => {
|
||||||
const t = typeof target === "string" ? doc.querySelector(target) : target;
|
const t = typeof target === "string" ? doc.querySelector(target) : target;
|
||||||
if (!t)
|
if (!t)
|
||||||
@@ -563,7 +591,7 @@
|
|||||||
MOUNTED_NODES.set(t, inst);
|
MOUNTED_NODES.set(t, inst);
|
||||||
return inst;
|
return inst;
|
||||||
};
|
};
|
||||||
var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Batch });
|
var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Anim, Batch });
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
Object.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 ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((t) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
"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) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
||||||
|
|||||||
2
dist/sigpro.min.js
vendored
2
dist/sigpro.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -16,7 +16,4 @@
|
|||||||
* [Tag](api/html.md)
|
* [Tag](api/html.md)
|
||||||
* [Tags](api/tags.md)
|
* [Tags](api/tags.md)
|
||||||
* [Global Store](api/global.md)
|
* [Global Store](api/global.md)
|
||||||
* [JSX Style](api/jsx.md)
|
* [JSX Style](api/jsx.md)
|
||||||
|
|
||||||
* **UI Components**
|
|
||||||
* [Quick Start](ui/quick.md)
|
|
||||||
@@ -134,94 +134,4 @@ const PersistDemo = () => {
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
Mount(PersistDemo, '#demo-persist');
|
Mount(PersistDemo, '#demo-persist');
|
||||||
```
|
```
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initExamples = () => {
|
|
||||||
|
|
||||||
const counterTarget = document.querySelector('#demo-counter');
|
|
||||||
if (counterTarget && !counterTarget.hasChildNodes()) {
|
|
||||||
const Counter = () => {
|
|
||||||
const $count = $(0);
|
|
||||||
return Div({ class: 'flex gap-4 items-center' }, [
|
|
||||||
Button({ class: 'btn btn-circle btn-outline', onclick: () => $count(c => c - 1) }, "-"),
|
|
||||||
Span({ class: 'text-2xl font-mono w-12 text-center' }, $count),
|
|
||||||
Button({ class: 'btn btn-circle btn-primary', onclick: () => $count(c => c + 1) }, "+")
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
Mount(Counter, counterTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Computed
|
|
||||||
const computedTarget = document.querySelector('#demo-computed');
|
|
||||||
if (computedTarget && !computedTarget.hasChildNodes()) {
|
|
||||||
const ComputedDemo = () => {
|
|
||||||
const $count = $(10);
|
|
||||||
const $double = $(() => $count() * 2);
|
|
||||||
return Div({ class: 'space-y-4 w-full' }, [
|
|
||||||
Input({ type: 'range', min: 1, max: 100, class: 'range range-primary', value: $count }),
|
|
||||||
P({ class: 'text-center' }, ["Base: ", $count, " ⮕ ", Span({class: 'text-primary font-bold'}, ["Double: ", $double])])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
Mount(ComputedDemo, computedTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. List
|
|
||||||
const listTarget = document.querySelector('#demo-list');
|
|
||||||
if (listTarget && !listTarget.hasChildNodes()) {
|
|
||||||
const ListDemo = () => {
|
|
||||||
const $todos = $(['Learn SigPro', 'Build an App']);
|
|
||||||
const $input = $("");
|
|
||||||
const addTodo = () => { if ($input()) { $todos(prev => [...prev, $input()]); $input(""); } };
|
|
||||||
return Div([
|
|
||||||
Div({ class: 'flex gap-2 mb-4' }, [
|
|
||||||
Input({ class: 'input input-bordered flex-1', value: $input, placeholder: 'New task...' }),
|
|
||||||
Button({ class: 'btn btn-primary', onclick: addTodo }, "Add")
|
|
||||||
]),
|
|
||||||
Ul({ class: 'menu bg-base-200 rounded-box p-2' }, For($todos, (item) => Li([A(item)]), (item) => item))
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
Mount(ListDemo, listTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. If
|
|
||||||
const ifTarget = document.querySelector('#demo-if');
|
|
||||||
if (ifTarget && !ifTarget.hasChildNodes()) {
|
|
||||||
const ConditionalDemo = () => {
|
|
||||||
const $isVisible = $(false);
|
|
||||||
return Div({ class: 'text-center w-full' }, [
|
|
||||||
Button({ class: 'btn btn-outline mb-4', onclick: () => $isVisible(! $isVisible()) }, "Toggle Secret"),
|
|
||||||
If($isVisible,
|
|
||||||
() => Div({ class: 'p-4 bg-warning text-warning-content rounded-lg' }, "🤫 SigPro is Awesome!"),
|
|
||||||
() => Div({ class: 'p-4 opacity-50' }, "Nothing to see here...")
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
Mount(ConditionalDemo, ifTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Persist
|
|
||||||
const persistTarget = document.querySelector('#demo-persist');
|
|
||||||
if (persistTarget && !persistTarget.hasChildNodes()) {
|
|
||||||
const PersistDemo = () => {
|
|
||||||
const $name = $("Guest", "sigpro-demo-name");
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
H3({ class: 'text-lg font-bold' }, ["Hello, ", $name]),
|
|
||||||
Input({ class: 'input input-bordered', value: $name }),
|
|
||||||
P({ class: 'text-xs opacity-50' }, "Refresh the page!")
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
Mount(PersistDemo, persistTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ejecutar inmediatamente y también en cada navegación de Docsify
|
|
||||||
initExamples();
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,38 +1,65 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="es">
|
<html lang="es">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>SigPro Docs</title>
|
<title>SigPro Docs</title>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css">
|
|
||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
|
|
||||||
<script>
|
<link
|
||||||
window.$docsify = {
|
rel="stylesheet"
|
||||||
name: 'SigPro',
|
href="//cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css"
|
||||||
repo: '',
|
/>
|
||||||
loadSidebar: true,
|
|
||||||
subMaxLevel: 0,
|
|
||||||
sidebarDisplayLevel: 1,
|
|
||||||
executeScript: true,
|
|
||||||
copyCode: {
|
|
||||||
buttonText: '<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>',
|
|
||||||
errorText: 'Error',
|
|
||||||
successText: '<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="20 6 9 17 4 12"></polyline></svg>'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script src="./sigpro.js"></script>
|
<link
|
||||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
href="https://cdn.jsdelivr.net/npm/daisyui@5"
|
||||||
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
rel="stylesheet"
|
||||||
</body>
|
type="text/css"
|
||||||
</html>
|
/>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.$docsify = {
|
||||||
|
name: "SigPro",
|
||||||
|
repo: "",
|
||||||
|
loadSidebar: true,
|
||||||
|
subMaxLevel: 0,
|
||||||
|
sidebarDisplayLevel: 1,
|
||||||
|
executeScript: true,
|
||||||
|
copyCode: {
|
||||||
|
buttonText:
|
||||||
|
'<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>',
|
||||||
|
errorText: "Error",
|
||||||
|
successText:
|
||||||
|
'<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="20 6 9 17 4 12"></polyline></svg>',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
function (hook, vm) {
|
||||||
|
hook.doneEach(function () {
|
||||||
|
const codeBlocks = document.querySelectorAll(
|
||||||
|
'pre[data-lang="javascript"] code',
|
||||||
|
);
|
||||||
|
|
||||||
|
codeBlocks.forEach((code) => {
|
||||||
|
try {
|
||||||
|
const runDemo = new Function(code.innerText);
|
||||||
|
runDemo();
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error en la demo de SigPro:", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="./sigpro.js"></script>
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
140
docs/sigpro.js
140
docs/sigpro.js
@@ -40,6 +40,7 @@
|
|||||||
If: () => If,
|
If: () => If,
|
||||||
For: () => For,
|
For: () => For,
|
||||||
Batch: () => Batch,
|
Batch: () => Batch,
|
||||||
|
Anim: () => Anim,
|
||||||
$$: () => $$,
|
$$: () => $$,
|
||||||
$: () => $
|
$: () => $
|
||||||
});
|
});
|
||||||
@@ -154,11 +155,11 @@
|
|||||||
if (!trigger && activeEffect && !activeEffect._disposed) {
|
if (!trigger && activeEffect && !activeEffect._disposed) {
|
||||||
subs.add(activeEffect);
|
subs.add(activeEffect);
|
||||||
(activeEffect._deps ||= new Set).add(subs);
|
(activeEffect._deps ||= new Set).add(subs);
|
||||||
} else if (trigger) {
|
} else if (trigger && subs.size > 0) {
|
||||||
let hasQueue = false;
|
let hasQueue = false;
|
||||||
subs.forEach((e) => {
|
for (const e of subs) {
|
||||||
if (e === activeEffect || e._disposed)
|
if (e === activeEffect || e._disposed)
|
||||||
return;
|
continue;
|
||||||
if (e._isComputed) {
|
if (e._isComputed) {
|
||||||
e._dirty = true;
|
e._dirty = true;
|
||||||
if (e._subs)
|
if (e._subs)
|
||||||
@@ -167,29 +168,29 @@
|
|||||||
effectQueue.add(e);
|
effectQueue.add(e);
|
||||||
hasQueue = true;
|
hasQueue = true;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
if (hasQueue && !isFlushing && batchDepth === 0)
|
if (hasQueue && !isFlushing && batchDepth === 0)
|
||||||
queueMicrotask(flush);
|
queueMicrotask(flush);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var $ = (val, key = null) => {
|
var $ = (val2, key = null) => {
|
||||||
const subs = new Set;
|
const subs = new Set;
|
||||||
if (isFunc(val)) {
|
if (isFunc(val2)) {
|
||||||
let cache, dirty = true;
|
let cache;
|
||||||
const computed = () => {
|
const computed = () => {
|
||||||
if (dirty) {
|
if (computed._dirty) {
|
||||||
const prev = activeEffect;
|
const prev = activeEffect;
|
||||||
activeEffect = computed;
|
activeEffect = computed;
|
||||||
try {
|
try {
|
||||||
const next = val();
|
const next = val2();
|
||||||
if (!Object.is(cache, next)) {
|
if (!Object.is(cache, next)) {
|
||||||
cache = next;
|
cache = next;
|
||||||
dirty = false;
|
|
||||||
trackUpdate(subs, true);
|
trackUpdate(subs, true);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
activeEffect = prev;
|
activeEffect = prev;
|
||||||
}
|
}
|
||||||
|
computed._dirty = false;
|
||||||
}
|
}
|
||||||
trackUpdate(subs);
|
trackUpdate(subs);
|
||||||
return cache;
|
return cache;
|
||||||
@@ -199,44 +200,35 @@
|
|||||||
computed._dirty = true;
|
computed._dirty = true;
|
||||||
computed._deps = null;
|
computed._deps = null;
|
||||||
computed._disposed = false;
|
computed._disposed = false;
|
||||||
computed.markDirty = () => {
|
computed.stop = () => {};
|
||||||
dirty = true;
|
|
||||||
};
|
|
||||||
computed.stop = () => {
|
|
||||||
computed._disposed = true;
|
|
||||||
if (computed._deps) {
|
|
||||||
computed._deps.forEach((depSet) => depSet.delete(computed));
|
|
||||||
computed._deps.clear();
|
|
||||||
}
|
|
||||||
subs.clear();
|
|
||||||
};
|
|
||||||
if (activeOwner)
|
if (activeOwner)
|
||||||
onUnmount(computed.stop);
|
onUnmount(computed.stop);
|
||||||
return computed;
|
return computed;
|
||||||
}
|
}
|
||||||
if (key)
|
if (key)
|
||||||
try {
|
try {
|
||||||
val = JSON.parse(localStorage.getItem(key)) ?? val;
|
val2 = JSON.parse(localStorage.getItem(key)) ?? val2;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
if (args.length) {
|
if (args.length) {
|
||||||
const next = isFunc(args[0]) ? args[0](val) : args[0];
|
const next = isFunc(args[0]) ? args[0](val2) : args[0];
|
||||||
if (!Object.is(val, next)) {
|
if (!Object.is(val2, next)) {
|
||||||
val = next;
|
val2 = next;
|
||||||
if (key)
|
if (key)
|
||||||
localStorage.setItem(key, JSON.stringify(val));
|
localStorage.setItem(key, JSON.stringify(val2));
|
||||||
trackUpdate(subs, true);
|
trackUpdate(subs, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trackUpdate(subs);
|
trackUpdate(subs);
|
||||||
return val;
|
return val2;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
var $$ = (target) => {
|
var $$ = (target) => {
|
||||||
if (!isObj(target))
|
if (!isObj(target))
|
||||||
return target;
|
return target;
|
||||||
if (proxyCache.has(target))
|
let proxy = proxyCache.get(target);
|
||||||
return proxyCache.get(target);
|
if (proxy)
|
||||||
|
return proxy;
|
||||||
const subsMap = new Map;
|
const subsMap = new Map;
|
||||||
const getSubs = (k) => {
|
const getSubs = (k) => {
|
||||||
let s = subsMap.get(k);
|
let s = subsMap.get(k);
|
||||||
@@ -244,28 +236,30 @@
|
|||||||
subsMap.set(k, s = new Set);
|
subsMap.set(k, s = new Set);
|
||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
const proxy = new Proxy(target, {
|
proxy = new Proxy(target, {
|
||||||
get(t, k) {
|
get(t, k, receiver) {
|
||||||
trackUpdate(getSubs(k));
|
if (typeof k !== "symbol")
|
||||||
return $$(t[k]);
|
trackUpdate(getSubs(k));
|
||||||
|
return $$(Reflect.get(t, k, receiver));
|
||||||
},
|
},
|
||||||
set(t, k, v) {
|
set(t, k, v, receiver) {
|
||||||
const isNew = !(k in t);
|
const isNew = !Reflect.has(t, k);
|
||||||
if (!Object.is(t[k], v)) {
|
const oldV = Reflect.get(t, k, receiver);
|
||||||
t[k] = v;
|
const result = Reflect.set(t, k, v, receiver);
|
||||||
|
if (result && !Object.is(oldV, v)) {
|
||||||
trackUpdate(getSubs(k), true);
|
trackUpdate(getSubs(k), true);
|
||||||
if (isNew)
|
if (isNew)
|
||||||
trackUpdate(getSubs(ITER), true);
|
trackUpdate(getSubs(ITER), true);
|
||||||
}
|
}
|
||||||
return true;
|
return result;
|
||||||
},
|
},
|
||||||
deleteProperty(t, k) {
|
deleteProperty(t, k) {
|
||||||
const res = Reflect.deleteProperty(t, k);
|
const result = Reflect.deleteProperty(t, k);
|
||||||
if (res) {
|
if (result) {
|
||||||
trackUpdate(getSubs(k), true);
|
trackUpdate(getSubs(k), true);
|
||||||
trackUpdate(getSubs(ITER), true);
|
trackUpdate(getSubs(ITER), true);
|
||||||
}
|
}
|
||||||
return res;
|
return result;
|
||||||
},
|
},
|
||||||
ownKeys(t) {
|
ownKeys(t) {
|
||||||
trackUpdate(getSubs(ITER));
|
trackUpdate(getSubs(ITER));
|
||||||
@@ -300,17 +294,17 @@
|
|||||||
};
|
};
|
||||||
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
||||||
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
|
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
|
||||||
var validateAttr = (key, val) => {
|
var validateAttr = (key, val2) => {
|
||||||
if (val == null || val === false)
|
if (val2 == null || val2 === false)
|
||||||
return null;
|
return null;
|
||||||
if (isDangerousAttr(key)) {
|
if (isDangerousAttr(key)) {
|
||||||
const sVal = String(val);
|
const sVal = String(val2);
|
||||||
if (DANGEROUS_PROTOCOL.test(sVal)) {
|
if (DANGEROUS_PROTOCOL.test(sVal)) {
|
||||||
console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`);
|
console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`);
|
||||||
return "#";
|
return "#";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return val;
|
return val2;
|
||||||
};
|
};
|
||||||
var Tag = (tag, props = {}, children = []) => {
|
var Tag = (tag, props = {}, children = []) => {
|
||||||
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
||||||
@@ -342,7 +336,7 @@
|
|||||||
isArr(node) ? node.forEach(attach) : attach(node);
|
isArr(node) ? node.forEach(attach) : attach(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag);
|
const isSVG = /^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(tag);
|
||||||
const el = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : doc.createElement(tag);
|
const el = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : doc.createElement(tag);
|
||||||
el._cleanups = new Set;
|
el._cleanups = new Set;
|
||||||
for (let k in props) {
|
for (let k in props) {
|
||||||
@@ -353,6 +347,11 @@
|
|||||||
isFunc(v) ? v(el) : v.current = el;
|
isFunc(v) ? v(el) : v.current = el;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (isSVG && k.startsWith("xlink:")) {
|
||||||
|
const ns = "http://www.w3.org/1999/xlink";
|
||||||
|
val == null ? el.removeAttributeNS(ns, k.slice(6)) : el.setAttributeNS(ns, k.slice(6), val);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (k.startsWith("on")) {
|
if (k.startsWith("on")) {
|
||||||
const ev = k.slice(2).toLowerCase();
|
const ev = k.slice(2).toLowerCase();
|
||||||
el.addEventListener(ev, v);
|
el.addEventListener(ev, v);
|
||||||
@@ -361,15 +360,15 @@
|
|||||||
onUnmount(off);
|
onUnmount(off);
|
||||||
} else if (isFunc(v)) {
|
} else if (isFunc(v)) {
|
||||||
const effect = createEffect(() => {
|
const effect = createEffect(() => {
|
||||||
const val = validateAttr(k, v());
|
const val2 = validateAttr(k, v());
|
||||||
if (k === "class")
|
if (k === "class")
|
||||||
el.className = val || "";
|
el.className = val2 || "";
|
||||||
else if (val == null)
|
else if (val2 == null)
|
||||||
el.removeAttribute(k);
|
el.removeAttribute(k);
|
||||||
else if (k in el && !isSVG)
|
else if (k in el && !isSVG)
|
||||||
el[k] = val;
|
el[k] = val2;
|
||||||
else
|
else
|
||||||
el.setAttribute(k, val === true ? "" : val);
|
el.setAttribute(k, val2 === true ? "" : val2);
|
||||||
});
|
});
|
||||||
effect();
|
effect();
|
||||||
el._cleanups.add(() => dispose(effect));
|
el._cleanups.add(() => dispose(effect));
|
||||||
@@ -379,12 +378,12 @@
|
|||||||
el.addEventListener(evType, (ev) => v(ev.target[k]));
|
el.addEventListener(evType, (ev) => v(ev.target[k]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const val = validateAttr(k, v);
|
const val2 = validateAttr(k, v);
|
||||||
if (val != null) {
|
if (val2 != null) {
|
||||||
if (k in el && !isSVG)
|
if (k in el && !isSVG)
|
||||||
el[k] = val;
|
el[k] = val2;
|
||||||
else
|
else
|
||||||
el.setAttribute(k, val === true ? "" : val);
|
el.setAttribute(k, val2 === true ? "" : val2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -552,6 +551,35 @@
|
|||||||
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(/^#/, "") || "/";
|
||||||
|
var Anim = (show, render, { enter, leave } = {}) => {
|
||||||
|
const wrap = Tag("div", { style: "display:contents" });
|
||||||
|
let view = null;
|
||||||
|
const wait = (el, cb) => {
|
||||||
|
let done = false;
|
||||||
|
const finish = () => !done && (done = true, cb());
|
||||||
|
if (!el)
|
||||||
|
return finish();
|
||||||
|
"transitionend animationend".split(" ").map((e) => el.addEventListener(e, finish, { once: true }));
|
||||||
|
setTimeout(finish, 500);
|
||||||
|
};
|
||||||
|
Watch(show, (on) => {
|
||||||
|
if (on && !view) {
|
||||||
|
const el = (view = Render(render)).container.firstChild;
|
||||||
|
wrap.appendChild(view.container);
|
||||||
|
if (enter && el) {
|
||||||
|
el.classList.add(enter);
|
||||||
|
el.clientTop;
|
||||||
|
el.classList.add(enter + "-active");
|
||||||
|
wait(el, () => el.classList.remove(enter, enter + "-active"));
|
||||||
|
}
|
||||||
|
} else if (!on && view) {
|
||||||
|
const el = view.container.firstChild;
|
||||||
|
const del = () => (view?.destroy(), view = null);
|
||||||
|
leave && el ? (el.classList.add(leave), wait(el, del)) : del();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return onUnmount(() => view?.destroy()), wrap;
|
||||||
|
};
|
||||||
var Mount = (comp, target) => {
|
var Mount = (comp, target) => {
|
||||||
const t = typeof target === "string" ? doc.querySelector(target) : target;
|
const t = typeof target === "string" ? doc.querySelector(target) : target;
|
||||||
if (!t)
|
if (!t)
|
||||||
@@ -563,7 +591,7 @@
|
|||||||
MOUNTED_NODES.set(t, inst);
|
MOUNTED_NODES.set(t, inst);
|
||||||
return inst;
|
return inst;
|
||||||
};
|
};
|
||||||
var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Batch });
|
var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Anim, Batch });
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
Object.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 ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((t) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
"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) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
||||||
|
|||||||
148
docs/ui/quick.md
148
docs/ui/quick.md
@@ -1,148 +0,0 @@
|
|||||||
# UI Components `(WIP)`
|
|
||||||
|
|
||||||
> **Status: Work In Progress.**
|
|
||||||
> SigPro UI is a complete component environment built with SigPro, Tailwind CSS and DaisyUI. It provides smart components that handle their own internal logic, reactivity, and professional styling.
|
|
||||||
|
|
||||||
<div class="alert alert-info shadow-md my-10 border-l-8 border-info bg-info/10 text-info-content">
|
|
||||||
<div class="flex flex-col md:flex-row items-start gap-4">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6 mt-1"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
|
|
||||||
<div>
|
|
||||||
<h2 class="font-black text-xl tracking-tight mb-2">📢 Official Documentation</h2>
|
|
||||||
<p class="text-sm opacity-90 leading-relaxed mb-2">
|
|
||||||
All official documentation, interactive examples, and usage guides are available at:
|
|
||||||
</p>
|
|
||||||
<div class="bg-base-100 p-3 rounded-lg inline-block">
|
|
||||||
<a href="https://natxocc.github.io/sigpro-ui/#/" target="_blank" class="text-info font-mono text-base font-bold hover:underline">
|
|
||||||
https://natxocc.github.io/sigpro-ui/#/
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<p class="text-xs mt-4 opacity-70 italic">
|
|
||||||
* Documentation is actively being updated as components are released.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. What are UI Components?
|
|
||||||
|
|
||||||
Unlike **Tag Helpers** (which are just functional mirrors of HTML tags), SigPro UI Components are smart abstractions:
|
|
||||||
|
|
||||||
* **Stateful**: They manage complex internal states (like date ranges, search filtering, or API lifecycles).
|
|
||||||
* **Reactive**: Attributes prefixed with `$` are automatically tracked via `Watch`.
|
|
||||||
* **Self-Sane**: They automatically use `._cleanups` to destroy observers or event listeners when removed from the DOM.
|
|
||||||
* **Themed**: Fully compatible with DaisyUI v5 theme system and Tailwind v4 utility classes.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Prerequisites
|
|
||||||
|
|
||||||
<div class="alert alert-warning shadow-md my-6 border-l-8 border-warning bg-warning/10">
|
|
||||||
<div class="flex items-start gap-3">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-5 h-5 mt-0.5"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>
|
|
||||||
<div>
|
|
||||||
<p class="text-sm font-semibold">To ensure all components render correctly, your project must have:</p>
|
|
||||||
<ul class="flex flex-wrap gap-3 mt-2">
|
|
||||||
<li class="badge badge-warning badge-md">Tailwind CSS v4+</li>
|
|
||||||
<li class="badge badge-warning badge-md">DaisyUI v5+</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. The UI Registry (Available Now)
|
|
||||||
|
|
||||||
| Category | Components |
|
|
||||||
| :--- | :--- |
|
|
||||||
| **Forms & Inputs** | `Button`, `Input`, `Select`, `Autocomplete`, `Datepicker`, `Colorpicker`, `CheckBox`, `Radio`, `Range`, `Rating`, `Swap` |
|
|
||||||
| **Feedback** | `Alert`, `Toast`, `Modal`, `Loading`, `Badge`, `Tooltip`, `Indicator` |
|
|
||||||
| **Navigation** | `Navbar`, `Menu`, `Drawer`, `Tabs`, `Accordion`, `Dropdown` |
|
|
||||||
| **Data & Layout** | `Request`, `Response`, `List`, `Stack`, `Timeline`, `Stat`, `Fieldset`, `Fab` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Examples with "Superpowers"
|
|
||||||
|
|
||||||
### A. The Declarative API Flow (`Request` & `Response`)
|
|
||||||
Instead of manually managing `loading` and `error` flags, use these together to handle data fetching elegantly.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 1. Define the request (it tracks dependencies automatically)
|
|
||||||
const userProfile = Request(
|
|
||||||
() => `https://api.example.com/user/${userId()}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// 2. Render the UI based on the request state
|
|
||||||
Div({ class: "p-4" }, [
|
|
||||||
Response(userProfile, (data) =>
|
|
||||||
Div([
|
|
||||||
H1(data.name),
|
|
||||||
P(data.email)
|
|
||||||
])
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
```
|
|
||||||
|
|
||||||
### B. Smart Inputs & Autocomplete
|
|
||||||
SigPro UI inputs handle labels, icons, password toggles, and validation states out of the box using DaisyUI v5 classes.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const searchQuery = $("");
|
|
||||||
|
|
||||||
Autocomplete({
|
|
||||||
label: "Find a Country",
|
|
||||||
placeholder: "Start typing...",
|
|
||||||
options: ["Spain", "France", "Germany", "Italy", "Portugal"],
|
|
||||||
$value: searchQuery,
|
|
||||||
onSelect: (val) => console.log("Selected:", val)
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### C. The Reactive Datepicker
|
|
||||||
Handles single dates or ranges with a clean, reactive interface that automatically syncs with your signals.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const myDate = $(""); // or { start: "", end: "" } for range
|
|
||||||
|
|
||||||
Datepicker({
|
|
||||||
label: "Select Expiry Date",
|
|
||||||
$value: myDate,
|
|
||||||
range: false
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### D. Imperative Toasts & Modals
|
|
||||||
Trigger complex UI elements from your logic. These components use `Mount` internally to ensure they are properly cleaned up from memory after they close.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Show a notification (Self-destroying after 3s)
|
|
||||||
Toast("Settings saved successfully!", "alert-success", 3000);
|
|
||||||
|
|
||||||
// Control a modal with a simple signal
|
|
||||||
const isModalOpen = $(false);
|
|
||||||
|
|
||||||
Modal({
|
|
||||||
$open: isModalOpen,
|
|
||||||
title: "Delete Account",
|
|
||||||
buttons: [
|
|
||||||
Button({ class: "btn-error", onclick: doDelete }, "Confirm")
|
|
||||||
]
|
|
||||||
}, "This action cannot be undone.");
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Internationalization (i18n)
|
|
||||||
|
|
||||||
The UI library comes with a built-in locale system.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Set the global UI language
|
|
||||||
Locale("en");
|
|
||||||
|
|
||||||
// Access translated strings (Returns a signal that tracks the current locale)
|
|
||||||
const t = tt("confirm");
|
|
||||||
```
|
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"index.js",
|
"index.js",
|
||||||
|
"sigpro.js",
|
||||||
"dist",
|
"dist",
|
||||||
"sigpro",
|
"sigpro",
|
||||||
"vite",
|
"vite",
|
||||||
|
|||||||
22
sigpro.js
22
sigpro.js
@@ -1,4 +1,4 @@
|
|||||||
// sigpro
|
// sigpro 1.2.12
|
||||||
const isFunc = f => typeof f === "function"
|
const isFunc = f => typeof f === "function"
|
||||||
const isObj = o => o && typeof o === "object"
|
const isObj = o => o && typeof o === "object"
|
||||||
const isArr = Array.isArray
|
const isArr = Array.isArray
|
||||||
@@ -123,19 +123,21 @@ const trackUpdate = (subs, trigger = false) => {
|
|||||||
const $ = (val, key = null) => {
|
const $ = (val, key = null) => {
|
||||||
const subs = new Set()
|
const subs = new Set()
|
||||||
if (isFunc(val)) {
|
if (isFunc(val)) {
|
||||||
let cache, dirty = true
|
let cache
|
||||||
const computed = () => {
|
const computed = () => {
|
||||||
if (dirty) {
|
if (computed._dirty) {
|
||||||
const prev = activeEffect
|
const prev = activeEffect
|
||||||
activeEffect = computed
|
activeEffect = computed
|
||||||
try {
|
try {
|
||||||
const next = val()
|
const next = val()
|
||||||
if (!Object.is(cache, next)) {
|
if (!Object.is(cache, next)) {
|
||||||
cache = next
|
cache = next
|
||||||
dirty = false
|
|
||||||
trackUpdate(subs, true)
|
trackUpdate(subs, true)
|
||||||
}
|
}
|
||||||
} finally { activeEffect = prev }
|
} finally {
|
||||||
|
activeEffect = prev
|
||||||
|
}
|
||||||
|
computed._dirty = false
|
||||||
}
|
}
|
||||||
trackUpdate(subs)
|
trackUpdate(subs)
|
||||||
return cache
|
return cache
|
||||||
@@ -145,15 +147,7 @@ const $ = (val, key = null) => {
|
|||||||
computed._dirty = true
|
computed._dirty = true
|
||||||
computed._deps = null
|
computed._deps = null
|
||||||
computed._disposed = false
|
computed._disposed = false
|
||||||
computed.markDirty = () => { dirty = true }
|
computed.stop = () => { }
|
||||||
computed.stop = () => {
|
|
||||||
computed._disposed = true
|
|
||||||
if (computed._deps) {
|
|
||||||
computed._deps.forEach(depSet => depSet.delete(computed))
|
|
||||||
computed._deps.clear()
|
|
||||||
}
|
|
||||||
subs.clear()
|
|
||||||
}
|
|
||||||
if (activeOwner) onUnmount(computed.stop)
|
if (activeOwner) onUnmount(computed.stop)
|
||||||
return computed
|
return computed
|
||||||
}
|
}
|
||||||
|
|||||||
510
sigworkPro.js
510
sigworkPro.js
@@ -1,510 +0,0 @@
|
|||||||
const isFunction = v => typeof v === 'function';
|
|
||||||
const isNode = v => v instanceof Node;
|
|
||||||
const doc = typeof document !== "undefined" ? document : null;
|
|
||||||
|
|
||||||
let activeEffect = null;
|
|
||||||
const pendingEffects = new Set();
|
|
||||||
let flushScheduled = false;
|
|
||||||
const nodeDisposers = new WeakMap();
|
|
||||||
|
|
||||||
const registerNodeCleanup = (node, disposer) => {
|
|
||||||
if (!nodeDisposers.has(node)) nodeDisposers.set(node, []);
|
|
||||||
nodeDisposers.get(node).push(disposer);
|
|
||||||
};
|
|
||||||
|
|
||||||
const flushEffects = () => {
|
|
||||||
if (pendingEffects.size === 0) return;
|
|
||||||
const all = Array.from(pendingEffects);
|
|
||||||
pendingEffects.clear();
|
|
||||||
all.sort((a, b) => a.depth - b.depth);
|
|
||||||
for (let i = 0; i < all.length; i++) {
|
|
||||||
const e = all[i];
|
|
||||||
if (!e.disposed) e.execute();
|
|
||||||
}
|
|
||||||
flushScheduled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const scheduleFlush = () => {
|
|
||||||
if (!flushScheduled) {
|
|
||||||
flushScheduled = true;
|
|
||||||
queueMicrotask(flushEffects);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const disposeEffectTree = effect => {
|
|
||||||
if (effect.disposed) return;
|
|
||||||
effect.disposed = true;
|
|
||||||
const stack = [effect];
|
|
||||||
while (stack.length) {
|
|
||||||
const cur = stack.pop();
|
|
||||||
if (cur.cleanups) {
|
|
||||||
for (const fn of cur.cleanups) fn();
|
|
||||||
cur.cleanups.clear();
|
|
||||||
}
|
|
||||||
if (cur.dependencies) {
|
|
||||||
for (const depSet of cur.dependencies) depSet.delete(cur);
|
|
||||||
cur.dependencies.clear();
|
|
||||||
}
|
|
||||||
if (cur.children) {
|
|
||||||
for (const child of cur.children) stack.push(child);
|
|
||||||
cur.children.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const createEffect = (fn) => {
|
|
||||||
const effect = {
|
|
||||||
execute: null,
|
|
||||||
dependencies: new Set(),
|
|
||||||
cleanups: new Set(),
|
|
||||||
children: new Set(),
|
|
||||||
depth: activeEffect ? activeEffect.depth + 1 : 0,
|
|
||||||
disposed: false,
|
|
||||||
owner: activeEffect
|
|
||||||
};
|
|
||||||
effect.execute = () => {
|
|
||||||
if (effect.disposed) return;
|
|
||||||
if (effect.dependencies) {
|
|
||||||
for (const depSet of effect.dependencies) depSet.delete(effect);
|
|
||||||
effect.dependencies.clear();
|
|
||||||
}
|
|
||||||
if (effect.cleanups) {
|
|
||||||
for (const fn of effect.cleanups) fn();
|
|
||||||
effect.cleanups.clear();
|
|
||||||
}
|
|
||||||
const prev = activeEffect;
|
|
||||||
activeEffect = effect;
|
|
||||||
try {
|
|
||||||
const cleanup = fn();
|
|
||||||
if (isFunction(cleanup)) effect.cleanups.add(cleanup);
|
|
||||||
} finally {
|
|
||||||
activeEffect = prev;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (activeEffect) activeEffect.children.add(effect);
|
|
||||||
effect.execute();
|
|
||||||
return () => disposeEffectTree(effect);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Watch = createEffect;
|
|
||||||
|
|
||||||
const getComponentContext = () => {
|
|
||||||
let eff = activeEffect;
|
|
||||||
while (eff) {
|
|
||||||
if (eff.componentContext) return eff.componentContext;
|
|
||||||
eff = eff.owner;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeNode = node => {
|
|
||||||
if (!node) return;
|
|
||||||
if (node.childNodes) {
|
|
||||||
[...node.childNodes].forEach(removeNode);
|
|
||||||
}
|
|
||||||
const disposers = nodeDisposers.get(node);
|
|
||||||
if (disposers) {
|
|
||||||
disposers.forEach(d => d());
|
|
||||||
nodeDisposers.delete(node);
|
|
||||||
}
|
|
||||||
if (node._raf) cancelAnimationFrame(node._raf);
|
|
||||||
if (node._stop) node._stop();
|
|
||||||
const ctx = node.componentContext;
|
|
||||||
if (ctx) {
|
|
||||||
ctx.unmount.forEach(fn => fn());
|
|
||||||
ctx.unmount = [];
|
|
||||||
}
|
|
||||||
if (node.leaveTransition) {
|
|
||||||
node.leaveTransition(() => node.remove());
|
|
||||||
} else {
|
|
||||||
node.remove?.();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const track = subs => {
|
|
||||||
if (activeEffect && !activeEffect.disposed) {
|
|
||||||
subs.add(activeEffect);
|
|
||||||
activeEffect.dependencies.add(subs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const trigger = subs => {
|
|
||||||
if (!subs) return;
|
|
||||||
for (const eff of subs) {
|
|
||||||
if (eff !== activeEffect && !eff.disposed) pendingEffects.add(eff);
|
|
||||||
}
|
|
||||||
scheduleFlush();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const $ = (initialValue, storageKey) => {
|
|
||||||
if (isFunction(initialValue)) {
|
|
||||||
let dirty = true, cached;
|
|
||||||
const s = $();
|
|
||||||
const stop = Watch(() => {
|
|
||||||
const v = initialValue();
|
|
||||||
if (!Object.is(v, cached)) { cached = v; dirty = false; s(v); }
|
|
||||||
});
|
|
||||||
const ctx = getComponentContext();
|
|
||||||
if (ctx) ctx.unmount.push(stop);
|
|
||||||
const signal = newVal => {
|
|
||||||
if (newVal === undefined) {
|
|
||||||
if (dirty) { cached = initialValue(); dirty = false; s(cached); }
|
|
||||||
return s();
|
|
||||||
}
|
|
||||||
return s(newVal);
|
|
||||||
};
|
|
||||||
return signal;
|
|
||||||
}
|
|
||||||
const subs = new Set();
|
|
||||||
let value = initialValue;
|
|
||||||
if (storageKey) {
|
|
||||||
try {
|
|
||||||
const item = localStorage.getItem(storageKey);
|
|
||||||
if (item !== null) value = JSON.parse(item);
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
const signal = newVal => {
|
|
||||||
if (newVal === undefined) {
|
|
||||||
track(subs);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
const next = isFunction(newVal) ? newVal(value) : newVal;
|
|
||||||
if (!Object.is(next, value)) {
|
|
||||||
value = next;
|
|
||||||
if (storageKey) {
|
|
||||||
try { localStorage.setItem(storageKey, JSON.stringify(value)); } catch (e) {}
|
|
||||||
}
|
|
||||||
trigger(subs);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
if (storageKey) {
|
|
||||||
const sync = Watch(() => { signal(value); });
|
|
||||||
const ctx = getComponentContext();
|
|
||||||
if (ctx) ctx.unmount.push(sync);
|
|
||||||
}
|
|
||||||
return signal;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const computed = fn => {
|
|
||||||
let dirty = true;
|
|
||||||
let cache;
|
|
||||||
const subs = new Set();
|
|
||||||
const evaluate = () => {
|
|
||||||
if (!dirty) return cache;
|
|
||||||
const prev = activeEffect;
|
|
||||||
activeEffect = null;
|
|
||||||
try {
|
|
||||||
cache = fn();
|
|
||||||
} finally {
|
|
||||||
activeEffect = prev;
|
|
||||||
}
|
|
||||||
dirty = false;
|
|
||||||
trigger(subs);
|
|
||||||
return cache;
|
|
||||||
};
|
|
||||||
const signal = () => {
|
|
||||||
track(subs);
|
|
||||||
return evaluate();
|
|
||||||
};
|
|
||||||
const effect = createEffect(() => {
|
|
||||||
fn();
|
|
||||||
dirty = true;
|
|
||||||
trigger(subs);
|
|
||||||
});
|
|
||||||
return signal;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const set = (signal, path, value) => {
|
|
||||||
if (value === undefined) {
|
|
||||||
signal(isFunction(path) ? path(signal()) : path);
|
|
||||||
} else {
|
|
||||||
const keys = path.split('.');
|
|
||||||
const last = keys.pop();
|
|
||||||
const obj = keys.reduce((o, k) => ({ ...o, [k]: { ...o[k] } }), { ...signal() });
|
|
||||||
obj[last] = value;
|
|
||||||
signal(obj);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const watch = (source, callback) => {
|
|
||||||
let first = true, oldVal;
|
|
||||||
return Watch(() => {
|
|
||||||
const newVal = isFunction(source) ? source() : source;
|
|
||||||
if (!first) untrack(() => callback(newVal, oldVal));
|
|
||||||
else first = false;
|
|
||||||
oldVal = newVal;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const untrack = fn => {
|
|
||||||
const prev = activeEffect;
|
|
||||||
activeEffect = null;
|
|
||||||
try { return fn(); } finally { activeEffect = prev; }
|
|
||||||
};
|
|
||||||
|
|
||||||
export const onMount = fn => {
|
|
||||||
const ctx = getComponentContext();
|
|
||||||
if (ctx) ctx.mount.push(fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const onUnmount = fn => {
|
|
||||||
const ctx = getComponentContext();
|
|
||||||
if (ctx) ctx.unmount.push(fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
const DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
|
||||||
const isDangerousAttr = key => key === 'src' || key === 'href' || key.startsWith('on');
|
|
||||||
|
|
||||||
const validateAttr = (key, val) => {
|
|
||||||
if (val == null || val === false) return null;
|
|
||||||
if (isDangerousAttr(key)) {
|
|
||||||
const sVal = String(val);
|
|
||||||
if (DANGEROUS_PROTOCOL.test(sVal)) {
|
|
||||||
console.warn(`[SP] Bloqueado protocolo peligroso en ${key}`);
|
|
||||||
return '#';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setProperty = (el, key, val, isSVG) => {
|
|
||||||
val = validateAttr(key, val);
|
|
||||||
if (key === 'class' || key === 'className') {
|
|
||||||
el.className = val || '';
|
|
||||||
} else if (key === 'style' && typeof val === 'object') {
|
|
||||||
Object.assign(el.style, val);
|
|
||||||
} else if (key in el && !isSVG) {
|
|
||||||
el[key] = val;
|
|
||||||
} else {
|
|
||||||
if (isSVG) {
|
|
||||||
if (key.startsWith('xlink:')) {
|
|
||||||
if (val == null || val === false) el.removeAttributeNS('http://www.w3.org/1999/xlink', key.slice(6));
|
|
||||||
else el.setAttributeNS('http://www.w3.org/1999/xlink', key, val);
|
|
||||||
} else if (key === 'xmlns' || key.startsWith('xmlns:')) {
|
|
||||||
if (val == null || val === false) el.removeAttributeNS('http://www.w3.org/2000/xmlns/', key);
|
|
||||||
else el.setAttributeNS('http://www.w3.org/2000/xmlns/', key, val);
|
|
||||||
} else {
|
|
||||||
if (val == null || val === false) el.removeAttribute(key);
|
|
||||||
else if (val === true) el.setAttribute(key, '');
|
|
||||||
else el.setAttribute(key, val);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (val == null || val === false) el.removeAttribute(key);
|
|
||||||
else if (val === true) el.setAttribute(key, '');
|
|
||||||
else el.setAttribute(key, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const appendChildNode = (parent, child) => {
|
|
||||||
if (child == null) return;
|
|
||||||
if (isFunction(child)) {
|
|
||||||
const anchor = doc.createTextNode('');
|
|
||||||
parent.appendChild(anchor);
|
|
||||||
let currentNodes = [];
|
|
||||||
const stop = Watch(() => {
|
|
||||||
const raw = child();
|
|
||||||
if (raw?._isFor) {
|
|
||||||
raw._reconcile(parent);
|
|
||||||
currentNodes = raw();
|
|
||||||
} else {
|
|
||||||
const next = (Array.isArray(raw) ? raw : [raw])
|
|
||||||
.flat(Infinity)
|
|
||||||
.filter(v => v != null)
|
|
||||||
.map(v => isNode(v) ? v : doc.createTextNode(String(v)));
|
|
||||||
for (const n of currentNodes) {
|
|
||||||
if (!next.includes(n)) removeNode(n);
|
|
||||||
}
|
|
||||||
let ref = anchor;
|
|
||||||
for (let i = next.length - 1; i >= 0; i--) {
|
|
||||||
const n = next[i];
|
|
||||||
if (n.parentNode !== parent) {
|
|
||||||
parent.insertBefore(n, ref);
|
|
||||||
} else if (n.nextSibling !== next[i + 1]) {
|
|
||||||
parent.insertBefore(n, ref);
|
|
||||||
}
|
|
||||||
if (n.componentContext) n.componentContext.mount.forEach(f => f());
|
|
||||||
ref = n;
|
|
||||||
}
|
|
||||||
currentNodes = next;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
registerNodeCleanup(anchor, stop);
|
|
||||||
} else if (isNode(child)) {
|
|
||||||
parent.appendChild(child);
|
|
||||||
} else {
|
|
||||||
parent.appendChild(doc.createTextNode(String(child)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const SVG_TAGS = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use|image|ellipse|foreignObject|linearGradient|radialGradient|stop|pattern|mask|clipPath|filter|feColorMatrix|feBlend|feGaussianBlur|animate|animateTransform|set|metadata|desc|title|symbol|marker|view)$/i;
|
|
||||||
|
|
||||||
export const Tag = (tag, props = {}, ...children) => {
|
|
||||||
children = children.flat(Infinity);
|
|
||||||
if (isFunction(tag)) {
|
|
||||||
const ctx = { mount: [], unmount: [], provisions: {} };
|
|
||||||
let rendered;
|
|
||||||
const stop = Watch(() => {
|
|
||||||
if (activeEffect) activeEffect.componentContext = ctx;
|
|
||||||
rendered = tag(props, { children, emit: (ev, ...args) => {
|
|
||||||
const h = props[`on${ev[0].toUpperCase()}${ev.slice(1)}`];
|
|
||||||
if (isFunction(h)) h(...args);
|
|
||||||
}});
|
|
||||||
if (activeEffect) activeEffect.componentContext = null;
|
|
||||||
});
|
|
||||||
if (isNode(rendered)) {
|
|
||||||
rendered.componentContext = ctx;
|
|
||||||
rendered._stop = stop;
|
|
||||||
queueMicrotask(() => ctx.mount.forEach(fn => fn()));
|
|
||||||
} else if (Array.isArray(rendered)) {
|
|
||||||
rendered.forEach(node => {
|
|
||||||
if (isNode(node)) {
|
|
||||||
node.componentContext = ctx;
|
|
||||||
node._stop = stop;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
queueMicrotask(() => ctx.mount.forEach(fn => fn()));
|
|
||||||
}
|
|
||||||
return rendered;
|
|
||||||
}
|
|
||||||
const isSVG = SVG_TAGS.test(tag);
|
|
||||||
const el = isSVG ? doc.createElementNS('http://www.w3.org/2000/svg', tag) : doc.createElement(tag);
|
|
||||||
for (const [k, v] of Object.entries(props)) {
|
|
||||||
if (k.startsWith('on')) {
|
|
||||||
const ev = k.slice(2).toLowerCase();
|
|
||||||
el.addEventListener(ev, v);
|
|
||||||
onUnmount(() => el.removeEventListener(ev, v));
|
|
||||||
} else if (isFunction(v)) {
|
|
||||||
const stopAttr = Watch(() => setProperty(el, k, v(), isSVG));
|
|
||||||
registerNodeCleanup(el, stopAttr);
|
|
||||||
if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === 'value' || k === 'checked')) {
|
|
||||||
const evType = k === 'checked' ? 'change' : 'input';
|
|
||||||
const handler = e => v(e.target[k]);
|
|
||||||
el.addEventListener(evType, handler);
|
|
||||||
onUnmount(() => el.removeEventListener(evType, handler));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setProperty(el, k, v, isSVG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const child of children) appendChildNode(el, child);
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const If = (cond, thenFn, elseFn = null, hooks = {}) => {
|
|
||||||
let lastResult = null;
|
|
||||||
let nodes = [];
|
|
||||||
let exitPromise = null;
|
|
||||||
return () => {
|
|
||||||
const condition = !!(isFunction(cond) ? cond() : cond);
|
|
||||||
if (condition === lastResult) return nodes.length === 1 ? nodes[0] : nodes;
|
|
||||||
if (nodes.length) {
|
|
||||||
if (hooks.leave) {
|
|
||||||
if (exitPromise && exitPromise.cancel) exitPromise.cancel();
|
|
||||||
const anim = hooks.leave(nodes);
|
|
||||||
exitPromise = anim;
|
|
||||||
if (anim && anim.finished) {
|
|
||||||
anim.finished.then(() => nodes.forEach(n => removeNode(n)));
|
|
||||||
} else {
|
|
||||||
nodes.forEach(n => removeNode(n));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nodes.forEach(n => removeNode(n));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastResult = condition;
|
|
||||||
const newNodes = condition ? thenFn() : elseFn?.() ?? [];
|
|
||||||
const newArr = (Array.isArray(newNodes) ? newNodes : [newNodes]).flat(Infinity).filter(v => v != null);
|
|
||||||
nodes = newArr;
|
|
||||||
if (nodes.length && hooks.enter) hooks.enter(nodes);
|
|
||||||
return nodes.length === 1 ? nodes[0] : nodes;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const For = ({ each, key, children }) => {
|
|
||||||
let cache = new Map();
|
|
||||||
let order = [];
|
|
||||||
const reconcile = (parent) => {
|
|
||||||
const items = isFunction(each) ? each() : each || [];
|
|
||||||
const newMap = new Map();
|
|
||||||
const newOrder = [];
|
|
||||||
const getKey = (item, i) => key ? (isFunction(key) ? key(item, i) : item[key]) : i;
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
const item = items[i];
|
|
||||||
const k = getKey(item, i);
|
|
||||||
let node = cache.get(k);
|
|
||||||
if (!node) node = Tag(children[0], { item, index: i });
|
|
||||||
newMap.set(k, node);
|
|
||||||
newOrder.push(node);
|
|
||||||
cache.delete(k);
|
|
||||||
}
|
|
||||||
if (parent) {
|
|
||||||
let ref = null;
|
|
||||||
for (let i = newOrder.length - 1; i >= 0; i--) {
|
|
||||||
const node = newOrder[i];
|
|
||||||
if (node.nextSibling !== newOrder[i + 1]) {
|
|
||||||
parent.insertBefore(node, ref);
|
|
||||||
}
|
|
||||||
ref = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const node of cache.values()) removeNode(node);
|
|
||||||
cache = newMap;
|
|
||||||
order = newOrder;
|
|
||||||
};
|
|
||||||
const renderFn = () => order;
|
|
||||||
renderFn._isFor = true;
|
|
||||||
renderFn._reconcile = reconcile;
|
|
||||||
return renderFn;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Router = ({ routes }) => {
|
|
||||||
const outlet = Tag('div', { class: 'router-outlet' });
|
|
||||||
const getHash = () => window.location.hash.slice(1) || '/';
|
|
||||||
const path = $(getHash());
|
|
||||||
const handler = () => { path(getHash()); };
|
|
||||||
window.addEventListener('hashchange', handler);
|
|
||||||
registerNodeCleanup(outlet, () => window.removeEventListener('hashchange', handler));
|
|
||||||
Watch(() => {
|
|
||||||
const current = path();
|
|
||||||
const matched = routes.find(r => {
|
|
||||||
const rSeg = r.path.split('/').filter(Boolean);
|
|
||||||
const pSeg = current.split('/').filter(Boolean);
|
|
||||||
return rSeg.length === pSeg.length && rSeg.every((s, i) => s[0] === ':' || s === pSeg[i]);
|
|
||||||
}) || routes.find(r => r.path === '*');
|
|
||||||
if (matched) {
|
|
||||||
while (outlet.firstChild) removeNode(outlet.firstChild);
|
|
||||||
const params = {};
|
|
||||||
matched.path.split('/').filter(Boolean).forEach((s, i) => {
|
|
||||||
if (s[0] === ':') params[s.slice(1)] = current.split('/').filter(Boolean)[i];
|
|
||||||
});
|
|
||||||
Router.params(params);
|
|
||||||
const view = Tag(matched.component, { params });
|
|
||||||
outlet.appendChild(view);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return outlet;
|
|
||||||
};
|
|
||||||
|
|
||||||
Router.params = $({});
|
|
||||||
|
|
||||||
export const navigate = path => { window.location.hash = path; };
|
|
||||||
export const currentPath = () => window.location.hash.slice(1) || '/';
|
|
||||||
|
|
||||||
export const createApp = (Root, rootProps = {}) => selector => {
|
|
||||||
const target = typeof selector === 'string' ? doc.querySelector(selector) : selector;
|
|
||||||
if (target.appUnmount) target.appUnmount();
|
|
||||||
const app = Tag(Root, rootProps);
|
|
||||||
target.appendChild(app);
|
|
||||||
if (app.componentContext) app.componentContext.mount.forEach(f => f());
|
|
||||||
target.appUnmount = () => removeNode(app);
|
|
||||||
return target.appUnmount;
|
|
||||||
};
|
|
||||||
|
|
||||||
'div span p a button input form label ul li ol header footer main section article nav aside h1 h2 h3 h4 h5 h6 img svg path circle rect line polyline polygon g defs text tspan use image ellipse foreignObject linearGradient radialGradient stop pattern mask clipPath filter feColorMatrix feBlend feGaussianBlur animate animateTransform set metadata desc title symbol marker view br hr pre code strong em table tr td th thead tbody tfoot select option textarea iframe video audio canvas'
|
|
||||||
.split(' ').forEach(tag => {
|
|
||||||
globalThis[tag[0].toUpperCase() + tag.slice(1)] = (props, ...children) => Tag(tag, props, ...children);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default { $, set, Watch, watch, computed, untrack, Tag, If, For, Router, createApp, removeNode, navigate, currentPath, onMount, onUnmount };
|
|
||||||
Reference in New Issue
Block a user