Reorder
This commit is contained in:
582
dist/sigpro.esm.js
vendored
582
dist/sigpro.esm.js
vendored
@@ -1,437 +1,437 @@
|
|||||||
// sigpro/index.js
|
// sigpro.js
|
||||||
var activeEffect = null;
|
var activeEffect = null;
|
||||||
var currentOwner = null;
|
var currentOwner = null;
|
||||||
var effectQueue = new Set;
|
var effectQueue = new Set;
|
||||||
var isFlushing = false;
|
var isFlushing = false;
|
||||||
var MOUNTED_NODES = new WeakMap;
|
var MOUNTED_NODES = new WeakMap;
|
||||||
var flush = () => {
|
var doc = document;
|
||||||
|
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 isObj = (o) => typeof o === "object" && o !== null;
|
||||||
|
var runWithContext = (effect, callback) => {
|
||||||
|
const previousEffect = activeEffect;
|
||||||
|
activeEffect = effect;
|
||||||
|
try {
|
||||||
|
return callback();
|
||||||
|
} finally {
|
||||||
|
activeEffect = previousEffect;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var cleanupNode = (node) => {
|
||||||
|
if (node._cleanups) {
|
||||||
|
node._cleanups.forEach((dispose) => dispose());
|
||||||
|
node._cleanups.clear();
|
||||||
|
}
|
||||||
|
node.childNodes?.forEach(cleanupNode);
|
||||||
|
};
|
||||||
|
var flushEffects = () => {
|
||||||
if (isFlushing)
|
if (isFlushing)
|
||||||
return;
|
return;
|
||||||
isFlushing = true;
|
isFlushing = true;
|
||||||
while (effectQueue.size > 0) {
|
while (effectQueue.size > 0) {
|
||||||
const sorted = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0));
|
const sortedEffects = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0));
|
||||||
effectQueue.clear();
|
effectQueue.clear();
|
||||||
for (const eff of sorted)
|
for (const effect of sortedEffects) {
|
||||||
if (!eff._deleted)
|
if (!effect._deleted)
|
||||||
eff();
|
effect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
isFlushing = false;
|
isFlushing = false;
|
||||||
};
|
};
|
||||||
var track = (subs) => {
|
var trackSubscription = (subscribers) => {
|
||||||
if (activeEffect && !activeEffect._deleted) {
|
if (activeEffect && !activeEffect._deleted) {
|
||||||
subs.add(activeEffect);
|
subscribers.add(activeEffect);
|
||||||
activeEffect._deps.add(subs);
|
activeEffect._deps.add(subscribers);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var trigger = (subs) => {
|
var triggerUpdate = (subscribers) => {
|
||||||
for (const eff of subs) {
|
subscribers.forEach((effect) => {
|
||||||
if (eff === activeEffect || eff._deleted)
|
if (effect === activeEffect || effect._deleted)
|
||||||
continue;
|
return;
|
||||||
if (eff._isComputed) {
|
if (effect._isComputed) {
|
||||||
eff.markDirty();
|
effect.markDirty();
|
||||||
if (eff._subs)
|
if (effect._subs)
|
||||||
trigger(eff._subs);
|
triggerUpdate(effect._subs);
|
||||||
} else {
|
} else {
|
||||||
effectQueue.add(eff);
|
effectQueue.add(effect);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
if (!isFlushing)
|
if (!isFlushing)
|
||||||
queueMicrotask(flush);
|
queueMicrotask(flushEffects);
|
||||||
};
|
};
|
||||||
var sweep = (node) => {
|
var Render = (renderFn) => {
|
||||||
if (node._cleanups) {
|
|
||||||
node._cleanups.forEach((f) => f());
|
|
||||||
node._cleanups.clear();
|
|
||||||
}
|
|
||||||
node.childNodes?.forEach(sweep);
|
|
||||||
};
|
|
||||||
var _view = (fn) => {
|
|
||||||
const cleanups = new Set;
|
const cleanups = new Set;
|
||||||
const prev = currentOwner;
|
const previousOwner = currentOwner;
|
||||||
const container = document.createElement("div");
|
const container = createEl("div");
|
||||||
container.style.display = "contents";
|
container.style.display = "contents";
|
||||||
currentOwner = { cleanups };
|
currentOwner = { cleanups };
|
||||||
try {
|
const processResult = (result) => {
|
||||||
const res = fn({ onCleanup: (f) => cleanups.add(f) });
|
if (!result)
|
||||||
const process = (n) => {
|
|
||||||
if (!n)
|
|
||||||
return;
|
return;
|
||||||
if (n._isRuntime) {
|
if (result._isRuntime) {
|
||||||
cleanups.add(n.destroy);
|
cleanups.add(result.destroy);
|
||||||
container.appendChild(n.container);
|
container.appendChild(result.container);
|
||||||
} else if (Array.isArray(n))
|
} else if (isArr(result)) {
|
||||||
n.forEach(process);
|
result.forEach(processResult);
|
||||||
else
|
} else {
|
||||||
container.appendChild(n instanceof Node ? n : document.createTextNode(String(n)));
|
container.appendChild(result instanceof Node ? result : createText(result));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
process(res);
|
try {
|
||||||
|
processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) }));
|
||||||
} finally {
|
} finally {
|
||||||
currentOwner = prev;
|
currentOwner = previousOwner;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
_isRuntime: true,
|
_isRuntime: true,
|
||||||
container,
|
container,
|
||||||
destroy: () => {
|
destroy: () => {
|
||||||
cleanups.forEach((f) => f());
|
cleanups.forEach((fn) => fn());
|
||||||
sweep(container);
|
cleanupNode(container);
|
||||||
container.remove();
|
container.remove();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
var $ = (initial, key = null) => {
|
var $ = (initialValue, storageKey = null) => {
|
||||||
if (typeof initial === "function") {
|
const subscribers = new Set;
|
||||||
const subs2 = new Set;
|
if (isFunc(initialValue)) {
|
||||||
let cached, dirty = true;
|
let cachedValue, isDirty = true;
|
||||||
const effect = () => {
|
const effect = () => {
|
||||||
if (effect._deleted)
|
if (effect._deleted)
|
||||||
return;
|
return;
|
||||||
effect._deps.forEach((s) => s.delete(effect));
|
effect._deps.forEach((dep) => dep.delete(effect));
|
||||||
effect._deps.clear();
|
effect._deps.clear();
|
||||||
const prev = activeEffect;
|
runWithContext(effect, () => {
|
||||||
activeEffect = effect;
|
const newValue = initialValue();
|
||||||
try {
|
if (!Object.is(cachedValue, newValue) || isDirty) {
|
||||||
const val = initial();
|
cachedValue = newValue;
|
||||||
if (!Object.is(cached, val) || dirty) {
|
isDirty = false;
|
||||||
cached = val;
|
triggerUpdate(subscribers);
|
||||||
dirty = false;
|
|
||||||
trigger(subs2);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
activeEffect = prev;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
effect._deps = new Set;
|
assign(effect, {
|
||||||
effect._isComputed = true;
|
_deps: new Set,
|
||||||
effect._subs = subs2;
|
_isComputed: true,
|
||||||
effect._deleted = false;
|
_subs: subscribers,
|
||||||
effect.markDirty = () => dirty = true;
|
_deleted: false,
|
||||||
effect.stop = () => {
|
markDirty: () => isDirty = true,
|
||||||
|
stop: () => {
|
||||||
effect._deleted = true;
|
effect._deleted = true;
|
||||||
effect._deps.forEach((s) => s.delete(effect));
|
effect._deps.forEach((dep) => dep.delete(effect));
|
||||||
subs2.clear();
|
subscribers.clear();
|
||||||
};
|
}
|
||||||
|
});
|
||||||
if (currentOwner)
|
if (currentOwner)
|
||||||
currentOwner.cleanups.add(effect.stop);
|
currentOwner.cleanups.add(effect.stop);
|
||||||
return () => {
|
return () => {
|
||||||
if (dirty)
|
if (isDirty)
|
||||||
effect();
|
effect();
|
||||||
track(subs2);
|
trackSubscription(subscribers);
|
||||||
return cached;
|
return cachedValue;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let value = initial;
|
let value = initialValue;
|
||||||
if (key) {
|
if (storageKey) {
|
||||||
try {
|
try {
|
||||||
const saved = localStorage.getItem(key);
|
const saved = localStorage.getItem(storageKey);
|
||||||
if (saved !== null)
|
if (saved !== null)
|
||||||
value = JSON.parse(saved);
|
value = JSON.parse(saved);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("SigPro: LocalStorage locked", e);
|
console.warn("SigPro Storage Lock", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const subs = new Set;
|
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
if (args.length) {
|
if (args.length) {
|
||||||
const next = typeof args[0] === "function" ? args[0](value) : args[0];
|
const nextValue = isFunc(args[0]) ? args[0](value) : args[0];
|
||||||
if (!Object.is(value, next)) {
|
if (!Object.is(value, nextValue)) {
|
||||||
value = next;
|
value = nextValue;
|
||||||
if (key)
|
if (storageKey)
|
||||||
localStorage.setItem(key, JSON.stringify(value));
|
localStorage.setItem(storageKey, JSON.stringify(value));
|
||||||
trigger(subs);
|
triggerUpdate(subscribers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
track(subs);
|
trackSubscription(subscribers);
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
var $watch = (target, fn) => {
|
var $$ = (object, cache = new WeakMap) => {
|
||||||
const isExplicit = Array.isArray(target);
|
if (!isObj(object))
|
||||||
const callback = isExplicit ? fn : target;
|
return object;
|
||||||
const depsInput = isExplicit ? target : null;
|
if (cache.has(object))
|
||||||
if (typeof callback !== "function")
|
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 Watch = (target, callbackFn) => {
|
||||||
|
const isExplicit = isArr(target);
|
||||||
|
const callback = isExplicit ? callbackFn : target;
|
||||||
|
if (!isFunc(callback))
|
||||||
return () => {};
|
return () => {};
|
||||||
const owner = currentOwner;
|
const owner = currentOwner;
|
||||||
const runner = () => {
|
const runner = () => {
|
||||||
if (runner._deleted)
|
if (runner._deleted)
|
||||||
return;
|
return;
|
||||||
runner._deps.forEach((s) => s.delete(runner));
|
runner._deps.forEach((dep) => dep.delete(runner));
|
||||||
runner._deps.clear();
|
runner._deps.clear();
|
||||||
runner._cleanups.forEach((c) => c());
|
runner._cleanups.forEach((cleanup) => cleanup());
|
||||||
runner._cleanups.clear();
|
runner._cleanups.clear();
|
||||||
const prevEffect = activeEffect;
|
const previousOwner = currentOwner;
|
||||||
const prevOwner = currentOwner;
|
runner.depth = activeEffect ? activeEffect.depth + 1 : 0;
|
||||||
activeEffect = runner;
|
runWithContext(runner, () => {
|
||||||
currentOwner = { cleanups: runner._cleanups };
|
currentOwner = { cleanups: runner._cleanups };
|
||||||
runner.depth = prevEffect ? prevEffect.depth + 1 : 0;
|
|
||||||
try {
|
|
||||||
if (isExplicit) {
|
if (isExplicit) {
|
||||||
activeEffect = null;
|
runWithContext(null, callback);
|
||||||
callback();
|
target.forEach((dep) => isFunc(dep) && dep());
|
||||||
activeEffect = runner;
|
|
||||||
depsInput.forEach((d) => typeof d === "function" && d());
|
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
} finally {
|
currentOwner = previousOwner;
|
||||||
activeEffect = prevEffect;
|
});
|
||||||
currentOwner = prevOwner;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
runner._deps = new Set;
|
assign(runner, {
|
||||||
runner._cleanups = new Set;
|
_deps: new Set,
|
||||||
runner._deleted = false;
|
_cleanups: new Set,
|
||||||
runner.stop = () => {
|
_deleted: false,
|
||||||
|
stop: () => {
|
||||||
if (runner._deleted)
|
if (runner._deleted)
|
||||||
return;
|
return;
|
||||||
runner._deleted = true;
|
runner._deleted = true;
|
||||||
effectQueue.delete(runner);
|
effectQueue.delete(runner);
|
||||||
runner._deps.forEach((s) => s.delete(runner));
|
runner._deps.forEach((dep) => dep.delete(runner));
|
||||||
runner._cleanups.forEach((c) => c());
|
runner._cleanups.forEach((cleanup) => cleanup());
|
||||||
if (owner)
|
if (owner)
|
||||||
owner.cleanups.delete(runner.stop);
|
owner.cleanups.delete(runner.stop);
|
||||||
};
|
}
|
||||||
|
});
|
||||||
if (owner)
|
if (owner)
|
||||||
owner.cleanups.add(runner.stop);
|
owner.cleanups.add(runner.stop);
|
||||||
runner();
|
runner();
|
||||||
return runner.stop;
|
return runner.stop;
|
||||||
};
|
};
|
||||||
var $html = (tag, props = {}, content = []) => {
|
var Tag = (tag, props = {}, children = []) => {
|
||||||
if (props instanceof Node || Array.isArray(props) || typeof props !== "object") {
|
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
||||||
content = props;
|
children = props;
|
||||||
props = {};
|
props = {};
|
||||||
}
|
}
|
||||||
const svgTags = ["svg", "path", "circle", "rect", "line", "polyline", "polygon", "g", "defs", "text", "tspan", "use"];
|
const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag);
|
||||||
const isSVG = svgTags.includes(tag);
|
const element = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : createEl(tag);
|
||||||
const el = isSVG ? document.createElementNS("http://www.w3.org/2000/svg", tag) : document.createElement(tag);
|
element._cleanups = new Set;
|
||||||
const _sanitize = (key, val) => (key === "src" || key === "href") && String(val).toLowerCase().includes("javascript:") ? "#" : val;
|
element.onUnmount = (fn) => element._cleanups.add(fn);
|
||||||
el._cleanups = new Set;
|
const booleanAttributes = ["disabled", "checked", "required", "readonly", "selected", "multiple", "autofocus"];
|
||||||
const boolAttrs = ["disabled", "checked", "required", "readonly", "selected", "multiple", "autofocus"];
|
const updateAttribute = (name, value) => {
|
||||||
for (let [key, val] of Object.entries(props)) {
|
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") {
|
if (key === "ref") {
|
||||||
typeof val === "function" ? val(el) : val.current = el;
|
isFunc(value) ? value(element) : value.current = element;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const isSignal = typeof val === "function", isInput = ["INPUT", "TEXTAREA", "SELECT"].includes(el.tagName), isBindAttr = key === "value" || key === "checked";
|
const isSignal = isFunc(value);
|
||||||
if (isInput && isBindAttr && isSignal) {
|
if (key.startsWith("on")) {
|
||||||
el._cleanups.add($watch(() => {
|
const eventName = key.slice(2).toLowerCase().split(".")[0];
|
||||||
const currentVal = val();
|
element.addEventListener(eventName, value);
|
||||||
if (el[key] !== currentVal)
|
element._cleanups.add(() => element.removeEventListener(eventName, value));
|
||||||
el[key] = currentVal;
|
|
||||||
}));
|
|
||||||
const eventName = key === "checked" ? "change" : "input", handler = (event) => val(event.target[key]);
|
|
||||||
el.addEventListener(eventName, handler);
|
|
||||||
el._cleanups.add(() => el.removeEventListener(eventName, handler));
|
|
||||||
} else if (key.startsWith("on")) {
|
|
||||||
const eventName = key.slice(2).toLowerCase().split(".")[0], handler = (event) => val(event);
|
|
||||||
el.addEventListener(eventName, handler);
|
|
||||||
el._cleanups.add(() => el.removeEventListener(eventName, handler));
|
|
||||||
} else if (isSignal) {
|
} else if (isSignal) {
|
||||||
el._cleanups.add($watch(() => {
|
element._cleanups.add(Watch(() => {
|
||||||
const currentVal = _sanitize(key, val());
|
const currentVal = value();
|
||||||
if (key === "class") {
|
key === "class" ? element.className = currentVal || "" : updateAttribute(key, currentVal);
|
||||||
el.className = currentVal || "";
|
|
||||||
} else if (boolAttrs.includes(key)) {
|
|
||||||
if (currentVal) {
|
|
||||||
el.setAttribute(key, "");
|
|
||||||
el[key] = true;
|
|
||||||
} else {
|
|
||||||
el.removeAttribute(key);
|
|
||||||
el[key] = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (currentVal == null) {
|
|
||||||
el.removeAttribute(key);
|
|
||||||
} else if (isSVG && typeof currentVal === "number") {
|
|
||||||
el.setAttribute(key, currentVal);
|
|
||||||
} else {
|
|
||||||
el.setAttribute(key, currentVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
} else {
|
if (["INPUT", "TEXTAREA", "SELECT"].includes(element.tagName) && (key === "value" || key === "checked")) {
|
||||||
if (boolAttrs.includes(key)) {
|
const event = key === "checked" ? "change" : "input";
|
||||||
if (val) {
|
const handler = (e) => value(e.target[key]);
|
||||||
el.setAttribute(key, "");
|
element.addEventListener(event, handler);
|
||||||
el[key] = true;
|
element._cleanups.add(() => element.removeEventListener(event, handler));
|
||||||
} else {
|
|
||||||
el.removeAttribute(key);
|
|
||||||
el[key] = false;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
el.setAttribute(key, _sanitize(key, val));
|
updateAttribute(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
const appendChildNode = (child) => {
|
||||||
const append = (child) => {
|
if (isArr(child))
|
||||||
if (Array.isArray(child))
|
return child.forEach(appendChildNode);
|
||||||
return child.forEach(append);
|
if (isFunc(child)) {
|
||||||
if (child instanceof Node) {
|
const marker = createText("");
|
||||||
el.appendChild(child);
|
element.appendChild(marker);
|
||||||
} else if (typeof child === "function") {
|
let currentNodes = [];
|
||||||
const marker = document.createTextNode("");
|
element._cleanups.add(Watch(() => {
|
||||||
el.appendChild(marker);
|
const result = child();
|
||||||
let nodes = [];
|
const nextNodes = (isArr(result) ? result : [result]).map((node) => node?._isRuntime ? node.container : node instanceof Node ? node : createText(node));
|
||||||
el._cleanups.add($watch(() => {
|
currentNodes.forEach((node) => {
|
||||||
const res = child(), next = (Array.isArray(res) ? res : [res]).map((i) => i?._isRuntime ? i.container : i instanceof Node ? i : document.createTextNode(i ?? ""));
|
cleanupNode(node);
|
||||||
nodes.forEach((n) => {
|
node.remove();
|
||||||
sweep?.(n);
|
|
||||||
n.remove();
|
|
||||||
});
|
});
|
||||||
next.forEach((n) => marker.parentNode?.insertBefore(n, marker));
|
nextNodes.forEach((node) => marker.parentNode?.insertBefore(node, marker));
|
||||||
nodes = next;
|
currentNodes = nextNodes;
|
||||||
}));
|
}));
|
||||||
} else
|
} else {
|
||||||
el.appendChild(document.createTextNode(child ?? ""));
|
element.appendChild(child instanceof Node ? child : createText(child));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
append(content);
|
appendChildNode(children);
|
||||||
return el;
|
return element;
|
||||||
};
|
};
|
||||||
var $if = (condition, thenVal, otherwiseVal = null, transition = null) => {
|
var If = (condition, thenVal, otherwiseVal = null, transition = null) => {
|
||||||
const marker = document.createTextNode("");
|
const marker = createText("");
|
||||||
const container = $html("div", { style: "display:contents" }, [marker]);
|
const container = Tag("div", { style: "display:contents" }, [marker]);
|
||||||
let current = null, last = null;
|
let currentView = null, lastState = null;
|
||||||
$watch(() => {
|
Watch(() => {
|
||||||
const state = !!(typeof condition === "function" ? condition() : condition);
|
const state = !!(isFunc(condition) ? condition() : condition);
|
||||||
if (state === last)
|
if (state === lastState)
|
||||||
return;
|
return;
|
||||||
last = state;
|
lastState = state;
|
||||||
if (current && !state && transition?.out) {
|
const dispose = () => {
|
||||||
transition.out(current.container, () => {
|
if (currentView)
|
||||||
current.destroy();
|
currentView.destroy();
|
||||||
current = null;
|
currentView = null;
|
||||||
});
|
};
|
||||||
|
if (currentView && !state && transition?.out) {
|
||||||
|
transition.out(currentView.container, dispose);
|
||||||
} else {
|
} else {
|
||||||
if (current)
|
dispose();
|
||||||
current.destroy();
|
|
||||||
current = null;
|
|
||||||
}
|
}
|
||||||
if (state || !state && otherwiseVal) {
|
|
||||||
const branch = state ? thenVal : otherwiseVal;
|
const branch = state ? thenVal : otherwiseVal;
|
||||||
if (branch) {
|
if (branch) {
|
||||||
current = _view(() => typeof branch === "function" ? branch() : branch);
|
currentView = Render(() => isFunc(branch) ? branch() : branch);
|
||||||
container.insertBefore(current.container, marker);
|
container.insertBefore(currentView.container, marker);
|
||||||
if (state && transition?.in)
|
if (state && transition?.in)
|
||||||
transition.in(current.container);
|
transition.in(currentView.container);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return container;
|
return container;
|
||||||
};
|
};
|
||||||
$if.not = (condition, thenVal, otherwiseVal) => $if(() => !(typeof condition === "function" ? condition() : condition), thenVal, otherwiseVal);
|
var For = (source, renderFn, keyFn, tag = "div", props = { style: "display:contents" }) => {
|
||||||
var $for = (source, render, keyFn, tag = "div", props = { style: "display:contents" }) => {
|
const marker = createText("");
|
||||||
const marker = document.createTextNode("");
|
const container = Tag(tag, props, [marker]);
|
||||||
const container = $html(tag, props, [marker]);
|
let viewCache = new Map;
|
||||||
let cache = new Map;
|
Watch(() => {
|
||||||
$watch(() => {
|
const items = (isFunc(source) ? source() : source) || [];
|
||||||
const items = (typeof source === "function" ? source() : source) || [];
|
const nextCache = new Map;
|
||||||
const newCache = new Map;
|
const order = [];
|
||||||
const newOrder = [];
|
|
||||||
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 run = cache.get(key);
|
let view = viewCache.get(key) || Render(() => renderFn(item, i));
|
||||||
if (!run) {
|
viewCache.delete(key);
|
||||||
run = _view(() => render(item, i));
|
nextCache.set(key, view);
|
||||||
} else {
|
order.push(key);
|
||||||
cache.delete(key);
|
|
||||||
}
|
}
|
||||||
newCache.set(key, run);
|
viewCache.forEach((view) => {
|
||||||
newOrder.push(key);
|
view.destroy();
|
||||||
}
|
view.container.remove();
|
||||||
cache.forEach((run) => {
|
|
||||||
run.destroy();
|
|
||||||
run.container.remove();
|
|
||||||
});
|
});
|
||||||
let anchor = marker;
|
let anchor = marker;
|
||||||
for (let i = newOrder.length - 1;i >= 0; i--) {
|
for (let i = order.length - 1;i >= 0; i--) {
|
||||||
const run = newCache.get(newOrder[i]);
|
const view = nextCache.get(order[i]);
|
||||||
if (run.container.nextSibling !== anchor) {
|
if (view.container.nextSibling !== anchor) {
|
||||||
container.insertBefore(run.container, anchor);
|
container.insertBefore(view.container, anchor);
|
||||||
}
|
}
|
||||||
anchor = run.container;
|
anchor = view.container;
|
||||||
}
|
}
|
||||||
cache = newCache;
|
viewCache = nextCache;
|
||||||
});
|
});
|
||||||
return container;
|
return container;
|
||||||
};
|
};
|
||||||
var $router = (routes) => {
|
var Router = (routes) => {
|
||||||
const sPath = $(window.location.hash.replace(/^#/, "") || "/");
|
const currentPath = $(window.location.hash.replace(/^#/, "") || "/");
|
||||||
window.addEventListener("hashchange", () => sPath(window.location.hash.replace(/^#/, "") || "/"));
|
window.addEventListener("hashchange", () => currentPath(window.location.hash.replace(/^#/, "") || "/"));
|
||||||
const outlet = $html("div", { class: "router-outlet" });
|
const outlet = Tag("div", { class: "router-outlet" });
|
||||||
let current = null;
|
let currentView = null;
|
||||||
$watch([sPath], async () => {
|
Watch([currentPath], async () => {
|
||||||
const path = sPath();
|
const path = currentPath();
|
||||||
const route = routes.find((r) => {
|
const route = routes.find((r) => {
|
||||||
const rp = r.path.split("/").filter(Boolean), pp = path.split("/").filter(Boolean);
|
const routeParts = r.path.split("/").filter(Boolean);
|
||||||
return rp.length === pp.length && rp.every((p, i) => p.startsWith(":") || p === pp[i]);
|
const pathParts = path.split("/").filter(Boolean);
|
||||||
|
return routeParts.length === pathParts.length && routeParts.every((part, i) => part.startsWith(":") || part === pathParts[i]);
|
||||||
}) || routes.find((r) => r.path === "*");
|
}) || routes.find((r) => r.path === "*");
|
||||||
if (route) {
|
if (route) {
|
||||||
let comp = route.component;
|
let component = route.component;
|
||||||
if (typeof comp === "function" && comp.toString().includes("import")) {
|
if (isFunc(component) && component.toString().includes("import")) {
|
||||||
comp = (await comp()).default || await comp();
|
component = (await component()).default || await component();
|
||||||
}
|
}
|
||||||
const params = {};
|
const params = {};
|
||||||
route.path.split("/").filter(Boolean).forEach((p, i) => {
|
route.path.split("/").filter(Boolean).forEach((part, i) => {
|
||||||
if (p.startsWith(":"))
|
if (part.startsWith(":"))
|
||||||
params[p.slice(1)] = path.split("/").filter(Boolean)[i];
|
params[part.slice(1)] = path.split("/").filter(Boolean)[i];
|
||||||
});
|
});
|
||||||
if (current)
|
if (currentView)
|
||||||
current.destroy();
|
currentView.destroy();
|
||||||
if ($router.params)
|
if (Router.params)
|
||||||
$router.params(params);
|
Router.params(params);
|
||||||
current = _view(() => {
|
currentView = Render(() => {
|
||||||
try {
|
try {
|
||||||
return typeof comp === "function" ? comp(params) : comp;
|
return isFunc(component) ? component(params) : component;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return $html("div", { class: "p-4 text-error" }, "Error loading view");
|
return Tag("div", { class: "p-4 text-error" }, "Error loading view");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
outlet.appendChild(current.container);
|
outlet.appendChild(currentView.container);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return outlet;
|
return outlet;
|
||||||
};
|
};
|
||||||
$router.params = $({});
|
Router.params = $({});
|
||||||
$router.to = (path) => window.location.hash = path.replace(/^#?\/?/, "#/");
|
Router.to = (path) => window.location.hash = path.replace(/^#?\/?/, "#/");
|
||||||
$router.back = () => window.history.back();
|
Router.back = () => window.history.back();
|
||||||
$router.path = () => window.location.hash.replace(/^#/, "") || "/";
|
Router.path = () => window.location.hash.replace(/^#/, "") || "/";
|
||||||
var $mount = (component, target) => {
|
var Mount = (component, target) => {
|
||||||
const el = typeof target === "string" ? document.querySelector(target) : target;
|
const targetEl = typeof target === "string" ? doc.querySelector(target) : target;
|
||||||
if (!el)
|
if (!targetEl)
|
||||||
return;
|
return;
|
||||||
if (MOUNTED_NODES.has(el))
|
if (MOUNTED_NODES.has(targetEl))
|
||||||
MOUNTED_NODES.get(el).destroy();
|
MOUNTED_NODES.get(targetEl).destroy();
|
||||||
const instance = _view(typeof component === "function" ? component : () => component);
|
const instance = Render(isFunc(component) ? component : () => component);
|
||||||
el.replaceChildren(instance.container);
|
targetEl.replaceChildren(instance.container);
|
||||||
MOUNTED_NODES.set(el, instance);
|
MOUNTED_NODES.set(targetEl, instance);
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
var Fragment = ({ children }) => children;
|
var Fragment = ({ children }) => children;
|
||||||
var SigProCore = { $, $watch, $html, $if, $for, $router, $mount, Fragment };
|
var SigPro = { $, $$, Render, Watch, Tag, If, For, Router, Mount, Fragment };
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
const install = (registry) => {
|
assign(window, SigPro);
|
||||||
Object.keys(registry).forEach((key) => {
|
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(" ");
|
||||||
window[key] = registry[key];
|
tags.forEach((tag) => {
|
||||||
|
const helper = tag[0].toUpperCase() + tag.slice(1);
|
||||||
|
if (!(helper in window))
|
||||||
|
window[helper] = (p, c) => Tag(tag, p, c);
|
||||||
});
|
});
|
||||||
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(/\s+/);
|
window.SigPro = Object.freeze(SigPro);
|
||||||
tags.forEach((tagName) => {
|
|
||||||
const helperName = tagName.charAt(0).toUpperCase() + tagName.slice(1);
|
|
||||||
if (!(helperName in window)) {
|
|
||||||
window[helperName] = (props, content) => $html(tagName, props, content);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.Fragment = Fragment;
|
|
||||||
window.SigPro = Object.freeze(registry);
|
|
||||||
};
|
|
||||||
install(SigProCore);
|
|
||||||
}
|
}
|
||||||
export {
|
export {
|
||||||
|
Watch,
|
||||||
|
Tag,
|
||||||
|
Router,
|
||||||
|
Render,
|
||||||
|
Mount,
|
||||||
|
If,
|
||||||
Fragment,
|
Fragment,
|
||||||
$watch,
|
For,
|
||||||
$router,
|
$$,
|
||||||
$mount,
|
|
||||||
$if,
|
|
||||||
$html,
|
|
||||||
$for,
|
|
||||||
$
|
$
|
||||||
};
|
};
|
||||||
|
|||||||
2
dist/sigpro.esm.min.js
vendored
2
dist/sigpro.esm.min.js
vendored
File diff suppressed because one or more lines are too long
582
dist/sigpro.js
vendored
582
dist/sigpro.js
vendored
@@ -30,441 +30,441 @@
|
|||||||
// index.js
|
// index.js
|
||||||
var exports_sigpro = {};
|
var exports_sigpro = {};
|
||||||
__export(exports_sigpro, {
|
__export(exports_sigpro, {
|
||||||
|
Watch: () => Watch,
|
||||||
|
Tag: () => Tag,
|
||||||
|
Router: () => Router,
|
||||||
|
Render: () => Render,
|
||||||
|
Mount: () => Mount,
|
||||||
|
If: () => If,
|
||||||
Fragment: () => Fragment,
|
Fragment: () => Fragment,
|
||||||
$watch: () => $watch,
|
For: () => For,
|
||||||
$router: () => $router,
|
$$: () => $$,
|
||||||
$mount: () => $mount,
|
|
||||||
$if: () => $if,
|
|
||||||
$html: () => $html,
|
|
||||||
$for: () => $for,
|
|
||||||
$: () => $
|
$: () => $
|
||||||
});
|
});
|
||||||
|
|
||||||
// sigpro/index.js
|
// sigpro.js
|
||||||
var activeEffect = null;
|
var activeEffect = null;
|
||||||
var currentOwner = null;
|
var currentOwner = null;
|
||||||
var effectQueue = new Set;
|
var effectQueue = new Set;
|
||||||
var isFlushing = false;
|
var isFlushing = false;
|
||||||
var MOUNTED_NODES = new WeakMap;
|
var MOUNTED_NODES = new WeakMap;
|
||||||
var flush = () => {
|
var doc = document;
|
||||||
|
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 isObj = (o) => typeof o === "object" && o !== null;
|
||||||
|
var runWithContext = (effect, callback) => {
|
||||||
|
const previousEffect = activeEffect;
|
||||||
|
activeEffect = effect;
|
||||||
|
try {
|
||||||
|
return callback();
|
||||||
|
} finally {
|
||||||
|
activeEffect = previousEffect;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var cleanupNode = (node) => {
|
||||||
|
if (node._cleanups) {
|
||||||
|
node._cleanups.forEach((dispose) => dispose());
|
||||||
|
node._cleanups.clear();
|
||||||
|
}
|
||||||
|
node.childNodes?.forEach(cleanupNode);
|
||||||
|
};
|
||||||
|
var flushEffects = () => {
|
||||||
if (isFlushing)
|
if (isFlushing)
|
||||||
return;
|
return;
|
||||||
isFlushing = true;
|
isFlushing = true;
|
||||||
while (effectQueue.size > 0) {
|
while (effectQueue.size > 0) {
|
||||||
const sorted = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0));
|
const sortedEffects = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0));
|
||||||
effectQueue.clear();
|
effectQueue.clear();
|
||||||
for (const eff of sorted)
|
for (const effect of sortedEffects) {
|
||||||
if (!eff._deleted)
|
if (!effect._deleted)
|
||||||
eff();
|
effect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
isFlushing = false;
|
isFlushing = false;
|
||||||
};
|
};
|
||||||
var track = (subs) => {
|
var trackSubscription = (subscribers) => {
|
||||||
if (activeEffect && !activeEffect._deleted) {
|
if (activeEffect && !activeEffect._deleted) {
|
||||||
subs.add(activeEffect);
|
subscribers.add(activeEffect);
|
||||||
activeEffect._deps.add(subs);
|
activeEffect._deps.add(subscribers);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var trigger = (subs) => {
|
var triggerUpdate = (subscribers) => {
|
||||||
for (const eff of subs) {
|
subscribers.forEach((effect) => {
|
||||||
if (eff === activeEffect || eff._deleted)
|
if (effect === activeEffect || effect._deleted)
|
||||||
continue;
|
return;
|
||||||
if (eff._isComputed) {
|
if (effect._isComputed) {
|
||||||
eff.markDirty();
|
effect.markDirty();
|
||||||
if (eff._subs)
|
if (effect._subs)
|
||||||
trigger(eff._subs);
|
triggerUpdate(effect._subs);
|
||||||
} else {
|
} else {
|
||||||
effectQueue.add(eff);
|
effectQueue.add(effect);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
if (!isFlushing)
|
if (!isFlushing)
|
||||||
queueMicrotask(flush);
|
queueMicrotask(flushEffects);
|
||||||
};
|
};
|
||||||
var sweep = (node) => {
|
var Render = (renderFn) => {
|
||||||
if (node._cleanups) {
|
|
||||||
node._cleanups.forEach((f) => f());
|
|
||||||
node._cleanups.clear();
|
|
||||||
}
|
|
||||||
node.childNodes?.forEach(sweep);
|
|
||||||
};
|
|
||||||
var _view = (fn) => {
|
|
||||||
const cleanups = new Set;
|
const cleanups = new Set;
|
||||||
const prev = currentOwner;
|
const previousOwner = currentOwner;
|
||||||
const container = document.createElement("div");
|
const container = createEl("div");
|
||||||
container.style.display = "contents";
|
container.style.display = "contents";
|
||||||
currentOwner = { cleanups };
|
currentOwner = { cleanups };
|
||||||
try {
|
const processResult = (result) => {
|
||||||
const res = fn({ onCleanup: (f) => cleanups.add(f) });
|
if (!result)
|
||||||
const process = (n) => {
|
|
||||||
if (!n)
|
|
||||||
return;
|
return;
|
||||||
if (n._isRuntime) {
|
if (result._isRuntime) {
|
||||||
cleanups.add(n.destroy);
|
cleanups.add(result.destroy);
|
||||||
container.appendChild(n.container);
|
container.appendChild(result.container);
|
||||||
} else if (Array.isArray(n))
|
} else if (isArr(result)) {
|
||||||
n.forEach(process);
|
result.forEach(processResult);
|
||||||
else
|
} else {
|
||||||
container.appendChild(n instanceof Node ? n : document.createTextNode(String(n)));
|
container.appendChild(result instanceof Node ? result : createText(result));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
process(res);
|
try {
|
||||||
|
processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) }));
|
||||||
} finally {
|
} finally {
|
||||||
currentOwner = prev;
|
currentOwner = previousOwner;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
_isRuntime: true,
|
_isRuntime: true,
|
||||||
container,
|
container,
|
||||||
destroy: () => {
|
destroy: () => {
|
||||||
cleanups.forEach((f) => f());
|
cleanups.forEach((fn) => fn());
|
||||||
sweep(container);
|
cleanupNode(container);
|
||||||
container.remove();
|
container.remove();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
var $ = (initial, key = null) => {
|
var $ = (initialValue, storageKey = null) => {
|
||||||
if (typeof initial === "function") {
|
const subscribers = new Set;
|
||||||
const subs2 = new Set;
|
if (isFunc(initialValue)) {
|
||||||
let cached, dirty = true;
|
let cachedValue, isDirty = true;
|
||||||
const effect = () => {
|
const effect = () => {
|
||||||
if (effect._deleted)
|
if (effect._deleted)
|
||||||
return;
|
return;
|
||||||
effect._deps.forEach((s) => s.delete(effect));
|
effect._deps.forEach((dep) => dep.delete(effect));
|
||||||
effect._deps.clear();
|
effect._deps.clear();
|
||||||
const prev = activeEffect;
|
runWithContext(effect, () => {
|
||||||
activeEffect = effect;
|
const newValue = initialValue();
|
||||||
try {
|
if (!Object.is(cachedValue, newValue) || isDirty) {
|
||||||
const val = initial();
|
cachedValue = newValue;
|
||||||
if (!Object.is(cached, val) || dirty) {
|
isDirty = false;
|
||||||
cached = val;
|
triggerUpdate(subscribers);
|
||||||
dirty = false;
|
|
||||||
trigger(subs2);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
activeEffect = prev;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
effect._deps = new Set;
|
assign(effect, {
|
||||||
effect._isComputed = true;
|
_deps: new Set,
|
||||||
effect._subs = subs2;
|
_isComputed: true,
|
||||||
effect._deleted = false;
|
_subs: subscribers,
|
||||||
effect.markDirty = () => dirty = true;
|
_deleted: false,
|
||||||
effect.stop = () => {
|
markDirty: () => isDirty = true,
|
||||||
|
stop: () => {
|
||||||
effect._deleted = true;
|
effect._deleted = true;
|
||||||
effect._deps.forEach((s) => s.delete(effect));
|
effect._deps.forEach((dep) => dep.delete(effect));
|
||||||
subs2.clear();
|
subscribers.clear();
|
||||||
};
|
}
|
||||||
|
});
|
||||||
if (currentOwner)
|
if (currentOwner)
|
||||||
currentOwner.cleanups.add(effect.stop);
|
currentOwner.cleanups.add(effect.stop);
|
||||||
return () => {
|
return () => {
|
||||||
if (dirty)
|
if (isDirty)
|
||||||
effect();
|
effect();
|
||||||
track(subs2);
|
trackSubscription(subscribers);
|
||||||
return cached;
|
return cachedValue;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let value = initial;
|
let value = initialValue;
|
||||||
if (key) {
|
if (storageKey) {
|
||||||
try {
|
try {
|
||||||
const saved = localStorage.getItem(key);
|
const saved = localStorage.getItem(storageKey);
|
||||||
if (saved !== null)
|
if (saved !== null)
|
||||||
value = JSON.parse(saved);
|
value = JSON.parse(saved);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("SigPro: LocalStorage locked", e);
|
console.warn("SigPro Storage Lock", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const subs = new Set;
|
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
if (args.length) {
|
if (args.length) {
|
||||||
const next = typeof args[0] === "function" ? args[0](value) : args[0];
|
const nextValue = isFunc(args[0]) ? args[0](value) : args[0];
|
||||||
if (!Object.is(value, next)) {
|
if (!Object.is(value, nextValue)) {
|
||||||
value = next;
|
value = nextValue;
|
||||||
if (key)
|
if (storageKey)
|
||||||
localStorage.setItem(key, JSON.stringify(value));
|
localStorage.setItem(storageKey, JSON.stringify(value));
|
||||||
trigger(subs);
|
triggerUpdate(subscribers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
track(subs);
|
trackSubscription(subscribers);
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
var $watch = (target, fn) => {
|
var $$ = (object, cache = new WeakMap) => {
|
||||||
const isExplicit = Array.isArray(target);
|
if (!isObj(object))
|
||||||
const callback = isExplicit ? fn : target;
|
return object;
|
||||||
const depsInput = isExplicit ? target : null;
|
if (cache.has(object))
|
||||||
if (typeof callback !== "function")
|
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 Watch = (target, callbackFn) => {
|
||||||
|
const isExplicit = isArr(target);
|
||||||
|
const callback = isExplicit ? callbackFn : target;
|
||||||
|
if (!isFunc(callback))
|
||||||
return () => {};
|
return () => {};
|
||||||
const owner = currentOwner;
|
const owner = currentOwner;
|
||||||
const runner = () => {
|
const runner = () => {
|
||||||
if (runner._deleted)
|
if (runner._deleted)
|
||||||
return;
|
return;
|
||||||
runner._deps.forEach((s) => s.delete(runner));
|
runner._deps.forEach((dep) => dep.delete(runner));
|
||||||
runner._deps.clear();
|
runner._deps.clear();
|
||||||
runner._cleanups.forEach((c) => c());
|
runner._cleanups.forEach((cleanup) => cleanup());
|
||||||
runner._cleanups.clear();
|
runner._cleanups.clear();
|
||||||
const prevEffect = activeEffect;
|
const previousOwner = currentOwner;
|
||||||
const prevOwner = currentOwner;
|
runner.depth = activeEffect ? activeEffect.depth + 1 : 0;
|
||||||
activeEffect = runner;
|
runWithContext(runner, () => {
|
||||||
currentOwner = { cleanups: runner._cleanups };
|
currentOwner = { cleanups: runner._cleanups };
|
||||||
runner.depth = prevEffect ? prevEffect.depth + 1 : 0;
|
|
||||||
try {
|
|
||||||
if (isExplicit) {
|
if (isExplicit) {
|
||||||
activeEffect = null;
|
runWithContext(null, callback);
|
||||||
callback();
|
target.forEach((dep) => isFunc(dep) && dep());
|
||||||
activeEffect = runner;
|
|
||||||
depsInput.forEach((d) => typeof d === "function" && d());
|
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
} finally {
|
currentOwner = previousOwner;
|
||||||
activeEffect = prevEffect;
|
});
|
||||||
currentOwner = prevOwner;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
runner._deps = new Set;
|
assign(runner, {
|
||||||
runner._cleanups = new Set;
|
_deps: new Set,
|
||||||
runner._deleted = false;
|
_cleanups: new Set,
|
||||||
runner.stop = () => {
|
_deleted: false,
|
||||||
|
stop: () => {
|
||||||
if (runner._deleted)
|
if (runner._deleted)
|
||||||
return;
|
return;
|
||||||
runner._deleted = true;
|
runner._deleted = true;
|
||||||
effectQueue.delete(runner);
|
effectQueue.delete(runner);
|
||||||
runner._deps.forEach((s) => s.delete(runner));
|
runner._deps.forEach((dep) => dep.delete(runner));
|
||||||
runner._cleanups.forEach((c) => c());
|
runner._cleanups.forEach((cleanup) => cleanup());
|
||||||
if (owner)
|
if (owner)
|
||||||
owner.cleanups.delete(runner.stop);
|
owner.cleanups.delete(runner.stop);
|
||||||
};
|
}
|
||||||
|
});
|
||||||
if (owner)
|
if (owner)
|
||||||
owner.cleanups.add(runner.stop);
|
owner.cleanups.add(runner.stop);
|
||||||
runner();
|
runner();
|
||||||
return runner.stop;
|
return runner.stop;
|
||||||
};
|
};
|
||||||
var $html = (tag, props = {}, content = []) => {
|
var Tag = (tag, props = {}, children = []) => {
|
||||||
if (props instanceof Node || Array.isArray(props) || typeof props !== "object") {
|
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
||||||
content = props;
|
children = props;
|
||||||
props = {};
|
props = {};
|
||||||
}
|
}
|
||||||
const svgTags = ["svg", "path", "circle", "rect", "line", "polyline", "polygon", "g", "defs", "text", "tspan", "use"];
|
const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag);
|
||||||
const isSVG = svgTags.includes(tag);
|
const element = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : createEl(tag);
|
||||||
const el = isSVG ? document.createElementNS("http://www.w3.org/2000/svg", tag) : document.createElement(tag);
|
element._cleanups = new Set;
|
||||||
const _sanitize = (key, val) => (key === "src" || key === "href") && String(val).toLowerCase().includes("javascript:") ? "#" : val;
|
element.onUnmount = (fn) => element._cleanups.add(fn);
|
||||||
el._cleanups = new Set;
|
const booleanAttributes = ["disabled", "checked", "required", "readonly", "selected", "multiple", "autofocus"];
|
||||||
const boolAttrs = ["disabled", "checked", "required", "readonly", "selected", "multiple", "autofocus"];
|
const updateAttribute = (name, value) => {
|
||||||
for (let [key, val] of Object.entries(props)) {
|
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") {
|
if (key === "ref") {
|
||||||
typeof val === "function" ? val(el) : val.current = el;
|
isFunc(value) ? value(element) : value.current = element;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const isSignal = typeof val === "function", isInput = ["INPUT", "TEXTAREA", "SELECT"].includes(el.tagName), isBindAttr = key === "value" || key === "checked";
|
const isSignal = isFunc(value);
|
||||||
if (isInput && isBindAttr && isSignal) {
|
if (key.startsWith("on")) {
|
||||||
el._cleanups.add($watch(() => {
|
const eventName = key.slice(2).toLowerCase().split(".")[0];
|
||||||
const currentVal = val();
|
element.addEventListener(eventName, value);
|
||||||
if (el[key] !== currentVal)
|
element._cleanups.add(() => element.removeEventListener(eventName, value));
|
||||||
el[key] = currentVal;
|
|
||||||
}));
|
|
||||||
const eventName = key === "checked" ? "change" : "input", handler = (event) => val(event.target[key]);
|
|
||||||
el.addEventListener(eventName, handler);
|
|
||||||
el._cleanups.add(() => el.removeEventListener(eventName, handler));
|
|
||||||
} else if (key.startsWith("on")) {
|
|
||||||
const eventName = key.slice(2).toLowerCase().split(".")[0], handler = (event) => val(event);
|
|
||||||
el.addEventListener(eventName, handler);
|
|
||||||
el._cleanups.add(() => el.removeEventListener(eventName, handler));
|
|
||||||
} else if (isSignal) {
|
} else if (isSignal) {
|
||||||
el._cleanups.add($watch(() => {
|
element._cleanups.add(Watch(() => {
|
||||||
const currentVal = _sanitize(key, val());
|
const currentVal = value();
|
||||||
if (key === "class") {
|
key === "class" ? element.className = currentVal || "" : updateAttribute(key, currentVal);
|
||||||
el.className = currentVal || "";
|
|
||||||
} else if (boolAttrs.includes(key)) {
|
|
||||||
if (currentVal) {
|
|
||||||
el.setAttribute(key, "");
|
|
||||||
el[key] = true;
|
|
||||||
} else {
|
|
||||||
el.removeAttribute(key);
|
|
||||||
el[key] = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (currentVal == null) {
|
|
||||||
el.removeAttribute(key);
|
|
||||||
} else if (isSVG && typeof currentVal === "number") {
|
|
||||||
el.setAttribute(key, currentVal);
|
|
||||||
} else {
|
|
||||||
el.setAttribute(key, currentVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
} else {
|
if (["INPUT", "TEXTAREA", "SELECT"].includes(element.tagName) && (key === "value" || key === "checked")) {
|
||||||
if (boolAttrs.includes(key)) {
|
const event = key === "checked" ? "change" : "input";
|
||||||
if (val) {
|
const handler = (e) => value(e.target[key]);
|
||||||
el.setAttribute(key, "");
|
element.addEventListener(event, handler);
|
||||||
el[key] = true;
|
element._cleanups.add(() => element.removeEventListener(event, handler));
|
||||||
} else {
|
|
||||||
el.removeAttribute(key);
|
|
||||||
el[key] = false;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
el.setAttribute(key, _sanitize(key, val));
|
updateAttribute(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
const appendChildNode = (child) => {
|
||||||
const append = (child) => {
|
if (isArr(child))
|
||||||
if (Array.isArray(child))
|
return child.forEach(appendChildNode);
|
||||||
return child.forEach(append);
|
if (isFunc(child)) {
|
||||||
if (child instanceof Node) {
|
const marker = createText("");
|
||||||
el.appendChild(child);
|
element.appendChild(marker);
|
||||||
} else if (typeof child === "function") {
|
let currentNodes = [];
|
||||||
const marker = document.createTextNode("");
|
element._cleanups.add(Watch(() => {
|
||||||
el.appendChild(marker);
|
const result = child();
|
||||||
let nodes = [];
|
const nextNodes = (isArr(result) ? result : [result]).map((node) => node?._isRuntime ? node.container : node instanceof Node ? node : createText(node));
|
||||||
el._cleanups.add($watch(() => {
|
currentNodes.forEach((node) => {
|
||||||
const res = child(), next = (Array.isArray(res) ? res : [res]).map((i) => i?._isRuntime ? i.container : i instanceof Node ? i : document.createTextNode(i ?? ""));
|
cleanupNode(node);
|
||||||
nodes.forEach((n) => {
|
node.remove();
|
||||||
sweep?.(n);
|
|
||||||
n.remove();
|
|
||||||
});
|
});
|
||||||
next.forEach((n) => marker.parentNode?.insertBefore(n, marker));
|
nextNodes.forEach((node) => marker.parentNode?.insertBefore(node, marker));
|
||||||
nodes = next;
|
currentNodes = nextNodes;
|
||||||
}));
|
}));
|
||||||
} else
|
} else {
|
||||||
el.appendChild(document.createTextNode(child ?? ""));
|
element.appendChild(child instanceof Node ? child : createText(child));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
append(content);
|
appendChildNode(children);
|
||||||
return el;
|
return element;
|
||||||
};
|
};
|
||||||
var $if = (condition, thenVal, otherwiseVal = null, transition = null) => {
|
var If = (condition, thenVal, otherwiseVal = null, transition = null) => {
|
||||||
const marker = document.createTextNode("");
|
const marker = createText("");
|
||||||
const container = $html("div", { style: "display:contents" }, [marker]);
|
const container = Tag("div", { style: "display:contents" }, [marker]);
|
||||||
let current = null, last = null;
|
let currentView = null, lastState = null;
|
||||||
$watch(() => {
|
Watch(() => {
|
||||||
const state = !!(typeof condition === "function" ? condition() : condition);
|
const state = !!(isFunc(condition) ? condition() : condition);
|
||||||
if (state === last)
|
if (state === lastState)
|
||||||
return;
|
return;
|
||||||
last = state;
|
lastState = state;
|
||||||
if (current && !state && transition?.out) {
|
const dispose = () => {
|
||||||
transition.out(current.container, () => {
|
if (currentView)
|
||||||
current.destroy();
|
currentView.destroy();
|
||||||
current = null;
|
currentView = null;
|
||||||
});
|
};
|
||||||
|
if (currentView && !state && transition?.out) {
|
||||||
|
transition.out(currentView.container, dispose);
|
||||||
} else {
|
} else {
|
||||||
if (current)
|
dispose();
|
||||||
current.destroy();
|
|
||||||
current = null;
|
|
||||||
}
|
}
|
||||||
if (state || !state && otherwiseVal) {
|
|
||||||
const branch = state ? thenVal : otherwiseVal;
|
const branch = state ? thenVal : otherwiseVal;
|
||||||
if (branch) {
|
if (branch) {
|
||||||
current = _view(() => typeof branch === "function" ? branch() : branch);
|
currentView = Render(() => isFunc(branch) ? branch() : branch);
|
||||||
container.insertBefore(current.container, marker);
|
container.insertBefore(currentView.container, marker);
|
||||||
if (state && transition?.in)
|
if (state && transition?.in)
|
||||||
transition.in(current.container);
|
transition.in(currentView.container);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return container;
|
return container;
|
||||||
};
|
};
|
||||||
$if.not = (condition, thenVal, otherwiseVal) => $if(() => !(typeof condition === "function" ? condition() : condition), thenVal, otherwiseVal);
|
var For = (source, renderFn, keyFn, tag = "div", props = { style: "display:contents" }) => {
|
||||||
var $for = (source, render, keyFn, tag = "div", props = { style: "display:contents" }) => {
|
const marker = createText("");
|
||||||
const marker = document.createTextNode("");
|
const container = Tag(tag, props, [marker]);
|
||||||
const container = $html(tag, props, [marker]);
|
let viewCache = new Map;
|
||||||
let cache = new Map;
|
Watch(() => {
|
||||||
$watch(() => {
|
const items = (isFunc(source) ? source() : source) || [];
|
||||||
const items = (typeof source === "function" ? source() : source) || [];
|
const nextCache = new Map;
|
||||||
const newCache = new Map;
|
const order = [];
|
||||||
const newOrder = [];
|
|
||||||
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 run = cache.get(key);
|
let view = viewCache.get(key) || Render(() => renderFn(item, i));
|
||||||
if (!run) {
|
viewCache.delete(key);
|
||||||
run = _view(() => render(item, i));
|
nextCache.set(key, view);
|
||||||
} else {
|
order.push(key);
|
||||||
cache.delete(key);
|
|
||||||
}
|
}
|
||||||
newCache.set(key, run);
|
viewCache.forEach((view) => {
|
||||||
newOrder.push(key);
|
view.destroy();
|
||||||
}
|
view.container.remove();
|
||||||
cache.forEach((run) => {
|
|
||||||
run.destroy();
|
|
||||||
run.container.remove();
|
|
||||||
});
|
});
|
||||||
let anchor = marker;
|
let anchor = marker;
|
||||||
for (let i = newOrder.length - 1;i >= 0; i--) {
|
for (let i = order.length - 1;i >= 0; i--) {
|
||||||
const run = newCache.get(newOrder[i]);
|
const view = nextCache.get(order[i]);
|
||||||
if (run.container.nextSibling !== anchor) {
|
if (view.container.nextSibling !== anchor) {
|
||||||
container.insertBefore(run.container, anchor);
|
container.insertBefore(view.container, anchor);
|
||||||
}
|
}
|
||||||
anchor = run.container;
|
anchor = view.container;
|
||||||
}
|
}
|
||||||
cache = newCache;
|
viewCache = nextCache;
|
||||||
});
|
});
|
||||||
return container;
|
return container;
|
||||||
};
|
};
|
||||||
var $router = (routes) => {
|
var Router = (routes) => {
|
||||||
const sPath = $(window.location.hash.replace(/^#/, "") || "/");
|
const currentPath = $(window.location.hash.replace(/^#/, "") || "/");
|
||||||
window.addEventListener("hashchange", () => sPath(window.location.hash.replace(/^#/, "") || "/"));
|
window.addEventListener("hashchange", () => currentPath(window.location.hash.replace(/^#/, "") || "/"));
|
||||||
const outlet = $html("div", { class: "router-outlet" });
|
const outlet = Tag("div", { class: "router-outlet" });
|
||||||
let current = null;
|
let currentView = null;
|
||||||
$watch([sPath], async () => {
|
Watch([currentPath], async () => {
|
||||||
const path = sPath();
|
const path = currentPath();
|
||||||
const route = routes.find((r) => {
|
const route = routes.find((r) => {
|
||||||
const rp = r.path.split("/").filter(Boolean), pp = path.split("/").filter(Boolean);
|
const routeParts = r.path.split("/").filter(Boolean);
|
||||||
return rp.length === pp.length && rp.every((p, i) => p.startsWith(":") || p === pp[i]);
|
const pathParts = path.split("/").filter(Boolean);
|
||||||
|
return routeParts.length === pathParts.length && routeParts.every((part, i) => part.startsWith(":") || part === pathParts[i]);
|
||||||
}) || routes.find((r) => r.path === "*");
|
}) || routes.find((r) => r.path === "*");
|
||||||
if (route) {
|
if (route) {
|
||||||
let comp = route.component;
|
let component = route.component;
|
||||||
if (typeof comp === "function" && comp.toString().includes("import")) {
|
if (isFunc(component) && component.toString().includes("import")) {
|
||||||
comp = (await comp()).default || await comp();
|
component = (await component()).default || await component();
|
||||||
}
|
}
|
||||||
const params = {};
|
const params = {};
|
||||||
route.path.split("/").filter(Boolean).forEach((p, i) => {
|
route.path.split("/").filter(Boolean).forEach((part, i) => {
|
||||||
if (p.startsWith(":"))
|
if (part.startsWith(":"))
|
||||||
params[p.slice(1)] = path.split("/").filter(Boolean)[i];
|
params[part.slice(1)] = path.split("/").filter(Boolean)[i];
|
||||||
});
|
});
|
||||||
if (current)
|
if (currentView)
|
||||||
current.destroy();
|
currentView.destroy();
|
||||||
if ($router.params)
|
if (Router.params)
|
||||||
$router.params(params);
|
Router.params(params);
|
||||||
current = _view(() => {
|
currentView = Render(() => {
|
||||||
try {
|
try {
|
||||||
return typeof comp === "function" ? comp(params) : comp;
|
return isFunc(component) ? component(params) : component;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return $html("div", { class: "p-4 text-error" }, "Error loading view");
|
return Tag("div", { class: "p-4 text-error" }, "Error loading view");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
outlet.appendChild(current.container);
|
outlet.appendChild(currentView.container);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return outlet;
|
return outlet;
|
||||||
};
|
};
|
||||||
$router.params = $({});
|
Router.params = $({});
|
||||||
$router.to = (path) => window.location.hash = path.replace(/^#?\/?/, "#/");
|
Router.to = (path) => window.location.hash = path.replace(/^#?\/?/, "#/");
|
||||||
$router.back = () => window.history.back();
|
Router.back = () => window.history.back();
|
||||||
$router.path = () => window.location.hash.replace(/^#/, "") || "/";
|
Router.path = () => window.location.hash.replace(/^#/, "") || "/";
|
||||||
var $mount = (component, target) => {
|
var Mount = (component, target) => {
|
||||||
const el = typeof target === "string" ? document.querySelector(target) : target;
|
const targetEl = typeof target === "string" ? doc.querySelector(target) : target;
|
||||||
if (!el)
|
if (!targetEl)
|
||||||
return;
|
return;
|
||||||
if (MOUNTED_NODES.has(el))
|
if (MOUNTED_NODES.has(targetEl))
|
||||||
MOUNTED_NODES.get(el).destroy();
|
MOUNTED_NODES.get(targetEl).destroy();
|
||||||
const instance = _view(typeof component === "function" ? component : () => component);
|
const instance = Render(isFunc(component) ? component : () => component);
|
||||||
el.replaceChildren(instance.container);
|
targetEl.replaceChildren(instance.container);
|
||||||
MOUNTED_NODES.set(el, instance);
|
MOUNTED_NODES.set(targetEl, instance);
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
var Fragment = ({ children }) => children;
|
var Fragment = ({ children }) => children;
|
||||||
var SigProCore = { $, $watch, $html, $if, $for, $router, $mount, Fragment };
|
var SigPro = { $, $$, Render, Watch, Tag, If, For, Router, Mount, Fragment };
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
const install = (registry) => {
|
assign(window, SigPro);
|
||||||
Object.keys(registry).forEach((key) => {
|
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(" ");
|
||||||
window[key] = registry[key];
|
tags.forEach((tag) => {
|
||||||
|
const helper = tag[0].toUpperCase() + tag.slice(1);
|
||||||
|
if (!(helper in window))
|
||||||
|
window[helper] = (p, c) => Tag(tag, p, c);
|
||||||
});
|
});
|
||||||
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(/\s+/);
|
window.SigPro = Object.freeze(SigPro);
|
||||||
tags.forEach((tagName) => {
|
|
||||||
const helperName = tagName.charAt(0).toUpperCase() + tagName.slice(1);
|
|
||||||
if (!(helperName in window)) {
|
|
||||||
window[helperName] = (props, content) => $html(tagName, props, content);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.Fragment = Fragment;
|
|
||||||
window.SigPro = Object.freeze(registry);
|
|
||||||
};
|
|
||||||
install(SigProCore);
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
2
dist/sigpro.min.js
vendored
2
dist/sigpro.min.js
vendored
File diff suppressed because one or more lines are too long
2
index.js
2
index.js
@@ -1,2 +1,2 @@
|
|||||||
// index.js
|
// index.js
|
||||||
export * from './sigpro/index.js';
|
export * from './sigpro.js';
|
||||||
@@ -10,10 +10,9 @@
|
|||||||
"types": "./sigpro.d.ts",
|
"types": "./sigpro.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"development": "./index.js",
|
|
||||||
"import": "./dist/sigpro.esm.min.js",
|
"import": "./dist/sigpro.esm.min.js",
|
||||||
"script": "./dist/sigpro.js",
|
"script": "./dist/sigpro.js",
|
||||||
"types": "./sigpro/sigpro.d.ts"
|
"types": "./sigpro.d.ts"
|
||||||
},
|
},
|
||||||
"./vite": "./vite/index.js",
|
"./vite": "./vite/index.js",
|
||||||
"./vite/*": "./vite/*.js"
|
"./vite/*": "./vite/*.js"
|
||||||
|
|||||||
@@ -429,5 +429,5 @@ if (typeof window !== "undefined") {
|
|||||||
window.SigPro = Object.freeze(SigPro);
|
window.SigPro = Object.freeze(SigPro);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { $, $$, Render, Watch, Tag, If, For, Router, Mount, Fragment };
|
export { $, $$, Render, Watch, Tag, If, For, Router, Mount };
|
||||||
export default SigPro;
|
export default SigPro;
|
||||||
Reference in New Issue
Block a user