include modular functions
This commit is contained in:
31
sigpro/fx.js
Normal file
31
sigpro/fx.js
Normal file
@@ -0,0 +1,31 @@
|
||||
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`;
|
||||
return el;
|
||||
}
|
||||
const hasTransform = scale || slide || rotate || blur;
|
||||
const initialTransform = [
|
||||
scale ? "scale(0.95)" : "",
|
||||
slide ? "translateY(-10px)" : "",
|
||||
rotate ? "rotate(-2deg)" : ""
|
||||
].filter(Boolean).join(" ");
|
||||
el.style.transition = `all ${duration}ms ease`;
|
||||
el.style.opacity = "0";
|
||||
if (hasTransform) el.style.transform = initialTransform;
|
||||
if (blur) el.style.filter = "blur(4px)";
|
||||
requestAnimationFrame(() => {
|
||||
el.style.opacity = "1";
|
||||
if (hasTransform) el.style.transform = "none";
|
||||
if (blur) el.style.filter = "none";
|
||||
});
|
||||
return el;
|
||||
};
|
||||
|
||||
// Side-effect global
|
||||
if (typeof window !== "undefined") {
|
||||
window.fx = fx;
|
||||
}
|
||||
|
||||
export { fx };
|
||||
51
sigpro/req.js
Normal file
51
sigpro/req.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import { $ } from './core.js';
|
||||
|
||||
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 };
|
||||
};
|
||||
|
||||
// Side-effect global
|
||||
if (typeof window !== "undefined") {
|
||||
window.req = req;
|
||||
}
|
||||
|
||||
export { req };
|
||||
41
sigpro/router.js
Normal file
41
sigpro/router.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { $, watch, h, onUnmount, isFunc, render } from './core.js';
|
||||
|
||||
const router = routes => {
|
||||
const getHash = () => window.location.hash.slice(1) || "/";
|
||||
const path = $(getHash());
|
||||
const handler = () => path(getHash());
|
||||
window.addEventListener("hashchange", handler);
|
||||
onUnmount(() => window.removeEventListener("hashchange", handler));
|
||||
const hook = h("div", { class: "router-hook" });
|
||||
let currentView = null;
|
||||
watch([path], () => {
|
||||
const cur = path();
|
||||
const route = routes.find(r => {
|
||||
const p1 = r.path.split("/").filter(Boolean);
|
||||
const p2 = cur.split("/").filter(Boolean);
|
||||
return p1.length === p2.length && p1.every((p, i) => p[0] === ":" || p === p2[i]);
|
||||
}) || routes.find(r => r.path === "*");
|
||||
if (route) {
|
||||
currentView?.destroy();
|
||||
const params = {};
|
||||
route.path.split("/").filter(Boolean).forEach((p, i) => {
|
||||
if (p[0] === ":") params[p.slice(1)] = cur.split("/").filter(Boolean)[i];
|
||||
});
|
||||
router.params(params);
|
||||
currentView = render(() => isFunc(route.component) ? route.component(params) : route.component);
|
||||
hook.replaceChildren(currentView.container);
|
||||
}
|
||||
});
|
||||
return hook;
|
||||
};
|
||||
|
||||
router.params = $({});
|
||||
router.to = p => (window.location.hash = p.replace(/^#?\/?/, "#/"));
|
||||
router.back = () => window.history.back();
|
||||
router.path = () => window.location.hash.replace(/^#/, "") || "/";
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
window.router = router;
|
||||
}
|
||||
|
||||
export { router };
|
||||
9
sigpro/tags.js
Normal file
9
sigpro/tags.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { h } from './sigpro.core.js';
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
'a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video'
|
||||
.split(' ').forEach(tag => {
|
||||
window[tag] = (props, children) => h(tag, props, children);
|
||||
});
|
||||
console.log('SigPro tags ready');
|
||||
}
|
||||
19
sigpro/xss.js
Normal file
19
sigpro/xss.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { setAttrFilter } from './sigpro.core.js';
|
||||
|
||||
const DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
||||
const DANGEROUS_URI_ATTRS = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]);
|
||||
const isDangerousAttr = key => DANGEROUS_URI_ATTRS.has(key) || 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 XSS] Locked ${key}`);
|
||||
return '#';
|
||||
}
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
setAttrFilter(validateAttr);
|
||||
Reference in New Issue
Block a user