simplify components
All checks were successful
Deploy Docs to Synology / deploy (push) Successful in 3s

This commit is contained in:
2026-04-20 13:12:13 +02:00
parent dfd358c950
commit 82fc96fac4
15 changed files with 164 additions and 172 deletions

View File

@@ -14,17 +14,11 @@ import { ui, val, getIcon } from "../utils.js";
* - btn-active, btn-disabled
*/
export const Button = (props, children) => {
const { class: className, loading, icon, ...rest } = props;
const iconEl = getIcon(icon);
const { class: className, ...rest } = props;
return Tag("button", {
...rest,
class: ui('btn', className),
disabled: () => val(loading) || val(props.disabled),
}, () => [
val(loading) && Tag("span", { class: "loading loading-spinner" }),
iconEl,
children
].filter(Boolean));
disabled: () => val(props.disabled),
}, () => children);
};

View File

@@ -1,45 +1,52 @@
import { $$ } from "sigpro";
export const Fetch = ({ url, options = {}, fallback = "Cargando..." }, { children }) => {
const state = $$({ data: null, loading: true, error: null });
let controller = null;
import { $$, Tag, isFunc } from "sigpro";
const run = async () => {
if (controller) controller.abort();
controller = new AbortController();
state.loading = true;
state.error = null;
const _cache = new Map();
try {
const targetUrl = isFunc(url) ? url() : url;
const fetchOpts = {
...(isFunc(options) ? options() : options),
signal: controller.signal
};
const getStore = (key) => {
if (!_cache.has(key)) {
_cache.set(key, $$({ data: null, loading: false, error: null }));
}
return _cache.get(key);
};
const res = await fetch(targetUrl, fetchOpts);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const contentType = res.headers.get("content-type");
state.data = contentType?.includes("application/json")
? await res.json()
: await res.text();
} catch (err) {
if (err.name !== 'AbortError') state.error = err.message;
} finally {
state.loading = false;
}
};
export const Request = async (key, url, opts = {}) => {
const store = getStore(key);
const { body, ...rest } = opts;
store.loading = true;
store.error = null;
if (isFunc(url)) Watch(url, run);
else run();
try {
const config = {
method: rest.method || 'GET',
headers: { 'Content-Type': 'application/json', ...rest.headers },
...rest
};
onUnmount(() => controller?.abort());
if (body) config.body = typeof body === 'object' ? JSON.stringify(body) : body;
return Tag("div", { class: "sigpro-fetch" }, [
const res = await fetch(url, config);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const result = await res.json();
store.data = result;
return result;
} catch (err) {
store.error = err.message;
throw err;
} finally {
store.loading = false;
}
};
export const Response = ({ name, loading, error }, { children }) => {
const store = getStore(name);
return Tag("div", { style: "display:contents" }, [
() => {
if (state.loading) return fallback;
if (state.error) return Tag("span", { style: "color:red" }, state.error);
if (state.data) return isFunc(children[0]) ? children[0](state.data) : children;
if (store.loading) return loading || "Cargando...";
if (store.error) return isFunc(error) ? error(store.error) : Tag("p", {}, store.error);
if (store.data) return isFunc(children[0]) ? children[0](store.data) : children;
return null;
}
]);

View File

@@ -1,53 +0,0 @@
import { $$, Tag, isFunc } from "sigpro";
const _cache = new Map();
const getStore = (key) => {
if (!_cache.has(key)) {
_cache.set(key, $$({ data: null, loading: false, error: null }));
}
return _cache.get(key);
};
export const Request = async (key, url, opts = {}) => {
const store = getStore(key);
const { body, ...rest } = opts;
store.loading = true;
store.error = null;
try {
const config = {
method: rest.method || 'GET',
headers: { 'Content-Type': 'application/json', ...rest.headers },
...rest
};
if (body) config.body = typeof body === 'object' ? JSON.stringify(body) : body;
const res = await fetch(url, config);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const result = await res.json();
store.data = result;
return result;
} catch (err) {
store.error = err.message;
throw err;
} finally {
store.loading = false;
}
};
export const Response = ({ name, loading, error }, { children }) => {
const store = getStore(name);
return Tag("div", { style: "display:contents" }, [
() => {
if (store.loading) return loading || "Cargando...";
if (store.error) return isFunc(error) ? error(store.error) : Tag("p", {}, store.error);
if (store.data) return isFunc(children[0]) ? children[0](store.data) : children;
return null;
}
]);
};

11
src/components/Spinner.js Normal file
View File

@@ -0,0 +1,11 @@
// components/Spinner.js
import { Tag } from "sigpro";
import { val } from "../utils.js";
export const Spinner = (props) => {
const { value, ...rest } = props;
return If(
() => val(value),
() => Tag("span", { class: "loading loading-spinner", ...rest })
);
};