solved toast

This commit is contained in:
2026-05-14 12:11:02 +02:00
parent 1d71340552
commit 7d8db0192a
9 changed files with 326 additions and 38 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{h as m,watch as x,$ as d,render as b,isF as g}from"./sigpro.js";var l=(t)=>{let e=()=>window.location.hash.slice(1)||"/",o=d(e()),n=()=>o(e());window.addEventListener("hashchange",n);let s=m("div",{class:"router-hook"}),h=null;return x([o],()=>{let f=o(),a=t.find((r)=>{let c=r.path.split("/").filter(Boolean),p=f.split("/").filter(Boolean);return c.length===p.length&&c.every((w,y)=>w[0]===":"||w===p[y])})||t.find((r)=>r.path==="*");if(a){h?.destroy();let r={};a.path.split("/").filter(Boolean).forEach((c,p)=>{if(c[0]===":")r[c.slice(1)]=f.split("/").filter(Boolean)[p]}),l.params(r),h=b(()=>g(a.component)?a.component(r):a.component),s.replaceChildren(h.container)}}),s.destroy=()=>{window.removeEventListener("hashchange",n),h?.destroy()},s};l.params=d({});l.to=(t)=>window.location.hash=t.replace(/^#?\/?/,"#/");l.back=()=>window.history.back();l.path=()=>window.location.hash.replace(/^#/,"")||"/";var v=async(t,e={},o=null)=>{if(o)o(!0);try{let n=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),credentials:"include"});if(!n.ok){let s=await n.text();throw Error(`Error ${n.status}: ${s}`)}return await n.json()}finally{if(o)o(!1)}},u=d("en"),i={},E=(t)=>{for(let e of Object.keys(t)){if(!i[e])i[e]={};Object.assign(i[e],t[e])}},L=(t)=>{if(t&&i[t])u(t)},B=(t)=>{return()=>i[u()]?.[t]??t};export{B as t,L as setLocale,l as router,v as db,E as addLang};
var{$:d,h:m,watch:x,render:g,isF:b}=window.SigPro,l=(t)=>{let e=()=>window.location.hash.slice(1)||"/",o=d(e()),n=()=>o(e());window.addEventListener("hashchange",n);let s=m("div",{class:"router-hook"}),h=null;return x([o],()=>{let w=o(),a=t.find((r)=>{let c=r.path.split("/").filter(Boolean),p=w.split("/").filter(Boolean);return c.length===p.length&&c.every((f,y)=>f[0]===":"||f===p[y])})||t.find((r)=>r.path==="*");if(a){h?.destroy();let r={};a.path.split("/").filter(Boolean).forEach((c,p)=>{if(c[0]===":")r[c.slice(1)]=w.split("/").filter(Boolean)[p]}),l.params(r),h=g(()=>b(a.component)?a.component(r):a.component),s.replaceChildren(h.container)}}),s.destroy=()=>{window.removeEventListener("hashchange",n),h?.destroy()},s};l.params=d({});l.to=(t)=>window.location.hash=t.replace(/^#?\/?/,"#/");l.back=()=>window.history.back();l.path=()=>window.location.hash.replace(/^#/,"")||"/";var k=async(t,e={},o=null)=>{if(o)o(!0);try{let n=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),credentials:"include"});if(!n.ok){let s=await n.text();throw Error(`Error ${n.status}: ${s}`)}return await n.json()}finally{if(o)o(!1)}},u=d("en"),i={},v=(t)=>{for(let e of Object.keys(t)){if(!i[e])i[e]={};Object.assign(i[e],t[e])}},E=(t)=>{if(t&&i[t])u(t)},L=(t)=>{return()=>i[u()]?.[t]??t};export{L as t,E as setLocale,l as router,k as db,v as addLang};

View File

@@ -1,14 +1,14 @@
<div id="ui"></div>
<div id="tab"></div>
<div id="demo-toast"></div>
```js
// ===== SIGNALS =====
const fruta = $("");
const color = $("#3b82f6");
const fecha = $("");
const rango = $({ start: null, end: null });
const pais = $("");
// ===== OPCIONES =====
const frutas = [
"Manzana",
"Pera",
@@ -32,7 +32,7 @@ mount(
div({ class: "p-8 max-w-md mx-auto flex flex-col gap-4" }, [
h1({ class: "text-2xl font-bold" }, "Field Components"),
// Autocomplete simple
ui.autocomplete({
label: "Fruta favorita",
items: frutas,
@@ -40,7 +40,7 @@ mount(
placeholder: "Buscar fruta...",
}),
// Autocomplete con objetos
ui.autocomplete({
label: "País",
items: paises,
@@ -48,14 +48,14 @@ mount(
placeholder: "Elige un país...",
}),
// Datepicker simple
ui.datepicker({
label: "Fecha de nacimiento",
value: fecha,
placeholder: "Selecciona fecha...",
}),
// Datepicker rango
ui.datepicker({
label: "Estancia",
range: true,
@@ -63,7 +63,7 @@ mount(
placeholder: "Check-in → Check-out",
}),
// Colorpicker
ui.colorpicker({
label: "Color favorito",
value: color,
@@ -72,7 +72,7 @@ mount(
ui.password({}),
ui.theme(),
// Preview
div(
{ class: "bg-base-200 rounded-box p-4 flex flex-col gap-2 text-sm" },
[
@@ -97,4 +97,291 @@ mount(
]),
"#ui",
);
const archivos = $([]);
const drag = $(false);
const error = $("");
const subiendo = $(false);
const progreso = $(0);
const MAX_SIZE = 5 * 1024 * 1024;
const MAX_FILES = 3;
const handleFiles = (files) => {
const arr = Array.from(files);
if (arr.length > MAX_FILES) {
error(`Máximo ${MAX_FILES} archivos`);
return;
}
const big = arr.find(f => f.size > MAX_SIZE);
if (big) {
error(`"${big.name}" supera los 5MB`);
return;
}
error("");
archivos(arr);
};
const subirArchivos = async () => {
const files = archivos();
if (!files.length) return toast("Selecciona archivos primero", "alert-warning");
subiendo(true);
progreso(0);
const formData = new FormData();
files.forEach(f => formData.append('files', f));
formData.append('carpeta', 'demo');
try {
const xhr = new XMLHttpRequest();
const promise = new Promise((resolve, reject) => {
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
progreso(Math.round((e.loaded / e.total) * 100));
}
};
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error(`Error ${xhr.status}`));
}
};
xhr.onerror = () => reject(new Error('Error de conexión'));
});
xhr.open('POST', '/api/upload');
xhr.send(formData);
await promise;
toast(`${files.length} archivo(s) subidos`, "alert-success");
archivos([]);
progreso(0);
} catch (err) {
toast(`${err.message}`, "alert-error");
} finally {
subiendo(false);
}
};
const fileInputProps = {
type: "file",
class: "hidden",
multiple: true,
accept: "image/*,.pdf,.doc,.docx",
onchange: (e) => { handleFiles(e.target.files); e.target.value = ''; }
};
mount(
() => div({ class: "p-8 max-w-md mx-auto flex flex-col gap-4" }, [
h1({ class: "text-2xl font-bold" }, "📁 Upload Files"),
// Zona drag & drop
h("label", {
class: () => `relative flex items-center justify-between h-14 px-4 border-2 border-dashed rounded-lg cursor-pointer transition-all ${
drag() ? 'border-primary bg-primary/10' : 'border-base-content/20 bg-base-100'
} ${subiendo() ? 'pointer-events-none opacity-50' : ''}`,
ondragover: (e) => { e.preventDefault(); if (!subiendo()) drag(true); },
ondragleave: () => drag(false),
ondrop: (e) => {
e.preventDefault();
drag(false);
if (subiendo()) return;
handleFiles(e.dataTransfer.files);
}
}, [
h("div", { class: "flex items-center gap-3" }, [
h("span", { class: "icon-[lucide--upload] w-5 h-5 text-base-content/60" }),
h("div", {}, [
h("div", { class: "text-sm font-medium" }, "Arrastra archivos aquí"),
h("div", { class: "text-xs text-base-content/50" }, `Máx ${MAX_FILES} archivos · ${MAX_SIZE / 1024 / 1024}MB c/u`),
]),
]),
h("span", { class: "text-xs text-base-content/40" }, "o haz clic"),
h("input", fileInputProps),
]),
() => error() ? ui.fileError({ message: error() }) : null,
() => archivos().length > 0 ? h("div", { class: "space-y-3" }, [
h("div", { class: "flex flex-wrap gap-2" },
archivos().map((f, i) => {
const isImage = f.type?.startsWith('image/');
const url = isImage ? URL.createObjectURL(f) : null;
return h("div", {
class: "relative group rounded-lg overflow-hidden border border-base-300 bg-base-200 w-20"
}, [
isImage ? h("img", {
src: url,
class: "w-20 h-20 object-cover",
onload: () => url && URL.revokeObjectURL(url)
}) : h("div", {
class: "w-20 h-20 flex flex-col items-center justify-center gap-1"
}, [
h("span", { class: "text-2xl" }, f.type?.includes('pdf') ? "📕" : "📄"),
h("span", { class: "text-[8px] uppercase opacity-50" }, f.name?.split('.').pop()),
]),
h("div", { class: "p-1" }, [
h("div", { class: "text-[9px] truncate font-medium leading-tight" }, f.name),
h("div", { class: "text-[8px] opacity-50" }, `${~~(f.size / 1024)} KB`),
]),
!subiendo() ? h("button", {
class: "absolute top-0.5 right-0.5 btn btn-circle btn-ghost btn-xs opacity-0 group-hover:opacity-100 bg-base-100/80",
onclick: () => archivos(archivos().filter((_, idx) => idx !== i))
}, h("span", { class: "icon-[lucide--x] w-3 h-3" })) : null,
])
})
),
() => subiendo() ? h("div", { class: "space-y-1" }, [
h("div", { class: "flex justify-between text-xs" }, [
h("span", {}, "Subiendo..."),
h("span", {}, () => `${progreso()}%`),
]),
h("progress", { class: "progress progress-primary w-full", value: progreso, max: "100" }),
]) : null,
h("div", { class: "flex gap-2" }, [
h("button", {
class: "btn btn-ghost btn-sm",
onclick: () => { archivos([]); error(""); }
}, "🗑 Limpiar"),
h("button", {
class: "btn btn-primary btn-sm",
disabled: subiendo,
onclick: subirArchivos
}, () => subiendo() ? "⏳ Subiendo..." : "☁️ Subir al servidor"),
]),
]) : null,
]),
"#ui",
);
const tabsSignal = $([
{ id: "a", label: "Tab A", content: "Content of tab A", open: true },
{ id: "b", label: "Tab B", content: "Content of tab B", closable: true },
{ id: "c", label: "Tab C", content: "Content of tab C" },
]);
mount(
() => ui.tabs(
{ class: "tabs-box" },
() => tabsSignal().flatMap((tab, i) =>
ui.tab({
name: "demo-tabs",
classContent: "bg-base-100 border-base-300 p-6",
label: tab.label,
content: tab.content,
checked: tab.open || false,
tabs: tabsSignal,
index: i,
closable: tab.closable || false,
})
)
),
"#tab",
);
mount(
div({ class: "flex flex-wrap gap-2" }, [
button({ class: "btn", onclick: () => toast("File saved!") }, "Simple"),
button(
{ class: "btn", onclick: () => toast("Error!", "alert-error", 5000) },
"Error (5s)",
),
button(
{
class: "btn",
onclick: () =>
toast(
div({ class: "flex items-center gap-2" }, [
span({ class: "icon-[lucide--check] text-lg" }),
span({}, "Report generated"),
]),
"alert-success",
),
},
"With icon",
),
button(
{
class: "btn",
onclick: () =>
toast(
div({ class: "flex flex-col" }, [
strong({}, "ATTENTION!"),
span({}, "Error saving!"),
button(
{
class: "btn btn-xs mt-1",
onclick: () => console.log("Retry"),
},
"Retry",
),
]),
"alert-warning",
7000,
),
},
"Complex",
),
]),
"#toast",
);
mount(
div({ class: "flex flex-wrap gap-2" }, [
button({ class: "btn", onclick: () => toast("File saved!") }, "Simple"),
button(
{ class: "btn", onclick: () => toast("Error!", "alert-error", 5000) },
"Error (5s)",
),
button(
{
class: "btn",
onclick: () =>
toast(
div({ class: "flex items-center gap-2" }, [
ui.icon("icon-[lucide--check]"),
span({}, "Report generated"),
]),
"alert-success",
),
},
"With icon",
),
button(
{
class: "btn",
onclick: () =>
toast(
div({ class: "flex flex-col" }, [
strong({}, "ATTENTION!"),
span({}, "Error saving!"),
button(
{
class: "btn btn-xs mt-1",
onclick: () => console.log("Retry"),
},
"Retry",
),
]),
"alert-warning",
7000,
),
},
"Complex",
),
]),
"#demo-toast",
);
```