Files
sigpro-ui/sigpro-helpers.js
natxocc 19279524d7
All checks were successful
Deploy Docs to Synology / deploy (push) Successful in 4s
Restructured
2026-04-27 16:48:06 +02:00

87 lines
3.2 KiB
JavaScript

// Helpers
export const get = val => typeof val === "function" ? val() : val;
export const getBy = (item, field = 'label') => (item && typeof item === 'object') ? item[field] : item;
export const cls = (...classes) => classes.filter(Boolean).join(' ').trim();
export const isFn = f => typeof f === "function";
export const filterBy = (items, query, field = 'label', q = String(query).toLowerCase()) => !query ? get(items) : get(items).filter(item => String(item && typeof item === 'object' ? item[field] : item).toLowerCase().includes(q));
export const rand = (r) => `${r}-${Math.random().toString(36).slice(2, 9)}`
export const close = () => document.activeElement?.blur()
export const listKey = (items, isOpen) => {
const cursor = $(-1);
const onKey = (e, select) => {
const list = get(items), i = cursor(), len = list.length;
if (!len) return;
const k = e.key;
k === 'ArrowDown' ? (e.preventDefault(), isOpen(true), cursor(Math.min(i + 1, len - 1))) :
k === 'ArrowUp' ? (e.preventDefault(), cursor(Math.max(i - 1, 0))) :
k === 'Enter' ? (i >= 0 && (e.preventDefault(), select(list[i]))) :
k === 'Escape' ? (isOpen(false), cursor(-1)) : null;
};
return { cursor, onKey };
};
export 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;
};
export 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 };
};