dropdown ok

This commit is contained in:
2026-04-02 21:40:07 +02:00
parent f0c710f8c2
commit b0629ef3d0
6 changed files with 301 additions and 323 deletions

415
dist/sigpro-ui.js vendored
View File

@@ -266,7 +266,7 @@
runner(); runner();
return runner.stop; return runner.stop;
}; };
var $html = (tag, props = {}, content = []) => { var $html2 = (tag, props = {}, content = []) => {
if (props instanceof Node || Array.isArray(props) || typeof props !== "object") { if (props instanceof Node || Array.isArray(props) || typeof props !== "object") {
content = props; content = props;
props = {}; props = {};
@@ -328,7 +328,7 @@
}; };
var $if = (condition, thenVal, otherwiseVal = null) => { var $if = (condition, thenVal, otherwiseVal = null) => {
const marker = document.createTextNode(""); const marker = document.createTextNode("");
const container = $html("div", { style: "display:contents" }, [marker]); const container = $html2("div", { style: "display:contents" }, [marker]);
let current = null, last = null; let current = null, last = null;
$watch(() => { $watch(() => {
const state = !!(typeof condition === "function" ? condition() : condition); const state = !!(typeof condition === "function" ? condition() : condition);
@@ -348,7 +348,7 @@
$if.not = (condition, thenVal, otherwiseVal) => $if(() => !(typeof condition === "function" ? condition() : condition), thenVal, otherwiseVal); $if.not = (condition, thenVal, otherwiseVal) => $if(() => !(typeof condition === "function" ? condition() : condition), thenVal, otherwiseVal);
var $for = (source, render, keyFn) => { var $for = (source, render, keyFn) => {
const marker = document.createTextNode(""); const marker = document.createTextNode("");
const container = $html("div", { style: "display:contents" }, [marker]); const container = $html2("div", { style: "display:contents" }, [marker]);
let cache = new Map; let cache = new Map;
$watch(() => { $watch(() => {
const items = (typeof source === "function" ? source() : source) || []; const items = (typeof source === "function" ? source() : source) || [];
@@ -385,7 +385,7 @@
var $router = (routes) => { var $router = (routes) => {
const sPath = $(window.location.hash.replace(/^#/, "") || "/"); const sPath = $(window.location.hash.replace(/^#/, "") || "/");
window.addEventListener("hashchange", () => sPath(window.location.hash.replace(/^#/, "") || "/")); window.addEventListener("hashchange", () => sPath(window.location.hash.replace(/^#/, "") || "/"));
const outlet = $html("div", { class: "router-outlet" }); const outlet = $html2("div", { class: "router-outlet" });
let current = null; let current = null;
$watch([sPath], async () => { $watch([sPath], async () => {
const path = sPath(); const path = sPath();
@@ -411,7 +411,7 @@
try { try {
return typeof comp === "function" ? comp(params) : comp; return typeof comp === "function" ? comp(params) : comp;
} catch (e) { } catch (e) {
return $html("div", { class: "p-4 text-error" }, "Error loading view"); return $html2("div", { class: "p-4 text-error" }, "Error loading view");
} }
}); });
outlet.appendChild(current.container); outlet.appendChild(current.container);
@@ -434,7 +434,7 @@
MOUNTED_NODES.set(el, instance); MOUNTED_NODES.set(el, instance);
return instance; return instance;
}; };
var SigProCore = { $, $watch, $html, $if, $for, $router, $mount }; var SigProCore = { $, $watch, $html: $html2, $if, $for, $router, $mount };
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
const install = (registry) => { const install = (registry) => {
Object.keys(registry).forEach((key) => { Object.keys(registry).forEach((key) => {
@@ -444,7 +444,7 @@
tags.forEach((tagName) => { tags.forEach((tagName) => {
const helperName = tagName.charAt(0).toUpperCase() + tagName.slice(1); const helperName = tagName.charAt(0).toUpperCase() + tagName.slice(1);
if (!(helperName in window)) { if (!(helperName in window)) {
window[helperName] = (props, content) => $html(tagName, props, content); window[helperName] = (props, content) => $html2(tagName, props, content);
} }
}); });
window.SigPro = Object.freeze(registry); window.SigPro = Object.freeze(registry);
@@ -509,10 +509,10 @@
if (!icon) if (!icon)
return null; return null;
if (typeof icon === "function") { if (typeof icon === "function") {
return $html("span", { class: "mr-1" }, icon()); return $html2("span", { class: "mr-1" }, icon());
} }
if (typeof icon === "object") { if (typeof icon === "object") {
return $html("span", { class: "mr-1" }, icon); return $html2("span", { class: "mr-1" }, icon);
} }
if (typeof icon === "string") { if (typeof icon === "string") {
const parts = icon.trim().split(/\s+/); const parts = icon.trim().split(/\s+/);
@@ -520,9 +520,9 @@
const iconClass = hasRight ? parts.slice(0, -1).join(" ") : icon; const iconClass = hasRight ? parts.slice(0, -1).join(" ") : icon;
const spacing = hasRight ? "ml-1" : "mr-1"; const spacing = hasRight ? "ml-1" : "mr-1";
if (iconClass && !iconClass.startsWith("icon-[") && !iconClass.includes("--")) { if (iconClass && !iconClass.startsWith("icon-[") && !iconClass.includes("--")) {
return $html("span", { class: spacing }, iconClass); return $html2("span", { class: spacing }, iconClass);
} }
return $html("span", { class: `${iconClass} ${spacing}`.trim() }); return $html2("span", { class: `${iconClass} ${spacing}`.trim() });
} }
return null; return null;
}; };
@@ -530,17 +530,17 @@
// src/components/Accordion.js // src/components/Accordion.js
var Accordion = (props, children) => { var Accordion = (props, children) => {
const { class: className, title, name, open, ...rest } = props; const { class: className, title, name, open, ...rest } = props;
return $html("div", { return $html2("div", {
...rest, ...rest,
class: ui("collapse collapse-arrow bg-base-200 mb-2", className) class: ui("collapse collapse-arrow bg-base-200 mb-2", className)
}, [ }, [
$html("input", { $html2("input", {
type: name ? "radio" : "checkbox", type: name ? "radio" : "checkbox",
name, name,
checked: val(open) checked: val(open)
}), }),
$html("div", { class: "collapse-title text-xl font-medium" }, title), $html2("div", { class: "collapse-title text-xl font-medium" }, title),
$html("div", { class: "collapse-content" }, children) $html2("div", { class: "collapse-content" }, children)
]); ]);
}; };
@@ -561,16 +561,16 @@
const softClass = soft ? "alert-soft" : ""; const softClass = soft ? "alert-soft" : "";
const allClasses = [typeClass, softClass, className].filter(Boolean).join(" "); const allClasses = [typeClass, softClass, className].filter(Boolean).join(" ");
const content = children || props.message; const content = children || props.message;
return $html("div", { return $html2("div", {
...rest, ...rest,
role: "alert", role: "alert",
class: ui("alert", allClasses) class: ui("alert", allClasses)
}, () => [ }, () => [
getIcon(iconMap[type]), getIcon(iconMap[type]),
$html("div", { class: "flex-1" }, [ $html2("div", { class: "flex-1" }, [
$html("span", {}, [typeof content === "function" ? content() : content]) $html2("span", {}, [typeof content === "function" ? content() : content])
]), ]),
actions ? $html("div", { class: "flex-none" }, [ actions ? $html2("div", { class: "flex-none" }, [
typeof actions === "function" ? actions() : actions typeof actions === "function" ? actions() : actions
]) : null ]) : null
].filter(Boolean)); ].filter(Boolean));
@@ -646,8 +646,8 @@
return "btn-lg"; return "btn-lg";
return "btn-md"; return "btn-md";
}; };
return $html("div", { class: "relative w-full" }, () => [ return $html2("div", { class: "relative w-full" }, () => [
$html("input", { $html2("input", {
...rest, ...rest,
type: () => isPassword ? visible() ? "text" : "password" : type, type: () => isPassword ? visible() ? "text" : "password" : type,
placeholder: placeholder || " ", placeholder: placeholder || " ",
@@ -656,10 +656,10 @@
oninput: (e) => oninput?.(e), oninput: (e) => oninput?.(e),
disabled: () => val(disabled) disabled: () => val(disabled)
}), }),
leftIcon ? $html("div", { leftIcon ? $html2("div", {
class: "absolute left-3 inset-y-0 flex items-center pointer-events-none text-base-content/60" class: "absolute left-3 inset-y-0 flex items-center pointer-events-none text-base-content/60"
}, leftIcon) : null, }, leftIcon) : null,
isPassword ? $html("button", { isPassword ? $html2("button", {
type: "button", type: "button",
class: ui("absolute right-3 inset-y-0 flex items-center", "btn btn-ghost btn-circle opacity-50 hover:opacity-100", buttonSize()), class: ui("absolute right-3 inset-y-0 flex items-center", "btn btn-ghost btn-circle opacity-50 hover:opacity-100", buttonSize()),
onclick: (e) => { onclick: (e) => {
@@ -707,7 +707,7 @@
isOpen(false); isOpen(false);
} }
}; };
return $html("div", { class: "relative w-full" }, [ return $html2("div", { class: "relative w-full" }, [
Input({ Input({
label, label,
class: className, class: className,
@@ -726,18 +726,18 @@
}, },
...rest ...rest
}), }),
$html("ul", { $html2("ul", {
class: "absolute left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50", class: "absolute left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",
style: () => isOpen() && list().length ? "display:block" : "display:none" style: () => isOpen() && list().length ? "display:block" : "display:none"
}, [ }, [
$for(list, (opt, i) => $html("li", {}, [ $for(list, (opt, i) => $html2("li", {}, [
$html("a", { $html2("a", {
class: () => `block w-full ${cursor() === i ? "active bg-primary text-primary-content" : ""}`, class: () => `block w-full ${cursor() === i ? "active bg-primary text-primary-content" : ""}`,
onclick: () => pick(opt), onclick: () => pick(opt),
onmouseenter: () => cursor(i) onmouseenter: () => cursor(i)
}, typeof opt === "string" ? opt : opt.label) }, typeof opt === "string" ? opt : opt.label)
]), (opt, i) => (typeof opt === "string" ? opt : opt.value) + i), ]), (opt, i) => (typeof opt === "string" ? opt : opt.value) + i),
() => list().length ? null : $html("li", { class: "p-2 text-center opacity-50" }, tt("nodata")()) () => list().length ? null : $html2("li", { class: "p-2 text-center opacity-50" }, tt("nodata")())
]) ])
]); ]);
}; };
@@ -749,7 +749,7 @@
}); });
var Badge = (props, children) => { var Badge = (props, children) => {
const { class: className, ...rest } = props; const { class: className, ...rest } = props;
return $html("span", { return $html2("span", {
...rest, ...rest,
class: ui("badge", className) class: ui("badge", className)
}, children); }, children);
@@ -763,12 +763,12 @@
var Button = (props, children) => { var Button = (props, children) => {
const { class: className, loading, icon, ...rest } = props; const { class: className, loading, icon, ...rest } = props;
const iconEl = getIcon(icon); const iconEl = getIcon(icon);
return $html("button", { return $html2("button", {
...rest, ...rest,
class: ui("btn", className), class: ui("btn", className),
disabled: () => val(loading) || val(props.disabled) disabled: () => val(loading) || val(props.disabled)
}, () => [ }, () => [
val(loading) && $html("span", { class: "loading loading-spinner" }), val(loading) && $html2("span", { class: "loading loading-spinner" }),
iconEl, iconEl,
children children
].filter(Boolean)); ].filter(Boolean));
@@ -781,17 +781,17 @@
}); });
var Checkbox = (props) => { var Checkbox = (props) => {
const { class: className, value, tooltip, toggle, label, ...rest } = props; const { class: className, value, tooltip, toggle, label, ...rest } = props;
const checkEl = $html("input", { const checkEl = $html2("input", {
...rest, ...rest,
type: "checkbox", type: "checkbox",
class: () => ui(val(toggle) ? "toggle" : "checkbox", className), class: () => ui(val(toggle) ? "toggle" : "checkbox", className),
checked: value checked: value
}); });
const layout = $html("label", { class: "label cursor-pointer justify-start gap-3" }, [ const layout = $html2("label", { class: "label cursor-pointer justify-start gap-3" }, [
checkEl, checkEl,
label ? $html("span", { class: "label-text" }, label) : null label ? $html2("span", { class: "label-text" }, label) : null
]); ]);
return tooltip ? $html("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout; return tooltip ? $html2("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout;
}; };
// src/components/Colorpicker.js // src/components/Colorpicker.js
@@ -813,8 +813,8 @@
...["#2e1065", "#4c1d95", "#6d28d9", "#7c3aed", "#8b5cf6", "#a855f7", "#d946ef", "#fae8ff"] ...["#2e1065", "#4c1d95", "#6d28d9", "#7c3aed", "#8b5cf6", "#a855f7", "#d946ef", "#fae8ff"]
]; ];
const getColor = () => val(value) || "#000000"; const getColor = () => val(value) || "#000000";
return $html("div", { class: ui("relative w-fit", className) }, [ return $html2("div", { class: ui("relative w-fit", className) }, [
$html("button", { $html2("button", {
type: "button", type: "button",
class: "btn px-3 bg-base-100 border-base-300 hover:border-primary/50 flex items-center gap-2 shadow-sm font-normal normal-case", class: "btn px-3 bg-base-100 border-base-300 hover:border-primary/50 flex items-center gap-2 shadow-sm font-normal normal-case",
onclick: (e) => { onclick: (e) => {
@@ -823,17 +823,17 @@
}, },
...rest ...rest
}, [ }, [
$html("div", { $html2("div", {
class: "size-5 rounded-sm shadow-inner border border-black/10 shrink-0", class: "size-5 rounded-sm shadow-inner border border-black/10 shrink-0",
style: () => `background-color: ${getColor()}` style: () => `background-color: ${getColor()}`
}), }),
label ? $html("span", { class: "opacity-80" }, label) : null label ? $html2("span", { class: "opacity-80" }, label) : null
]), ]),
$if(isOpen, () => $html("div", { $if(isOpen, () => $html2("div", {
class: "absolute left-0 mt-2 p-3 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[110] w-64 select-none", class: "absolute left-0 mt-2 p-3 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[110] w-64 select-none",
onclick: (e) => e.stopPropagation() onclick: (e) => e.stopPropagation()
}, [ }, [
$html("div", { class: "grid grid-cols-8 gap-1" }, palette.map((c) => $html("button", { $html2("div", { class: "grid grid-cols-8 gap-1" }, palette.map((c) => $html2("button", {
type: "button", type: "button",
style: `background-color: ${c}`, style: `background-color: ${c}`,
class: () => { class: () => {
@@ -848,7 +848,7 @@
} }
}))) })))
])), ])),
$if(isOpen, () => $html("div", { $if(isOpen, () => $html2("div", {
class: "fixed inset-0 z-[100]", class: "fixed inset-0 z-[100]",
onclick: () => isOpen(false) onclick: () => isOpen(false)
})) }))
@@ -936,9 +936,9 @@
internalDate(new Date(d.getFullYear() + y, d.getMonth(), 1)); internalDate(new Date(d.getFullYear() + y, d.getMonth(), 1));
}; };
const HourSlider = ({ value: hVal, onChange }) => { const HourSlider = ({ value: hVal, onChange }) => {
return $html("div", { class: "flex-1" }, [ return $html2("div", { class: "flex-1" }, [
$html("div", { class: "flex gap-2 items-center" }, [ $html2("div", { class: "flex gap-2 items-center" }, [
$html("input", { $html2("input", {
type: "range", type: "range",
min: 0, min: 0,
max: 23, max: 23,
@@ -949,11 +949,11 @@
onChange(newHour); onChange(newHour);
} }
}), }),
$html("span", { class: "text-sm font-mono min-w-[48px] text-center" }, () => String(val(hVal)).padStart(2, "0") + ":00") $html2("span", { class: "text-sm font-mono min-w-[48px] text-center" }, () => String(val(hVal)).padStart(2, "0") + ":00")
]) ])
]); ]);
}; };
return $html("div", { class: ui("relative w-full", className) }, [ return $html2("div", { class: ui("relative w-full", className) }, [
Input({ Input({
label, label,
placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha..."), placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha..."),
@@ -966,25 +966,25 @@
}, },
...rest ...rest
}), }),
$if(isOpen, () => $html("div", { $if(isOpen, () => $html2("div", {
class: "absolute left-0 mt-2 p-4 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[100] w-80 select-none", class: "absolute left-0 mt-2 p-4 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[100] w-80 select-none",
onclick: (e) => e.stopPropagation() onclick: (e) => e.stopPropagation()
}, [ }, [
$html("div", { class: "flex justify-between items-center mb-4 gap-1" }, [ $html2("div", { class: "flex justify-between items-center mb-4 gap-1" }, [
$html("div", { class: "flex gap-0.5" }, [ $html2("div", { class: "flex gap-0.5" }, [
$html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) }, getIcon("icon-[lucide--chevrons-left]")), $html2("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) }, getIcon("icon-[lucide--chevrons-left]")),
$html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) }, getIcon("icon-[lucide--chevron-left]")) $html2("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) }, getIcon("icon-[lucide--chevron-left]"))
]), ]),
$html("span", { class: "font-bold uppercase flex-1 text-center" }, [ $html2("span", { class: "font-bold uppercase flex-1 text-center" }, [
() => internalDate().toLocaleString("es-ES", { month: "short", year: "numeric" }) () => internalDate().toLocaleString("es-ES", { month: "short", year: "numeric" })
]), ]),
$html("div", { class: "flex gap-0.5" }, [ $html2("div", { class: "flex gap-0.5" }, [
$html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) }, getIcon("icon-[lucide--chevron-right]")), $html2("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) }, getIcon("icon-[lucide--chevron-right]")),
$html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) }, getIcon("icon-[lucide--chevrons-right]")) $html2("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) }, getIcon("icon-[lucide--chevrons-right]"))
]) ])
]), ]),
$html("div", { class: "grid grid-cols-7 gap-1", onmouseleave: () => hoverDate(null) }, [ $html2("div", { class: "grid grid-cols-7 gap-1", onmouseleave: () => hoverDate(null) }, [
...["L", "M", "X", "J", "V", "S", "D"].map((d) => $html("div", { class: "text-[10px] opacity-40 font-bold text-center" }, d)), ...["L", "M", "X", "J", "V", "S", "D"].map((d) => $html2("div", { class: "text-[10px] opacity-40 font-bold text-center" }, d)),
() => { () => {
const d = internalDate(); const d = internalDate();
const year = d.getFullYear(); const year = d.getFullYear();
@@ -994,11 +994,11 @@
const daysInMonth = new Date(year, month + 1, 0).getDate(); const daysInMonth = new Date(year, month + 1, 0).getDate();
const nodes = []; const nodes = [];
for (let i = 0;i < offset; i++) for (let i = 0;i < offset; i++)
nodes.push($html("div")); nodes.push($html2("div"));
for (let i = 1;i <= daysInMonth; i++) { for (let i = 1;i <= daysInMonth; i++) {
const date = new Date(year, month, i); const date = new Date(year, month, i);
const dStr = formatDate(date); const dStr = formatDate(date);
nodes.push($html("button", { nodes.push($html2("button", {
type: "button", type: "button",
class: () => { class: () => {
const v = val(value); const v = val(value);
@@ -1029,8 +1029,8 @@
return nodes; return nodes;
} }
]), ]),
hour ? $html("div", { class: "mt-3 pt-2 border-t border-base-300" }, [ hour ? $html2("div", { class: "mt-3 pt-2 border-t border-base-300" }, [
isRangeMode() ? $html("div", { class: "flex gap-4" }, [ isRangeMode() ? $html2("div", { class: "flex gap-4" }, [
HourSlider({ HourSlider({
value: startHour, value: startHour,
onChange: (newHour) => { onChange: (newHour) => {
@@ -1061,7 +1061,7 @@
}) })
]) : null ]) : null
])), ])),
$if(isOpen, () => $html("div", { class: "fixed inset-0 z-[90]", onclick: () => isOpen(false) })) $if(isOpen, () => $html2("div", { class: "fixed inset-0 z-[90]", onclick: () => isOpen(false) }))
]); ]);
}; };
@@ -1073,11 +1073,11 @@
var Drawer = (props, children) => { var Drawer = (props, children) => {
const { class: className, id, open, side, content, ...rest } = props; const { class: className, id, open, side, content, ...rest } = props;
const drawerId = id || `drawer-${Math.random().toString(36).slice(2, 9)}`; const drawerId = id || `drawer-${Math.random().toString(36).slice(2, 9)}`;
return $html("div", { return $html2("div", {
...rest, ...rest,
class: ui("drawer", className) class: ui("drawer", className)
}, [ }, [
$html("input", { $html2("input", {
id: drawerId, id: drawerId,
type: "checkbox", type: "checkbox",
class: "drawer-toggle", class: "drawer-toggle",
@@ -1087,11 +1087,11 @@
open(e.target.checked); open(e.target.checked);
} }
}), }),
$html("div", { class: "drawer-content" }, [ $html2("div", { class: "drawer-content" }, [
typeof content === "function" ? content() : content typeof content === "function" ? content() : content
]), ]),
$html("div", { class: "drawer-side" }, [ $html2("div", { class: "drawer-side" }, [
$html("label", { $html2("label", {
for: drawerId, for: drawerId,
class: "drawer-overlay", class: "drawer-overlay",
onclick: () => { onclick: () => {
@@ -1099,7 +1099,7 @@
open(false); open(false);
} }
}), }),
$html("div", { class: "min-h-full bg-base-200 w-80" }, [ $html2("div", { class: "min-h-full bg-base-200 w-80" }, [
typeof side === "function" ? side() : side typeof side === "function" ? side() : side
]) ])
]) ])
@@ -1111,51 +1111,64 @@
__export(exports_Dropdown, { __export(exports_Dropdown, {
Dropdown: () => Dropdown Dropdown: () => Dropdown
}); });
var Dropdown = (props, children) => { var currentOpen = null;
if (typeof window !== "undefined" && !window.__dropdownHandlerRegistered) {
window.addEventListener("click", (e) => {
if (currentOpen && !currentOpen.contains(e.target)) {
currentOpen.open = false;
currentOpen = null;
}
});
window.__dropdownHandlerRegistered = true;
}
var Dropdown = (props) => {
const { class: className, label, icon, items, ...rest } = props; const { class: className, label, icon, items, ...rest } = props;
const renderContent = () => { return $html("details", {
if (items) { ...rest,
const source = typeof items === "function" ? items : () => items; class: ui("dropdown", className)
return $html("ul", { }, [
tabindex: 0, $html("summary", {
class: "btn m-1 flex items-center gap-2 list-none cursor-pointer",
style: "display: inline-flex;",
onclick: (e) => {
const details = e.currentTarget.closest("details");
if (currentOpen && currentOpen !== details) {
currentOpen.open = false;
}
setTimeout(() => {
currentOpen = details.open ? details : null;
}, 0);
}
}, [
() => icon ? typeof icon === "function" ? icon() : icon : null,
() => label ? typeof label === "function" ? label() : label : null
]),
$html("ul", {
tabindex: "-1",
class: "dropdown-content z-[50] menu p-2 shadow bg-base-100 rounded-box w-52 border border-base-300" class: "dropdown-content z-[50] menu p-2 shadow bg-base-100 rounded-box w-52 border border-base-300"
}, [ }, [
$for(source, (item) => $html("li", {}, [ () => {
const currentItems = typeof items === "function" ? items() : items || [];
return currentItems.map((item) => $html("li", {}, [
$html("a", { $html("a", {
class: item.class || "", class: item.class || "",
onclick: (e) => { onclick: (e) => {
if (item.onclick) if (item.onclick)
item.onclick(e); item.onclick(e);
if (document.activeElement) const details = e.currentTarget.closest("details");
document.activeElement.blur(); if (details) {
details.open = false;
if (currentOpen === details)
currentOpen = null;
}
} }
}, [ }, [
item.icon ? $html("span", {}, item.icon) : null, item.icon ? $html("span", {}, item.icon) : null,
$html("span", {}, item.label) $html("span", {}, item.label)
]) ])
])) ]));
]);
} }
return $html("div", { ])
tabindex: 0,
class: "dropdown-content z-[50] p-2 shadow bg-base-100 rounded-box min-w-max border border-base-300"
}, [
typeof children === "function" ? children() : children
]);
};
return $html("div", {
...rest,
class: ui("dropdown", className)
}, [
$html("div", {
tabindex: 0,
role: "button",
class: "btn m-1 flex items-center gap-2"
}, [
icon ? typeof icon === "function" ? icon() : icon : null,
label ? typeof label === "function" ? label() : label : null
]),
renderContent()
]); ]);
}; };
@@ -1166,11 +1179,11 @@
}); });
var Fab = (props) => { var Fab = (props) => {
const { class: className, icon, label, actions = [], position = "bottom-6 right-6", ...rest } = props; const { class: className, icon, label, actions = [], position = "bottom-6 right-6", ...rest } = props;
return $html("div", { return $html2("div", {
...rest, ...rest,
class: ui(`fab absolute ${position} flex flex-col-reverse items-end gap-3 z-[100]`, className) class: ui(`fab absolute ${position} flex flex-col-reverse items-end gap-3 z-[100]`, className)
}, [ }, [
$html("div", { $html2("div", {
tabindex: 0, tabindex: 0,
role: "button", role: "button",
class: "btn btn-lg btn-circle btn-primary shadow-2xl" class: "btn btn-lg btn-circle btn-primary shadow-2xl"
@@ -1178,9 +1191,9 @@
icon ? getIcon(icon) : null, icon ? getIcon(icon) : null,
!icon && label ? label : null !icon && label ? label : null
]), ]),
...val(actions).map((act) => $html("div", { class: "flex items-center gap-3 transition-all duration-300" }, [ ...val(actions).map((act) => $html2("div", { class: "flex items-center gap-3 transition-all duration-300" }, [
act.label ? $html("span", { class: "badge badge-ghost shadow-sm whitespace-nowrap" }, act.label) : null, act.label ? $html2("span", { class: "badge badge-ghost shadow-sm whitespace-nowrap" }, act.label) : null,
$html("button", { $html2("button", {
type: "button", type: "button",
class: `btn btn-circle shadow-lg ${act.class || ""}`, class: `btn btn-circle shadow-lg ${act.class || ""}`,
onclick: (e) => { onclick: (e) => {
@@ -1199,13 +1212,13 @@
}); });
var Fieldset = (props, children) => { var Fieldset = (props, children) => {
const { class: className, legend, ...rest } = props; const { class: className, legend, ...rest } = props;
return $html("fieldset", { return $html2("fieldset", {
...rest, ...rest,
class: ui("fieldset bg-base-200 border border-base-300 p-4 rounded-lg", className) class: ui("fieldset bg-base-200 border border-base-300 p-4 rounded-lg", className)
}, [ }, [
() => { () => {
const legendText = val(legend); const legendText = val(legend);
return legendText ? $html("legend", { class: "fieldset-legend font-bold" }, [legendText]) : null; return legendText ? $html2("legend", { class: "fieldset-legend font-bold" }, [legendText]) : null;
}, },
children children
]); ]);
@@ -1238,12 +1251,12 @@
selectedFiles(updated); selectedFiles(updated);
onSelect?.(updated); onSelect?.(updated);
}; };
return $html("fieldset", { ...rest, class: ui("fieldset w-full p-0", className) }, [ return $html2("fieldset", { ...rest, class: ui("fieldset w-full p-0", className) }, [
$html("div", { $html2("div", {
class: () => `w-full ${tooltip ? "tooltip tooltip-top before:z-50 after:z-50" : ""}`, class: () => `w-full ${tooltip ? "tooltip tooltip-top before:z-50 after:z-50" : ""}`,
"data-tip": tooltip "data-tip": tooltip
}, [ }, [
$html("label", { $html2("label", {
class: () => ` class: () => `
relative flex items-center justify-between w-full h-12 px-4 relative flex items-center justify-between w-full h-12 px-4
border-2 border-dashed rounded-lg cursor-pointer border-2 border-dashed rounded-lg cursor-pointer
@@ -1261,12 +1274,12 @@
handleFiles(e.dataTransfer.files); handleFiles(e.dataTransfer.files);
} }
}, [ }, [
$html("div", { class: "flex items-center gap-3 w-full" }, [ $html2("div", { class: "flex items-center gap-3 w-full" }, [
getIcon("icon-[lucide--upload]"), getIcon("icon-[lucide--upload]"),
$html("span", { class: "text-sm opacity-70 truncate grow text-left" }, "Arrastra o selecciona archivos..."), $html2("span", { class: "text-sm opacity-70 truncate grow text-left" }, "Arrastra o selecciona archivos..."),
$html("span", { class: "text-[10px] opacity-40 shrink-0" }, `Máx ${max}MB`) $html2("span", { class: "text-[10px] opacity-40 shrink-0" }, `Máx ${max}MB`)
]), ]),
$html("input", { $html2("input", {
type: "file", type: "file",
multiple: true, multiple: true,
accept, accept,
@@ -1275,15 +1288,15 @@
}) })
]) ])
]), ]),
() => error() ? $html("span", { class: "text-[10px] text-error mt-1 px-1 font-medium" }, error()) : null, () => error() ? $html2("span", { class: "text-[10px] text-error mt-1 px-1 font-medium" }, error()) : null,
$if(() => selectedFiles().length > 0, () => $html("ul", { class: "mt-2 space-y-1" }, [ $if(() => selectedFiles().length > 0, () => $html2("ul", { class: "mt-2 space-y-1" }, [
$for(selectedFiles, (file, index) => $html("li", { class: "flex items-center justify-between p-1.5 pl-3 text-xs bg-base-200/50 rounded-md border border-base-300" }, [ $for(selectedFiles, (file, index) => $html2("li", { class: "flex items-center justify-between p-1.5 pl-3 text-xs bg-base-200/50 rounded-md border border-base-300" }, [
$html("div", { class: "flex items-center gap-2 truncate" }, [ $html2("div", { class: "flex items-center gap-2 truncate" }, [
$html("span", { class: "opacity-50" }, "\uD83D\uDCC4"), $html2("span", { class: "opacity-50" }, "\uD83D\uDCC4"),
$html("span", { class: "truncate font-medium max-w-[200px]" }, file.name), $html2("span", { class: "truncate font-medium max-w-[200px]" }, file.name),
$html("span", { class: "text-[9px] opacity-40" }, `(${(file.size / 1024).toFixed(0)} KB)`) $html2("span", { class: "text-[9px] opacity-40" }, `(${(file.size / 1024).toFixed(0)} KB)`)
]), ]),
$html("button", { $html2("button", {
type: "button", type: "button",
class: "btn btn-ghost btn-xs btn-circle", class: "btn btn-ghost btn-xs btn-circle",
onclick: (e) => { onclick: (e) => {
@@ -1302,9 +1315,9 @@
__export(exports_Indicator, { __export(exports_Indicator, {
Indicator: () => Indicator Indicator: () => Indicator
}); });
var Indicator = (props, children) => $html("div", { class: "indicator" }, () => [ var Indicator = (props, children) => $html2("div", { class: "indicator" }, () => [
children, children,
props.value && $html("span", { class: ui("indicator-item badge", props.class) }, props.value) props.value && $html2("span", { class: ui("indicator-item badge", props.class) }, props.value)
].filter(Boolean)); ].filter(Boolean));
// src/components/Label.js // src/components/Label.js
@@ -1315,16 +1328,16 @@
var Label = (props) => { var Label = (props) => {
const { children, value, floating = false, error, required, class: className, ...rest } = props; const { children, value, floating = false, error, required, class: className, ...rest } = props;
if (floating) { if (floating) {
return $html("label", { class: ui("floating-label w-full", className), ...rest }, () => [ return $html2("label", { class: ui("floating-label w-full", className), ...rest }, () => [
value ? $html("span", {}, value) : null, value ? $html2("span", {}, value) : null,
children, children,
error ? $html("span", { class: "text-error text-xs" }, val(error)) : null error ? $html2("span", { class: "text-error text-xs" }, val(error)) : null
]); ]);
} }
return $html("label", { class: ui("input w-full", className), ...rest }, () => [ return $html2("label", { class: ui("input w-full", className), ...rest }, () => [
value ? $html("span", { class: "label" }, value) : null, value ? $html2("span", { class: "label" }, value) : null,
children, children,
error ? $html("span", { class: "text-error text-xs" }, val(error)) : null error ? $html2("span", { class: "text-error text-xs" }, val(error)) : null
]); ]);
}; };
@@ -1335,11 +1348,11 @@
}); });
var List = (props) => { var List = (props) => {
const { class: className, items, header, render, keyFn = (item, index) => index, ...rest } = props; const { class: className, items, header, render, keyFn = (item, index) => index, ...rest } = props;
const listItems = $for(items, (item, index) => $html("li", { class: "list-row" }, [render(item, index)]), keyFn); const listItems = $for(items, (item, index) => $html2("li", { class: "list-row" }, [render(item, index)]), keyFn);
return $html("ul", { return $html2("ul", {
...rest, ...rest,
class: ui("list bg-base-100 rounded-box shadow-md", className) class: ui("list bg-base-100 rounded-box shadow-md", className)
}, header ? [$if(header, () => $html("li", { class: "p-4 pb-2 text-xs opacity-60" }, [val(header)])), listItems] : listItems); }, header ? [$if(header, () => $html2("li", { class: "p-4 pb-2 text-xs opacity-60" }, [val(header)])), listItems] : listItems);
}; };
// src/components/Loading.js // src/components/Loading.js
@@ -1348,10 +1361,10 @@
Loading: () => Loading Loading: () => Loading
}); });
var Loading = (props) => { var Loading = (props) => {
return $if(props.$show, () => $html("div", { return $if(props.$show, () => $html2("div", {
class: "fixed inset-0 z-[100] flex items-center justify-center backdrop-blur-sm bg-base-100/30" class: "fixed inset-0 z-[100] flex items-center justify-center backdrop-blur-sm bg-base-100/30"
}, [ }, [
$html("span", { class: "loading loading-spinner loading-lg text-primary" }) $html2("span", { class: "loading loading-spinner loading-lg text-primary" })
])); ]));
}; };
@@ -1362,16 +1375,16 @@
}); });
var Menu = (props) => { var Menu = (props) => {
const { class: className, items, ...rest } = props; const { class: className, items, ...rest } = props;
const renderItems = (items2) => $for(() => items2 || [], (it) => $html("li", {}, [ const renderItems = (items2) => $for(() => items2 || [], (it) => $html2("li", {}, [
it.children ? $html("details", { open: it.open }, [ it.children ? $html2("details", { open: it.open }, [
$html("summary", {}, [it.icon && $html("span", { class: "mr-2" }, it.icon), it.label]), $html2("summary", {}, [it.icon && $html2("span", { class: "mr-2" }, it.icon), it.label]),
$html("ul", {}, renderItems(it.children)) $html2("ul", {}, renderItems(it.children))
]) : $html("a", { class: () => val(it.active) ? "active" : "", onclick: it.onclick }, [ ]) : $html2("a", { class: () => val(it.active) ? "active" : "", onclick: it.onclick }, [
it.icon && $html("span", { class: "mr-2" }, it.icon), it.icon && $html2("span", { class: "mr-2" }, it.icon),
it.label it.label
]) ])
]), (it, i) => it.label || i); ]), (it, i) => it.label || i);
return $html("ul", { ...rest, class: ui("menu bg-base-200 rounded-box", className) }, renderItems(items)); return $html2("ul", { ...rest, class: ui("menu bg-base-200 rounded-box", className) }, renderItems(items));
}; };
// src/components/Modal.js // src/components/Modal.js
@@ -1400,28 +1413,28 @@
if (typeof open === "function") if (typeof open === "function")
open(false); open(false);
}; };
return $html("dialog", { return $html2("dialog", {
...rest, ...rest,
ref: dialogRef, ref: dialogRef,
class: ui("modal", className), class: ui("modal", className),
oncancel: () => typeof open === "function" && open(false) oncancel: () => typeof open === "function" && open(false)
}, [ }, [
$html("div", { class: "modal-box" }, [ $html2("div", { class: "modal-box" }, [
title ? $html("h3", { class: "text-lg font-bold mb-4" }, title) : null, title ? $html2("h3", { class: "text-lg font-bold mb-4" }, title) : null,
$html("div", { class: "py-2" }, [ $html2("div", { class: "py-2" }, [
typeof children === "function" ? children() : children typeof children === "function" ? children() : children
]), ]),
$html("div", { class: "modal-action flex gap-2" }, [ $html2("div", { class: "modal-action flex gap-2" }, [
...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean), ...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean),
Button({ type: "button", onclick: close }, tt("close")()) Button({ type: "button", onclick: close }, tt("close")())
]) ])
]), ]),
$html("form", { $html2("form", {
method: "dialog", method: "dialog",
class: "modal-backdrop", class: "modal-backdrop",
onsubmit: close onsubmit: close
}, [ }, [
$html("button", {}, "close") $html2("button", {}, "close")
]) ])
]); ]);
}; };
@@ -1433,7 +1446,7 @@
}); });
var Navbar = (props, children) => { var Navbar = (props, children) => {
const { class: className, ...rest } = props; const { class: className, ...rest } = props;
return $html("div", { ...rest, class: ui("navbar bg-base-100 shadow-sm px-4", className) }, children); return $html2("div", { ...rest, class: ui("navbar bg-base-100 shadow-sm px-4", className) }, children);
}; };
// src/components/Radio.js // src/components/Radio.js
@@ -1443,7 +1456,7 @@
}); });
var Radio = (props) => { var Radio = (props) => {
const { class: className, label, tooltip, value, inputValue, name, ...rest } = props; const { class: className, label, tooltip, value, inputValue, name, ...rest } = props;
const radioEl = $html("input", { const radioEl = $html2("input", {
...rest, ...rest,
type: "radio", type: "radio",
name, name,
@@ -1456,11 +1469,11 @@
}); });
if (!label && !tooltip) if (!label && !tooltip)
return radioEl; return radioEl;
const layout = $html("label", { class: "label cursor-pointer justify-start gap-3" }, [ const layout = $html2("label", { class: "label cursor-pointer justify-start gap-3" }, [
radioEl, radioEl,
label ? $html("span", { class: "label-text" }, label) : null label ? $html2("span", { class: "label-text" }, label) : null
]); ]);
return tooltip ? $html("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout; return tooltip ? $html2("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout;
}; };
// src/components/Range.js // src/components/Range.js
@@ -1470,7 +1483,7 @@
}); });
var Range = (props) => { var Range = (props) => {
const { class: className, label, tooltip, value, ...rest } = props; const { class: className, label, tooltip, value, ...rest } = props;
const rangeEl = $html("input", { const rangeEl = $html2("input", {
...rest, ...rest,
type: "range", type: "range",
class: ui("range", className), class: ui("range", className),
@@ -1479,11 +1492,11 @@
}); });
if (!label && !tooltip) if (!label && !tooltip)
return rangeEl; return rangeEl;
const layout = $html("div", { class: "flex flex-col gap-2" }, [ const layout = $html2("div", { class: "flex flex-col gap-2" }, [
label ? $html("span", { class: "label-text" }, label) : null, label ? $html2("span", { class: "label-text" }, label) : null,
rangeEl rangeEl
]); ]);
return tooltip ? $html("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout; return tooltip ? $html2("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout;
}; };
// src/components/Rating.js // src/components/Rating.js
@@ -1494,12 +1507,12 @@
var Rating = (props) => { var Rating = (props) => {
const { class: className, value, count = 5, mask = "mask-star", readonly = false, onchange, ...rest } = props; const { class: className, value, count = 5, mask = "mask-star", readonly = false, onchange, ...rest } = props;
const ratingGroup = `rating-${Math.random().toString(36).slice(2, 7)}`; const ratingGroup = `rating-${Math.random().toString(36).slice(2, 7)}`;
return $html("div", { return $html2("div", {
...rest, ...rest,
class: () => ui(`rating ${val(readonly) ? "pointer-events-none" : ""}`, className) class: () => ui(`rating ${val(readonly) ? "pointer-events-none" : ""}`, className)
}, Array.from({ length: val(count) }, (_, i) => { }, Array.from({ length: val(count) }, (_, i) => {
const starValue = i + 1; const starValue = i + 1;
return $html("input", { return $html2("input", {
type: "radio", type: "radio",
name: ratingGroup, name: ratingGroup,
class: `mask ${mask}`, class: `mask ${mask}`,
@@ -1524,18 +1537,18 @@
}); });
var Select = (props) => { var Select = (props) => {
const { class: className, label, items, value, ...rest } = props; const { class: className, label, items, value, ...rest } = props;
const selectEl = $html("select", { const selectEl = $html2("select", {
...rest, ...rest,
class: ui("select select-bordered w-full", className), class: ui("select select-bordered w-full", className),
value value
}, $for(() => val(items) || [], (opt) => $html("option", { }, $for(() => val(items) || [], (opt) => $html2("option", {
value: opt.value, value: opt.value,
$selected: () => String(val(value)) === String(opt.value) $selected: () => String(val(value)) === String(opt.value)
}, opt.label), (opt) => opt.value)); }, opt.label), (opt) => opt.value));
if (!label) if (!label)
return selectEl; return selectEl;
return $html("label", { class: "fieldset-label flex flex-col gap-1" }, [ return $html2("label", { class: "fieldset-label flex flex-col gap-1" }, [
$html("span", {}, label), $html2("span", {}, label),
selectEl selectEl
]); ]);
}; };
@@ -1547,7 +1560,7 @@
}); });
var Stack = (props, children) => { var Stack = (props, children) => {
const { class: className, ...rest } = props; const { class: className, ...rest } = props;
return $html("div", { ...rest, class: ui("stack", className) }, children); return $html2("div", { ...rest, class: ui("stack", className) }, children);
}; };
// src/components/Stat.js // src/components/Stat.js
@@ -1557,11 +1570,11 @@
}); });
var Stat = (props) => { var Stat = (props) => {
const { class: className, icon, label, value, desc, ...rest } = props; const { class: className, icon, label, value, desc, ...rest } = props;
return $html("div", { ...rest, class: ui("stat", className) }, [ return $html2("div", { ...rest, class: ui("stat", className) }, [
icon && $html("div", { class: "stat-figure text-secondary" }, icon), icon && $html2("div", { class: "stat-figure text-secondary" }, icon),
label && $html("div", { class: "stat-title" }, label), label && $html2("div", { class: "stat-title" }, label),
$html("div", { class: "stat-value" }, () => val(value) ?? value), $html2("div", { class: "stat-value" }, () => val(value) ?? value),
desc && $html("div", { class: "stat-desc" }, desc) desc && $html2("div", { class: "stat-desc" }, desc)
]); ]);
}; };
@@ -1572,13 +1585,13 @@
}); });
var Swap = (props) => { var Swap = (props) => {
const { class: className, value, on, off, ...rest } = props; const { class: className, value, on, off, ...rest } = props;
return $html("label", { ...rest, class: ui("swap", className) }, [ return $html2("label", { ...rest, class: ui("swap", className) }, [
$html("input", { $html2("input", {
type: "checkbox", type: "checkbox",
checked: val(value) checked: val(value)
}), }),
$html("div", { class: "swap-on" }, on), $html2("div", { class: "swap-on" }, on),
$html("div", { class: "swap-off" }, off) $html2("div", { class: "swap-off" }, off)
]); ]);
}; };
@@ -1594,31 +1607,31 @@
const pinRowsClass = val(pinRows) ? "table-pin-rows" : ""; const pinRowsClass = val(pinRows) ? "table-pin-rows" : "";
return ui("table", className, zebraClass, pinRowsClass); return ui("table", className, zebraClass, pinRowsClass);
}; };
return $html("div", { class: "overflow-x-auto w-full bg-base-100 rounded-box border border-base-300" }, [ return $html2("div", { class: "overflow-x-auto w-full bg-base-100 rounded-box border border-base-300" }, [
$html("table", { ...rest, class: tableClass }, [ $html2("table", { ...rest, class: tableClass }, [
$html("thead", {}, [ $html2("thead", {}, [
$html("tr", {}, columns.map((col) => $html("th", { class: col.class || "" }, col.label))) $html2("tr", {}, columns.map((col) => $html2("th", { class: col.class || "" }, col.label)))
]), ]),
$html("tbody", {}, [ $html2("tbody", {}, [
$for(items, (item, index) => { $for(items, (item, index) => {
return $html("tr", { class: "hover" }, columns.map((col) => { return $html2("tr", { class: "hover" }, columns.map((col) => {
const cellContent = () => { const cellContent = () => {
if (col.render) if (col.render)
return col.render(item, index); return col.render(item, index);
const value = item[col.key]; const value = item[col.key];
return val(value); return val(value);
}; };
return $html("td", { class: col.class || "" }, [cellContent]); return $html2("td", { class: col.class || "" }, [cellContent]);
})); }));
}, keyFn || ((item, idx) => item.id || idx)), }, keyFn || ((item, idx) => item.id || idx)),
$if(() => val(items).length === 0, () => $html("tr", {}, [ $if(() => val(items).length === 0, () => $html2("tr", {}, [
$html("td", { colspan: columns.length, class: "text-center p-10 opacity-50" }, [ $html2("td", { colspan: columns.length, class: "text-center p-10 opacity-50" }, [
val(empty) val(empty)
]) ])
])) ]))
]), ]),
$if(() => columns.some((c) => c.footer), () => $html("tfoot", {}, [ $if(() => columns.some((c) => c.footer), () => $html2("tfoot", {}, [
$html("tr", {}, columns.map((col) => $html("th", {}, col.footer || ""))) $html2("tr", {}, columns.map((col) => $html2("th", {}, col.footer || "")))
])) ]))
]) ])
]); ]);
@@ -1640,13 +1653,13 @@
} }
onClick?.(e); onClick?.(e);
}; };
return $html("div", { ...rest, class: "flex flex-col gap-4 w-full" }, [ return $html2("div", { ...rest, class: "flex flex-col gap-4 w-full" }, [
$html("div", { $html2("div", {
role: "tablist", role: "tablist",
class: ui("tabs tabs-box", className) class: ui("tabs tabs-box", className)
}, $for(itemsSignal, (it, idx) => { }, $for(itemsSignal, (it, idx) => {
const isActive = val(it.active) ?? currentActive() === idx; const isActive = val(it.active) ?? currentActive() === idx;
return $html("a", { return $html2("a", {
role: "tab", role: "tab",
class: () => ui("tab", isActive ? "tab-active" : "", val(it.disabled) ? "tab-disabled" : ""), class: () => ui("tab", isActive ? "tab-active" : "", val(it.disabled) ? "tab-disabled" : ""),
onclick: !val(it.disabled) ? handleTabClick(idx, it.onclick) : undefined onclick: !val(it.disabled) ? handleTabClick(idx, it.onclick) : undefined
@@ -1657,7 +1670,7 @@
if (!activeItem) if (!activeItem)
return null; return null;
const content = val(activeItem.content); const content = val(activeItem.content);
return $html("div", { class: "p-4" }, [ return $html2("div", { class: "p-4" }, [
typeof content === "function" ? content() : content typeof content === "function" ? content() : content
]); ]);
} }
@@ -1677,7 +1690,7 @@
warning: "icon-[lucide--alert-triangle]", warning: "icon-[lucide--alert-triangle]",
error: "icon-[lucide--alert-circle]" error: "icon-[lucide--alert-circle]"
}; };
return $html("ul", { return $html2("ul", {
...rest, ...rest,
class: () => ui(`timeline ${val(vertical) ? "timeline-vertical" : "timeline-horizontal"} ${val(compact) ? "timeline-compact" : ""}`, className) class: () => ui(`timeline ${val(vertical) ? "timeline-vertical" : "timeline-horizontal"} ${val(compact) ? "timeline-compact" : ""}`, className)
}, [ }, [
@@ -1686,14 +1699,14 @@
const isLast = i === val(items).length - 1; const isLast = i === val(items).length - 1;
const itemType = item.type || "success"; const itemType = item.type || "success";
const renderSlot = (content) => typeof content === "function" ? content() : content; const renderSlot = (content) => typeof content === "function" ? content() : content;
return $html("li", { class: "flex-1" }, [ return $html2("li", { class: "flex-1" }, [
!isFirst ? $html("hr", { class: item.completed ? "bg-primary" : "" }) : null, !isFirst ? $html2("hr", { class: item.completed ? "bg-primary" : "" }) : null,
$html("div", { class: "timeline-start" }, [renderSlot(item.title)]), $html2("div", { class: "timeline-start" }, [renderSlot(item.title)]),
$html("div", { class: "timeline-middle" }, [ $html2("div", { class: "timeline-middle" }, [
item.icon ? getIcon(item.icon) : getIcon(iconMap[itemType] || iconMap.success) item.icon ? getIcon(item.icon) : getIcon(iconMap[itemType] || iconMap.success)
]), ]),
$html("div", { class: "timeline-end timeline-box shadow-sm" }, [renderSlot(item.detail)]), $html2("div", { class: "timeline-end timeline-box shadow-sm" }, [renderSlot(item.detail)]),
!isLast ? $html("hr", { class: item.completed ? "bg-primary" : "" }) : null !isLast ? $html2("hr", { class: item.completed ? "bg-primary" : "" }) : null
]); ]);
}, (item, i) => item.id || i) }, (item, i) => item.id || i)
]); ]);
@@ -1707,13 +1720,13 @@
var Toast = (message, type = "alert-success", duration = 3500) => { var Toast = (message, type = "alert-success", duration = 3500) => {
let container = document.getElementById("sigpro-toast-container"); let container = document.getElementById("sigpro-toast-container");
if (!container) { if (!container) {
container = $html("div", { container = $html2("div", {
id: "sigpro-toast-container", id: "sigpro-toast-container",
class: "fixed top-0 right-0 z-[9999] p-4 flex flex-col gap-2 pointer-events-none" class: "fixed top-0 right-0 z-[9999] p-4 flex flex-col gap-2 pointer-events-none"
}); });
document.body.appendChild(container); document.body.appendChild(container);
} }
const toastHost = $html("div", { style: "display: contents" }); const toastHost = $html2("div", { style: "display: contents" });
container.appendChild(toastHost); container.appendChild(toastHost);
let timeoutId; let timeoutId;
const close = () => { const close = () => {
@@ -1734,10 +1747,10 @@
}; };
const ToastComponent = () => { const ToastComponent = () => {
const closeIcon = getIcon("icon-[lucide--x]"); const closeIcon = getIcon("icon-[lucide--x]");
const el = $html("div", { const el = $html2("div", {
class: `alert alert-soft ${type} shadow-lg transition-all duration-300 translate-x-10 opacity-0 pointer-events-auto` class: `alert alert-soft ${type} shadow-lg transition-all duration-300 translate-x-10 opacity-0 pointer-events-auto`
}, [ }, [
$html("span", {}, [typeof message === "function" ? message() : message]), $html2("span", {}, [typeof message === "function" ? message() : message]),
Button({ Button({
class: "btn-xs btn-circle btn-ghost", class: "btn-xs btn-circle btn-ghost",
onclick: close onclick: close
@@ -1758,7 +1771,7 @@
__export(exports_Tooltip, { __export(exports_Tooltip, {
Tooltip: () => Tooltip Tooltip: () => Tooltip
}); });
var Tooltip = (props, children) => $html("div", { var Tooltip = (props, children) => $html2("div", {
...props, ...props,
class: () => ui("tooltip", props.ui, props.class), class: () => ui("tooltip", props.ui, props.class),
"data-tip": props.tip "data-tip": props.tip

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
# Dropdown # Dropdown
Dropdown component for creating menus, selectors, and action panels that appear when triggered. Supports both array-based items and custom content. Dropdown component for creating menus that appear when triggered. Uses DaisyUI's native details/summary pattern.
## Tag ## Tag
@@ -12,11 +12,10 @@ Dropdown component for creating menus, selectors, and action panels that appear
| :--- | :--- | :--- | :--- | | :--- | :--- | :--- | :--- |
| `label` | `string \| VNode \| Signal` | `-` | Button label or content | | `label` | `string \| VNode \| Signal` | `-` | Button label or content |
| `icon` | `string \| VNode \| Signal` | `-` | Icon displayed next to label | | `icon` | `string \| VNode \| Signal` | `-` | Icon displayed next to label |
| `items` | `Array<MenuItem> \| Signal<Array>` | `-` | Array of menu items (alternative to children) | | `items` | `Array<MenuItem> \| Signal<Array>` | Required | Array of menu items |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) | | `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `children` | `VNode \| function` | `-` | Custom dropdown content (alternative to items) |
### MenuItem Structure (when using `items`) ### MenuItem Structure
| Property | Type | Description | | Property | Type | Description |
| :--- | :--- | :--- | | :--- | :--- | :--- |
@@ -43,14 +42,16 @@ Dropdown supports all **daisyUI Dropdown classes**:
Dropdown({ Dropdown({
label: "Menu", label: "Menu",
class: "dropdown-end dropdown-hover", class: "dropdown-end dropdown-hover",
items: menuItems items: [
{ label: "Profile", onclick: () => console.log("Profile") },
{ label: "Settings", onclick: () => console.log("Settings") }
]
}); });
// Applies: right-aligned, opens on hover
``` ```
## Live Examples ## Live Examples
### Basic Dropdown (Items Array) ### Basic Dropdown
<div class="card bg-base-200 border border-base-300 shadow-sm my-6"> <div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body"> <div class="card-body">
@@ -73,7 +74,7 @@ const BasicDemo = () => {
$mount(BasicDemo, '#demo-basic'); $mount(BasicDemo, '#demo-basic');
``` ```
### With Icons (Items Array) ### With Icons
<div class="card bg-base-200 border border-base-300 shadow-sm my-6"> <div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body"> <div class="card-body">
@@ -98,7 +99,7 @@ const IconsDemo = () => {
$mount(IconsDemo, '#demo-icons'); $mount(IconsDemo, '#demo-icons');
``` ```
### Action Dropdown (Items Array) ### Action Dropdown
<div class="card bg-base-200 border border-base-300 shadow-sm my-6"> <div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body"> <div class="card-body">
@@ -126,7 +127,7 @@ const ActionsDemo = () => {
$mount(ActionsDemo, '#demo-actions'); $mount(ActionsDemo, '#demo-actions');
``` ```
### User Dropdown (Items Array) ### User Dropdown
<div class="card bg-base-200 border border-base-300 shadow-sm my-6"> <div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body"> <div class="card-body">
@@ -167,7 +168,7 @@ $mount(UserDropdown, '#demo-user');
```javascript ```javascript
const ReactiveDropdown = () => { const ReactiveDropdown = () => {
const count = $(0); const count = $(0);
$watch(()=>console.log(count()));
const items = () => [ const items = () => [
{ label: `Count: ${count()}`, onclick: () => {} }, { label: `Count: ${count()}`, onclick: () => {} },
{ label: 'Increment', onclick: () => count(count() + 1) }, { label: 'Increment', onclick: () => count(count() + 1) },
@@ -183,7 +184,7 @@ const ReactiveDropdown = () => {
$mount(ReactiveDropdown, '#demo-reactive'); $mount(ReactiveDropdown, '#demo-reactive');
``` ```
### Notification Dropdown (Custom Children) ### Notification Dropdown
<div class="card bg-base-200 border border-base-300 shadow-sm my-6"> <div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body"> <div class="card-body">
@@ -200,81 +201,34 @@ const NotificationsDropdown = () => {
{ id: 3, title: 'Task completed', time: '2 hours ago', read: true } { id: 3, title: 'Task completed', time: '2 hours ago', read: true }
]); ]);
const unreadCount = () => notifications().filter(n => !n.read).length;
const markAsRead = (id) => { const markAsRead = (id) => {
notifications(notifications().map(n => notifications(notifications().map(n =>
n.id === id ? { ...n, read: true } : n n.id === id ? { ...n, read: true } : n
)); ));
}; };
const unreadCount = () => notifications().filter(n => !n.read).length;
return Dropdown({ return Dropdown({
label: Span({ class: 'relative' }, [ label: Span({ class: 'relative' }, [
'🔔', '🔔',
() => unreadCount() > 0 ? Span({ class: 'badge badge-xs badge-error absolute -top-1 -right-2' }, unreadCount()) : null () => unreadCount() > 0 ? Span({ class: 'badge badge-xs badge-error absolute -top-1 -right-2' }, unreadCount()) : null
]), ]),
class: 'dropdown-end', class: 'dropdown-end',
children: () => Div({ class: 'w-80' }, [ items: () => notifications().map(notif => ({
Div({ class: 'p-3 border-b border-base-300 font-bold' }, `Notifications (${unreadCount()} unread)`), label: Div({ class: 'flex flex-col' }, [
Div({ class: 'max-h-64 overflow-y-auto' }, notifications().map(notif => Span({ class: 'font-medium' }, notif.title),
Div({ Span({ class: 'text-xs opacity-60' }, notif.time)
class: `p-3 border-b border-base-300 cursor-pointer hover:bg-base-200 ${!notif.read ? 'bg-primary/5' : ''}`, ]),
class: notif.read ? '' : 'bg-primary/5',
onclick: () => markAsRead(notif.id) onclick: () => markAsRead(notif.id)
}, [ }))
Div({ class: 'font-medium' }, notif.title),
Div({ class: 'text-xs opacity-60' }, notif.time),
!notif.read && Span({ class: 'badge badge-xs badge-primary mt-1' }, 'New')
])
)),
Div({ class: 'p-2 text-center' }, [
Button({
class: 'btn btn-xs btn-ghost w-full',
onclick: () => notifications([])
}, 'Clear all')
])
])
}); });
}; };
$mount(NotificationsDropdown, '#demo-notifications'); $mount(NotificationsDropdown, '#demo-notifications');
``` ```
### Custom Content Dropdown
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-custom" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
</div>
</div>
```javascript
const CustomDropdown = () => {
const selected = $('Option 1');
return Dropdown({
label: () => selected(),
children: () => Div({ class: 'p-4 min-w-48' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Select an option'),
Div({ class: 'flex flex-col gap-1' }, [
Button({
class: 'btn btn-ghost btn-sm justify-start',
onclick: () => selected('Option 1')
}, 'Option 1'),
Button({
class: 'btn btn-ghost btn-sm justify-start',
onclick: () => selected('Option 2')
}, 'Option 2'),
Button({
class: 'btn btn-ghost btn-sm justify-start',
onclick: () => selected('Option 3')
}, 'Option 3')
])
])
});
};
$mount(CustomDropdown, '#demo-custom');
```
### All Variants ### All Variants
<div class="card bg-base-200 border border-base-300 shadow-sm my-6"> <div class="card bg-base-200 border border-base-300 shadow-sm my-6">

File diff suppressed because one or more lines are too long

View File

@@ -12,7 +12,6 @@ import { ui } from "../core/utils.js";
export const Drawer = (props, children) => { export const Drawer = (props, children) => {
const { class: className, id, open, side, content, ...rest } = props; const { class: className, id, open, side, content, ...rest } = props;
// Generar un id único si no se proporciona
const drawerId = id || `drawer-${Math.random().toString(36).slice(2, 9)}`; const drawerId = id || `drawer-${Math.random().toString(36).slice(2, 9)}`;
return $html("div", { return $html("div", {
@@ -32,7 +31,6 @@ export const Drawer = (props, children) => {
typeof content === "function" ? content() : content typeof content === "function" ? content() : content
]), ]),
$html("div", { class: "drawer-side" }, [ $html("div", { class: "drawer-side" }, [
// El overlay debe tener for apuntando al checkbox
$html("label", { $html("label", {
for: drawerId, for: drawerId,
class: "drawer-overlay", class: "drawer-overlay",

View File

@@ -1,65 +1,78 @@
// components/Dropdown.js // components/Dropdown.js
import { $html, $for } from "sigpro"; // import { $html, $for, $watch } from "sigpro";
import { ui } from "../core/utils.js"; import { ui } from "../core/utils.js";
/** /**
* Dropdown component * Dropdown component - Solo soporta menús (items)
* *
* daisyUI classes used: * daisyUI classes used:
* - dropdown, dropdown-content, dropdown-end, dropdown-top, dropdown-bottom * - dropdown, dropdown-content, dropdown-end, dropdown-top, dropdown-bottom
* - menu, menu-dropdown, menu-dropdown-show * - menu, btn
* - btn, btn-ghost, btn-sm, btn-md, btn-lg * - bg-base-100, shadow, rounded-box, border
* - bg-base-100, shadow, rounded-box, border, border-base-300 * - z-50, p-2, w-52
* - z-50, p-2, w-52, min-w-max
* - m-1, flex, items-center, gap-2 * - m-1, flex, items-center, gap-2
*/ */
export const Dropdown = (props, children) => {
let currentOpen = null;
if (typeof window !== 'undefined' && !window.__dropdownHandlerRegistered) {
window.addEventListener('click', (e) => {
if (currentOpen && !currentOpen.contains(e.target)) {
currentOpen.open = false;
currentOpen = null;
}
});
window.__dropdownHandlerRegistered = true;
}
export const Dropdown = (props) => {
const { class: className, label, icon, items, ...rest } = props; const { class: className, label, icon, items, ...rest } = props;
const renderContent = () => { return $html("details", {
if (items) { ...rest,
const source = typeof items === "function" ? items : () => items; class: ui('dropdown', className),
return $html("ul", { }, [
tabindex: 0, $html("summary", {
class: "btn m-1 flex items-center gap-2 list-none cursor-pointer",
style: "display: inline-flex;",
onclick: (e) => {
const details = e.currentTarget.closest('details');
if (currentOpen && currentOpen !== details) {
currentOpen.open = false;
}
setTimeout(() => {
currentOpen = details.open ? details : null;
}, 0);
}
}, [
() => icon ? (typeof icon === "function" ? icon() : icon) : null,
() => label ? (typeof label === "function" ? label() : label) : null
]),
$html("ul", {
tabindex: "-1",
class: "dropdown-content z-[50] menu p-2 shadow bg-base-100 rounded-box w-52 border border-base-300" class: "dropdown-content z-[50] menu p-2 shadow bg-base-100 rounded-box w-52 border border-base-300"
}, [ }, [
$for(source, (item) => () => {
const currentItems = typeof items === "function" ? items() : (items || []);
return currentItems.map(item =>
$html("li", {}, [ $html("li", {}, [
$html("a", { $html("a", {
class: item.class || "", class: item.class || "",
onclick: (e) => { onclick: (e) => {
if (item.onclick) item.onclick(e); if (item.onclick) item.onclick(e);
if (document.activeElement) document.activeElement.blur(); const details = e.currentTarget.closest('details');
if (details) {
details.open = false;
if (currentOpen === details) currentOpen = null;
}
} }
}, [ }, [
item.icon ? $html("span", {}, item.icon) : null, item.icon ? $html("span", {}, item.icon) : null,
$html("span", {}, item.label) $html("span", {}, item.label)
]) ])
]) ])
) );
]);
} }
])
return $html("div", {
tabindex: 0,
class: "dropdown-content z-[50] p-2 shadow bg-base-100 rounded-box min-w-max border border-base-300"
}, [
typeof children === "function" ? children() : children
]);
};
return $html("div", {
...rest,
class: ui('dropdown', className),
}, [
$html("div", {
tabindex: 0,
role: "button",
class: "btn m-1 flex items-center gap-2",
}, [
icon ? (typeof icon === "function" ? icon() : icon) : null,
label ? (typeof label === "function" ? label() : label) : null
]),
renderContent()
]); ]);
}; };