new structure
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-button",
|
||||
(props, { emit, slot }) => {
|
||||
const spinner = () => html`
|
||||
<span .class="${() => `loading loading-spinner loading-xs ${props.loading() ? "" : "hidden"}`}"></span>
|
||||
`;
|
||||
|
||||
return html`
|
||||
<div class="${props.tooltip() ? "tooltip" : ""}" data-tip=${() => props.tooltip() ?? ""}>
|
||||
<button
|
||||
class="${() => `btn ${props.ui() ?? ""} ${props.badge() ? "indicator" : ""}`}"
|
||||
?disabled=${() => props.disabled()}
|
||||
@click=${(e) => {
|
||||
e.stopPropagation();
|
||||
if (!props.loading() && !props.disabled()) emit("click", e);
|
||||
}}>
|
||||
${spinner()} ${slot()}
|
||||
${() =>
|
||||
props.badge()
|
||||
? html`
|
||||
<span class="indicator-item badge badge-secondary">${props.badge()}</span>
|
||||
`
|
||||
: null}
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
["ui", "loading", "badge", "tooltip", "disabled"],
|
||||
);
|
||||
@@ -1,26 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-card",
|
||||
(props, host) => {
|
||||
return html`
|
||||
<div class="${() => `card bg-base-100 shadow-sm ${props.ui() ?? ""}`}">
|
||||
${() =>
|
||||
props.img()
|
||||
? html`
|
||||
<figure>
|
||||
<img src="${() => props.img()}" alt="${() => props.alt() ?? "Card image"}" />
|
||||
</figure>
|
||||
`
|
||||
: null}
|
||||
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">${host.slot("title")}</h2>
|
||||
<div class="card-content">${host.slot("body")}</div>
|
||||
<div class="card-actions justify-end">${host.slot("actions")}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
["img", "alt", "ui"],
|
||||
);
|
||||
@@ -1,50 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
const getVal = (props, key, def) => {
|
||||
const v = props[key];
|
||||
if (v === undefined || v === null) return def;
|
||||
if (typeof v === "function") {
|
||||
try {
|
||||
return v();
|
||||
} catch {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
};
|
||||
|
||||
const toString = (val) => {
|
||||
if (val === undefined || val === null) return "";
|
||||
return String(val);
|
||||
};
|
||||
|
||||
$.component(
|
||||
"c-check",
|
||||
(props, { emit }) => {
|
||||
const label = toString(getVal(props, "label", ""));
|
||||
const disabled = getVal(props, "disabled", false);
|
||||
const isToggle = getVal(props, "toggle", false);
|
||||
|
||||
return html`
|
||||
<label class="label cursor-pointer flex gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="${isToggle ? "toggle" : "checkbox"}"
|
||||
?disabled="${disabled}"
|
||||
.checked=${() => getVal(props, "checked", false)}
|
||||
@change="${(e) => {
|
||||
if (disabled) return;
|
||||
const val = e.target.checked;
|
||||
if (typeof props.checked === "function") props.checked(val);
|
||||
emit("change", val);
|
||||
}}" />
|
||||
${label
|
||||
? html`
|
||||
<span class="label-text">${label}</span>
|
||||
`
|
||||
: ""}
|
||||
</label>
|
||||
`;
|
||||
},
|
||||
["label", "checked", "disabled", "toggle"],
|
||||
);
|
||||
@@ -1,65 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
const p1 = ["#000", "#1A1A1A", "#333", "#4D4D4D", "#666", "#808080", "#B3B3B3", "#FFF"];
|
||||
const p2 = ["#450a0a", "#7f1d1d", "#991b1b", "#b91c1c", "#dc2626", "#ef4444", "#f87171", "#fca5a5"];
|
||||
const p3 = ["#431407", "#7c2d12", "#9a3412", "#c2410c", "#ea580c", "#f97316", "#fb923c", "#ffedd5"];
|
||||
const p4 = ["#713f12", "#a16207", "#ca8a04", "#eab308", "#facc15", "#fde047", "#fef08a", "#fff9c4"];
|
||||
const p5 = ["#064e3b", "#065f46", "#059669", "#10b981", "#34d399", "#4ade80", "#84cc16", "#d9f99d"];
|
||||
const p6 = ["#082f49", "#075985", "#0284c7", "#0ea5e9", "#38bdf8", "#7dd3fc", "#22d3ee", "#cffafe"];
|
||||
const p7 = ["#1e1b4b", "#312e81", "#4338ca", "#4f46e5", "#6366f1", "#818cf8", "#a5b4fc", "#e0e7ff"];
|
||||
const p8 = ["#2e1065", "#4c1d95", "#6d28d9", "#7c3aed", "#8b5cf6", "#a855f7", "#d946ef", "#fae8ff"];
|
||||
|
||||
const palette = [...p1, ...p2, ...p3, ...p4, ...p5, ...p6, ...p7, ...p8];
|
||||
|
||||
$.component(
|
||||
"c-colorpicker",
|
||||
(props, { emit }) => {
|
||||
const handleSelect = (c) => {
|
||||
if (typeof props.color === "function") props.color(c);
|
||||
emit("select", c);
|
||||
};
|
||||
|
||||
const getColor = () => props.color() ?? "#000000";
|
||||
|
||||
return html`
|
||||
<div class="card bg-base-200 border-base-300 w-fit border p-2 shadow-sm select-none">
|
||||
<div class="grid grid-cols-8 gap-0.5">
|
||||
${() =>
|
||||
palette.map(
|
||||
(c) => html`
|
||||
<button
|
||||
type="button"
|
||||
.style=${`background-color: ${c}`}
|
||||
.class=${() => {
|
||||
const active = getColor() === c;
|
||||
return `size-5 rounded-xs cursor-pointer transition-all hover:scale-125 hover:z-10 active:scale-90 outline-none border border-black/5 ${
|
||||
active ? "ring-2 ring-offset-1 ring-primary z-10 scale-110" : ""
|
||||
}`;
|
||||
}}
|
||||
@click=${() => handleSelect(c)}></button>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1 mt-2">
|
||||
<input
|
||||
type="text"
|
||||
class="input input-bordered input-xs h-6 px-1 font-mono text-[10px] w-full"
|
||||
.value=${() => props.color()}
|
||||
@input=${(e) => handleSelect(e.target.value)} />
|
||||
|
||||
<div class="tooltip" data-tip="Copiar">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-xs btn-square border border-base-content/20 shadow-inner"
|
||||
.style=${() => `background-color: ${getColor()}`}
|
||||
@click=${() => navigator.clipboard.writeText(getColor())}>
|
||||
<span class="icon-[lucide--copy] text-white mix-blend-difference"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
["color"],
|
||||
);
|
||||
@@ -1,168 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-datepicker",
|
||||
(props, { emit }) => {
|
||||
const viewDate = $(new Date());
|
||||
const hoveredDate = $(null);
|
||||
const todayISO = new Date().toLocaleDateString("en-CA");
|
||||
|
||||
const toISOLocal = (date) => {
|
||||
if (!date) return null;
|
||||
return date.toISOString().split("T")[0];
|
||||
};
|
||||
|
||||
// Función unificada para navegar tiempo
|
||||
const navigate = (type, offset) => {
|
||||
hoveredDate(null);
|
||||
const d = viewDate();
|
||||
if (type === "month") {
|
||||
viewDate(new Date(d.getFullYear(), d.getMonth() + offset, 1));
|
||||
} else if (type === "year") {
|
||||
viewDate(new Date(d.getFullYear() + offset, d.getMonth(), 1));
|
||||
}
|
||||
};
|
||||
|
||||
const selectDate = (dateObj) => {
|
||||
const isoDate = toISOLocal(dateObj);
|
||||
const isRange = props.range() === "true" || props.range() === true;
|
||||
const currentVal = typeof props.value === "function" ? props.value() : props.value;
|
||||
|
||||
let result;
|
||||
if (!isRange) {
|
||||
result = isoDate;
|
||||
} else {
|
||||
const s = currentVal?.start || null;
|
||||
const e = currentVal?.end || null;
|
||||
if (!s || (s && e)) {
|
||||
result = { start: isoDate, end: null };
|
||||
} else {
|
||||
result = isoDate < s ? { start: isoDate, end: s } : { start: s, end: isoDate };
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof props.value === "function") {
|
||||
props.value(isRange ? { ...result } : result);
|
||||
}
|
||||
emit("change", result);
|
||||
};
|
||||
|
||||
const handleGridClick = (e) => {
|
||||
const btn = e.target.closest("button[data-date]");
|
||||
if (!btn) return;
|
||||
selectDate(new Date(btn.getAttribute("data-date")));
|
||||
};
|
||||
|
||||
const days = $(() => {
|
||||
const d = viewDate();
|
||||
const year = d.getFullYear();
|
||||
const month = d.getMonth();
|
||||
const firstDay = new Date(year, month, 1).getDay();
|
||||
const offset = firstDay === 0 ? 6 : firstDay - 1;
|
||||
const total = new Date(year, month + 1, 0).getDate();
|
||||
let grid = Array(offset).fill(null);
|
||||
for (let i = 1; i <= total; i++) grid.push(new Date(year, month, i));
|
||||
return grid;
|
||||
});
|
||||
|
||||
const getWeekNumber = (d) => {
|
||||
const t = new Date(d.valueOf());
|
||||
t.setDate(t.getDate() - ((d.getDay() + 6) % 7) + 3);
|
||||
const firstThurs = t.valueOf();
|
||||
t.setMonth(0, 1);
|
||||
if (t.getDay() !== 4) t.setMonth(0, 1 + ((4 - t.getDay() + 7) % 7));
|
||||
return 1 + Math.ceil((firstThurs - t.getTime()) / 604800000);
|
||||
};
|
||||
|
||||
return html`
|
||||
<div class="card bg-base-100 shadow-xl border border-base-300 w-80 p-4 pb-6 rounded-box select-none">
|
||||
<div class="flex justify-between items-center mb-4 gap-1">
|
||||
<div class="flex gap-0.5">
|
||||
<button type="button" class="btn btn-ghost btn-xs px-1" @click=${() => navigate("year", -1)}>
|
||||
<span class="icon-[lucide--chevrons-left] w-4 h-4 opacity-50"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost btn-xs px-1" @click=${() => navigate("month", -1)}>
|
||||
<span class="icon-[lucide--chevron-left] w-4 h-4"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<span class="text-xs font-bold capitalize flex-1 text-center">
|
||||
${() => viewDate().toLocaleString("es-ES", { month: "long" }).toUpperCase()}
|
||||
<span class="opacity-50 ml-1">${() => viewDate().getFullYear()}</span>
|
||||
</span>
|
||||
|
||||
<div class="flex gap-0.5">
|
||||
<button type="button" class="btn btn-ghost btn-xs px-1" @click=${() => navigate("month", 1)}>
|
||||
<span class="icon-[lucide--chevron-right] w-4 h-4"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost btn-xs px-1" @click=${() => navigate("year", 1)}>
|
||||
<span class="icon-[lucide--chevrons-right] w-4 h-4 opacity-50"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-8 gap-1 px-1" @click=${handleGridClick}>
|
||||
<div class="flex items-center justify-center text-[10px] opacity-40 font-bold uppercase"></div>
|
||||
${() =>
|
||||
["L", "M", "X", "J", "V", "S", "D"].map(
|
||||
(l) => html`
|
||||
<div class="flex items-center justify-center text-[10px] opacity-40 font-bold uppercase">${l}</div>
|
||||
`,
|
||||
)}
|
||||
${() =>
|
||||
days().map((date, i) => {
|
||||
const isFirstCol = i % 7 === 0;
|
||||
const iso = date ? toISOLocal(date) : null;
|
||||
|
||||
const btnClass = () => {
|
||||
if (!date) return "";
|
||||
const val = typeof props.value === "function" ? props.value() : props.value;
|
||||
const isR = props.range() === "true" || props.range() === true;
|
||||
const sDate = isR ? val?.start : typeof val === "string" ? val : val?.start;
|
||||
const eDate = isR ? val?.end : null;
|
||||
const hDate = hoveredDate();
|
||||
|
||||
const isSel = iso === sDate || iso === eDate;
|
||||
const tEnd = eDate || hDate;
|
||||
const inRange = isR && sDate && tEnd && !isSel && ((iso > sDate && iso < tEnd) || (iso < sDate && iso > tEnd));
|
||||
|
||||
return `btn btn-xs p-0 aspect-square min-h-0 h-auto font-normal rounded-md relative
|
||||
${isSel ? "btn-primary !text-primary-content shadow-md" : "btn-ghost"}
|
||||
${inRange ? "!bg-primary/20 !text-base-content" : ""}`;
|
||||
};
|
||||
|
||||
return html`
|
||||
${isFirstCol
|
||||
? html`
|
||||
<div class="flex items-center justify-center text-[10px] opacity-30 italic bg-base-200/50 rounded-md aspect-square">
|
||||
${date ? getWeekNumber(date) : days()[i + 6] ? getWeekNumber(days()[i + 6]) : ""}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${date
|
||||
? html`
|
||||
<button
|
||||
type="button"
|
||||
class="${btnClass}"
|
||||
data-date="${date.toISOString()}"
|
||||
@mouseenter=${() => hoveredDate(iso)}
|
||||
@mouseleave=${() => hoveredDate(null)}>
|
||||
${iso === todayISO
|
||||
? html`
|
||||
<span class="absolute -inset-px border-2 border-primary/60 rounded-md pointer-events-none"></span>
|
||||
`
|
||||
: ""}
|
||||
<span class="relative z-10 pointer-events-none">${date.getDate()}</span>
|
||||
</button>
|
||||
`
|
||||
: html`
|
||||
<div class="aspect-square"></div>
|
||||
`}
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
["range", "value"],
|
||||
);
|
||||
@@ -1,37 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-dialog",
|
||||
(props, { slot, emit }) => {
|
||||
return html`
|
||||
<dialog
|
||||
.class=${() => `modal ${props.open() ? "modal-open" : ""}`}
|
||||
.open=${() => props.open()}
|
||||
@close=${(e) => {
|
||||
if (typeof props.open === "function") props.open(false);
|
||||
emit("close", e);
|
||||
}}>
|
||||
<div class="modal-box">
|
||||
<div class="flex flex-col gap-4">${slot()}</div>
|
||||
|
||||
<div class="modal-action">
|
||||
<form method="dialog" @submit=${() => props.open(false)}>
|
||||
${slot("buttons")}
|
||||
${() =>
|
||||
!slot("buttons").length
|
||||
? html`
|
||||
<button class="btn">Cerrar</button>
|
||||
`
|
||||
: ""}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="dialog" class="modal-backdrop" @submit=${() => props.open(false)}>
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
`;
|
||||
},
|
||||
["open"],
|
||||
);
|
||||
@@ -1,31 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-drawer",
|
||||
(props, { emit, slot }) => {
|
||||
const id = `drawer-${Math.random().toString(36).substring(2, 9)}`;
|
||||
|
||||
return html`
|
||||
<div class="drawer">
|
||||
<input
|
||||
id="${id}"
|
||||
type="checkbox"
|
||||
class="drawer-toggle"
|
||||
.checked=${() => props.open()}
|
||||
@change=${(e) => {
|
||||
const isChecked = e.target.checked;
|
||||
if (typeof props.open === "function") props.open(isChecked);
|
||||
emit("change", isChecked);
|
||||
}} />
|
||||
|
||||
<div class="drawer-content">${slot("content")}</div>
|
||||
|
||||
<div class="drawer-side z-999">
|
||||
<label for="${id}" aria-label="close sidebar" class="drawer-overlay"></label>
|
||||
<div class="bg-base-200 min-h-full w-80">${slot()}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
["open"],
|
||||
);
|
||||
@@ -1,20 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-dropdown",
|
||||
(props, { slot }) => {
|
||||
// Generamos un ID único para el anclaje nativo
|
||||
const id = props.id() ?? `pop-${Math.random().toString(36).slice(2, 7)}`;
|
||||
|
||||
return html`
|
||||
<div class="inline-block">
|
||||
<button class="btn" popovertarget="${id}" style="anchor-name: --${id}">${slot("trigger")}</button>
|
||||
|
||||
<div popover id="${id}" style="position-anchor: --${id}" class="dropdown menu bg-base-100 rounded-box shadow-sm border border-base-300">
|
||||
${slot()}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
["id"],
|
||||
);
|
||||
@@ -1,37 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-fab",
|
||||
(props, { emit }) => {
|
||||
const handleClick = (e, item) => {
|
||||
if (item.onclick) item.onclick(e);
|
||||
emit("select", item);
|
||||
if (document.activeElement instanceof HTMLElement) document.activeElement.blur();
|
||||
};
|
||||
|
||||
return html`
|
||||
<div class="dropdown dropdown-top dropdown-end fixed bottom-6 right-6 z-100">
|
||||
<div tabindex="0" role="button" .class=${() => `btn btn-lg btn-circle btn-primary shadow-2xl ${props.ui() ?? ""}`}>
|
||||
<span class="${() => props["main-icon"]() || "icon-[lucide--plus]"} w-6 h-6"></span>
|
||||
</div>
|
||||
|
||||
<ul tabindex="0" class="dropdown-content menu mb-4 p-0 flex flex-col gap-3 items-center">
|
||||
${() =>
|
||||
(props.actions() || []).map(
|
||||
(item) => html`
|
||||
<li class="p-0">
|
||||
<button
|
||||
.class=${() => `btn btn-circle shadow-lg ${item.ui() ?? "btn-secondary"}`}
|
||||
@click=${(e) => handleClick(e, item)}
|
||||
.title=${item.label}>
|
||||
<span class="${item.icon} w-5 h-5"></span>
|
||||
</button>
|
||||
</li>
|
||||
`,
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
["main-icon", "actions", "ui"],
|
||||
);
|
||||
@@ -1,26 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-input",
|
||||
(props, { slot, emit }) => {
|
||||
return html`
|
||||
<div class="${props.tooltip() ? "tooltip" : ""}" data-tip=${() => props.tooltip() ?? ""}>
|
||||
<label class="floating-label">
|
||||
<span>${() => props.label() ?? ""}</span>
|
||||
<label class=${() => `input ${props.ui() ?? ""}`}>
|
||||
<input
|
||||
type=${() => props.type() ?? "text"}
|
||||
class="input"
|
||||
:value=${props.value}
|
||||
placeholder=${() => props.place() ?? props.label() ?? ""}
|
||||
@input=${(e) => emit("input", e.target.value)}
|
||||
@change=${(e) => emit("change", e.target.value)} />
|
||||
<span>${slot("icon-action")}</span>
|
||||
<span class=${() => props.icon() ?? ""}></span>
|
||||
</label>
|
||||
</label>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
["label", "value", "icon", "tooltip", "ui", "place", "type"],
|
||||
);
|
||||
@@ -1,46 +0,0 @@
|
||||
import { html } from "sigpro";
|
||||
|
||||
export const loading = (show = true, msg = "Cargando...") => {
|
||||
const body = document.body;
|
||||
|
||||
if (!show) {
|
||||
if (loadingEl) {
|
||||
loadingEl.classList.replace("opacity-100", "opacity-0");
|
||||
body.style.removeProperty("overflow"); // Restaurar scroll
|
||||
|
||||
const elToRemove = loadingEl; // Captura para el closure
|
||||
elToRemove.addEventListener(
|
||||
"transitionend",
|
||||
() => {
|
||||
if (elToRemove === loadingEl) {
|
||||
// Solo si sigue siendo el actual
|
||||
elToRemove.remove();
|
||||
loadingEl = null;
|
||||
}
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadingEl?.isConnected) {
|
||||
loadingEl.querySelector(".loading-text").textContent = msg;
|
||||
return;
|
||||
}
|
||||
|
||||
body.style.overflow = "hidden"; // Bloquear scroll
|
||||
|
||||
loadingEl = html`
|
||||
<div
|
||||
class="fixed inset-0 z-9999 flex items-center justify-center bg-base-300/40 backdrop-blur-md transition-opacity duration-300 opacity-0 pointer-events-auto select-none">
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<span class="loading loading-spinner loading-lg text-primary"></span>
|
||||
<span class="loading-text font-bold text-lg text-base-content">${msg}</span>
|
||||
</div>
|
||||
</div>
|
||||
`.firstElementChild;
|
||||
|
||||
body.appendChild(loadingEl);
|
||||
requestAnimationFrame(() => loadingEl.classList.replace("opacity-0", "opacity-100"));
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-menu",
|
||||
(props, { emit }) => {
|
||||
const getItems = () => props.items() || [];
|
||||
|
||||
const renderItems = (data) => {
|
||||
return data.map((item) => {
|
||||
const hasChildren = item.sub && item.sub.length > 0;
|
||||
const content = html`
|
||||
${item.icon
|
||||
? html`
|
||||
<span class="${item.icon} h-4 w-4"></span>
|
||||
`
|
||||
: ""}
|
||||
<span>${item.label}</span>
|
||||
`;
|
||||
|
||||
if (hasChildren) {
|
||||
return html`
|
||||
<li>
|
||||
<details .open="${!!item.open}">
|
||||
<summary>${content}</summary>
|
||||
<ul>
|
||||
${renderItems(item.sub)}
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<li>
|
||||
<a
|
||||
href="${item.href || "#"}"
|
||||
.class=${item.active ? "active" : ""}
|
||||
@click="${(e) => {
|
||||
if (!item.href || item.href === "#") e.preventDefault();
|
||||
if (item.onClick) item.onClick(item);
|
||||
emit("select", item);
|
||||
}}">
|
||||
${content}
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
});
|
||||
};
|
||||
|
||||
return html`
|
||||
<ul .class=${() => `menu bg-base-200 rounded-box w-full ${props.ui() ?? ""}`}>
|
||||
${() => renderItems(getItems())}
|
||||
</ul>
|
||||
`;
|
||||
},
|
||||
["items", "ui"],
|
||||
);
|
||||
@@ -1,28 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-radio",
|
||||
(props, { emit }) => {
|
||||
return html`
|
||||
<label class="label cursor-pointer flex justify-start gap-4">
|
||||
<input
|
||||
type="radio"
|
||||
.name=${() => props.name()}
|
||||
.value=${() => props.value()}
|
||||
.class=${() => `radio ${props.ui() ?? ""}`}
|
||||
.disabled=${() => props.disabled()}
|
||||
.checked=${() => props.checked()}
|
||||
@change=${(e) => {
|
||||
if (e.target.checked) emit("change", props.value());
|
||||
}} />
|
||||
${() =>
|
||||
props.label()
|
||||
? html`
|
||||
<span class="label-text">${() => props.label()}</span>
|
||||
`
|
||||
: ""}
|
||||
</label>
|
||||
`;
|
||||
},
|
||||
["checked", "name", "label", "ui", "disabled", "value"],
|
||||
);
|
||||
@@ -1,24 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-range",
|
||||
(props, { emit }) => {
|
||||
return html`
|
||||
<input
|
||||
type="range"
|
||||
.min=${() => props.min() ?? 0}
|
||||
.max=${() => props.max() ?? 100}
|
||||
.step=${() => props.step() ?? 1}
|
||||
.value=${() => props.value()}
|
||||
.class=${() => `range ${props.ui() ?? ""}`}
|
||||
@input=${(e) => {
|
||||
const val = e.target.value;
|
||||
if (typeof props.value === "function") props.value(val);
|
||||
|
||||
emit("input", val);
|
||||
emit("change", val);
|
||||
}} />
|
||||
`;
|
||||
},
|
||||
["ui", "value", "min", "max", "step"],
|
||||
);
|
||||
@@ -1,34 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-rating",
|
||||
(props, { emit }) => {
|
||||
const count = () => parseInt(props.count() ?? 5);
|
||||
|
||||
const getVal = () => {
|
||||
const v = props.value();
|
||||
return v === false || v == null ? 0 : Number(v);
|
||||
};
|
||||
|
||||
return html`
|
||||
<div .class=${() => `rating ${props.mask() ?? ""}`}>
|
||||
${() =>
|
||||
Array.from({ length: count() }).map((_, i) => {
|
||||
const radioValue = i + 1;
|
||||
return html`
|
||||
<input
|
||||
type="radio"
|
||||
.name=${() => props.name()}
|
||||
.class=${() => `mask ${props.mask() ?? "mask-star"}`}
|
||||
.checked=${() => getVal() === radioValue}
|
||||
@change=${() => {
|
||||
if (typeof props.value === "function") props.value(radioValue);
|
||||
emit("change", radioValue);
|
||||
}} />
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
["value", "count", "name", "mask"],
|
||||
);
|
||||
@@ -1,31 +0,0 @@
|
||||
import { $, html } from "sigpro";
|
||||
|
||||
$.component(
|
||||
"c-tab",
|
||||
(props, { emit, slot }) => {
|
||||
const groupName = `tab-group-${Math.random().toString(36).substring(2, 9)}`;
|
||||
const items = () => props.items() || [];
|
||||
|
||||
return html`
|
||||
<div .class=${() => `tabs ${props.ui() ?? "tabs-lifted"}`}>
|
||||
${() =>
|
||||
items().map(
|
||||
(item) => html`
|
||||
<input
|
||||
type="radio"
|
||||
name="${groupName}"
|
||||
class="tab"
|
||||
.checked=${() => props.value() === item.value}
|
||||
@change=${() => {
|
||||
if (typeof props.value === "function") props.value(item.value);
|
||||
emit("change", item.value);
|
||||
}} />
|
||||
<label class="tab">${item.label}</label>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
<div class="tab-content bg-base-100 border-base-300 p-6">${() => slot(props.value())}</div>
|
||||
`;
|
||||
},
|
||||
["items", "value", "ui"],
|
||||
);
|
||||
@@ -1,49 +0,0 @@
|
||||
import { html } from "sigpro";
|
||||
|
||||
let container = null;
|
||||
|
||||
export const toast = (msg, type = "alert-success", ms = 3500) => {
|
||||
if (!container || !container.isConnected) {
|
||||
container = document.createElement("div");
|
||||
container.className = "fixed top-0 right-0 z-9999 p-6 flex flex-col gap-4 pointer-events-none items-end";
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
const close = (n) => {
|
||||
if (!n || n._c) return;
|
||||
n._c = 1;
|
||||
Object.assign(n.style, { transform: "translateX(100%)", opacity: 0 });
|
||||
|
||||
setTimeout(() => {
|
||||
Object.assign(n.style, { maxHeight: "0px", marginBottom: "-1rem", marginTop: "0px", padding: "0px" });
|
||||
}, 100);
|
||||
|
||||
n.addEventListener("transitionend", (e) => {
|
||||
if (["max-height", "opacity"].includes(e.propertyName)) {
|
||||
n.remove();
|
||||
if (!container.hasChildNodes()) (container.remove(), (container = null));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const el = html`
|
||||
<div
|
||||
class="card bg-base-100 shadow-xl border border-base-200 w-80 sm:w-96 overflow-hidden transition-all duration-500 ease-in-out transform translate-x-full opacity-0 pointer-events-auto"
|
||||
style="max-height:200px">
|
||||
<div class="card-body p-1">
|
||||
<div role="alert" class="${`alert ${type} alert-soft border-none p-2`}">
|
||||
<div class="flex items-center justify-between w-full gap-2">
|
||||
<span class="font-medium text-sm">${msg}</span>
|
||||
<button class="btn btn-ghost btn-xs btn-circle" @click="${(e) => close(e.target.closest(".card"))}">
|
||||
<span class="icon-[lucide--circle-x] w-5 h-5"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`.firstElementChild;
|
||||
|
||||
container.appendChild(el);
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => el.classList.remove("translate-x-full", "opacity-0")));
|
||||
setTimeout(() => close(el), ms);
|
||||
};
|
||||
69
UI/index.js
69
UI/index.js
@@ -1,69 +0,0 @@
|
||||
// index.js
|
||||
import "./sigproui.css";
|
||||
|
||||
import "./components/Button.js";
|
||||
import "./components/Card.js";
|
||||
import "./components/Checkbox.js";
|
||||
import "./components/ColorPicker.js";
|
||||
import "./components/DatePicker.js";
|
||||
import "./components/Dialog.js";
|
||||
import "./components/Drawer.js";
|
||||
import "./components/Dropdown.js";
|
||||
import "./components/Fab.js";
|
||||
import "./components/Input.js";
|
||||
import "./components/Loading.js";
|
||||
import "./components/Menu.js";
|
||||
import "./components/Radio.js";
|
||||
import "./components/Range.js";
|
||||
import "./components/Rating.js";
|
||||
import "./components/Tab.js";
|
||||
import "./components/Toast.js";
|
||||
|
||||
export { default as Button } from "./components/Button.js";
|
||||
export { default as Card } from "./components/Card.js";
|
||||
export { default as Checkbox } from "./components/Checkbox.js";
|
||||
export { default as ColorPicker } from "./components/ColorPicker.js";
|
||||
export { default as DatePicker } from "./components/DatePicker.js";
|
||||
export { default as Dialog } from "./components/Dialog.js";
|
||||
export { default as Drawer } from "./components/Drawer.js";
|
||||
export { default as Dropdown } from "./components/Dropdown.js";
|
||||
export { default as Fab } from "./components/Fab.js";
|
||||
export { default as Input } from "./components/Input.js";
|
||||
export { default as Loading } from "./components/Loading.js";
|
||||
export { default as Menu } from "./components/Menu.js";
|
||||
export { default as Radio } from "./components/Radio.js";
|
||||
export { default as Range } from "./components/Range.js";
|
||||
export { default as Rating } from "./components/Rating.js";
|
||||
export { default as Tab } from "./components/Tab.js";
|
||||
export { default as Toast } from "./components/Toast.js";
|
||||
|
||||
export const components = [
|
||||
"Button",
|
||||
"Card",
|
||||
"Checkbox",
|
||||
"ColorPicker",
|
||||
"DatePicker",
|
||||
"Dialog",
|
||||
"Drawer",
|
||||
"Dropdown",
|
||||
"Fab",
|
||||
"Input",
|
||||
"Loading",
|
||||
"Menu",
|
||||
"Radio",
|
||||
"Range",
|
||||
"Rating",
|
||||
"Tab",
|
||||
"Toast",
|
||||
];
|
||||
|
||||
// Exportar versión
|
||||
export const version = "1.0.0";
|
||||
export const name = "SigProUI";
|
||||
|
||||
export default {
|
||||
version,
|
||||
name,
|
||||
description: "Biblioteca de componentes UI basada en SigPro, Tailwind CSS y DaisyUI",
|
||||
components,
|
||||
};
|
||||
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* SigProUI - Estilos de la biblioteca de componentes UI
|
||||
* Requiere Tailwind CSS y DaisyUI
|
||||
*/
|
||||
|
||||
/* Utilidades personalizadas de SigProUI */
|
||||
.btn-ghost {
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
.floating-label > span {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* Transiciones para componentes */
|
||||
.input {
|
||||
transition: all 0.3s ease-in-out;
|
||||
outline: none;
|
||||
appearance: none;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input:hover {
|
||||
background-color: var(--color-base-300);
|
||||
}
|
||||
|
||||
/* Indicadores y badges */
|
||||
.indicator {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.indicator-item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: translate(50%, -50%);
|
||||
}
|
||||
|
||||
/* Tooltips */
|
||||
.tooltip {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tooltip::after {
|
||||
content: attr(data-tip);
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 0.5rem 1rem;
|
||||
background: oklch(var(--p));
|
||||
color: oklch(var(--pc));
|
||||
border-radius: 0.375rem;
|
||||
font-size: 0.75rem;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.tooltip:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Estados de carga */
|
||||
.loading {
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border: 2px solid currentColor;
|
||||
border-right-color: transparent;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.75s linear infinite;
|
||||
}
|
||||
|
||||
.loading.loading-xs {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user