Correcto sigpro
This commit is contained in:
@@ -6020,11 +6020,6 @@
|
|||||||
.filter {
|
.filter {
|
||||||
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
||||||
}
|
}
|
||||||
.transition {
|
|
||||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
|
|
||||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
|
||||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
|
||||||
}
|
|
||||||
.transition-all {
|
.transition-all {
|
||||||
transition-property: all;
|
transition-property: all;
|
||||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||||
|
|||||||
2
css/sigpro.min.css
vendored
2
css/sigpro.min.css
vendored
File diff suppressed because one or more lines are too long
771
dist/sigpro-ui.esm.js
vendored
771
dist/sigpro-ui.esm.js
vendored
@@ -10,75 +10,379 @@ var __export = (target, all) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// src/sigpro.js
|
// src/sigpro.js
|
||||||
var activeEffect = null;
|
var doc = typeof document !== "undefined" ? document : null;
|
||||||
var currentOwner = null;
|
|
||||||
var effectQueue = new Set;
|
|
||||||
var isFlushing = false;
|
|
||||||
var MOUNTED_NODES = new WeakMap;
|
|
||||||
var doc = document;
|
|
||||||
var isArr = Array.isArray;
|
var isArr = Array.isArray;
|
||||||
var assign = Object.assign;
|
|
||||||
var createEl = (t) => doc.createElement(t);
|
|
||||||
var createText = (t) => doc.createTextNode(String(t ?? ""));
|
|
||||||
var isFunc = (f) => typeof f === "function";
|
var isFunc = (f) => typeof f === "function";
|
||||||
var isObj = (o) => typeof o === "object" && o !== null;
|
var isObj = (o) => o && typeof o === "object";
|
||||||
var runWithContext = (effect, callback) => {
|
var ensureNode = (n) => n?._isRuntime ? n.container : n instanceof Node ? n : doc.createTextNode(n == null ? "" : String(n));
|
||||||
const previousEffect = activeEffect;
|
var activeEffect = null;
|
||||||
activeEffect = effect;
|
var activeOwner = null;
|
||||||
|
var isFlushing = false;
|
||||||
|
var effectQueue = new Set;
|
||||||
|
var MOUNTED_NODES = new WeakMap;
|
||||||
|
var dispose = (eff) => {
|
||||||
|
if (!eff || eff._disposed)
|
||||||
|
return;
|
||||||
|
eff._disposed = true;
|
||||||
|
const stack = [eff];
|
||||||
|
while (stack.length) {
|
||||||
|
const e = stack.pop();
|
||||||
|
if (e._cleanups) {
|
||||||
|
e._cleanups.forEach((fn) => fn());
|
||||||
|
e._cleanups.clear();
|
||||||
|
}
|
||||||
|
if (e._children) {
|
||||||
|
e._children.forEach((child) => stack.push(child));
|
||||||
|
e._children.clear();
|
||||||
|
}
|
||||||
|
if (e._deps) {
|
||||||
|
e._deps.forEach((depSet) => depSet.delete(e));
|
||||||
|
e._deps.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var onUnmount = (fn) => {
|
||||||
|
if (activeOwner)
|
||||||
|
(activeOwner._cleanups ||= new Set).add(fn);
|
||||||
|
};
|
||||||
|
var createEffect = (fn, isComputed = false) => {
|
||||||
|
const effect = () => {
|
||||||
|
if (effect._disposed)
|
||||||
|
return;
|
||||||
|
if (effect._deps)
|
||||||
|
effect._deps.forEach((depSet) => depSet.delete(effect));
|
||||||
|
if (effect._cleanups) {
|
||||||
|
effect._cleanups.forEach((cl) => cl());
|
||||||
|
effect._cleanups.clear();
|
||||||
|
}
|
||||||
|
const prevEffect = activeEffect, prevOwner = activeOwner;
|
||||||
|
activeEffect = activeOwner = effect;
|
||||||
try {
|
try {
|
||||||
return callback();
|
const res = isComputed ? fn() : (fn(), undefined);
|
||||||
|
if (!isComputed)
|
||||||
|
effect._result = res;
|
||||||
|
return res;
|
||||||
} finally {
|
} finally {
|
||||||
activeEffect = previousEffect;
|
activeEffect = prevEffect;
|
||||||
|
activeOwner = prevOwner;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var cleanupNode = (node) => {
|
effect._deps = effect._cleanups = effect._children = null;
|
||||||
if (node._cleanups) {
|
effect._disposed = false;
|
||||||
node._cleanups.forEach((dispose) => dispose());
|
effect._isComputed = isComputed;
|
||||||
node._cleanups.clear();
|
effect._depth = activeEffect ? activeEffect._depth + 1 : 0;
|
||||||
}
|
effect._mounts = [];
|
||||||
node.childNodes?.forEach(cleanupNode);
|
effect._parent = activeOwner;
|
||||||
|
effect._provisions = activeOwner ? { ...activeOwner._provisions } : {};
|
||||||
|
if (activeOwner)
|
||||||
|
(activeOwner._children ||= new Set).add(effect);
|
||||||
|
return effect;
|
||||||
};
|
};
|
||||||
var flushEffects = () => {
|
var flush = () => {
|
||||||
if (isFlushing)
|
if (isFlushing)
|
||||||
return;
|
return;
|
||||||
isFlushing = true;
|
isFlushing = true;
|
||||||
while (effectQueue.size > 0) {
|
const sorted = Array.from(effectQueue).sort((a, b) => a._depth - b._depth);
|
||||||
const sortedEffects = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0));
|
|
||||||
effectQueue.clear();
|
effectQueue.clear();
|
||||||
for (const effect of sortedEffects) {
|
for (const e of sorted)
|
||||||
if (!effect._deleted)
|
if (!e._disposed)
|
||||||
effect();
|
e();
|
||||||
}
|
|
||||||
}
|
|
||||||
isFlushing = false;
|
isFlushing = false;
|
||||||
};
|
};
|
||||||
var trackSubscription = (subscribers) => {
|
var trackUpdate = (subs, trigger = false) => {
|
||||||
if (activeEffect && !activeEffect._deleted) {
|
if (!trigger && activeEffect && !activeEffect._disposed) {
|
||||||
subscribers.add(activeEffect);
|
subs.add(activeEffect);
|
||||||
activeEffect._deps.add(subscribers);
|
(activeEffect._deps ||= new Set).add(subs);
|
||||||
}
|
} else if (trigger) {
|
||||||
};
|
let hasQueue = false;
|
||||||
var triggerUpdate = (subscribers) => {
|
subs.forEach((e) => {
|
||||||
subscribers.forEach((effect) => {
|
if (e === activeEffect || e._disposed)
|
||||||
if (effect === activeEffect || effect._deleted)
|
|
||||||
return;
|
return;
|
||||||
if (effect._isComputed) {
|
if (e._isComputed) {
|
||||||
effect.markDirty();
|
e._dirty = true;
|
||||||
if (effect._subs)
|
if (e._subs)
|
||||||
triggerUpdate(effect._subs);
|
trackUpdate(e._subs, true);
|
||||||
} else {
|
} else {
|
||||||
effectQueue.add(effect);
|
effectQueue.add(e);
|
||||||
|
hasQueue = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!isFlushing)
|
if (hasQueue && !isFlushing)
|
||||||
queueMicrotask(flushEffects);
|
queueMicrotask(flush);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var untrack = (fn) => {
|
||||||
|
const p = activeEffect;
|
||||||
|
activeEffect = null;
|
||||||
|
try {
|
||||||
|
return fn();
|
||||||
|
} finally {
|
||||||
|
activeEffect = p;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var onMount = (fn) => {
|
||||||
|
if (activeOwner)
|
||||||
|
(activeOwner._mounts ||= []).push(fn);
|
||||||
|
};
|
||||||
|
var $2 = (val, key = null) => {
|
||||||
|
const subs = new Set;
|
||||||
|
if (isFunc(val)) {
|
||||||
|
let cache, dirty = true;
|
||||||
|
const computed = () => {
|
||||||
|
if (dirty) {
|
||||||
|
const prev = activeEffect;
|
||||||
|
activeEffect = computed;
|
||||||
|
try {
|
||||||
|
const next = val();
|
||||||
|
if (!Object.is(cache, next)) {
|
||||||
|
cache = next;
|
||||||
|
dirty = false;
|
||||||
|
trackUpdate(subs, true);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
activeEffect = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trackUpdate(subs);
|
||||||
|
return cache;
|
||||||
|
};
|
||||||
|
computed._isComputed = true;
|
||||||
|
computed._subs = subs;
|
||||||
|
computed._dirty = true;
|
||||||
|
computed._deps = null;
|
||||||
|
computed._disposed = false;
|
||||||
|
computed.markDirty = () => {
|
||||||
|
dirty = true;
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
return computed;
|
||||||
|
}
|
||||||
|
if (key)
|
||||||
|
try {
|
||||||
|
val = JSON.parse(localStorage.getItem(key)) ?? val;
|
||||||
|
} catch (e) {}
|
||||||
|
return (...args) => {
|
||||||
|
if (args.length) {
|
||||||
|
const next = isFunc(args[0]) ? args[0](val) : args[0];
|
||||||
|
if (!Object.is(val, next)) {
|
||||||
|
val = next;
|
||||||
|
if (key)
|
||||||
|
localStorage.setItem(key, JSON.stringify(val));
|
||||||
|
trackUpdate(subs, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trackUpdate(subs);
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var $$ = (obj, cache = new WeakMap) => {
|
||||||
|
if (!isObj(obj))
|
||||||
|
return obj;
|
||||||
|
if (cache.has(obj))
|
||||||
|
return cache.get(obj);
|
||||||
|
const subs = {};
|
||||||
|
const proxy = new Proxy(obj, {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cache.set(obj, proxy);
|
||||||
|
return proxy;
|
||||||
|
};
|
||||||
|
var Watch2 = (sources, cb) => {
|
||||||
|
if (cb === undefined) {
|
||||||
|
const effect2 = createEffect(sources);
|
||||||
|
effect2();
|
||||||
|
return () => dispose(effect2);
|
||||||
|
}
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const vals = Array.isArray(sources) ? sources.map((s) => s()) : sources();
|
||||||
|
untrack(() => cb(vals));
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
return () => dispose(effect);
|
||||||
|
};
|
||||||
|
var cleanupNode = (node) => {
|
||||||
|
if (node._cleanups) {
|
||||||
|
node._cleanups.forEach((fn) => fn());
|
||||||
|
node._cleanups.clear();
|
||||||
|
}
|
||||||
|
if (node._ownerEffect)
|
||||||
|
dispose(node._ownerEffect);
|
||||||
|
if (node.childNodes)
|
||||||
|
node.childNodes.forEach(cleanupNode);
|
||||||
|
};
|
||||||
|
var provide = (key, value) => {
|
||||||
|
if (activeOwner)
|
||||||
|
activeOwner._provisions[key] = value;
|
||||||
|
};
|
||||||
|
var inject = (key, defaultValue) => {
|
||||||
|
let ctx = activeOwner;
|
||||||
|
while (ctx) {
|
||||||
|
if (key in ctx._provisions)
|
||||||
|
return ctx._provisions[key];
|
||||||
|
ctx = ctx._parent;
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
};
|
||||||
|
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
||||||
|
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
|
||||||
|
var validateAttr = (key, val) => {
|
||||||
|
if (val == null || val === false)
|
||||||
|
return null;
|
||||||
|
if (isDangerousAttr(key)) {
|
||||||
|
const sVal = String(val);
|
||||||
|
if (DANGEROUS_PROTOCOL.test(sVal)) {
|
||||||
|
console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`);
|
||||||
|
return "#";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
var Tag2 = (tag, props = {}, children = []) => {
|
||||||
|
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
||||||
|
children = props;
|
||||||
|
props = {};
|
||||||
|
}
|
||||||
|
if (isFunc(tag)) {
|
||||||
|
const ctx = {
|
||||||
|
_mounts: [],
|
||||||
|
_cleanups: new Set,
|
||||||
|
_provisions: activeOwner?._provisions ? { ...activeOwner._provisions } : {}
|
||||||
|
};
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const result2 = tag(props, {
|
||||||
|
children,
|
||||||
|
emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args)
|
||||||
|
});
|
||||||
|
effect._result = result2;
|
||||||
|
return result2;
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
ctx._mounts = effect._mounts || [];
|
||||||
|
ctx._cleanups = effect._cleanups || new Set;
|
||||||
|
const result = effect._result;
|
||||||
|
const attachLifecycle = (node) => node && typeof node === "object" && !node._isRuntime && (node._mounts = ctx._mounts, node._cleanups = ctx._cleanups, node._ownerEffect = effect);
|
||||||
|
isArr(result) ? result.forEach(attachLifecycle) : attachLifecycle(result);
|
||||||
|
if (result == null)
|
||||||
|
return null;
|
||||||
|
if (result instanceof Node || isArr(result) && result.every((n) => n instanceof Node))
|
||||||
|
return result;
|
||||||
|
return doc.createTextNode(String(result));
|
||||||
|
}
|
||||||
|
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) : doc.createElement(tag);
|
||||||
|
el._cleanups = new Set;
|
||||||
|
for (let k in props) {
|
||||||
|
if (!props.hasOwnProperty(k))
|
||||||
|
continue;
|
||||||
|
let v = props[k];
|
||||||
|
if (k === "ref") {
|
||||||
|
isFunc(v) ? v(el) : v.current = el;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (k.startsWith("on")) {
|
||||||
|
const ev = k.slice(2).toLowerCase();
|
||||||
|
el.addEventListener(ev, v);
|
||||||
|
const off = () => el.removeEventListener(ev, v);
|
||||||
|
el._cleanups.add(off);
|
||||||
|
onUnmount(off);
|
||||||
|
} else if (isFunc(v)) {
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const val = validateAttr(k, v());
|
||||||
|
if (k === "class")
|
||||||
|
el.className = val || "";
|
||||||
|
else if (val == null)
|
||||||
|
el.removeAttribute(k);
|
||||||
|
else if (k in el && !isSVG)
|
||||||
|
el[k] = val;
|
||||||
|
else
|
||||||
|
el.setAttribute(k, val === true ? "" : val);
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
el._cleanups.add(() => dispose(effect));
|
||||||
|
onUnmount(() => dispose(effect));
|
||||||
|
if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) {
|
||||||
|
const evType = k === "checked" ? "change" : "input";
|
||||||
|
el.addEventListener(evType, (ev) => v(ev.target[k]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const val = validateAttr(k, v);
|
||||||
|
if (val != null) {
|
||||||
|
if (k in el && !isSVG)
|
||||||
|
el[k] = val;
|
||||||
|
else
|
||||||
|
el.setAttribute(k, val === true ? "" : val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const append = (c) => {
|
||||||
|
if (isArr(c))
|
||||||
|
return c.forEach(append);
|
||||||
|
if (isFunc(c)) {
|
||||||
|
const anchor = doc.createTextNode("");
|
||||||
|
el.appendChild(anchor);
|
||||||
|
let currentNodes = [];
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const res = c();
|
||||||
|
const next = (isArr(res) ? res : [res]).map(ensureNode);
|
||||||
|
currentNodes.forEach((n) => {
|
||||||
|
if (n._isRuntime)
|
||||||
|
n.destroy();
|
||||||
|
else
|
||||||
|
cleanupNode(n);
|
||||||
|
if (n.parentNode)
|
||||||
|
n.remove();
|
||||||
|
});
|
||||||
|
let ref = anchor;
|
||||||
|
for (let i = next.length - 1;i >= 0; i--) {
|
||||||
|
const node = next[i];
|
||||||
|
if (node.parentNode !== ref.parentNode)
|
||||||
|
ref.parentNode?.insertBefore(node, ref);
|
||||||
|
if (node._mounts)
|
||||||
|
node._mounts.forEach((fn) => fn());
|
||||||
|
ref = node;
|
||||||
|
}
|
||||||
|
currentNodes = next;
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
el._cleanups.add(() => dispose(effect));
|
||||||
|
onUnmount(() => dispose(effect));
|
||||||
|
} else {
|
||||||
|
const node = ensureNode(c);
|
||||||
|
el.appendChild(node);
|
||||||
|
if (node._mounts)
|
||||||
|
node._mounts.forEach((fn) => fn());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
append(children);
|
||||||
|
return el;
|
||||||
};
|
};
|
||||||
var Render = (renderFn) => {
|
var Render = (renderFn) => {
|
||||||
const cleanups = new Set;
|
const cleanups = new Set;
|
||||||
const previousOwner = currentOwner;
|
const mounts = [];
|
||||||
const container = createEl("div");
|
const previousOwner = activeOwner;
|
||||||
|
const container = doc.createElement("div");
|
||||||
container.style.display = "contents";
|
container.style.display = "contents";
|
||||||
currentOwner = { cleanups };
|
activeOwner = { _cleanups: cleanups, _mounts: mounts };
|
||||||
const processResult = (result) => {
|
const processResult = (result) => {
|
||||||
if (!result)
|
if (!result)
|
||||||
return;
|
return;
|
||||||
@@ -88,14 +392,15 @@ var Render = (renderFn) => {
|
|||||||
} else if (isArr(result)) {
|
} else if (isArr(result)) {
|
||||||
result.forEach(processResult);
|
result.forEach(processResult);
|
||||||
} else {
|
} else {
|
||||||
container.appendChild(result instanceof Node ? result : createText(result));
|
container.appendChild(result instanceof Node ? result : doc.createTextNode(String(result == null ? "" : result)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) }));
|
processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) }));
|
||||||
} finally {
|
} finally {
|
||||||
currentOwner = previousOwner;
|
activeOwner = previousOwner;
|
||||||
}
|
}
|
||||||
|
mounts.forEach((fn) => fn());
|
||||||
return {
|
return {
|
||||||
_isRuntime: true,
|
_isRuntime: true,
|
||||||
container,
|
container,
|
||||||
@@ -106,336 +411,110 @@ var Render = (renderFn) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
var $2 = (initialValue, storageKey = null) => {
|
var If2 = (cond, ifYes, ifNot = null, trans = null) => {
|
||||||
const subscribers = new Set;
|
const anchor = doc.createTextNode("");
|
||||||
if (isFunc(initialValue)) {
|
const root = Tag2("div", { style: "display:contents" }, [anchor]);
|
||||||
let cachedValue, isDirty = true;
|
let currentView = null, last = null;
|
||||||
const effect = () => {
|
Watch2(() => !!(isFunc(cond) ? cond() : cond), (show) => {
|
||||||
if (effect._deleted)
|
if (show === last)
|
||||||
return;
|
return;
|
||||||
effect._deps.forEach((dep) => dep.delete(effect));
|
last = show;
|
||||||
effect._deps.clear();
|
const disposeView = () => {
|
||||||
runWithContext(effect, () => {
|
if (currentView) {
|
||||||
const newValue = initialValue();
|
|
||||||
if (!Object.is(cachedValue, newValue) || isDirty) {
|
|
||||||
cachedValue = newValue;
|
|
||||||
isDirty = false;
|
|
||||||
triggerUpdate(subscribers);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
assign(effect, {
|
|
||||||
_deps: new Set,
|
|
||||||
_isComputed: true,
|
|
||||||
_subs: subscribers,
|
|
||||||
_deleted: false,
|
|
||||||
markDirty: () => isDirty = true,
|
|
||||||
stop: () => {
|
|
||||||
effect._deleted = true;
|
|
||||||
effect._deps.forEach((dep) => dep.delete(effect));
|
|
||||||
subscribers.clear();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (currentOwner)
|
|
||||||
currentOwner.cleanups.add(effect.stop);
|
|
||||||
return () => {
|
|
||||||
if (isDirty)
|
|
||||||
effect();
|
|
||||||
trackSubscription(subscribers);
|
|
||||||
return cachedValue;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let value = initialValue;
|
|
||||||
if (storageKey) {
|
|
||||||
try {
|
|
||||||
const saved = localStorage.getItem(storageKey);
|
|
||||||
if (saved !== null)
|
|
||||||
value = JSON.parse(saved);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("SigPro Storage Lock", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (...args) => {
|
|
||||||
if (args.length) {
|
|
||||||
const nextValue = isFunc(args[0]) ? args[0](value) : args[0];
|
|
||||||
if (!Object.is(value, nextValue)) {
|
|
||||||
value = nextValue;
|
|
||||||
if (storageKey)
|
|
||||||
localStorage.setItem(storageKey, JSON.stringify(value));
|
|
||||||
triggerUpdate(subscribers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trackSubscription(subscribers);
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var $$ = (object, cache = new WeakMap) => {
|
|
||||||
if (!isObj(object))
|
|
||||||
return object;
|
|
||||||
if (cache.has(object))
|
|
||||||
return cache.get(object);
|
|
||||||
const keySubscribers = {};
|
|
||||||
const proxy = new Proxy(object, {
|
|
||||||
get(target, key) {
|
|
||||||
if (activeEffect)
|
|
||||||
trackSubscription(keySubscribers[key] ??= new Set);
|
|
||||||
const value = Reflect.get(target, key);
|
|
||||||
return isObj(value) ? $$(value, cache) : value;
|
|
||||||
},
|
|
||||||
set(target, key, value) {
|
|
||||||
if (Object.is(target[key], value))
|
|
||||||
return true;
|
|
||||||
const success = Reflect.set(target, key, value);
|
|
||||||
if (keySubscribers[key])
|
|
||||||
triggerUpdate(keySubscribers[key]);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cache.set(object, proxy);
|
|
||||||
return proxy;
|
|
||||||
};
|
|
||||||
var Watch2 = (target, callbackFn) => {
|
|
||||||
const isExplicit = isArr(target);
|
|
||||||
const callback = isExplicit ? callbackFn : target;
|
|
||||||
if (!isFunc(callback))
|
|
||||||
return () => {};
|
|
||||||
const owner = currentOwner;
|
|
||||||
const runner = () => {
|
|
||||||
if (runner._deleted)
|
|
||||||
return;
|
|
||||||
runner._deps.forEach((dep) => dep.delete(runner));
|
|
||||||
runner._deps.clear();
|
|
||||||
runner._cleanups.forEach((cleanup) => cleanup());
|
|
||||||
runner._cleanups.clear();
|
|
||||||
const previousOwner = currentOwner;
|
|
||||||
runner.depth = activeEffect ? activeEffect.depth + 1 : 0;
|
|
||||||
runWithContext(runner, () => {
|
|
||||||
currentOwner = { cleanups: runner._cleanups };
|
|
||||||
if (isExplicit) {
|
|
||||||
runWithContext(null, callback);
|
|
||||||
target.forEach((dep) => isFunc(dep) && dep());
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
currentOwner = previousOwner;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
assign(runner, {
|
|
||||||
_deps: new Set,
|
|
||||||
_cleanups: new Set,
|
|
||||||
_deleted: false,
|
|
||||||
stop: () => {
|
|
||||||
if (runner._deleted)
|
|
||||||
return;
|
|
||||||
runner._deleted = true;
|
|
||||||
effectQueue.delete(runner);
|
|
||||||
runner._deps.forEach((dep) => dep.delete(runner));
|
|
||||||
runner._cleanups.forEach((cleanup) => cleanup());
|
|
||||||
if (owner)
|
|
||||||
owner.cleanups.delete(runner.stop);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (owner)
|
|
||||||
owner.cleanups.add(runner.stop);
|
|
||||||
runner();
|
|
||||||
return runner.stop;
|
|
||||||
};
|
|
||||||
var Tag2 = (tag, props = {}, children = []) => {
|
|
||||||
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 element = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : createEl(tag);
|
|
||||||
element._cleanups = new Set;
|
|
||||||
element.onUnmount = (fn) => element._cleanups.add(fn);
|
|
||||||
const booleanAttributes = ["disabled", "checked", "required", "readonly", "selected", "multiple", "autofocus"];
|
|
||||||
const updateAttribute = (name, value) => {
|
|
||||||
const sanitized = (name === "src" || name === "href") && String(value).toLowerCase().includes("javascript:") ? "#" : value;
|
|
||||||
if (booleanAttributes.includes(name)) {
|
|
||||||
element[name] = !!sanitized;
|
|
||||||
sanitized ? element.setAttribute(name, "") : element.removeAttribute(name);
|
|
||||||
} else {
|
|
||||||
sanitized == null ? element.removeAttribute(name) : element.setAttribute(name, sanitized);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for (let [key, value] of Object.entries(props)) {
|
|
||||||
if (key === "ref") {
|
|
||||||
isFunc(value) ? value(element) : value.current = element;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const isSignal = isFunc(value);
|
|
||||||
if (key.startsWith("on")) {
|
|
||||||
const eventName = key.slice(2).toLowerCase().split(".")[0];
|
|
||||||
element.addEventListener(eventName, value);
|
|
||||||
element._cleanups.add(() => element.removeEventListener(eventName, value));
|
|
||||||
} else if (isSignal) {
|
|
||||||
element._cleanups.add(Watch2(() => {
|
|
||||||
const currentVal = value();
|
|
||||||
key === "class" ? element.className = currentVal || "" : updateAttribute(key, currentVal);
|
|
||||||
}));
|
|
||||||
if (["INPUT", "TEXTAREA", "SELECT"].includes(element.tagName) && (key === "value" || key === "checked")) {
|
|
||||||
const event = key === "checked" ? "change" : "input";
|
|
||||||
const handler = (e) => value(e.target[key]);
|
|
||||||
element.addEventListener(event, handler);
|
|
||||||
element._cleanups.add(() => element.removeEventListener(event, handler));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updateAttribute(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const appendChildNode = (child) => {
|
|
||||||
if (isArr(child))
|
|
||||||
return child.forEach(appendChildNode);
|
|
||||||
if (isFunc(child)) {
|
|
||||||
const marker = createText("");
|
|
||||||
element.appendChild(marker);
|
|
||||||
let currentNodes = [];
|
|
||||||
element._cleanups.add(Watch2(() => {
|
|
||||||
const result = child();
|
|
||||||
const nextNodes = (isArr(result) ? result : [result]).map((node) => node?._isRuntime ? node.container : node instanceof Node ? node : createText(node));
|
|
||||||
currentNodes.forEach((node) => {
|
|
||||||
cleanupNode(node);
|
|
||||||
node.remove();
|
|
||||||
});
|
|
||||||
nextNodes.forEach((node) => marker.parentNode?.insertBefore(node, marker));
|
|
||||||
currentNodes = nextNodes;
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
element.appendChild(child instanceof Node ? child : createText(child));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
appendChildNode(children);
|
|
||||||
return element;
|
|
||||||
};
|
|
||||||
var If2 = (condition, thenVal, otherwiseVal = null, transition = null) => {
|
|
||||||
const marker = createText("");
|
|
||||||
const container = Tag2("div", { style: "display:contents" }, [marker]);
|
|
||||||
let currentView = null, lastState = null;
|
|
||||||
Watch2(() => {
|
|
||||||
const state = !!(isFunc(condition) ? condition() : condition);
|
|
||||||
if (state === lastState)
|
|
||||||
return;
|
|
||||||
lastState = state;
|
|
||||||
const dispose = () => {
|
|
||||||
if (currentView)
|
|
||||||
currentView.destroy();
|
currentView.destroy();
|
||||||
currentView = null;
|
currentView = null;
|
||||||
};
|
|
||||||
if (currentView && !state && transition?.out) {
|
|
||||||
transition.out(currentView.container, dispose);
|
|
||||||
} else {
|
|
||||||
dispose();
|
|
||||||
}
|
}
|
||||||
const branch = state ? thenVal : otherwiseVal;
|
};
|
||||||
if (branch) {
|
if (currentView && !show && trans?.out)
|
||||||
currentView = Render(() => isFunc(branch) ? branch() : branch);
|
trans.out(currentView.container, disposeView);
|
||||||
container.insertBefore(currentView.container, marker);
|
else
|
||||||
if (state && transition?.in)
|
disposeView();
|
||||||
transition.in(currentView.container);
|
const content = show ? ifYes : ifNot;
|
||||||
|
if (content) {
|
||||||
|
currentView = Render(() => isFunc(content) ? content() : content);
|
||||||
|
root.insertBefore(currentView.container, anchor);
|
||||||
|
if (trans?.in)
|
||||||
|
trans.in(currentView.container);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return container;
|
return root;
|
||||||
};
|
};
|
||||||
var For2 = (source, renderFn, keyFn, tag = "div", props = { style: "display:contents" }) => {
|
var For2 = (src, itemFn, keyFn) => {
|
||||||
const marker = createText("");
|
const anchor = doc.createTextNode("");
|
||||||
const container = Tag2(tag, props, [marker]);
|
const root = Tag2("div", { style: "display:contents" }, [anchor]);
|
||||||
let viewCache = new Map;
|
let cache = new Map;
|
||||||
Watch2(() => {
|
Watch2(() => (isFunc(src) ? src() : src) || [], (items) => {
|
||||||
const items = (isFunc(source) ? source() : source) || [];
|
const next = new Map, order = [];
|
||||||
const nextCache = new Map;
|
|
||||||
const order = [];
|
|
||||||
for (let i = 0;i < items.length; i++) {
|
for (let i = 0;i < items.length; i++) {
|
||||||
const item = items[i];
|
const item = items[i];
|
||||||
const key = keyFn ? keyFn(item, i) : i;
|
const key = keyFn ? keyFn(item, i) : i;
|
||||||
let view = viewCache.get(key);
|
let view = cache.get(key);
|
||||||
if (!view) {
|
if (!view)
|
||||||
const result = renderFn(item, i);
|
view = Render(() => itemFn(item, i));
|
||||||
view = result instanceof Node ? { container: result, destroy: () => {
|
next.set(key, view);
|
||||||
cleanupNode(result);
|
|
||||||
result.remove();
|
|
||||||
} } : Render(() => result);
|
|
||||||
}
|
|
||||||
viewCache.delete(key);
|
|
||||||
nextCache.set(key, view);
|
|
||||||
order.push(key);
|
order.push(key);
|
||||||
|
cache.delete(key);
|
||||||
}
|
}
|
||||||
viewCache.forEach((v) => v.destroy());
|
cache.forEach((v) => v.destroy());
|
||||||
let anchor = marker;
|
cache = next;
|
||||||
|
let ref = anchor;
|
||||||
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 view = next.get(order[i]);
|
||||||
if (view.container.nextSibling !== anchor) {
|
if (view.container.nextSibling !== ref)
|
||||||
container.insertBefore(view.container, anchor);
|
root.insertBefore(view.container, ref);
|
||||||
|
ref = view.container;
|
||||||
}
|
}
|
||||||
anchor = view.container;
|
|
||||||
}
|
|
||||||
viewCache = nextCache;
|
|
||||||
});
|
});
|
||||||
return container;
|
return root;
|
||||||
};
|
};
|
||||||
var Router = (routes) => {
|
var Router = (routes) => {
|
||||||
const currentPath = $2(window.location.hash.replace(/^#/, "") || "/");
|
const getHash = () => window.location.hash.slice(1) || "/";
|
||||||
window.addEventListener("hashchange", () => currentPath(window.location.hash.replace(/^#/, "") || "/"));
|
const path = $2(getHash());
|
||||||
const outlet = Tag2("div", { class: "router-transition" });
|
const handler = () => path(getHash());
|
||||||
|
window.addEventListener("hashchange", handler);
|
||||||
|
onUnmount(() => window.removeEventListener("hashchange", handler));
|
||||||
|
const outlet = Tag2("div", { class: "router-outlet" });
|
||||||
let currentView = null;
|
let currentView = null;
|
||||||
Watch2([currentPath], async () => {
|
Watch2([path], () => {
|
||||||
const path = currentPath();
|
const cur = path();
|
||||||
const route = routes.find((r) => {
|
const route = routes.find((r) => {
|
||||||
const routeParts = r.path.split("/").filter(Boolean);
|
const p1 = r.path.split("/").filter(Boolean);
|
||||||
const pathParts = path.split("/").filter(Boolean);
|
const p2 = cur.split("/").filter(Boolean);
|
||||||
return routeParts.length === pathParts.length && routeParts.every((part, i) => part.startsWith(":") || part === pathParts[i]);
|
return p1.length === p2.length && p1.every((p, i) => p[0] === ":" || p === p2[i]);
|
||||||
}) || routes.find((r) => r.path === "*");
|
}) || routes.find((r) => r.path === "*");
|
||||||
if (route) {
|
if (route) {
|
||||||
let component = route.component;
|
currentView?.destroy();
|
||||||
if (isFunc(component) && component.toString().includes("import")) {
|
|
||||||
component = (await component()).default || await component();
|
|
||||||
}
|
|
||||||
const params = {};
|
const params = {};
|
||||||
route.path.split("/").filter(Boolean).forEach((part, i) => {
|
route.path.split("/").filter(Boolean).forEach((p, i) => {
|
||||||
if (part.startsWith(":"))
|
if (p[0] === ":")
|
||||||
params[part.slice(1)] = path.split("/").filter(Boolean)[i];
|
params[p.slice(1)] = cur.split("/").filter(Boolean)[i];
|
||||||
});
|
});
|
||||||
if (currentView)
|
|
||||||
currentView.destroy();
|
|
||||||
if (Router.params)
|
|
||||||
Router.params(params);
|
Router.params(params);
|
||||||
currentView = Render(() => {
|
currentView = Render(() => isFunc(route.component) ? route.component(params) : route.component);
|
||||||
try {
|
outlet.replaceChildren(currentView.container);
|
||||||
return isFunc(component) ? component(params) : component;
|
|
||||||
} catch (e) {
|
|
||||||
return Tag2("div", { class: "p-4 text-error" }, "Error loading view");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
outlet.appendChild(currentView.container);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return outlet;
|
return outlet;
|
||||||
};
|
};
|
||||||
Router.params = $2({});
|
Router.params = $2({});
|
||||||
Router.to = (path) => window.location.hash = path.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 Mount2 = (component, target) => {
|
var Mount2 = (comp, target) => {
|
||||||
const targetEl = typeof target === "string" ? doc.querySelector(target) : target;
|
const t = typeof target === "string" ? doc.querySelector(target) : target;
|
||||||
if (!targetEl)
|
if (!t)
|
||||||
return;
|
return;
|
||||||
if (MOUNTED_NODES.has(targetEl))
|
if (MOUNTED_NODES.has(t))
|
||||||
MOUNTED_NODES.get(targetEl).destroy();
|
MOUNTED_NODES.get(t).destroy();
|
||||||
const instance = Render(isFunc(component) ? component : () => component);
|
const inst = Render(isFunc(comp) ? comp : () => comp);
|
||||||
targetEl.replaceChildren(instance.container);
|
t.replaceChildren(inst.container);
|
||||||
MOUNTED_NODES.set(targetEl, instance);
|
MOUNTED_NODES.set(t, inst);
|
||||||
return instance;
|
return inst;
|
||||||
};
|
};
|
||||||
var SigPro = { $: $2, $$, Render, Watch: Watch2, Tag: Tag2, If: If2, For: For2, Router, Mount: Mount2 };
|
var SigPro = Object.freeze({ $: $2, $$, Watch: Watch2, Tag: Tag2, Render, If: If2, For: For2, Router, Mount: Mount2, untrack, onMount, onUnmount, provide, inject });
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
assign(window, SigPro);
|
Object.assign(window, SigPro);
|
||||||
const tags = `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`.split(" ");
|
"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));
|
||||||
tags.forEach((tag) => {
|
|
||||||
const helper = tag[0].toUpperCase() + tag.slice(1);
|
|
||||||
if (!(helper in window))
|
|
||||||
window[helper] = (p, c) => Tag2(tag, p, c);
|
|
||||||
});
|
|
||||||
window.SigPro = Object.freeze(SigPro);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// src/components/index.js
|
// src/components/index.js
|
||||||
|
|||||||
8
dist/sigpro-ui.esm.min.js
vendored
8
dist/sigpro-ui.esm.min.js
vendored
File diff suppressed because one or more lines are too long
771
dist/sigpro-ui.js
vendored
771
dist/sigpro-ui.js
vendored
@@ -78,75 +78,379 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// src/sigpro.js
|
// src/sigpro.js
|
||||||
var activeEffect = null;
|
var doc = typeof document !== "undefined" ? document : null;
|
||||||
var currentOwner = null;
|
|
||||||
var effectQueue = new Set;
|
|
||||||
var isFlushing = false;
|
|
||||||
var MOUNTED_NODES = new WeakMap;
|
|
||||||
var doc = document;
|
|
||||||
var isArr = Array.isArray;
|
var isArr = Array.isArray;
|
||||||
var assign = Object.assign;
|
|
||||||
var createEl = (t) => doc.createElement(t);
|
|
||||||
var createText = (t) => doc.createTextNode(String(t ?? ""));
|
|
||||||
var isFunc = (f) => typeof f === "function";
|
var isFunc = (f) => typeof f === "function";
|
||||||
var isObj = (o) => typeof o === "object" && o !== null;
|
var isObj = (o) => o && typeof o === "object";
|
||||||
var runWithContext = (effect, callback) => {
|
var ensureNode = (n) => n?._isRuntime ? n.container : n instanceof Node ? n : doc.createTextNode(n == null ? "" : String(n));
|
||||||
const previousEffect = activeEffect;
|
var activeEffect = null;
|
||||||
activeEffect = effect;
|
var activeOwner = null;
|
||||||
|
var isFlushing = false;
|
||||||
|
var effectQueue = new Set;
|
||||||
|
var MOUNTED_NODES = new WeakMap;
|
||||||
|
var dispose = (eff) => {
|
||||||
|
if (!eff || eff._disposed)
|
||||||
|
return;
|
||||||
|
eff._disposed = true;
|
||||||
|
const stack = [eff];
|
||||||
|
while (stack.length) {
|
||||||
|
const e = stack.pop();
|
||||||
|
if (e._cleanups) {
|
||||||
|
e._cleanups.forEach((fn) => fn());
|
||||||
|
e._cleanups.clear();
|
||||||
|
}
|
||||||
|
if (e._children) {
|
||||||
|
e._children.forEach((child) => stack.push(child));
|
||||||
|
e._children.clear();
|
||||||
|
}
|
||||||
|
if (e._deps) {
|
||||||
|
e._deps.forEach((depSet) => depSet.delete(e));
|
||||||
|
e._deps.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var onUnmount = (fn) => {
|
||||||
|
if (activeOwner)
|
||||||
|
(activeOwner._cleanups ||= new Set).add(fn);
|
||||||
|
};
|
||||||
|
var createEffect = (fn, isComputed = false) => {
|
||||||
|
const effect = () => {
|
||||||
|
if (effect._disposed)
|
||||||
|
return;
|
||||||
|
if (effect._deps)
|
||||||
|
effect._deps.forEach((depSet) => depSet.delete(effect));
|
||||||
|
if (effect._cleanups) {
|
||||||
|
effect._cleanups.forEach((cl) => cl());
|
||||||
|
effect._cleanups.clear();
|
||||||
|
}
|
||||||
|
const prevEffect = activeEffect, prevOwner = activeOwner;
|
||||||
|
activeEffect = activeOwner = effect;
|
||||||
try {
|
try {
|
||||||
return callback();
|
const res = isComputed ? fn() : (fn(), undefined);
|
||||||
|
if (!isComputed)
|
||||||
|
effect._result = res;
|
||||||
|
return res;
|
||||||
} finally {
|
} finally {
|
||||||
activeEffect = previousEffect;
|
activeEffect = prevEffect;
|
||||||
|
activeOwner = prevOwner;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var cleanupNode = (node) => {
|
effect._deps = effect._cleanups = effect._children = null;
|
||||||
if (node._cleanups) {
|
effect._disposed = false;
|
||||||
node._cleanups.forEach((dispose) => dispose());
|
effect._isComputed = isComputed;
|
||||||
node._cleanups.clear();
|
effect._depth = activeEffect ? activeEffect._depth + 1 : 0;
|
||||||
}
|
effect._mounts = [];
|
||||||
node.childNodes?.forEach(cleanupNode);
|
effect._parent = activeOwner;
|
||||||
|
effect._provisions = activeOwner ? { ...activeOwner._provisions } : {};
|
||||||
|
if (activeOwner)
|
||||||
|
(activeOwner._children ||= new Set).add(effect);
|
||||||
|
return effect;
|
||||||
};
|
};
|
||||||
var flushEffects = () => {
|
var flush = () => {
|
||||||
if (isFlushing)
|
if (isFlushing)
|
||||||
return;
|
return;
|
||||||
isFlushing = true;
|
isFlushing = true;
|
||||||
while (effectQueue.size > 0) {
|
const sorted = Array.from(effectQueue).sort((a, b) => a._depth - b._depth);
|
||||||
const sortedEffects = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0));
|
|
||||||
effectQueue.clear();
|
effectQueue.clear();
|
||||||
for (const effect of sortedEffects) {
|
for (const e of sorted)
|
||||||
if (!effect._deleted)
|
if (!e._disposed)
|
||||||
effect();
|
e();
|
||||||
}
|
|
||||||
}
|
|
||||||
isFlushing = false;
|
isFlushing = false;
|
||||||
};
|
};
|
||||||
var trackSubscription = (subscribers) => {
|
var trackUpdate = (subs, trigger = false) => {
|
||||||
if (activeEffect && !activeEffect._deleted) {
|
if (!trigger && activeEffect && !activeEffect._disposed) {
|
||||||
subscribers.add(activeEffect);
|
subs.add(activeEffect);
|
||||||
activeEffect._deps.add(subscribers);
|
(activeEffect._deps ||= new Set).add(subs);
|
||||||
}
|
} else if (trigger) {
|
||||||
};
|
let hasQueue = false;
|
||||||
var triggerUpdate = (subscribers) => {
|
subs.forEach((e) => {
|
||||||
subscribers.forEach((effect) => {
|
if (e === activeEffect || e._disposed)
|
||||||
if (effect === activeEffect || effect._deleted)
|
|
||||||
return;
|
return;
|
||||||
if (effect._isComputed) {
|
if (e._isComputed) {
|
||||||
effect.markDirty();
|
e._dirty = true;
|
||||||
if (effect._subs)
|
if (e._subs)
|
||||||
triggerUpdate(effect._subs);
|
trackUpdate(e._subs, true);
|
||||||
} else {
|
} else {
|
||||||
effectQueue.add(effect);
|
effectQueue.add(e);
|
||||||
|
hasQueue = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!isFlushing)
|
if (hasQueue && !isFlushing)
|
||||||
queueMicrotask(flushEffects);
|
queueMicrotask(flush);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var untrack = (fn) => {
|
||||||
|
const p = activeEffect;
|
||||||
|
activeEffect = null;
|
||||||
|
try {
|
||||||
|
return fn();
|
||||||
|
} finally {
|
||||||
|
activeEffect = p;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var onMount = (fn) => {
|
||||||
|
if (activeOwner)
|
||||||
|
(activeOwner._mounts ||= []).push(fn);
|
||||||
|
};
|
||||||
|
var $2 = (val, key = null) => {
|
||||||
|
const subs = new Set;
|
||||||
|
if (isFunc(val)) {
|
||||||
|
let cache, dirty = true;
|
||||||
|
const computed = () => {
|
||||||
|
if (dirty) {
|
||||||
|
const prev = activeEffect;
|
||||||
|
activeEffect = computed;
|
||||||
|
try {
|
||||||
|
const next = val();
|
||||||
|
if (!Object.is(cache, next)) {
|
||||||
|
cache = next;
|
||||||
|
dirty = false;
|
||||||
|
trackUpdate(subs, true);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
activeEffect = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trackUpdate(subs);
|
||||||
|
return cache;
|
||||||
|
};
|
||||||
|
computed._isComputed = true;
|
||||||
|
computed._subs = subs;
|
||||||
|
computed._dirty = true;
|
||||||
|
computed._deps = null;
|
||||||
|
computed._disposed = false;
|
||||||
|
computed.markDirty = () => {
|
||||||
|
dirty = true;
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
return computed;
|
||||||
|
}
|
||||||
|
if (key)
|
||||||
|
try {
|
||||||
|
val = JSON.parse(localStorage.getItem(key)) ?? val;
|
||||||
|
} catch (e) {}
|
||||||
|
return (...args) => {
|
||||||
|
if (args.length) {
|
||||||
|
const next = isFunc(args[0]) ? args[0](val) : args[0];
|
||||||
|
if (!Object.is(val, next)) {
|
||||||
|
val = next;
|
||||||
|
if (key)
|
||||||
|
localStorage.setItem(key, JSON.stringify(val));
|
||||||
|
trackUpdate(subs, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trackUpdate(subs);
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var $$ = (obj, cache = new WeakMap) => {
|
||||||
|
if (!isObj(obj))
|
||||||
|
return obj;
|
||||||
|
if (cache.has(obj))
|
||||||
|
return cache.get(obj);
|
||||||
|
const subs = {};
|
||||||
|
const proxy = new Proxy(obj, {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cache.set(obj, proxy);
|
||||||
|
return proxy;
|
||||||
|
};
|
||||||
|
var Watch2 = (sources, cb) => {
|
||||||
|
if (cb === undefined) {
|
||||||
|
const effect2 = createEffect(sources);
|
||||||
|
effect2();
|
||||||
|
return () => dispose(effect2);
|
||||||
|
}
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const vals = Array.isArray(sources) ? sources.map((s) => s()) : sources();
|
||||||
|
untrack(() => cb(vals));
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
return () => dispose(effect);
|
||||||
|
};
|
||||||
|
var cleanupNode = (node) => {
|
||||||
|
if (node._cleanups) {
|
||||||
|
node._cleanups.forEach((fn) => fn());
|
||||||
|
node._cleanups.clear();
|
||||||
|
}
|
||||||
|
if (node._ownerEffect)
|
||||||
|
dispose(node._ownerEffect);
|
||||||
|
if (node.childNodes)
|
||||||
|
node.childNodes.forEach(cleanupNode);
|
||||||
|
};
|
||||||
|
var provide = (key, value) => {
|
||||||
|
if (activeOwner)
|
||||||
|
activeOwner._provisions[key] = value;
|
||||||
|
};
|
||||||
|
var inject = (key, defaultValue) => {
|
||||||
|
let ctx = activeOwner;
|
||||||
|
while (ctx) {
|
||||||
|
if (key in ctx._provisions)
|
||||||
|
return ctx._provisions[key];
|
||||||
|
ctx = ctx._parent;
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
};
|
||||||
|
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
||||||
|
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
|
||||||
|
var validateAttr = (key, val) => {
|
||||||
|
if (val == null || val === false)
|
||||||
|
return null;
|
||||||
|
if (isDangerousAttr(key)) {
|
||||||
|
const sVal = String(val);
|
||||||
|
if (DANGEROUS_PROTOCOL.test(sVal)) {
|
||||||
|
console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`);
|
||||||
|
return "#";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
var Tag2 = (tag, props = {}, children = []) => {
|
||||||
|
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
||||||
|
children = props;
|
||||||
|
props = {};
|
||||||
|
}
|
||||||
|
if (isFunc(tag)) {
|
||||||
|
const ctx = {
|
||||||
|
_mounts: [],
|
||||||
|
_cleanups: new Set,
|
||||||
|
_provisions: activeOwner?._provisions ? { ...activeOwner._provisions } : {}
|
||||||
|
};
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const result2 = tag(props, {
|
||||||
|
children,
|
||||||
|
emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args)
|
||||||
|
});
|
||||||
|
effect._result = result2;
|
||||||
|
return result2;
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
ctx._mounts = effect._mounts || [];
|
||||||
|
ctx._cleanups = effect._cleanups || new Set;
|
||||||
|
const result = effect._result;
|
||||||
|
const attachLifecycle = (node) => node && typeof node === "object" && !node._isRuntime && (node._mounts = ctx._mounts, node._cleanups = ctx._cleanups, node._ownerEffect = effect);
|
||||||
|
isArr(result) ? result.forEach(attachLifecycle) : attachLifecycle(result);
|
||||||
|
if (result == null)
|
||||||
|
return null;
|
||||||
|
if (result instanceof Node || isArr(result) && result.every((n) => n instanceof Node))
|
||||||
|
return result;
|
||||||
|
return doc.createTextNode(String(result));
|
||||||
|
}
|
||||||
|
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) : doc.createElement(tag);
|
||||||
|
el._cleanups = new Set;
|
||||||
|
for (let k in props) {
|
||||||
|
if (!props.hasOwnProperty(k))
|
||||||
|
continue;
|
||||||
|
let v = props[k];
|
||||||
|
if (k === "ref") {
|
||||||
|
isFunc(v) ? v(el) : v.current = el;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (k.startsWith("on")) {
|
||||||
|
const ev = k.slice(2).toLowerCase();
|
||||||
|
el.addEventListener(ev, v);
|
||||||
|
const off = () => el.removeEventListener(ev, v);
|
||||||
|
el._cleanups.add(off);
|
||||||
|
onUnmount(off);
|
||||||
|
} else if (isFunc(v)) {
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const val = validateAttr(k, v());
|
||||||
|
if (k === "class")
|
||||||
|
el.className = val || "";
|
||||||
|
else if (val == null)
|
||||||
|
el.removeAttribute(k);
|
||||||
|
else if (k in el && !isSVG)
|
||||||
|
el[k] = val;
|
||||||
|
else
|
||||||
|
el.setAttribute(k, val === true ? "" : val);
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
el._cleanups.add(() => dispose(effect));
|
||||||
|
onUnmount(() => dispose(effect));
|
||||||
|
if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) {
|
||||||
|
const evType = k === "checked" ? "change" : "input";
|
||||||
|
el.addEventListener(evType, (ev) => v(ev.target[k]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const val = validateAttr(k, v);
|
||||||
|
if (val != null) {
|
||||||
|
if (k in el && !isSVG)
|
||||||
|
el[k] = val;
|
||||||
|
else
|
||||||
|
el.setAttribute(k, val === true ? "" : val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const append = (c) => {
|
||||||
|
if (isArr(c))
|
||||||
|
return c.forEach(append);
|
||||||
|
if (isFunc(c)) {
|
||||||
|
const anchor = doc.createTextNode("");
|
||||||
|
el.appendChild(anchor);
|
||||||
|
let currentNodes = [];
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const res = c();
|
||||||
|
const next = (isArr(res) ? res : [res]).map(ensureNode);
|
||||||
|
currentNodes.forEach((n) => {
|
||||||
|
if (n._isRuntime)
|
||||||
|
n.destroy();
|
||||||
|
else
|
||||||
|
cleanupNode(n);
|
||||||
|
if (n.parentNode)
|
||||||
|
n.remove();
|
||||||
|
});
|
||||||
|
let ref = anchor;
|
||||||
|
for (let i = next.length - 1;i >= 0; i--) {
|
||||||
|
const node = next[i];
|
||||||
|
if (node.parentNode !== ref.parentNode)
|
||||||
|
ref.parentNode?.insertBefore(node, ref);
|
||||||
|
if (node._mounts)
|
||||||
|
node._mounts.forEach((fn) => fn());
|
||||||
|
ref = node;
|
||||||
|
}
|
||||||
|
currentNodes = next;
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
el._cleanups.add(() => dispose(effect));
|
||||||
|
onUnmount(() => dispose(effect));
|
||||||
|
} else {
|
||||||
|
const node = ensureNode(c);
|
||||||
|
el.appendChild(node);
|
||||||
|
if (node._mounts)
|
||||||
|
node._mounts.forEach((fn) => fn());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
append(children);
|
||||||
|
return el;
|
||||||
};
|
};
|
||||||
var Render = (renderFn) => {
|
var Render = (renderFn) => {
|
||||||
const cleanups = new Set;
|
const cleanups = new Set;
|
||||||
const previousOwner = currentOwner;
|
const mounts = [];
|
||||||
const container = createEl("div");
|
const previousOwner = activeOwner;
|
||||||
|
const container = doc.createElement("div");
|
||||||
container.style.display = "contents";
|
container.style.display = "contents";
|
||||||
currentOwner = { cleanups };
|
activeOwner = { _cleanups: cleanups, _mounts: mounts };
|
||||||
const processResult = (result) => {
|
const processResult = (result) => {
|
||||||
if (!result)
|
if (!result)
|
||||||
return;
|
return;
|
||||||
@@ -156,14 +460,15 @@
|
|||||||
} else if (isArr(result)) {
|
} else if (isArr(result)) {
|
||||||
result.forEach(processResult);
|
result.forEach(processResult);
|
||||||
} else {
|
} else {
|
||||||
container.appendChild(result instanceof Node ? result : createText(result));
|
container.appendChild(result instanceof Node ? result : doc.createTextNode(String(result == null ? "" : result)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) }));
|
processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) }));
|
||||||
} finally {
|
} finally {
|
||||||
currentOwner = previousOwner;
|
activeOwner = previousOwner;
|
||||||
}
|
}
|
||||||
|
mounts.forEach((fn) => fn());
|
||||||
return {
|
return {
|
||||||
_isRuntime: true,
|
_isRuntime: true,
|
||||||
container,
|
container,
|
||||||
@@ -174,336 +479,110 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
var $2 = (initialValue, storageKey = null) => {
|
var If2 = (cond, ifYes, ifNot = null, trans = null) => {
|
||||||
const subscribers = new Set;
|
const anchor = doc.createTextNode("");
|
||||||
if (isFunc(initialValue)) {
|
const root = Tag2("div", { style: "display:contents" }, [anchor]);
|
||||||
let cachedValue, isDirty = true;
|
let currentView = null, last = null;
|
||||||
const effect = () => {
|
Watch2(() => !!(isFunc(cond) ? cond() : cond), (show) => {
|
||||||
if (effect._deleted)
|
if (show === last)
|
||||||
return;
|
return;
|
||||||
effect._deps.forEach((dep) => dep.delete(effect));
|
last = show;
|
||||||
effect._deps.clear();
|
const disposeView = () => {
|
||||||
runWithContext(effect, () => {
|
if (currentView) {
|
||||||
const newValue = initialValue();
|
|
||||||
if (!Object.is(cachedValue, newValue) || isDirty) {
|
|
||||||
cachedValue = newValue;
|
|
||||||
isDirty = false;
|
|
||||||
triggerUpdate(subscribers);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
assign(effect, {
|
|
||||||
_deps: new Set,
|
|
||||||
_isComputed: true,
|
|
||||||
_subs: subscribers,
|
|
||||||
_deleted: false,
|
|
||||||
markDirty: () => isDirty = true,
|
|
||||||
stop: () => {
|
|
||||||
effect._deleted = true;
|
|
||||||
effect._deps.forEach((dep) => dep.delete(effect));
|
|
||||||
subscribers.clear();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (currentOwner)
|
|
||||||
currentOwner.cleanups.add(effect.stop);
|
|
||||||
return () => {
|
|
||||||
if (isDirty)
|
|
||||||
effect();
|
|
||||||
trackSubscription(subscribers);
|
|
||||||
return cachedValue;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let value = initialValue;
|
|
||||||
if (storageKey) {
|
|
||||||
try {
|
|
||||||
const saved = localStorage.getItem(storageKey);
|
|
||||||
if (saved !== null)
|
|
||||||
value = JSON.parse(saved);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("SigPro Storage Lock", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (...args) => {
|
|
||||||
if (args.length) {
|
|
||||||
const nextValue = isFunc(args[0]) ? args[0](value) : args[0];
|
|
||||||
if (!Object.is(value, nextValue)) {
|
|
||||||
value = nextValue;
|
|
||||||
if (storageKey)
|
|
||||||
localStorage.setItem(storageKey, JSON.stringify(value));
|
|
||||||
triggerUpdate(subscribers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trackSubscription(subscribers);
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var $$ = (object, cache = new WeakMap) => {
|
|
||||||
if (!isObj(object))
|
|
||||||
return object;
|
|
||||||
if (cache.has(object))
|
|
||||||
return cache.get(object);
|
|
||||||
const keySubscribers = {};
|
|
||||||
const proxy = new Proxy(object, {
|
|
||||||
get(target, key) {
|
|
||||||
if (activeEffect)
|
|
||||||
trackSubscription(keySubscribers[key] ??= new Set);
|
|
||||||
const value = Reflect.get(target, key);
|
|
||||||
return isObj(value) ? $$(value, cache) : value;
|
|
||||||
},
|
|
||||||
set(target, key, value) {
|
|
||||||
if (Object.is(target[key], value))
|
|
||||||
return true;
|
|
||||||
const success = Reflect.set(target, key, value);
|
|
||||||
if (keySubscribers[key])
|
|
||||||
triggerUpdate(keySubscribers[key]);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cache.set(object, proxy);
|
|
||||||
return proxy;
|
|
||||||
};
|
|
||||||
var Watch2 = (target, callbackFn) => {
|
|
||||||
const isExplicit = isArr(target);
|
|
||||||
const callback = isExplicit ? callbackFn : target;
|
|
||||||
if (!isFunc(callback))
|
|
||||||
return () => {};
|
|
||||||
const owner = currentOwner;
|
|
||||||
const runner = () => {
|
|
||||||
if (runner._deleted)
|
|
||||||
return;
|
|
||||||
runner._deps.forEach((dep) => dep.delete(runner));
|
|
||||||
runner._deps.clear();
|
|
||||||
runner._cleanups.forEach((cleanup) => cleanup());
|
|
||||||
runner._cleanups.clear();
|
|
||||||
const previousOwner = currentOwner;
|
|
||||||
runner.depth = activeEffect ? activeEffect.depth + 1 : 0;
|
|
||||||
runWithContext(runner, () => {
|
|
||||||
currentOwner = { cleanups: runner._cleanups };
|
|
||||||
if (isExplicit) {
|
|
||||||
runWithContext(null, callback);
|
|
||||||
target.forEach((dep) => isFunc(dep) && dep());
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
currentOwner = previousOwner;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
assign(runner, {
|
|
||||||
_deps: new Set,
|
|
||||||
_cleanups: new Set,
|
|
||||||
_deleted: false,
|
|
||||||
stop: () => {
|
|
||||||
if (runner._deleted)
|
|
||||||
return;
|
|
||||||
runner._deleted = true;
|
|
||||||
effectQueue.delete(runner);
|
|
||||||
runner._deps.forEach((dep) => dep.delete(runner));
|
|
||||||
runner._cleanups.forEach((cleanup) => cleanup());
|
|
||||||
if (owner)
|
|
||||||
owner.cleanups.delete(runner.stop);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (owner)
|
|
||||||
owner.cleanups.add(runner.stop);
|
|
||||||
runner();
|
|
||||||
return runner.stop;
|
|
||||||
};
|
|
||||||
var Tag2 = (tag, props = {}, children = []) => {
|
|
||||||
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 element = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : createEl(tag);
|
|
||||||
element._cleanups = new Set;
|
|
||||||
element.onUnmount = (fn) => element._cleanups.add(fn);
|
|
||||||
const booleanAttributes = ["disabled", "checked", "required", "readonly", "selected", "multiple", "autofocus"];
|
|
||||||
const updateAttribute = (name, value) => {
|
|
||||||
const sanitized = (name === "src" || name === "href") && String(value).toLowerCase().includes("javascript:") ? "#" : value;
|
|
||||||
if (booleanAttributes.includes(name)) {
|
|
||||||
element[name] = !!sanitized;
|
|
||||||
sanitized ? element.setAttribute(name, "") : element.removeAttribute(name);
|
|
||||||
} else {
|
|
||||||
sanitized == null ? element.removeAttribute(name) : element.setAttribute(name, sanitized);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for (let [key, value] of Object.entries(props)) {
|
|
||||||
if (key === "ref") {
|
|
||||||
isFunc(value) ? value(element) : value.current = element;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const isSignal = isFunc(value);
|
|
||||||
if (key.startsWith("on")) {
|
|
||||||
const eventName = key.slice(2).toLowerCase().split(".")[0];
|
|
||||||
element.addEventListener(eventName, value);
|
|
||||||
element._cleanups.add(() => element.removeEventListener(eventName, value));
|
|
||||||
} else if (isSignal) {
|
|
||||||
element._cleanups.add(Watch2(() => {
|
|
||||||
const currentVal = value();
|
|
||||||
key === "class" ? element.className = currentVal || "" : updateAttribute(key, currentVal);
|
|
||||||
}));
|
|
||||||
if (["INPUT", "TEXTAREA", "SELECT"].includes(element.tagName) && (key === "value" || key === "checked")) {
|
|
||||||
const event = key === "checked" ? "change" : "input";
|
|
||||||
const handler = (e) => value(e.target[key]);
|
|
||||||
element.addEventListener(event, handler);
|
|
||||||
element._cleanups.add(() => element.removeEventListener(event, handler));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updateAttribute(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const appendChildNode = (child) => {
|
|
||||||
if (isArr(child))
|
|
||||||
return child.forEach(appendChildNode);
|
|
||||||
if (isFunc(child)) {
|
|
||||||
const marker = createText("");
|
|
||||||
element.appendChild(marker);
|
|
||||||
let currentNodes = [];
|
|
||||||
element._cleanups.add(Watch2(() => {
|
|
||||||
const result = child();
|
|
||||||
const nextNodes = (isArr(result) ? result : [result]).map((node) => node?._isRuntime ? node.container : node instanceof Node ? node : createText(node));
|
|
||||||
currentNodes.forEach((node) => {
|
|
||||||
cleanupNode(node);
|
|
||||||
node.remove();
|
|
||||||
});
|
|
||||||
nextNodes.forEach((node) => marker.parentNode?.insertBefore(node, marker));
|
|
||||||
currentNodes = nextNodes;
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
element.appendChild(child instanceof Node ? child : createText(child));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
appendChildNode(children);
|
|
||||||
return element;
|
|
||||||
};
|
|
||||||
var If2 = (condition, thenVal, otherwiseVal = null, transition = null) => {
|
|
||||||
const marker = createText("");
|
|
||||||
const container = Tag2("div", { style: "display:contents" }, [marker]);
|
|
||||||
let currentView = null, lastState = null;
|
|
||||||
Watch2(() => {
|
|
||||||
const state = !!(isFunc(condition) ? condition() : condition);
|
|
||||||
if (state === lastState)
|
|
||||||
return;
|
|
||||||
lastState = state;
|
|
||||||
const dispose = () => {
|
|
||||||
if (currentView)
|
|
||||||
currentView.destroy();
|
currentView.destroy();
|
||||||
currentView = null;
|
currentView = null;
|
||||||
};
|
|
||||||
if (currentView && !state && transition?.out) {
|
|
||||||
transition.out(currentView.container, dispose);
|
|
||||||
} else {
|
|
||||||
dispose();
|
|
||||||
}
|
}
|
||||||
const branch = state ? thenVal : otherwiseVal;
|
};
|
||||||
if (branch) {
|
if (currentView && !show && trans?.out)
|
||||||
currentView = Render(() => isFunc(branch) ? branch() : branch);
|
trans.out(currentView.container, disposeView);
|
||||||
container.insertBefore(currentView.container, marker);
|
else
|
||||||
if (state && transition?.in)
|
disposeView();
|
||||||
transition.in(currentView.container);
|
const content = show ? ifYes : ifNot;
|
||||||
|
if (content) {
|
||||||
|
currentView = Render(() => isFunc(content) ? content() : content);
|
||||||
|
root.insertBefore(currentView.container, anchor);
|
||||||
|
if (trans?.in)
|
||||||
|
trans.in(currentView.container);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return container;
|
return root;
|
||||||
};
|
};
|
||||||
var For2 = (source, renderFn, keyFn, tag = "div", props = { style: "display:contents" }) => {
|
var For2 = (src, itemFn, keyFn) => {
|
||||||
const marker = createText("");
|
const anchor = doc.createTextNode("");
|
||||||
const container = Tag2(tag, props, [marker]);
|
const root = Tag2("div", { style: "display:contents" }, [anchor]);
|
||||||
let viewCache = new Map;
|
let cache = new Map;
|
||||||
Watch2(() => {
|
Watch2(() => (isFunc(src) ? src() : src) || [], (items) => {
|
||||||
const items = (isFunc(source) ? source() : source) || [];
|
const next = new Map, order = [];
|
||||||
const nextCache = new Map;
|
|
||||||
const order = [];
|
|
||||||
for (let i = 0;i < items.length; i++) {
|
for (let i = 0;i < items.length; i++) {
|
||||||
const item = items[i];
|
const item = items[i];
|
||||||
const key = keyFn ? keyFn(item, i) : i;
|
const key = keyFn ? keyFn(item, i) : i;
|
||||||
let view = viewCache.get(key);
|
let view = cache.get(key);
|
||||||
if (!view) {
|
if (!view)
|
||||||
const result = renderFn(item, i);
|
view = Render(() => itemFn(item, i));
|
||||||
view = result instanceof Node ? { container: result, destroy: () => {
|
next.set(key, view);
|
||||||
cleanupNode(result);
|
|
||||||
result.remove();
|
|
||||||
} } : Render(() => result);
|
|
||||||
}
|
|
||||||
viewCache.delete(key);
|
|
||||||
nextCache.set(key, view);
|
|
||||||
order.push(key);
|
order.push(key);
|
||||||
|
cache.delete(key);
|
||||||
}
|
}
|
||||||
viewCache.forEach((v) => v.destroy());
|
cache.forEach((v) => v.destroy());
|
||||||
let anchor = marker;
|
cache = next;
|
||||||
|
let ref = anchor;
|
||||||
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 view = next.get(order[i]);
|
||||||
if (view.container.nextSibling !== anchor) {
|
if (view.container.nextSibling !== ref)
|
||||||
container.insertBefore(view.container, anchor);
|
root.insertBefore(view.container, ref);
|
||||||
|
ref = view.container;
|
||||||
}
|
}
|
||||||
anchor = view.container;
|
|
||||||
}
|
|
||||||
viewCache = nextCache;
|
|
||||||
});
|
});
|
||||||
return container;
|
return root;
|
||||||
};
|
};
|
||||||
var Router = (routes) => {
|
var Router = (routes) => {
|
||||||
const currentPath = $2(window.location.hash.replace(/^#/, "") || "/");
|
const getHash = () => window.location.hash.slice(1) || "/";
|
||||||
window.addEventListener("hashchange", () => currentPath(window.location.hash.replace(/^#/, "") || "/"));
|
const path = $2(getHash());
|
||||||
const outlet = Tag2("div", { class: "router-transition" });
|
const handler = () => path(getHash());
|
||||||
|
window.addEventListener("hashchange", handler);
|
||||||
|
onUnmount(() => window.removeEventListener("hashchange", handler));
|
||||||
|
const outlet = Tag2("div", { class: "router-outlet" });
|
||||||
let currentView = null;
|
let currentView = null;
|
||||||
Watch2([currentPath], async () => {
|
Watch2([path], () => {
|
||||||
const path = currentPath();
|
const cur = path();
|
||||||
const route = routes.find((r) => {
|
const route = routes.find((r) => {
|
||||||
const routeParts = r.path.split("/").filter(Boolean);
|
const p1 = r.path.split("/").filter(Boolean);
|
||||||
const pathParts = path.split("/").filter(Boolean);
|
const p2 = cur.split("/").filter(Boolean);
|
||||||
return routeParts.length === pathParts.length && routeParts.every((part, i) => part.startsWith(":") || part === pathParts[i]);
|
return p1.length === p2.length && p1.every((p, i) => p[0] === ":" || p === p2[i]);
|
||||||
}) || routes.find((r) => r.path === "*");
|
}) || routes.find((r) => r.path === "*");
|
||||||
if (route) {
|
if (route) {
|
||||||
let component = route.component;
|
currentView?.destroy();
|
||||||
if (isFunc(component) && component.toString().includes("import")) {
|
|
||||||
component = (await component()).default || await component();
|
|
||||||
}
|
|
||||||
const params = {};
|
const params = {};
|
||||||
route.path.split("/").filter(Boolean).forEach((part, i) => {
|
route.path.split("/").filter(Boolean).forEach((p, i) => {
|
||||||
if (part.startsWith(":"))
|
if (p[0] === ":")
|
||||||
params[part.slice(1)] = path.split("/").filter(Boolean)[i];
|
params[p.slice(1)] = cur.split("/").filter(Boolean)[i];
|
||||||
});
|
});
|
||||||
if (currentView)
|
|
||||||
currentView.destroy();
|
|
||||||
if (Router.params)
|
|
||||||
Router.params(params);
|
Router.params(params);
|
||||||
currentView = Render(() => {
|
currentView = Render(() => isFunc(route.component) ? route.component(params) : route.component);
|
||||||
try {
|
outlet.replaceChildren(currentView.container);
|
||||||
return isFunc(component) ? component(params) : component;
|
|
||||||
} catch (e) {
|
|
||||||
return Tag2("div", { class: "p-4 text-error" }, "Error loading view");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
outlet.appendChild(currentView.container);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return outlet;
|
return outlet;
|
||||||
};
|
};
|
||||||
Router.params = $2({});
|
Router.params = $2({});
|
||||||
Router.to = (path) => window.location.hash = path.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 Mount2 = (component, target) => {
|
var Mount2 = (comp, target) => {
|
||||||
const targetEl = typeof target === "string" ? doc.querySelector(target) : target;
|
const t = typeof target === "string" ? doc.querySelector(target) : target;
|
||||||
if (!targetEl)
|
if (!t)
|
||||||
return;
|
return;
|
||||||
if (MOUNTED_NODES.has(targetEl))
|
if (MOUNTED_NODES.has(t))
|
||||||
MOUNTED_NODES.get(targetEl).destroy();
|
MOUNTED_NODES.get(t).destroy();
|
||||||
const instance = Render(isFunc(component) ? component : () => component);
|
const inst = Render(isFunc(comp) ? comp : () => comp);
|
||||||
targetEl.replaceChildren(instance.container);
|
t.replaceChildren(inst.container);
|
||||||
MOUNTED_NODES.set(targetEl, instance);
|
MOUNTED_NODES.set(t, inst);
|
||||||
return instance;
|
return inst;
|
||||||
};
|
};
|
||||||
var SigPro = { $: $2, $$, Render, Watch: Watch2, Tag: Tag2, If: If2, For: For2, Router, Mount: Mount2 };
|
var SigPro = Object.freeze({ $: $2, $$, Watch: Watch2, Tag: Tag2, Render, If: If2, For: For2, Router, Mount: Mount2, untrack, onMount, onUnmount, provide, inject });
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
assign(window, SigPro);
|
Object.assign(window, SigPro);
|
||||||
const tags = `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`.split(" ");
|
"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));
|
||||||
tags.forEach((tag) => {
|
|
||||||
const helper = tag[0].toUpperCase() + tag.slice(1);
|
|
||||||
if (!(helper in window))
|
|
||||||
window[helper] = (p, c) => Tag2(tag, p, c);
|
|
||||||
});
|
|
||||||
window.SigPro = Object.freeze(SigPro);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// src/components/index.js
|
// src/components/index.js
|
||||||
|
|||||||
8
dist/sigpro-ui.min.js
vendored
8
dist/sigpro-ui.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -66,6 +66,23 @@ const BasicDemo = () => {
|
|||||||
Mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Open the modal using ID.showModal() method -->
|
||||||
|
<button class="btn" onclick="my_modal_1.showModal()">TEST DIRECTO</button>
|
||||||
|
<dialog id="my_modal_1" class="modal">
|
||||||
|
<div class="modal-box">
|
||||||
|
<h3 class="text-lg font-bold">Hello!</h3>
|
||||||
|
<p class="py-4">Press ESC key or click the button below to close</p>
|
||||||
|
<div class="modal-action">
|
||||||
|
<form method="dialog">
|
||||||
|
<!-- if there is a button in form, it will close the modal -->
|
||||||
|
<button class="btn">Close</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
|
||||||
### Modal with Actions
|
### Modal with Actions
|
||||||
|
|
||||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
|
|||||||
@@ -53,11 +53,7 @@
|
|||||||
const codeBlocks = document.querySelectorAll('pre[data-lang="javascript"] code');
|
const codeBlocks = document.querySelectorAll('pre[data-lang="javascript"] code');
|
||||||
|
|
||||||
codeBlocks.forEach(code => {
|
codeBlocks.forEach(code => {
|
||||||
// Usamos un bloque try/catch por si el código del Markdown tiene errores
|
|
||||||
try {
|
try {
|
||||||
// Ejecutamos el código.
|
|
||||||
// Como tu librería ya puso $, Mount, Button, etc. en 'window',
|
|
||||||
// el código los encontrará directamente.
|
|
||||||
const runDemo = new Function(code.innerText);
|
const runDemo = new Function(code.innerText);
|
||||||
runDemo();
|
runDemo();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
2052
docs/sigpro-ui.min.js
vendored
2052
docs/sigpro-ui.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -51,7 +51,7 @@
|
|||||||
"build:js": "bun run build:js:iife && bun run build:js:esm",
|
"build:js": "bun run build:js:iife && bun run build:js:esm",
|
||||||
"build:js:iife": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.js --format=iife --global-name=SigProUI && bun build ./index.js --bundle --outfile=./dist/sigpro-ui.min.js --format=iife --global-name=SigProUI --minify",
|
"build:js:iife": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.js --format=iife --global-name=SigProUI && bun build ./index.js --bundle --outfile=./dist/sigpro-ui.min.js --format=iife --global-name=SigProUI --minify",
|
||||||
"build:js:esm": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.esm.js --format=esm && bun build ./index.js --bundle --outfile=./dist/sigpro-ui.esm.min.js --format=esm --minify",
|
"build:js:esm": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.esm.js --format=esm && bun build ./index.js --bundle --outfile=./dist/sigpro-ui.esm.min.js --format=esm --minify",
|
||||||
"build:jsdocs": "bun build ./index.js --bundle --outfile=./docs/sigpro-ui.min.js --format=iife --global-name=SigProUI --minify",
|
"build:jsdocs": "bun build ./index.js --bundle --outfile=./docs/sigpro-ui.min.js --format=iife --global-name=SigProUI ",
|
||||||
"build": "bun run clean && bun run build:css && bun run build:js && bun run build:jsdocs && bun run build:cssdocs && bun run build:cssmin",
|
"build": "bun run clean && bun run build:css && bun run build:js && bun run build:jsdocs && bun run build:cssdocs && bun run build:cssmin",
|
||||||
"prepublishOnly": "bun run build",
|
"prepublishOnly": "bun run build",
|
||||||
"docs": "bun x serve docs"
|
"docs": "bun x serve docs"
|
||||||
|
|||||||
719
src/sigpro.js
719
src/sigpro.js
@@ -1,71 +1,306 @@
|
|||||||
let activeEffect = null;
|
/** SigPro - Signals & Proxies */
|
||||||
let currentOwner = null;
|
|
||||||
const effectQueue = new Set();
|
|
||||||
let isFlushing = false;
|
|
||||||
const MOUNTED_NODES = new WeakMap();
|
|
||||||
|
|
||||||
const doc = document;
|
// Helpers
|
||||||
const isArr = Array.isArray;
|
const doc = typeof document !== "undefined" ? document : null;
|
||||||
const assign = Object.assign;
|
const isArr = Array.isArray, isFunc = f => typeof f === "function", isObj = o => o && typeof o === "object";
|
||||||
const createEl = (t) => doc.createElement(t);
|
const ensureNode = n => n?._isRuntime ? n.container : (n instanceof Node ? n : doc.createTextNode(n == null ? "" : String(n)));
|
||||||
const createText = (t) => doc.createTextNode(String(t ?? ""));
|
|
||||||
const isFunc = (f) => typeof f === "function";
|
|
||||||
const isObj = (o) => typeof o === "object" && o !== null;
|
|
||||||
|
|
||||||
const runWithContext = (effect, callback) => {
|
let activeEffect = null, activeOwner = null, isFlushing = false;
|
||||||
const previousEffect = activeEffect;
|
const effectQueue = new Set(), MOUNTED_NODES = new WeakMap();
|
||||||
activeEffect = effect;
|
|
||||||
try { return callback(); }
|
|
||||||
finally { activeEffect = previousEffect; }
|
|
||||||
};
|
|
||||||
|
|
||||||
const cleanupNode = (node) => {
|
const dispose = eff => {
|
||||||
if (node._cleanups) {
|
if (!eff || eff._disposed) return;
|
||||||
node._cleanups.forEach((dispose) => dispose());
|
eff._disposed = true;
|
||||||
node._cleanups.clear();
|
const stack = [eff];
|
||||||
|
while (stack.length) {
|
||||||
|
const e = stack.pop();
|
||||||
|
if (e._cleanups) { e._cleanups.forEach(fn => fn()); e._cleanups.clear(); }
|
||||||
|
if (e._children) { e._children.forEach(child => stack.push(child)); e._children.clear(); }
|
||||||
|
if (e._deps) { e._deps.forEach(depSet => depSet.delete(e)); e._deps.clear(); }
|
||||||
}
|
}
|
||||||
node.childNodes?.forEach(cleanupNode);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const flushEffects = () => {
|
const onUnmount = fn => {
|
||||||
|
if (activeOwner) (activeOwner._cleanups ||= new Set()).add(fn);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Effect creation
|
||||||
|
const createEffect = (fn, isComputed = false) => {
|
||||||
|
const effect = () => {
|
||||||
|
if (effect._disposed) return;
|
||||||
|
if (effect._deps) effect._deps.forEach(depSet => depSet.delete(effect));
|
||||||
|
if (effect._cleanups) { effect._cleanups.forEach(cl => cl()); effect._cleanups.clear(); }
|
||||||
|
const prevEffect = activeEffect, prevOwner = activeOwner;
|
||||||
|
activeEffect = activeOwner = effect;
|
||||||
|
try {
|
||||||
|
const res = isComputed ? fn() : (fn(), undefined);
|
||||||
|
if (!isComputed) effect._result = res;
|
||||||
|
return res;
|
||||||
|
} finally {
|
||||||
|
activeEffect = prevEffect;
|
||||||
|
activeOwner = prevOwner;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
effect._deps = effect._cleanups = effect._children = null;
|
||||||
|
effect._disposed = false;
|
||||||
|
effect._isComputed = isComputed;
|
||||||
|
effect._depth = activeEffect ? activeEffect._depth + 1 : 0;
|
||||||
|
effect._mounts = [];
|
||||||
|
effect._parent = activeOwner;
|
||||||
|
effect._provisions = activeOwner ? { ...activeOwner._provisions } : {};
|
||||||
|
if (activeOwner) (activeOwner._children ||= new Set()).add(effect);
|
||||||
|
return effect;
|
||||||
|
};
|
||||||
|
|
||||||
|
const flush = () => {
|
||||||
if (isFlushing) return;
|
if (isFlushing) return;
|
||||||
isFlushing = true;
|
isFlushing = true;
|
||||||
while (effectQueue.size > 0) {
|
const sorted = Array.from(effectQueue).sort((a, b) => a._depth - b._depth);
|
||||||
const sortedEffects = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0));
|
|
||||||
effectQueue.clear();
|
effectQueue.clear();
|
||||||
for (const effect of sortedEffects) {
|
for (const e of sorted) if (!e._disposed) e();
|
||||||
if (!effect._deleted) effect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isFlushing = false;
|
isFlushing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const trackSubscription = (subscribers) => {
|
const trackUpdate = (subs, trigger = false) => {
|
||||||
if (activeEffect && !activeEffect._deleted) {
|
if (!trigger && activeEffect && !activeEffect._disposed) {
|
||||||
subscribers.add(activeEffect);
|
subs.add(activeEffect);
|
||||||
activeEffect._deps.add(subscribers);
|
(activeEffect._deps ||= new Set()).add(subs);
|
||||||
}
|
} else if (trigger) {
|
||||||
};
|
let hasQueue = false;
|
||||||
|
subs.forEach(e => {
|
||||||
const triggerUpdate = (subscribers) => {
|
if (e === activeEffect || e._disposed) return;
|
||||||
subscribers.forEach((effect) => {
|
if (e._isComputed) {
|
||||||
if (effect === activeEffect || effect._deleted) return;
|
e._dirty = true;
|
||||||
if (effect._isComputed) {
|
if (e._subs) trackUpdate(e._subs, true);
|
||||||
effect.markDirty();
|
|
||||||
if (effect._subs) triggerUpdate(effect._subs);
|
|
||||||
} else {
|
} else {
|
||||||
effectQueue.add(effect);
|
effectQueue.add(e);
|
||||||
|
hasQueue = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!isFlushing) queueMicrotask(flushEffects);
|
if (hasQueue && !isFlushing) queueMicrotask(flush);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const untrack = fn => { const p = activeEffect; activeEffect = null; try { return fn(); } finally { activeEffect = p; } };
|
||||||
|
|
||||||
|
const onMount = fn => {
|
||||||
|
if (activeOwner) (activeOwner._mounts ||= []).push(fn);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reactive state
|
||||||
|
const $ = (val, key = null) => {
|
||||||
|
const subs = new Set();
|
||||||
|
if (isFunc(val)) {
|
||||||
|
let cache, dirty = true;
|
||||||
|
const computed = () => {
|
||||||
|
if (dirty) {
|
||||||
|
const prev = activeEffect;
|
||||||
|
activeEffect = computed;
|
||||||
|
try {
|
||||||
|
const next = val();
|
||||||
|
if (!Object.is(cache, next)) { cache = next; dirty = false; trackUpdate(subs, true); }
|
||||||
|
} finally { activeEffect = prev; }
|
||||||
|
}
|
||||||
|
trackUpdate(subs);
|
||||||
|
return cache;
|
||||||
|
};
|
||||||
|
computed._isComputed = true;
|
||||||
|
computed._subs = subs;
|
||||||
|
computed._dirty = true;
|
||||||
|
computed._deps = null;
|
||||||
|
computed._disposed = false;
|
||||||
|
computed.markDirty = () => { dirty = true; };
|
||||||
|
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);
|
||||||
|
return computed;
|
||||||
|
}
|
||||||
|
if (key) try { val = JSON.parse(localStorage.getItem(key)) ?? val; } catch (e) { }
|
||||||
|
return (...args) => {
|
||||||
|
if (args.length) {
|
||||||
|
const next = isFunc(args[0]) ? args[0](val) : args[0];
|
||||||
|
if (!Object.is(val, next)) { val = next; if (key) localStorage.setItem(key, JSON.stringify(val)); trackUpdate(subs, true); }
|
||||||
|
}
|
||||||
|
trackUpdate(subs);
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const $$ = (obj, cache = new WeakMap()) => {
|
||||||
|
if (!isObj(obj)) return obj;
|
||||||
|
if (cache.has(obj)) return cache.get(obj);
|
||||||
|
const subs = {};
|
||||||
|
const proxy = new Proxy(obj, {
|
||||||
|
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; }
|
||||||
|
});
|
||||||
|
cache.set(obj, proxy);
|
||||||
|
return proxy;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Watchers
|
||||||
|
const Watch = (sources, cb) => {
|
||||||
|
if (cb === undefined) {
|
||||||
|
const effect = createEffect(sources);
|
||||||
|
effect();
|
||||||
|
return () => dispose(effect);
|
||||||
|
}
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const vals = Array.isArray(sources) ? sources.map(s => s()) : sources();
|
||||||
|
untrack(() => cb(vals));
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
return () => dispose(effect);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cleanupNode = node => {
|
||||||
|
if (node._cleanups) { node._cleanups.forEach(fn => fn()); node._cleanups.clear(); }
|
||||||
|
if (node._ownerEffect) dispose(node._ownerEffect);
|
||||||
|
if (node.childNodes) node.childNodes.forEach(cleanupNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
// provide/inject
|
||||||
|
const provide = (key, value) => {
|
||||||
|
if (activeOwner) activeOwner._provisions[key] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const inject = (key, defaultValue) => {
|
||||||
|
let ctx = activeOwner;
|
||||||
|
while (ctx) {
|
||||||
|
if (key in ctx._provisions) return ctx._provisions[key];
|
||||||
|
ctx = ctx._parent;
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Seguridad optimizada ---
|
||||||
|
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(`[SigPro] Bloqueado protocolo peligroso en ${key}`);
|
||||||
|
return '#';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
// CreateElement
|
||||||
|
const Tag = (tag, props = {}, children = []) => {
|
||||||
|
if (props instanceof Node || isArr(props) || !isObj(props)) { children = props; props = {}; }
|
||||||
|
if (isFunc(tag)) {
|
||||||
|
const ctx = {
|
||||||
|
_mounts: [],
|
||||||
|
_cleanups: new Set(),
|
||||||
|
_provisions: activeOwner?._provisions ? { ...activeOwner._provisions } : {}
|
||||||
|
};
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const result = tag(props, {
|
||||||
|
children,
|
||||||
|
emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args)
|
||||||
|
});
|
||||||
|
effect._result = result;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
ctx._mounts = effect._mounts || [];
|
||||||
|
ctx._cleanups = effect._cleanups || new Set();
|
||||||
|
const result = effect._result;
|
||||||
|
const attachLifecycle = node => node && typeof node === 'object' && !node._isRuntime && (node._mounts = ctx._mounts, node._cleanups = ctx._cleanups, node._ownerEffect = effect);
|
||||||
|
isArr(result) ? result.forEach(attachLifecycle) : attachLifecycle(result);
|
||||||
|
if (result == null) return null;
|
||||||
|
if (result instanceof Node || (isArr(result) && result.every(n => n instanceof Node))) return result;
|
||||||
|
return doc.createTextNode(String(result));
|
||||||
|
}
|
||||||
|
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) : doc.createElement(tag);
|
||||||
|
el._cleanups = new Set();
|
||||||
|
|
||||||
|
for (let k in props) {
|
||||||
|
if (!props.hasOwnProperty(k)) continue;
|
||||||
|
let v = props[k];
|
||||||
|
if (k === "ref") { isFunc(v) ? v(el) : (v.current = el); continue; }
|
||||||
|
if (k.startsWith("on")) {
|
||||||
|
const ev = k.slice(2).toLowerCase();
|
||||||
|
el.addEventListener(ev, v);
|
||||||
|
const off = () => el.removeEventListener(ev, v);
|
||||||
|
el._cleanups.add(off);
|
||||||
|
onUnmount(off);
|
||||||
|
} else if (isFunc(v)) {
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const val = validateAttr(k, v());
|
||||||
|
if (k === "class") el.className = val || "";
|
||||||
|
else if (val == null) el.removeAttribute(k);
|
||||||
|
else if (k in el && !isSVG) el[k] = val;
|
||||||
|
else el.setAttribute(k, val === true ? "" : val);
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
el._cleanups.add(() => dispose(effect));
|
||||||
|
onUnmount(() => dispose(effect));
|
||||||
|
if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) {
|
||||||
|
const evType = k === "checked" ? "change" : "input";
|
||||||
|
el.addEventListener(evType, ev => v(ev.target[k]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const val = validateAttr(k, v);
|
||||||
|
if (val != null) {
|
||||||
|
if (k in el && !isSVG) el[k] = val;
|
||||||
|
else el.setAttribute(k, val === true ? "" : val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const append = c => {
|
||||||
|
if (isArr(c)) return c.forEach(append);
|
||||||
|
if (isFunc(c)) {
|
||||||
|
const anchor = doc.createTextNode("");
|
||||||
|
el.appendChild(anchor);
|
||||||
|
let currentNodes = [];
|
||||||
|
const effect = createEffect(() => {
|
||||||
|
const res = c();
|
||||||
|
const next = (isArr(res) ? res : [res]).map(ensureNode);
|
||||||
|
currentNodes.forEach(n => {
|
||||||
|
if (n._isRuntime) n.destroy();
|
||||||
|
else cleanupNode(n);
|
||||||
|
if (n.parentNode) n.remove();
|
||||||
|
});
|
||||||
|
let ref = anchor;
|
||||||
|
for (let i = next.length - 1; i >= 0; i--) {
|
||||||
|
const node = next[i];
|
||||||
|
if (node.parentNode !== ref.parentNode) ref.parentNode?.insertBefore(node, ref);
|
||||||
|
if (node._mounts) node._mounts.forEach(fn => fn());
|
||||||
|
ref = node;
|
||||||
|
}
|
||||||
|
currentNodes = next;
|
||||||
|
});
|
||||||
|
effect();
|
||||||
|
el._cleanups.add(() => dispose(effect));
|
||||||
|
onUnmount(() => dispose(effect));
|
||||||
|
} else {
|
||||||
|
const node = ensureNode(c);
|
||||||
|
el.appendChild(node);
|
||||||
|
if (node._mounts) node._mounts.forEach(fn => fn());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
append(children);
|
||||||
|
return el;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Render
|
||||||
const Render = (renderFn) => {
|
const Render = (renderFn) => {
|
||||||
const cleanups = new Set();
|
const cleanups = new Set();
|
||||||
const previousOwner = currentOwner;
|
const mounts = [];
|
||||||
const container = createEl("div");
|
const previousOwner = activeOwner;
|
||||||
|
const container = doc.createElement("div");
|
||||||
container.style.display = "contents";
|
container.style.display = "contents";
|
||||||
currentOwner = { cleanups };
|
activeOwner = { _cleanups: cleanups, _mounts: mounts };
|
||||||
|
|
||||||
const processResult = (result) => {
|
const processResult = (result) => {
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
@@ -75,14 +310,15 @@ const Render = (renderFn) => {
|
|||||||
} else if (isArr(result)) {
|
} else if (isArr(result)) {
|
||||||
result.forEach(processResult);
|
result.forEach(processResult);
|
||||||
} else {
|
} else {
|
||||||
container.appendChild(result instanceof Node ? result : createText(result));
|
container.appendChild(result instanceof Node ? result : doc.createTextNode(String(result == null ? "" : result)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) }));
|
processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) }));
|
||||||
} finally { currentOwner = previousOwner; }
|
} finally { activeOwner = previousOwner; }
|
||||||
|
|
||||||
|
mounts.forEach(fn => fn());
|
||||||
return {
|
return {
|
||||||
_isRuntime: true,
|
_isRuntime: true,
|
||||||
container,
|
container,
|
||||||
@@ -94,347 +330,110 @@ const Render = (renderFn) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const $ = (initialValue, storageKey = null) => {
|
// If
|
||||||
const subscribers = new Set();
|
const If = (cond, ifYes, ifNot = null, trans = null) => {
|
||||||
|
const anchor = doc.createTextNode("");
|
||||||
if (isFunc(initialValue)) {
|
const root = Tag("div", { style: "display:contents" }, [anchor]);
|
||||||
let cachedValue, isDirty = true;
|
let currentView = null, last = null;
|
||||||
const effect = () => {
|
Watch(
|
||||||
if (effect._deleted) return;
|
() => !!(isFunc(cond) ? cond() : cond),
|
||||||
effect._deps.forEach((dep) => dep.delete(effect));
|
(show) => {
|
||||||
effect._deps.clear();
|
if (show === last) return;
|
||||||
|
last = show;
|
||||||
runWithContext(effect, () => {
|
const disposeView = () => { if (currentView) { currentView.destroy(); currentView = null; } };
|
||||||
const newValue = initialValue();
|
if (currentView && !show && trans?.out) trans.out(currentView.container, disposeView);
|
||||||
if (!Object.is(cachedValue, newValue) || isDirty) {
|
else disposeView();
|
||||||
cachedValue = newValue;
|
const content = show ? ifYes : ifNot;
|
||||||
isDirty = false;
|
if (content) {
|
||||||
triggerUpdate(subscribers);
|
currentView = Render(() => isFunc(content) ? content() : content);
|
||||||
}
|
root.insertBefore(currentView.container, anchor);
|
||||||
});
|
if (trans?.in) trans.in(currentView.container);
|
||||||
};
|
|
||||||
|
|
||||||
assign(effect, {
|
|
||||||
_deps: new Set(),
|
|
||||||
_isComputed: true,
|
|
||||||
_subs: subscribers,
|
|
||||||
_deleted: false,
|
|
||||||
markDirty: () => (isDirty = true),
|
|
||||||
stop: () => {
|
|
||||||
effect._deleted = true;
|
|
||||||
effect._deps.forEach((dep) => dep.delete(effect));
|
|
||||||
subscribers.clear();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (currentOwner) currentOwner.cleanups.add(effect.stop);
|
|
||||||
return () => { if (isDirty) effect(); trackSubscription(subscribers); return cachedValue; };
|
|
||||||
}
|
|
||||||
|
|
||||||
let value = initialValue;
|
|
||||||
if (storageKey) {
|
|
||||||
try {
|
|
||||||
const saved = localStorage.getItem(storageKey);
|
|
||||||
if (saved !== null) value = JSON.parse(saved);
|
|
||||||
} catch (e) { console.warn("SigPro Storage Lock", e); }
|
|
||||||
}
|
|
||||||
|
|
||||||
return (...args) => {
|
|
||||||
if (args.length) {
|
|
||||||
const nextValue = isFunc(args[0]) ? args[0](value) : args[0];
|
|
||||||
if (!Object.is(value, nextValue)) {
|
|
||||||
value = nextValue;
|
|
||||||
if (storageKey) localStorage.setItem(storageKey, JSON.stringify(value));
|
|
||||||
triggerUpdate(subscribers);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trackSubscription(subscribers);
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const $$ = (object, cache = new WeakMap()) => {
|
|
||||||
if (!isObj(object)) return object;
|
|
||||||
if (cache.has(object)) return cache.get(object);
|
|
||||||
|
|
||||||
const keySubscribers = {};
|
|
||||||
const proxy = new Proxy(object, {
|
|
||||||
get(target, key) {
|
|
||||||
if (activeEffect) trackSubscription(keySubscribers[key] ??= new Set());
|
|
||||||
const value = Reflect.get(target, key);
|
|
||||||
return isObj(value) ? $$(value, cache) : value;
|
|
||||||
},
|
|
||||||
set(target, key, value) {
|
|
||||||
if (Object.is(target[key], value)) return true;
|
|
||||||
const success = Reflect.set(target, key, value);
|
|
||||||
if (keySubscribers[key]) triggerUpdate(keySubscribers[key]);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
cache.set(object, proxy);
|
|
||||||
return proxy;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Watch = (target, callbackFn) => {
|
|
||||||
const isExplicit = isArr(target);
|
|
||||||
const callback = isExplicit ? callbackFn : target;
|
|
||||||
if (!isFunc(callback)) return () => { };
|
|
||||||
|
|
||||||
const owner = currentOwner;
|
|
||||||
const runner = () => {
|
|
||||||
if (runner._deleted) return;
|
|
||||||
runner._deps.forEach((dep) => dep.delete(runner));
|
|
||||||
runner._deps.clear();
|
|
||||||
runner._cleanups.forEach((cleanup) => cleanup());
|
|
||||||
runner._cleanups.clear();
|
|
||||||
|
|
||||||
const previousOwner = currentOwner;
|
|
||||||
runner.depth = activeEffect ? activeEffect.depth + 1 : 0;
|
|
||||||
|
|
||||||
runWithContext(runner, () => {
|
|
||||||
currentOwner = { cleanups: runner._cleanups };
|
|
||||||
if (isExplicit) {
|
|
||||||
runWithContext(null, callback);
|
|
||||||
target.forEach((dep) => isFunc(dep) && dep());
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
currentOwner = previousOwner;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
assign(runner, {
|
|
||||||
_deps: new Set(),
|
|
||||||
_cleanups: new Set(),
|
|
||||||
_deleted: false,
|
|
||||||
stop: () => {
|
|
||||||
if (runner._deleted) return;
|
|
||||||
runner._deleted = true;
|
|
||||||
effectQueue.delete(runner);
|
|
||||||
runner._deps.forEach((dep) => dep.delete(runner));
|
|
||||||
runner._cleanups.forEach((cleanup) => cleanup());
|
|
||||||
if (owner) owner.cleanups.delete(runner.stop);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (owner) owner.cleanups.add(runner.stop);
|
|
||||||
runner();
|
|
||||||
return runner.stop;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Tag = (tag, props = {}, children = []) => {
|
|
||||||
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 element = isSVG
|
|
||||||
? doc.createElementNS("http://www.w3.org/2000/svg", tag)
|
|
||||||
: createEl(tag);
|
|
||||||
|
|
||||||
element._cleanups = new Set();
|
|
||||||
element.onUnmount = (fn) => element._cleanups.add(fn);
|
|
||||||
const booleanAttributes = ["disabled", "checked", "required", "readonly", "selected", "multiple", "autofocus"];
|
|
||||||
|
|
||||||
const updateAttribute = (name, value) => {
|
|
||||||
const sanitized = (name === 'src' || name === 'href') && String(value).toLowerCase().includes('javascript:') ? '#' : value;
|
|
||||||
if (booleanAttributes.includes(name)) {
|
|
||||||
element[name] = !!sanitized;
|
|
||||||
sanitized ? element.setAttribute(name, "") : element.removeAttribute(name);
|
|
||||||
} else {
|
|
||||||
sanitized == null ? element.removeAttribute(name) : element.setAttribute(name, sanitized);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let [key, value] of Object.entries(props)) {
|
|
||||||
if (key === "ref") { (isFunc(value) ? value(element) : (value.current = element)); continue; }
|
|
||||||
|
|
||||||
const isSignal = isFunc(value);
|
|
||||||
if (key.startsWith("on")) {
|
|
||||||
const eventName = key.slice(2).toLowerCase().split(".")[0];
|
|
||||||
element.addEventListener(eventName, value);
|
|
||||||
element._cleanups.add(() => element.removeEventListener(eventName, value));
|
|
||||||
} else if (isSignal) {
|
|
||||||
element._cleanups.add(Watch(() => {
|
|
||||||
const currentVal = value();
|
|
||||||
key === "class" ? (element.className = currentVal || "") : updateAttribute(key, currentVal);
|
|
||||||
}));
|
|
||||||
if (["INPUT", "TEXTAREA", "SELECT"].includes(element.tagName) && (key === "value" || key === "checked")) {
|
|
||||||
const event = key === "checked" ? "change" : "input";
|
|
||||||
const handler = (e) => value(e.target[key]);
|
|
||||||
element.addEventListener(event, handler);
|
|
||||||
element._cleanups.add(() => element.removeEventListener(event, handler));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updateAttribute(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const appendChildNode = (child) => {
|
|
||||||
if (isArr(child)) return child.forEach(appendChildNode);
|
|
||||||
if (isFunc(child)) {
|
|
||||||
const marker = createText("");
|
|
||||||
element.appendChild(marker);
|
|
||||||
let currentNodes = [];
|
|
||||||
element._cleanups.add(Watch(() => {
|
|
||||||
const result = child();
|
|
||||||
const nextNodes = (isArr(result) ? result : [result]).map((node) =>
|
|
||||||
node?._isRuntime ? node.container : node instanceof Node ? node : createText(node)
|
|
||||||
);
|
);
|
||||||
currentNodes.forEach((node) => { cleanupNode(node); node.remove(); });
|
return root;
|
||||||
nextNodes.forEach((node) => marker.parentNode?.insertBefore(node, marker));
|
|
||||||
currentNodes = nextNodes;
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
element.appendChild(child instanceof Node ? child : createText(child));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
appendChildNode(children);
|
// For
|
||||||
return element;
|
const For = (src, itemFn, keyFn) => {
|
||||||
};
|
const anchor = doc.createTextNode("");
|
||||||
|
const root = Tag("div", { style: "display:contents" }, [anchor]);
|
||||||
const If = (condition, thenVal, otherwiseVal = null, transition = null) => {
|
let cache = new Map();
|
||||||
const marker = createText("");
|
Watch(
|
||||||
const container = Tag("div", { style: "display:contents" }, [marker]);
|
() => (isFunc(src) ? src() : src) || [],
|
||||||
let currentView = null, lastState = null;
|
(items) => {
|
||||||
|
const next = new Map(), order = [];
|
||||||
Watch(() => {
|
|
||||||
const state = !!(isFunc(condition) ? condition() : condition);
|
|
||||||
if (state === lastState) return;
|
|
||||||
lastState = state;
|
|
||||||
|
|
||||||
const dispose = () => { if (currentView) 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 marker = createText("");
|
|
||||||
const container = Tag(tag, props, [marker]);
|
|
||||||
let viewCache = new Map();
|
|
||||||
|
|
||||||
Watch(() => {
|
|
||||||
const items = (isFunc(source) ? source() : source) || [];
|
|
||||||
const nextCache = new Map();
|
|
||||||
const order = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
const item = items[i];
|
const item = items[i];
|
||||||
const key = keyFn ? keyFn(item, i) : i;
|
const key = keyFn ? keyFn(item, i) : i;
|
||||||
let view = viewCache.get(key);
|
let view = cache.get(key);
|
||||||
|
if (!view) view = Render(() => itemFn(item, i));
|
||||||
if (!view) {
|
next.set(key, view);
|
||||||
const result = renderFn(item, i);
|
|
||||||
view = result instanceof Node
|
|
||||||
? { container: result, destroy: () => { cleanupNode(result); result.remove(); } }
|
|
||||||
: Render(() => result);
|
|
||||||
}
|
|
||||||
|
|
||||||
viewCache.delete(key);
|
|
||||||
nextCache.set(key, view);
|
|
||||||
order.push(key);
|
order.push(key);
|
||||||
|
cache.delete(key);
|
||||||
}
|
}
|
||||||
|
cache.forEach(v => v.destroy());
|
||||||
viewCache.forEach(v => v.destroy());
|
cache = next;
|
||||||
|
let ref = anchor;
|
||||||
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 view = next.get(order[i]);
|
||||||
if (view.container.nextSibling !== anchor) {
|
if (view.container.nextSibling !== ref) root.insertBefore(view.container, ref);
|
||||||
container.insertBefore(view.container, anchor);
|
ref = view.container;
|
||||||
}
|
}
|
||||||
anchor = view.container;
|
|
||||||
}
|
}
|
||||||
viewCache = nextCache;
|
);
|
||||||
});
|
return root;
|
||||||
|
|
||||||
return container;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Router = (routes) => {
|
// Router
|
||||||
const currentPath = $(window.location.hash.replace(/^#/, "") || "/");
|
const Router = routes => {
|
||||||
window.addEventListener("hashchange", () => currentPath(window.location.hash.replace(/^#/, "") || "/"));
|
const getHash = () => window.location.hash.slice(1) || "/";
|
||||||
const outlet = Tag("div", { class: "router-transition" });
|
const path = $(getHash());
|
||||||
|
const handler = () => path(getHash());
|
||||||
|
window.addEventListener("hashchange", handler);
|
||||||
|
onUnmount(() => window.removeEventListener("hashchange", handler));
|
||||||
|
const outlet = Tag("div", { class: "router-outlet" });
|
||||||
let currentView = null;
|
let currentView = null;
|
||||||
|
Watch([path], () => {
|
||||||
Watch([currentPath], async () => {
|
const cur = path();
|
||||||
const path = currentPath();
|
|
||||||
const route = routes.find(r => {
|
const route = routes.find(r => {
|
||||||
const routeParts = r.path.split("/").filter(Boolean);
|
const p1 = r.path.split("/").filter(Boolean);
|
||||||
const pathParts = path.split("/").filter(Boolean);
|
const p2 = cur.split("/").filter(Boolean);
|
||||||
return routeParts.length === pathParts.length && routeParts.every((part, i) => part.startsWith(":") || part === pathParts[i]);
|
return p1.length === p2.length && p1.every((p, i) => p[0] === ":" || p === p2[i]);
|
||||||
}) || routes.find(r => r.path === "*");
|
}) || routes.find(r => r.path === "*");
|
||||||
|
|
||||||
if (route) {
|
if (route) {
|
||||||
let component = route.component;
|
currentView?.destroy();
|
||||||
if (isFunc(component) && component.toString().includes('import')) {
|
|
||||||
component = (await component()).default || (await component());
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = {};
|
const params = {};
|
||||||
route.path.split("/").filter(Boolean).forEach((part, i) => {
|
route.path.split("/").filter(Boolean).forEach((p, i) => { if (p[0] === ":") params[p.slice(1)] = cur.split("/").filter(Boolean)[i]; });
|
||||||
if (part.startsWith(":")) params[part.slice(1)] = path.split("/").filter(Boolean)[i];
|
Router.params(params);
|
||||||
});
|
currentView = Render(() => isFunc(route.component) ? route.component(params) : route.component);
|
||||||
|
outlet.replaceChildren(currentView.container);
|
||||||
if (currentView) currentView.destroy();
|
|
||||||
if (Router.params) Router.params(params);
|
|
||||||
|
|
||||||
currentView = Render(() => {
|
|
||||||
try {
|
|
||||||
return isFunc(component) ? component(params) : component;
|
|
||||||
} catch (e) {
|
|
||||||
return Tag("div", { class: "p-4 text-error" }, "Error loading view");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
outlet.appendChild(currentView.container);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return outlet;
|
return outlet;
|
||||||
};
|
};
|
||||||
|
|
||||||
Router.params = $({});
|
Router.params = $({});
|
||||||
Router.to = (path) => (window.location.hash = path.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 = (component, target) => {
|
// Mount
|
||||||
const targetEl = typeof target === "string" ? doc.querySelector(target) : target;
|
const Mount = (comp, target) => {
|
||||||
if (!targetEl) return;
|
const t = typeof target === "string" ? doc.querySelector(target) : target;
|
||||||
if (MOUNTED_NODES.has(targetEl)) MOUNTED_NODES.get(targetEl).destroy();
|
if (!t) return;
|
||||||
const instance = Render(isFunc(component) ? component : () => component);
|
if (MOUNTED_NODES.has(t)) MOUNTED_NODES.get(t).destroy();
|
||||||
targetEl.replaceChildren(instance.container);
|
const inst = Render(isFunc(comp) ? comp : () => comp);
|
||||||
MOUNTED_NODES.set(targetEl, instance);
|
t.replaceChildren(inst.container);
|
||||||
return instance;
|
MOUNTED_NODES.set(t, inst);
|
||||||
|
return inst;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, untrack, onMount, onUnmount, provide, inject });
|
||||||
const SigPro = { $, $$, Render, Watch, Tag, If, For, Router, Mount };
|
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
assign(window, SigPro);
|
Object.assign(window, SigPro);
|
||||||
const tags = `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`.split(" ");
|
"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"
|
||||||
tags.forEach((tag) => {
|
.split(" ").forEach(t => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
||||||
const helper = tag[0].toUpperCase() + tag.slice(1);
|
|
||||||
if (!(helper in window)) window[helper] = (p, c) => Tag(tag, p, c);
|
|
||||||
});
|
|
||||||
window.SigPro = Object.freeze(SigPro);
|
|
||||||
}
|
}
|
||||||
|
export { $, $$, Watch, Tag, Render, If, For, Router, Mount, untrack, onMount, onUnmount, provide, inject };
|
||||||
export { $, $$, Render, Watch, Tag, If, For, Router, Mount };
|
|
||||||
export default SigPro;
|
export default SigPro;
|
||||||
|
|||||||
Reference in New Issue
Block a user