// 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 }; };