Include fx, req, recover if in actual when
All checks were successful
Deploy Docs to Synology / deploy (push) Successful in 8s

This commit is contained in:
2026-04-22 21:39:07 +02:00
parent f05511dad8
commit 482ff19adb
6 changed files with 433 additions and 151 deletions

157
sigpro.js
View File

@@ -1,4 +1,4 @@
// sigpro 1.2.15
// sigpro 1.2.16
const isFunc = f => typeof f === "function"
const isObj = o => o && typeof o === "object"
const isArr = Array.isArray
@@ -222,14 +222,21 @@ const watch = (sources, cb) => {
return () => dispose(effect)
}
const cleanupNode = node => {
const cleanupNode = (node, skipLeave = false) => {
if (!node) return;
if (node._cleanups) {
node._cleanups.forEach(fn => fn())
node._cleanups.clear()
node._cleanups.forEach(fn => fn());
node._cleanups.clear();
}
if (node._ownerEffect) dispose(node._ownerEffect)
if (node.childNodes) node.childNodes.forEach(cleanupNode)
}
if (node._ownerEffect) dispose(node._ownerEffect);
if (!skipLeave && node._sig_leave) {
return node._sig_leave(() => {
if (node.childNodes) node.childNodes.forEach(n => cleanupNode(n, true));
node.remove();
});
}
if (node.childNodes) node.childNodes.forEach(n => cleanupNode(n, false));
};
const DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i
const isDangerousAttr = key => key === 'src' || key === 'href' || key.startsWith('on')
@@ -251,7 +258,7 @@ const h = (tag, props = {}, children = []) => {
children = props
props = {}
}
if (isFunc(tag)) {
const effect = createEffect(() => {
const result = tag(props, {
@@ -398,48 +405,70 @@ const render = renderFn => {
destroy: () => {
cleanups.forEach(fn => fn())
cleanupNode(container)
container.remove()
if (!container._sig_leave) container.remove()
}
}
}
const when = (cond, render, { enter, leave } = {}) => {
const wrap = h('div', { style: 'display:contents' })
let view = null
const when = (cond, SIP, NOP = null) => {
const anchor = doc.createTextNode("")
const root = h("div", { style: "display:contents" }, [anchor])
let currentView = null
const wait = (el, cb) => {
if (!el) return cb()
let done = false
const finish = () => !done && (done = true, cb())
el.addEventListener('transitionend', finish, { once: true })
el.addEventListener('animationend', finish, { once: true })
setTimeout(finish, 500)
}
watch(cond, on => {
if (on && !view) {
const el = (view = render(render)).container.firstChild
wrap.appendChild(view.container)
if (enter && el) {
el.classList.add(enter); el.clientTop
el.classList.add(enter + '-active')
wait(el, () => el.classList.remove(enter, enter + '-active'))
watch(
() => !!(isFunc(cond) ? cond() : cond),
show => {
if (currentView) {
currentView.destroy()
currentView = null
}
} else if (!on && view) {
const el = view.container.firstChild
const destroyView = () => (view.destroy(), view = null)
if (leave && el) {
el.classList.add(leave)
wait(el, destroyView)
} else {
destroyView()
const content = show ? SIP : NOP
if (content) {
currentView = render(() => isFunc(content) ? content() : content)
root.insertBefore(currentView.container, anchor)
}
}
})
)
return onUnmount(() => view?.destroy()), wrap
onUnmount(() => currentView?.destroy())
return root
}
const fx = ({ name, duration = 200, scale, slide, rotate, blur }, child) => {
const el = typeof child === 'function' ? child() : child;
if (!(el instanceof Node)) return el;
if (name) {
el.style.animation = `${name}-in ${duration}ms`;
el._sig_leave = (done) => {
el.style.animation = `${name}-out ${duration}ms`;
el.addEventListener('animationend', done, { once: true });
};
return el;
}
const hasTransform = scale || slide || rotate || blur;
el.style.transition = hasTransform ? `all ${duration}ms` : '';
el.style.opacity = '0';
if (scale) el.style.transform = 'scale(0.95)';
if (slide) el.style.transform = 'translateY(-10px)';
if (rotate) el.style.transform = 'rotate(-2deg)';
if (blur) el.style.filter = 'blur(4px)';
requestAnimationFrame(() => {
el.style.opacity = '1';
el.style.transform = scale || slide || rotate || blur ? '' : 'none';
});
el._sig_leave = (done) => {
el.style.opacity = '0';
el.addEventListener('transitionend', done, { once: true });
};
return el;
};
const each = (src, itemFn, keyFn) => {
const anchor = doc.createTextNode("")
const root = h("div", { style: "display:contents" }, [anchor])
@@ -503,6 +532,52 @@ router.to = p => window.location.hash = p.replace(/^#?\/?/, "#/")
router.back = () => window.history.back()
router.path = () => window.location.hash.replace(/^#/, "") || "/"
const req = ({ url, method = 'GET', headers = {} }) => {
const loading = $(false);
const error = $(null);
const data = $(null);
let controller = null;
let timeoutId = null;
const run = async (body = null) => {
controller?.abort();
clearTimeout(timeoutId);
controller = new AbortController();
timeoutId = setTimeout(() => controller.abort(), 10000);
loading(true);
error(null);
try {
const isFormData = body instanceof FormData;
const res = await fetch(url, {
method,
headers: isFormData ? headers : { 'Content-Type': 'application/json', ...headers },
body: isFormData ? body : (body ? JSON.stringify(body) : undefined),
signal: controller.signal
});
const text = await res.text();
const json = text ? JSON.parse(text) : null;
if (!res.ok) throw new Error(json?.message || res.statusText);
data(json);
return json;
} catch (e) {
if (e.name !== 'AbortError') error(e.message);
throw e;
} finally {
loading(false);
clearTimeout(timeoutId);
controller = null;
timeoutId = null;
}
};
const abort = () => controller?.abort();
return { run, abort, loading, error, data };
};
const mount = (comp, target) => {
const t = typeof target === "string" ? doc.querySelector(target) : target
if (!t) return
@@ -513,7 +588,7 @@ const mount = (comp, target) => {
return inst
}
const SigPro = Object.freeze({ $, $$, watch, h, when, each, router, mount, batch })
const SigPro = Object.freeze({ $, $$, watch, h, when, each, fx, router, req, mount, batch })
if (typeof window !== "undefined") {
Object.assign(window, SigPro)
@@ -523,4 +598,4 @@ if (typeof window !== "undefined") {
})
}
export { $, $$, watch, h, when, each, router, mount, batch }
export { $, $$, watch, h, when, each, fx, router, req, mount, batch }