Tabs Con pestañas cerrables
This commit is contained in:
@@ -4544,6 +4544,9 @@
|
|||||||
width: calc(var(--spacing) * 6);
|
width: calc(var(--spacing) * 6);
|
||||||
height: calc(var(--spacing) * 6);
|
height: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
|
.h-3\.5 {
|
||||||
|
height: calc(var(--spacing) * 3.5);
|
||||||
|
}
|
||||||
.h-8 {
|
.h-8 {
|
||||||
height: calc(var(--spacing) * 8);
|
height: calc(var(--spacing) * 8);
|
||||||
}
|
}
|
||||||
@@ -4618,6 +4621,9 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.w-3\.5 {
|
||||||
|
width: calc(var(--spacing) * 3.5);
|
||||||
|
}
|
||||||
.w-8 {
|
.w-8 {
|
||||||
width: calc(var(--spacing) * 8);
|
width: calc(var(--spacing) * 8);
|
||||||
}
|
}
|
||||||
@@ -6769,6 +6775,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.hover\:opacity-70 {
|
||||||
|
&:hover {
|
||||||
|
@media (hover: hover) {
|
||||||
|
opacity: 70%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.hover\:opacity-100 {
|
.hover\:opacity-100 {
|
||||||
&:hover {
|
&:hover {
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
@@ -6857,6 +6870,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
:root {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
.input, .select, .textarea {
|
.input, .select, .textarea {
|
||||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
&:focus, &:focus-within {
|
&:focus, &:focus-within {
|
||||||
|
|||||||
2
css/sigpro.min.css
vendored
2
css/sigpro.min.css
vendored
File diff suppressed because one or more lines are too long
110
dist/sigpro-ui.esm.js
vendored
110
dist/sigpro-ui.esm.js
vendored
@@ -579,11 +579,11 @@ var exports_utils = {};
|
|||||||
__export(exports_utils, {
|
__export(exports_utils, {
|
||||||
val: () => val,
|
val: () => val,
|
||||||
ui: () => ui,
|
ui: () => ui,
|
||||||
getIcon: () => getIcon
|
getIcon: () => getIcon2
|
||||||
});
|
});
|
||||||
var val = (t) => typeof t === "function" ? t() : t;
|
var val = (t) => typeof t === "function" ? t() : t;
|
||||||
var ui = (baseClass, additionalClassOrFn) => typeof additionalClassOrFn === "function" ? () => `${baseClass} ${additionalClassOrFn() || ""}`.trim() : `${baseClass} ${additionalClassOrFn || ""}`.trim();
|
var ui = (baseClass, additionalClassOrFn) => typeof additionalClassOrFn === "function" ? () => `${baseClass} ${additionalClassOrFn() || ""}`.trim() : `${baseClass} ${additionalClassOrFn || ""}`.trim();
|
||||||
var getIcon = (icon) => {
|
var getIcon2 = (icon) => {
|
||||||
if (!icon)
|
if (!icon)
|
||||||
return null;
|
return null;
|
||||||
if (typeof icon === "function") {
|
if (typeof icon === "function") {
|
||||||
@@ -644,7 +644,7 @@ var Alert = (props, children) => {
|
|||||||
role: "alert",
|
role: "alert",
|
||||||
class: ui("alert", allClasses)
|
class: ui("alert", allClasses)
|
||||||
}, () => [
|
}, () => [
|
||||||
getIcon(iconMap[type]),
|
getIcon2(iconMap[type]),
|
||||||
Tag("div", { class: "flex-1" }, [
|
Tag("div", { class: "flex-1" }, [
|
||||||
Tag("span", {}, [typeof content === "function" ? content() : content])
|
Tag("span", {}, [typeof content === "function" ? content() : content])
|
||||||
]),
|
]),
|
||||||
@@ -713,8 +713,8 @@ var Input = (props) => {
|
|||||||
tel: "icon-[lucide--phone]",
|
tel: "icon-[lucide--phone]",
|
||||||
url: "icon-[lucide--link]"
|
url: "icon-[lucide--link]"
|
||||||
};
|
};
|
||||||
const leftIcon = icon ? getIcon(icon) : iconMap[type] ? getIcon(iconMap[type]) : null;
|
const leftIcon = icon ? getIcon2(icon) : iconMap[type] ? getIcon2(iconMap[type]) : null;
|
||||||
const getPasswordIcon = () => getIcon(visible() ? "icon-[lucide--eye-off]" : "icon-[lucide--eye]");
|
const getPasswordIcon = () => getIcon2(visible() ? "icon-[lucide--eye-off]" : "icon-[lucide--eye]");
|
||||||
const paddingLeft = leftIcon ? "pl-10" : "";
|
const paddingLeft = leftIcon ? "pl-10" : "";
|
||||||
const paddingRight = isPassword ? "pr-10" : "";
|
const paddingRight = isPassword ? "pr-10" : "";
|
||||||
const buttonSize = () => {
|
const buttonSize = () => {
|
||||||
@@ -871,7 +871,7 @@ __export(exports_Button, {
|
|||||||
});
|
});
|
||||||
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 = getIcon2(icon);
|
||||||
return Tag("button", {
|
return Tag("button", {
|
||||||
...rest,
|
...rest,
|
||||||
class: ui("btn", className),
|
class: ui("btn", className),
|
||||||
@@ -1067,7 +1067,7 @@ var Datepicker = (props) => {
|
|||||||
placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha..."),
|
placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha..."),
|
||||||
value: displayValue,
|
value: displayValue,
|
||||||
readonly: true,
|
readonly: true,
|
||||||
icon: getIcon("icon-[lucide--calendar]"),
|
icon: getIcon2("icon-[lucide--calendar]"),
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
isOpen(!isOpen());
|
isOpen(!isOpen());
|
||||||
@@ -1080,15 +1080,15 @@ var Datepicker = (props) => {
|
|||||||
}, [
|
}, [
|
||||||
Tag("div", { class: "flex justify-between items-center mb-4 gap-1" }, [
|
Tag("div", { class: "flex justify-between items-center mb-4 gap-1" }, [
|
||||||
Tag("div", { class: "flex gap-0.5" }, [
|
Tag("div", { class: "flex gap-0.5" }, [
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) }, getIcon("icon-[lucide--chevrons-left]")),
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) }, getIcon2("icon-[lucide--chevrons-left]")),
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) }, getIcon("icon-[lucide--chevron-left]"))
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) }, getIcon2("icon-[lucide--chevron-left]"))
|
||||||
]),
|
]),
|
||||||
Tag("span", { class: "font-bold uppercase flex-1 text-center" }, [
|
Tag("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" })
|
||||||
]),
|
]),
|
||||||
Tag("div", { class: "flex gap-0.5" }, [
|
Tag("div", { class: "flex gap-0.5" }, [
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) }, getIcon("icon-[lucide--chevron-right]")),
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) }, getIcon2("icon-[lucide--chevron-right]")),
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) }, getIcon("icon-[lucide--chevrons-right]"))
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) }, getIcon2("icon-[lucide--chevrons-right]"))
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
Tag("div", { class: "grid grid-cols-7 gap-1", onmouseleave: () => hoverDate(null) }, [
|
Tag("div", { class: "grid grid-cols-7 gap-1", onmouseleave: () => hoverDate(null) }, [
|
||||||
@@ -1296,7 +1296,7 @@ var Fab = (props) => {
|
|||||||
role: "button",
|
role: "button",
|
||||||
class: "btn btn-lg btn-circle btn-primary shadow-2xl"
|
class: "btn btn-lg btn-circle btn-primary shadow-2xl"
|
||||||
}, [
|
}, [
|
||||||
icon ? getIcon(icon) : null,
|
icon ? getIcon2(icon) : null,
|
||||||
!icon && label ? label : null
|
!icon && label ? label : null
|
||||||
]),
|
]),
|
||||||
...val(actions).map((act) => Tag("div", { class: "flex items-center gap-3 transition-all duration-300" }, [
|
...val(actions).map((act) => Tag("div", { class: "flex items-center gap-3 transition-all duration-300" }, [
|
||||||
@@ -1308,7 +1308,7 @@ var Fab = (props) => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
act.onclick?.(e);
|
act.onclick?.(e);
|
||||||
}
|
}
|
||||||
}, [act.icon ? getIcon(act.icon) : act.text || ""])
|
}, [act.icon ? getIcon2(act.icon) : act.text || ""])
|
||||||
]))
|
]))
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
@@ -1383,7 +1383,7 @@ var Fileinput = (props) => {
|
|||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
Tag("div", { class: "flex items-center gap-3 w-full" }, [
|
Tag("div", { class: "flex items-center gap-3 w-full" }, [
|
||||||
getIcon("icon-[lucide--upload]"),
|
getIcon2("icon-[lucide--upload]"),
|
||||||
Tag("span", { class: "text-sm opacity-70 truncate grow text-left" }, "Arrastra o selecciona archivos..."),
|
Tag("span", { class: "text-sm opacity-70 truncate grow text-left" }, "Arrastra o selecciona archivos..."),
|
||||||
Tag("span", { class: "text-[10px] opacity-40 shrink-0" }, `Máx ${max}MB`)
|
Tag("span", { class: "text-[10px] opacity-40 shrink-0" }, `Máx ${max}MB`)
|
||||||
]),
|
]),
|
||||||
@@ -1412,7 +1412,7 @@ var Fileinput = (props) => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
removeFile(index);
|
removeFile(index);
|
||||||
}
|
}
|
||||||
}, [getIcon("icon-[lucide--x]")])
|
}, [getIcon2("icon-[lucide--x]")])
|
||||||
]), (file) => file.name + file.lastModified)
|
]), (file) => file.name + file.lastModified)
|
||||||
]))
|
]))
|
||||||
]);
|
]);
|
||||||
@@ -1764,18 +1764,57 @@ var Tabs = (props) => {
|
|||||||
const itemsSignal = typeof items === "function" ? items : () => items || [];
|
const itemsSignal = typeof items === "function" ? items : () => items || [];
|
||||||
const activeIndex = $(0);
|
const activeIndex = $(0);
|
||||||
Watch(() => {
|
Watch(() => {
|
||||||
const idx = itemsSignal().findIndex((it) => val(it.active) === true);
|
const list = itemsSignal();
|
||||||
if (idx !== -1 && idx !== activeIndex())
|
const idx = list.findIndex((it) => val(it.active) === true);
|
||||||
|
if (idx !== -1 && activeIndex() !== idx) {
|
||||||
activeIndex(idx);
|
activeIndex(idx);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
const removeTab = (indexToRemove, item) => {
|
||||||
|
if (item.onClose)
|
||||||
|
item.onClose();
|
||||||
|
const currentItems = itemsSignal();
|
||||||
|
const newItems = currentItems.filter((_, idx) => idx !== indexToRemove);
|
||||||
|
const isWritableSignal = typeof items === "function" && !items._isComputed;
|
||||||
|
if (!isWritableSignal) {
|
||||||
|
console.warn("Tabs: items must be a writable signal to support closable tabs");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
items(newItems);
|
||||||
|
if (newItems.length === 0)
|
||||||
|
return;
|
||||||
|
let newActive = activeIndex();
|
||||||
|
if (indexToRemove < newActive)
|
||||||
|
newActive--;
|
||||||
|
else if (indexToRemove === newActive)
|
||||||
|
newActive = Math.min(newActive, newItems.length - 1);
|
||||||
|
activeIndex(newActive);
|
||||||
|
};
|
||||||
return Tag("div", { ...rest, class: ui("tabs", className) }, () => {
|
return Tag("div", { ...rest, class: ui("tabs", className) }, () => {
|
||||||
const list = itemsSignal();
|
const list = itemsSignal();
|
||||||
const elements = [];
|
const elements = [];
|
||||||
for (let i = 0;i < list.length; i++) {
|
for (let i = 0;i < list.length; i++) {
|
||||||
const item = list[i];
|
const item = list[i];
|
||||||
const isActive = () => activeIndex() === i;
|
const label = val(item.label);
|
||||||
|
const labelNode = label instanceof Node ? label : document.createTextNode(String(label));
|
||||||
|
const buttonChildren = [];
|
||||||
|
if (item.closable) {
|
||||||
|
const closeIcon = getIcon("icon-[lucide--x]");
|
||||||
|
closeIcon.classList.add("w-3.5", "h-3.5", "ml-2", "cursor-pointer", "hover:opacity-70");
|
||||||
|
closeIcon.onclick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
removeTab(i, item);
|
||||||
|
};
|
||||||
|
const wrapper = Tag("span", { class: "flex items-center" }, [labelNode, closeIcon]);
|
||||||
|
buttonChildren.push(wrapper);
|
||||||
|
} else {
|
||||||
|
buttonChildren.push(labelNode);
|
||||||
|
}
|
||||||
const button = Tag("button", {
|
const button = Tag("button", {
|
||||||
class: () => ui("tab", isActive() ? "tab-active" : ""),
|
class: () => {
|
||||||
|
const isActive = activeIndex() === i;
|
||||||
|
return ui("tab", isActive ? "tab-active" : "");
|
||||||
|
},
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!val(item.disabled)) {
|
if (!val(item.disabled)) {
|
||||||
@@ -1783,21 +1822,24 @@ var Tabs = (props) => {
|
|||||||
item.onclick();
|
item.onclick();
|
||||||
activeIndex(i);
|
activeIndex(i);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
title: item.tip || ""
|
||||||
const label = val(item.label);
|
}, buttonChildren);
|
||||||
if (label instanceof Node) {
|
|
||||||
button.replaceChildren(label);
|
|
||||||
} else {
|
|
||||||
button.textContent = String(label);
|
|
||||||
}
|
|
||||||
elements.push(button);
|
elements.push(button);
|
||||||
|
let contentNode;
|
||||||
|
const rawContent = val(item.content);
|
||||||
|
if (typeof rawContent === "function") {
|
||||||
|
contentNode = rawContent();
|
||||||
|
} else if (rawContent instanceof Node) {
|
||||||
|
contentNode = rawContent;
|
||||||
|
} else {
|
||||||
|
contentNode = document.createTextNode(String(rawContent));
|
||||||
|
}
|
||||||
|
const inner = Tag("div", { class: "tab-content-inner" }, contentNode);
|
||||||
const panel = Tag("div", {
|
const panel = Tag("div", {
|
||||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||||
style: () => isActive() ? "display: block" : "display: none"
|
style: () => activeIndex() === i ? "display: block" : "display: none"
|
||||||
}, [
|
}, inner);
|
||||||
Tag("div", { class: "tab-content-inner" }, () => val(item.content))
|
|
||||||
]);
|
|
||||||
elements.push(panel);
|
elements.push(panel);
|
||||||
}
|
}
|
||||||
return elements;
|
return elements;
|
||||||
@@ -1833,7 +1875,7 @@ var Timeline = (props) => {
|
|||||||
!isFirst ? Tag("hr", { class: () => prevCompleted() ? "bg-primary" : "" }) : null,
|
!isFirst ? Tag("hr", { class: () => prevCompleted() ? "bg-primary" : "" }) : null,
|
||||||
Tag("div", { class: "timeline-start" }, [() => renderSlot(item.title)]),
|
Tag("div", { class: "timeline-start" }, [() => renderSlot(item.title)]),
|
||||||
Tag("div", { class: "timeline-middle" }, [
|
Tag("div", { class: "timeline-middle" }, [
|
||||||
() => item.icon ? getIcon(item.icon) : getIcon(iconMap[itemType] || iconMap.success)
|
() => item.icon ? getIcon2(item.icon) : getIcon2(iconMap[itemType] || iconMap.success)
|
||||||
]),
|
]),
|
||||||
Tag("div", { class: "timeline-end timeline-box shadow-sm" }, [() => renderSlot(item.detail)]),
|
Tag("div", { class: "timeline-end timeline-box shadow-sm" }, [() => renderSlot(item.detail)]),
|
||||||
!isLast ? Tag("hr", { class: () => isCompleted() ? "bg-primary" : "" }) : null
|
!isLast ? Tag("hr", { class: () => isCompleted() ? "bg-primary" : "" }) : null
|
||||||
@@ -1876,7 +1918,7 @@ var Toast = (message, type = "alert-success", duration = 3500) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const ToastComponent = () => {
|
const ToastComponent = () => {
|
||||||
const closeIcon = getIcon("icon-[lucide--x]");
|
const closeIcon = getIcon2("icon-[lucide--x]");
|
||||||
const el = Tag("div", {
|
const el = Tag("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`
|
||||||
}, [
|
}, [
|
||||||
@@ -1987,7 +2029,7 @@ export {
|
|||||||
val,
|
val,
|
||||||
ui,
|
ui,
|
||||||
tt,
|
tt,
|
||||||
getIcon,
|
getIcon2 as getIcon,
|
||||||
Watch2 as Watch,
|
Watch2 as Watch,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Toast,
|
Toast,
|
||||||
|
|||||||
6
dist/sigpro-ui.esm.min.js
vendored
6
dist/sigpro-ui.esm.min.js
vendored
File diff suppressed because one or more lines are too long
110
dist/sigpro-ui.js
vendored
110
dist/sigpro-ui.js
vendored
@@ -33,7 +33,7 @@
|
|||||||
val: () => val,
|
val: () => val,
|
||||||
ui: () => ui,
|
ui: () => ui,
|
||||||
tt: () => tt,
|
tt: () => tt,
|
||||||
getIcon: () => getIcon,
|
getIcon: () => getIcon2,
|
||||||
Watch: () => Watch2,
|
Watch: () => Watch2,
|
||||||
Tooltip: () => Tooltip,
|
Tooltip: () => Tooltip,
|
||||||
Toast: () => Toast,
|
Toast: () => Toast,
|
||||||
@@ -645,11 +645,11 @@
|
|||||||
__export(exports_utils, {
|
__export(exports_utils, {
|
||||||
val: () => val,
|
val: () => val,
|
||||||
ui: () => ui,
|
ui: () => ui,
|
||||||
getIcon: () => getIcon
|
getIcon: () => getIcon2
|
||||||
});
|
});
|
||||||
var val = (t) => typeof t === "function" ? t() : t;
|
var val = (t) => typeof t === "function" ? t() : t;
|
||||||
var ui = (baseClass, additionalClassOrFn) => typeof additionalClassOrFn === "function" ? () => `${baseClass} ${additionalClassOrFn() || ""}`.trim() : `${baseClass} ${additionalClassOrFn || ""}`.trim();
|
var ui = (baseClass, additionalClassOrFn) => typeof additionalClassOrFn === "function" ? () => `${baseClass} ${additionalClassOrFn() || ""}`.trim() : `${baseClass} ${additionalClassOrFn || ""}`.trim();
|
||||||
var getIcon = (icon) => {
|
var getIcon2 = (icon) => {
|
||||||
if (!icon)
|
if (!icon)
|
||||||
return null;
|
return null;
|
||||||
if (typeof icon === "function") {
|
if (typeof icon === "function") {
|
||||||
@@ -710,7 +710,7 @@
|
|||||||
role: "alert",
|
role: "alert",
|
||||||
class: ui("alert", allClasses)
|
class: ui("alert", allClasses)
|
||||||
}, () => [
|
}, () => [
|
||||||
getIcon(iconMap[type]),
|
getIcon2(iconMap[type]),
|
||||||
Tag("div", { class: "flex-1" }, [
|
Tag("div", { class: "flex-1" }, [
|
||||||
Tag("span", {}, [typeof content === "function" ? content() : content])
|
Tag("span", {}, [typeof content === "function" ? content() : content])
|
||||||
]),
|
]),
|
||||||
@@ -779,8 +779,8 @@
|
|||||||
tel: "icon-[lucide--phone]",
|
tel: "icon-[lucide--phone]",
|
||||||
url: "icon-[lucide--link]"
|
url: "icon-[lucide--link]"
|
||||||
};
|
};
|
||||||
const leftIcon = icon ? getIcon(icon) : iconMap[type] ? getIcon(iconMap[type]) : null;
|
const leftIcon = icon ? getIcon2(icon) : iconMap[type] ? getIcon2(iconMap[type]) : null;
|
||||||
const getPasswordIcon = () => getIcon(visible() ? "icon-[lucide--eye-off]" : "icon-[lucide--eye]");
|
const getPasswordIcon = () => getIcon2(visible() ? "icon-[lucide--eye-off]" : "icon-[lucide--eye]");
|
||||||
const paddingLeft = leftIcon ? "pl-10" : "";
|
const paddingLeft = leftIcon ? "pl-10" : "";
|
||||||
const paddingRight = isPassword ? "pr-10" : "";
|
const paddingRight = isPassword ? "pr-10" : "";
|
||||||
const buttonSize = () => {
|
const buttonSize = () => {
|
||||||
@@ -937,7 +937,7 @@
|
|||||||
});
|
});
|
||||||
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 = getIcon2(icon);
|
||||||
return Tag("button", {
|
return Tag("button", {
|
||||||
...rest,
|
...rest,
|
||||||
class: ui("btn", className),
|
class: ui("btn", className),
|
||||||
@@ -1133,7 +1133,7 @@
|
|||||||
placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha..."),
|
placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha..."),
|
||||||
value: displayValue,
|
value: displayValue,
|
||||||
readonly: true,
|
readonly: true,
|
||||||
icon: getIcon("icon-[lucide--calendar]"),
|
icon: getIcon2("icon-[lucide--calendar]"),
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
isOpen(!isOpen());
|
isOpen(!isOpen());
|
||||||
@@ -1146,15 +1146,15 @@
|
|||||||
}, [
|
}, [
|
||||||
Tag("div", { class: "flex justify-between items-center mb-4 gap-1" }, [
|
Tag("div", { class: "flex justify-between items-center mb-4 gap-1" }, [
|
||||||
Tag("div", { class: "flex gap-0.5" }, [
|
Tag("div", { class: "flex gap-0.5" }, [
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) }, getIcon("icon-[lucide--chevrons-left]")),
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) }, getIcon2("icon-[lucide--chevrons-left]")),
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) }, getIcon("icon-[lucide--chevron-left]"))
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) }, getIcon2("icon-[lucide--chevron-left]"))
|
||||||
]),
|
]),
|
||||||
Tag("span", { class: "font-bold uppercase flex-1 text-center" }, [
|
Tag("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" })
|
||||||
]),
|
]),
|
||||||
Tag("div", { class: "flex gap-0.5" }, [
|
Tag("div", { class: "flex gap-0.5" }, [
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) }, getIcon("icon-[lucide--chevron-right]")),
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) }, getIcon2("icon-[lucide--chevron-right]")),
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) }, getIcon("icon-[lucide--chevrons-right]"))
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) }, getIcon2("icon-[lucide--chevrons-right]"))
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
Tag("div", { class: "grid grid-cols-7 gap-1", onmouseleave: () => hoverDate(null) }, [
|
Tag("div", { class: "grid grid-cols-7 gap-1", onmouseleave: () => hoverDate(null) }, [
|
||||||
@@ -1362,7 +1362,7 @@
|
|||||||
role: "button",
|
role: "button",
|
||||||
class: "btn btn-lg btn-circle btn-primary shadow-2xl"
|
class: "btn btn-lg btn-circle btn-primary shadow-2xl"
|
||||||
}, [
|
}, [
|
||||||
icon ? getIcon(icon) : null,
|
icon ? getIcon2(icon) : null,
|
||||||
!icon && label ? label : null
|
!icon && label ? label : null
|
||||||
]),
|
]),
|
||||||
...val(actions).map((act) => Tag("div", { class: "flex items-center gap-3 transition-all duration-300" }, [
|
...val(actions).map((act) => Tag("div", { class: "flex items-center gap-3 transition-all duration-300" }, [
|
||||||
@@ -1374,7 +1374,7 @@
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
act.onclick?.(e);
|
act.onclick?.(e);
|
||||||
}
|
}
|
||||||
}, [act.icon ? getIcon(act.icon) : act.text || ""])
|
}, [act.icon ? getIcon2(act.icon) : act.text || ""])
|
||||||
]))
|
]))
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
@@ -1449,7 +1449,7 @@
|
|||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
Tag("div", { class: "flex items-center gap-3 w-full" }, [
|
Tag("div", { class: "flex items-center gap-3 w-full" }, [
|
||||||
getIcon("icon-[lucide--upload]"),
|
getIcon2("icon-[lucide--upload]"),
|
||||||
Tag("span", { class: "text-sm opacity-70 truncate grow text-left" }, "Arrastra o selecciona archivos..."),
|
Tag("span", { class: "text-sm opacity-70 truncate grow text-left" }, "Arrastra o selecciona archivos..."),
|
||||||
Tag("span", { class: "text-[10px] opacity-40 shrink-0" }, `Máx ${max}MB`)
|
Tag("span", { class: "text-[10px] opacity-40 shrink-0" }, `Máx ${max}MB`)
|
||||||
]),
|
]),
|
||||||
@@ -1478,7 +1478,7 @@
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
removeFile(index);
|
removeFile(index);
|
||||||
}
|
}
|
||||||
}, [getIcon("icon-[lucide--x]")])
|
}, [getIcon2("icon-[lucide--x]")])
|
||||||
]), (file) => file.name + file.lastModified)
|
]), (file) => file.name + file.lastModified)
|
||||||
]))
|
]))
|
||||||
]);
|
]);
|
||||||
@@ -1830,18 +1830,57 @@
|
|||||||
const itemsSignal = typeof items === "function" ? items : () => items || [];
|
const itemsSignal = typeof items === "function" ? items : () => items || [];
|
||||||
const activeIndex = $(0);
|
const activeIndex = $(0);
|
||||||
Watch(() => {
|
Watch(() => {
|
||||||
const idx = itemsSignal().findIndex((it) => val(it.active) === true);
|
const list = itemsSignal();
|
||||||
if (idx !== -1 && idx !== activeIndex())
|
const idx = list.findIndex((it) => val(it.active) === true);
|
||||||
|
if (idx !== -1 && activeIndex() !== idx) {
|
||||||
activeIndex(idx);
|
activeIndex(idx);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
const removeTab = (indexToRemove, item) => {
|
||||||
|
if (item.onClose)
|
||||||
|
item.onClose();
|
||||||
|
const currentItems = itemsSignal();
|
||||||
|
const newItems = currentItems.filter((_, idx) => idx !== indexToRemove);
|
||||||
|
const isWritableSignal = typeof items === "function" && !items._isComputed;
|
||||||
|
if (!isWritableSignal) {
|
||||||
|
console.warn("Tabs: items must be a writable signal to support closable tabs");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
items(newItems);
|
||||||
|
if (newItems.length === 0)
|
||||||
|
return;
|
||||||
|
let newActive = activeIndex();
|
||||||
|
if (indexToRemove < newActive)
|
||||||
|
newActive--;
|
||||||
|
else if (indexToRemove === newActive)
|
||||||
|
newActive = Math.min(newActive, newItems.length - 1);
|
||||||
|
activeIndex(newActive);
|
||||||
|
};
|
||||||
return Tag("div", { ...rest, class: ui("tabs", className) }, () => {
|
return Tag("div", { ...rest, class: ui("tabs", className) }, () => {
|
||||||
const list = itemsSignal();
|
const list = itemsSignal();
|
||||||
const elements = [];
|
const elements = [];
|
||||||
for (let i = 0;i < list.length; i++) {
|
for (let i = 0;i < list.length; i++) {
|
||||||
const item = list[i];
|
const item = list[i];
|
||||||
const isActive = () => activeIndex() === i;
|
const label = val(item.label);
|
||||||
|
const labelNode = label instanceof Node ? label : document.createTextNode(String(label));
|
||||||
|
const buttonChildren = [];
|
||||||
|
if (item.closable) {
|
||||||
|
const closeIcon = getIcon("icon-[lucide--x]");
|
||||||
|
closeIcon.classList.add("w-3.5", "h-3.5", "ml-2", "cursor-pointer", "hover:opacity-70");
|
||||||
|
closeIcon.onclick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
removeTab(i, item);
|
||||||
|
};
|
||||||
|
const wrapper = Tag("span", { class: "flex items-center" }, [labelNode, closeIcon]);
|
||||||
|
buttonChildren.push(wrapper);
|
||||||
|
} else {
|
||||||
|
buttonChildren.push(labelNode);
|
||||||
|
}
|
||||||
const button = Tag("button", {
|
const button = Tag("button", {
|
||||||
class: () => ui("tab", isActive() ? "tab-active" : ""),
|
class: () => {
|
||||||
|
const isActive = activeIndex() === i;
|
||||||
|
return ui("tab", isActive ? "tab-active" : "");
|
||||||
|
},
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!val(item.disabled)) {
|
if (!val(item.disabled)) {
|
||||||
@@ -1849,21 +1888,24 @@
|
|||||||
item.onclick();
|
item.onclick();
|
||||||
activeIndex(i);
|
activeIndex(i);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
title: item.tip || ""
|
||||||
const label = val(item.label);
|
}, buttonChildren);
|
||||||
if (label instanceof Node) {
|
|
||||||
button.replaceChildren(label);
|
|
||||||
} else {
|
|
||||||
button.textContent = String(label);
|
|
||||||
}
|
|
||||||
elements.push(button);
|
elements.push(button);
|
||||||
|
let contentNode;
|
||||||
|
const rawContent = val(item.content);
|
||||||
|
if (typeof rawContent === "function") {
|
||||||
|
contentNode = rawContent();
|
||||||
|
} else if (rawContent instanceof Node) {
|
||||||
|
contentNode = rawContent;
|
||||||
|
} else {
|
||||||
|
contentNode = document.createTextNode(String(rawContent));
|
||||||
|
}
|
||||||
|
const inner = Tag("div", { class: "tab-content-inner" }, contentNode);
|
||||||
const panel = Tag("div", {
|
const panel = Tag("div", {
|
||||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||||
style: () => isActive() ? "display: block" : "display: none"
|
style: () => activeIndex() === i ? "display: block" : "display: none"
|
||||||
}, [
|
}, inner);
|
||||||
Tag("div", { class: "tab-content-inner" }, () => val(item.content))
|
|
||||||
]);
|
|
||||||
elements.push(panel);
|
elements.push(panel);
|
||||||
}
|
}
|
||||||
return elements;
|
return elements;
|
||||||
@@ -1899,7 +1941,7 @@
|
|||||||
!isFirst ? Tag("hr", { class: () => prevCompleted() ? "bg-primary" : "" }) : null,
|
!isFirst ? Tag("hr", { class: () => prevCompleted() ? "bg-primary" : "" }) : null,
|
||||||
Tag("div", { class: "timeline-start" }, [() => renderSlot(item.title)]),
|
Tag("div", { class: "timeline-start" }, [() => renderSlot(item.title)]),
|
||||||
Tag("div", { class: "timeline-middle" }, [
|
Tag("div", { class: "timeline-middle" }, [
|
||||||
() => item.icon ? getIcon(item.icon) : getIcon(iconMap[itemType] || iconMap.success)
|
() => item.icon ? getIcon2(item.icon) : getIcon2(iconMap[itemType] || iconMap.success)
|
||||||
]),
|
]),
|
||||||
Tag("div", { class: "timeline-end timeline-box shadow-sm" }, [() => renderSlot(item.detail)]),
|
Tag("div", { class: "timeline-end timeline-box shadow-sm" }, [() => renderSlot(item.detail)]),
|
||||||
!isLast ? Tag("hr", { class: () => isCompleted() ? "bg-primary" : "" }) : null
|
!isLast ? Tag("hr", { class: () => isCompleted() ? "bg-primary" : "" }) : null
|
||||||
@@ -1942,7 +1984,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const ToastComponent = () => {
|
const ToastComponent = () => {
|
||||||
const closeIcon = getIcon("icon-[lucide--x]");
|
const closeIcon = getIcon2("icon-[lucide--x]");
|
||||||
const el = Tag("div", {
|
const el = Tag("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`
|
||||||
}, [
|
}, [
|
||||||
|
|||||||
6
dist/sigpro-ui.min.js
vendored
6
dist/sigpro-ui.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -275,7 +275,7 @@ const CartDrawer = () => {
|
|||||||
Button({
|
Button({
|
||||||
class: 'btn btn-ghost btn-circle btn-sm',
|
class: 'btn btn-ghost btn-circle btn-sm',
|
||||||
onclick: () => isOpen(false)
|
onclick: () => isOpen(false)
|
||||||
}, '✕')
|
}, Span('✕'))
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
Div({ class: 'flex-1 overflow-y-auto p-4' }, cart().length === 0
|
Div({ class: 'flex-1 overflow-y-auto p-4' }, cart().length === 0
|
||||||
@@ -290,12 +290,12 @@ const CartDrawer = () => {
|
|||||||
Button({
|
Button({
|
||||||
class: 'btn btn-xs btn-circle',
|
class: 'btn btn-xs btn-circle',
|
||||||
onclick: () => updateQuantity(item.id, -1)
|
onclick: () => updateQuantity(item.id, -1)
|
||||||
}, '-'),
|
}, Span('-')),
|
||||||
Span({ class: 'w-8 text-center' }, item.quantity),
|
Span({ class: 'w-8 text-center' }, item.quantity),
|
||||||
Button({
|
Button({
|
||||||
class: 'btn btn-xs btn-circle',
|
class: 'btn btn-xs btn-circle',
|
||||||
onclick: () => updateQuantity(item.id, 1)
|
onclick: () => updateQuantity(item.id, 1)
|
||||||
}, '+')
|
}, Span('+'))
|
||||||
]),
|
]),
|
||||||
Span({ class: 'font-bold w-16 text-right' }, `$${item.price * item.quantity}`)
|
Span({ class: 'font-bold w-16 text-right' }, `$${item.price * item.quantity}`)
|
||||||
])
|
])
|
||||||
@@ -313,7 +313,7 @@ const CartDrawer = () => {
|
|||||||
Toast('Checkout initiated!', 'alert-success', 2000);
|
Toast('Checkout initiated!', 'alert-success', 2000);
|
||||||
},
|
},
|
||||||
disabled: () => cart().length === 0
|
disabled: () => cart().length === 0
|
||||||
}, 'Checkout')
|
}, Span('Checkout'))
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
content: Div({ class: 'p-4' }, [
|
content: Div({ class: 'p-4' }, [
|
||||||
@@ -322,7 +322,7 @@ const CartDrawer = () => {
|
|||||||
Button({
|
Button({
|
||||||
class: 'btn btn-primary',
|
class: 'btn btn-primary',
|
||||||
onclick: () => isOpen(true)
|
onclick: () => isOpen(true)
|
||||||
}, () => `🛒 Cart (${cart().length})`)
|
}, Span({ class: 'flex items-center gap-2' }, [Span('🛒'), Span(`Cart (${cart().length})`)]))
|
||||||
]),
|
]),
|
||||||
Div({ class: 'mt-4 grid grid-cols-2 gap-4' }, [
|
Div({ class: 'mt-4 grid grid-cols-2 gap-4' }, [
|
||||||
Button({
|
Button({
|
||||||
@@ -331,7 +331,7 @@ const CartDrawer = () => {
|
|||||||
cart([...cart(), { id: Date.now(), name: 'New Product', price: 39, quantity: 1 }]);
|
cart([...cart(), { id: Date.now(), name: 'New Product', price: 39, quantity: 1 }]);
|
||||||
Toast('Added to cart!', 'alert-success', 1500);
|
Toast('Added to cart!', 'alert-success', 1500);
|
||||||
}
|
}
|
||||||
}, ['📦', 'Add to Cart'])
|
}, Span({ class: 'flex flex-col items-center gap-1' }, [Span('📦'), Span('Add to Cart')]))
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -416,4 +416,40 @@ const VariantsDemo = () => {
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
Mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Closable Tabs
|
||||||
|
|
||||||
|
<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-closable" class="bg-base-100 p-6 rounded-xl border border-base-300"></div> </div> </div>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
let nextTabId = 4;
|
||||||
|
|
||||||
|
const ClosableTabsDemo = () => {
|
||||||
|
const tabs = $([
|
||||||
|
{ label: 'Tab 1', content: Div('Content 1') }, // ❌ quita active: true
|
||||||
|
{ label: 'Tab 2', content: Div('Content 2'), closable: true },
|
||||||
|
{ label: 'Tab 3', content: Div('Content 3'), closable: true }
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Opcional: si quieres que la primera pestaña esté activa al inicio,
|
||||||
|
// puedes hacerlo mediante una señal externa o simplemente confiar en que
|
||||||
|
// activeIndex empieza en 0 (que es el comportamiento por defecto).
|
||||||
|
|
||||||
|
const addTab = () => {
|
||||||
|
const newId = nextTabId++;
|
||||||
|
tabs([...tabs(), {
|
||||||
|
label: `Tab ${newId}`,
|
||||||
|
content: Div(`Content ${newId}`),
|
||||||
|
closable: true
|
||||||
|
}]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return Div({ class: 'flex flex-col gap-4' }, [
|
||||||
|
Button({ class: 'btn btn-sm btn-outline mb-2', onclick: addTab }, 'Add Tab'),
|
||||||
|
Tabs({ items: tabs })
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
Mount(ClosableTabsDemo, '#demo-closable');
|
||||||
```
|
```
|
||||||
110
docs/sigpro-ui.min.js
vendored
110
docs/sigpro-ui.min.js
vendored
@@ -33,7 +33,7 @@
|
|||||||
val: () => val,
|
val: () => val,
|
||||||
ui: () => ui,
|
ui: () => ui,
|
||||||
tt: () => tt,
|
tt: () => tt,
|
||||||
getIcon: () => getIcon,
|
getIcon: () => getIcon2,
|
||||||
Watch: () => Watch2,
|
Watch: () => Watch2,
|
||||||
Tooltip: () => Tooltip,
|
Tooltip: () => Tooltip,
|
||||||
Toast: () => Toast,
|
Toast: () => Toast,
|
||||||
@@ -645,11 +645,11 @@
|
|||||||
__export(exports_utils, {
|
__export(exports_utils, {
|
||||||
val: () => val,
|
val: () => val,
|
||||||
ui: () => ui,
|
ui: () => ui,
|
||||||
getIcon: () => getIcon
|
getIcon: () => getIcon2
|
||||||
});
|
});
|
||||||
var val = (t) => typeof t === "function" ? t() : t;
|
var val = (t) => typeof t === "function" ? t() : t;
|
||||||
var ui = (baseClass, additionalClassOrFn) => typeof additionalClassOrFn === "function" ? () => `${baseClass} ${additionalClassOrFn() || ""}`.trim() : `${baseClass} ${additionalClassOrFn || ""}`.trim();
|
var ui = (baseClass, additionalClassOrFn) => typeof additionalClassOrFn === "function" ? () => `${baseClass} ${additionalClassOrFn() || ""}`.trim() : `${baseClass} ${additionalClassOrFn || ""}`.trim();
|
||||||
var getIcon = (icon) => {
|
var getIcon2 = (icon) => {
|
||||||
if (!icon)
|
if (!icon)
|
||||||
return null;
|
return null;
|
||||||
if (typeof icon === "function") {
|
if (typeof icon === "function") {
|
||||||
@@ -710,7 +710,7 @@
|
|||||||
role: "alert",
|
role: "alert",
|
||||||
class: ui("alert", allClasses)
|
class: ui("alert", allClasses)
|
||||||
}, () => [
|
}, () => [
|
||||||
getIcon(iconMap[type]),
|
getIcon2(iconMap[type]),
|
||||||
Tag("div", { class: "flex-1" }, [
|
Tag("div", { class: "flex-1" }, [
|
||||||
Tag("span", {}, [typeof content === "function" ? content() : content])
|
Tag("span", {}, [typeof content === "function" ? content() : content])
|
||||||
]),
|
]),
|
||||||
@@ -779,8 +779,8 @@
|
|||||||
tel: "icon-[lucide--phone]",
|
tel: "icon-[lucide--phone]",
|
||||||
url: "icon-[lucide--link]"
|
url: "icon-[lucide--link]"
|
||||||
};
|
};
|
||||||
const leftIcon = icon ? getIcon(icon) : iconMap[type] ? getIcon(iconMap[type]) : null;
|
const leftIcon = icon ? getIcon2(icon) : iconMap[type] ? getIcon2(iconMap[type]) : null;
|
||||||
const getPasswordIcon = () => getIcon(visible() ? "icon-[lucide--eye-off]" : "icon-[lucide--eye]");
|
const getPasswordIcon = () => getIcon2(visible() ? "icon-[lucide--eye-off]" : "icon-[lucide--eye]");
|
||||||
const paddingLeft = leftIcon ? "pl-10" : "";
|
const paddingLeft = leftIcon ? "pl-10" : "";
|
||||||
const paddingRight = isPassword ? "pr-10" : "";
|
const paddingRight = isPassword ? "pr-10" : "";
|
||||||
const buttonSize = () => {
|
const buttonSize = () => {
|
||||||
@@ -937,7 +937,7 @@
|
|||||||
});
|
});
|
||||||
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 = getIcon2(icon);
|
||||||
return Tag("button", {
|
return Tag("button", {
|
||||||
...rest,
|
...rest,
|
||||||
class: ui("btn", className),
|
class: ui("btn", className),
|
||||||
@@ -1133,7 +1133,7 @@
|
|||||||
placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha..."),
|
placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha..."),
|
||||||
value: displayValue,
|
value: displayValue,
|
||||||
readonly: true,
|
readonly: true,
|
||||||
icon: getIcon("icon-[lucide--calendar]"),
|
icon: getIcon2("icon-[lucide--calendar]"),
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
isOpen(!isOpen());
|
isOpen(!isOpen());
|
||||||
@@ -1146,15 +1146,15 @@
|
|||||||
}, [
|
}, [
|
||||||
Tag("div", { class: "flex justify-between items-center mb-4 gap-1" }, [
|
Tag("div", { class: "flex justify-between items-center mb-4 gap-1" }, [
|
||||||
Tag("div", { class: "flex gap-0.5" }, [
|
Tag("div", { class: "flex gap-0.5" }, [
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) }, getIcon("icon-[lucide--chevrons-left]")),
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) }, getIcon2("icon-[lucide--chevrons-left]")),
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) }, getIcon("icon-[lucide--chevron-left]"))
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) }, getIcon2("icon-[lucide--chevron-left]"))
|
||||||
]),
|
]),
|
||||||
Tag("span", { class: "font-bold uppercase flex-1 text-center" }, [
|
Tag("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" })
|
||||||
]),
|
]),
|
||||||
Tag("div", { class: "flex gap-0.5" }, [
|
Tag("div", { class: "flex gap-0.5" }, [
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) }, getIcon("icon-[lucide--chevron-right]")),
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) }, getIcon2("icon-[lucide--chevron-right]")),
|
||||||
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) }, getIcon("icon-[lucide--chevrons-right]"))
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) }, getIcon2("icon-[lucide--chevrons-right]"))
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
Tag("div", { class: "grid grid-cols-7 gap-1", onmouseleave: () => hoverDate(null) }, [
|
Tag("div", { class: "grid grid-cols-7 gap-1", onmouseleave: () => hoverDate(null) }, [
|
||||||
@@ -1362,7 +1362,7 @@
|
|||||||
role: "button",
|
role: "button",
|
||||||
class: "btn btn-lg btn-circle btn-primary shadow-2xl"
|
class: "btn btn-lg btn-circle btn-primary shadow-2xl"
|
||||||
}, [
|
}, [
|
||||||
icon ? getIcon(icon) : null,
|
icon ? getIcon2(icon) : null,
|
||||||
!icon && label ? label : null
|
!icon && label ? label : null
|
||||||
]),
|
]),
|
||||||
...val(actions).map((act) => Tag("div", { class: "flex items-center gap-3 transition-all duration-300" }, [
|
...val(actions).map((act) => Tag("div", { class: "flex items-center gap-3 transition-all duration-300" }, [
|
||||||
@@ -1374,7 +1374,7 @@
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
act.onclick?.(e);
|
act.onclick?.(e);
|
||||||
}
|
}
|
||||||
}, [act.icon ? getIcon(act.icon) : act.text || ""])
|
}, [act.icon ? getIcon2(act.icon) : act.text || ""])
|
||||||
]))
|
]))
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
@@ -1449,7 +1449,7 @@
|
|||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
Tag("div", { class: "flex items-center gap-3 w-full" }, [
|
Tag("div", { class: "flex items-center gap-3 w-full" }, [
|
||||||
getIcon("icon-[lucide--upload]"),
|
getIcon2("icon-[lucide--upload]"),
|
||||||
Tag("span", { class: "text-sm opacity-70 truncate grow text-left" }, "Arrastra o selecciona archivos..."),
|
Tag("span", { class: "text-sm opacity-70 truncate grow text-left" }, "Arrastra o selecciona archivos..."),
|
||||||
Tag("span", { class: "text-[10px] opacity-40 shrink-0" }, `Máx ${max}MB`)
|
Tag("span", { class: "text-[10px] opacity-40 shrink-0" }, `Máx ${max}MB`)
|
||||||
]),
|
]),
|
||||||
@@ -1478,7 +1478,7 @@
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
removeFile(index);
|
removeFile(index);
|
||||||
}
|
}
|
||||||
}, [getIcon("icon-[lucide--x]")])
|
}, [getIcon2("icon-[lucide--x]")])
|
||||||
]), (file) => file.name + file.lastModified)
|
]), (file) => file.name + file.lastModified)
|
||||||
]))
|
]))
|
||||||
]);
|
]);
|
||||||
@@ -1830,18 +1830,57 @@
|
|||||||
const itemsSignal = typeof items === "function" ? items : () => items || [];
|
const itemsSignal = typeof items === "function" ? items : () => items || [];
|
||||||
const activeIndex = $(0);
|
const activeIndex = $(0);
|
||||||
Watch(() => {
|
Watch(() => {
|
||||||
const idx = itemsSignal().findIndex((it) => val(it.active) === true);
|
const list = itemsSignal();
|
||||||
if (idx !== -1 && idx !== activeIndex())
|
const idx = list.findIndex((it) => val(it.active) === true);
|
||||||
|
if (idx !== -1 && activeIndex() !== idx) {
|
||||||
activeIndex(idx);
|
activeIndex(idx);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
const removeTab = (indexToRemove, item) => {
|
||||||
|
if (item.onClose)
|
||||||
|
item.onClose();
|
||||||
|
const currentItems = itemsSignal();
|
||||||
|
const newItems = currentItems.filter((_, idx) => idx !== indexToRemove);
|
||||||
|
const isWritableSignal = typeof items === "function" && !items._isComputed;
|
||||||
|
if (!isWritableSignal) {
|
||||||
|
console.warn("Tabs: items must be a writable signal to support closable tabs");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
items(newItems);
|
||||||
|
if (newItems.length === 0)
|
||||||
|
return;
|
||||||
|
let newActive = activeIndex();
|
||||||
|
if (indexToRemove < newActive)
|
||||||
|
newActive--;
|
||||||
|
else if (indexToRemove === newActive)
|
||||||
|
newActive = Math.min(newActive, newItems.length - 1);
|
||||||
|
activeIndex(newActive);
|
||||||
|
};
|
||||||
return Tag("div", { ...rest, class: ui("tabs", className) }, () => {
|
return Tag("div", { ...rest, class: ui("tabs", className) }, () => {
|
||||||
const list = itemsSignal();
|
const list = itemsSignal();
|
||||||
const elements = [];
|
const elements = [];
|
||||||
for (let i = 0;i < list.length; i++) {
|
for (let i = 0;i < list.length; i++) {
|
||||||
const item = list[i];
|
const item = list[i];
|
||||||
const isActive = () => activeIndex() === i;
|
const label = val(item.label);
|
||||||
|
const labelNode = label instanceof Node ? label : document.createTextNode(String(label));
|
||||||
|
const buttonChildren = [];
|
||||||
|
if (item.closable) {
|
||||||
|
const closeIcon = getIcon("icon-[lucide--x]");
|
||||||
|
closeIcon.classList.add("w-3.5", "h-3.5", "ml-2", "cursor-pointer", "hover:opacity-70");
|
||||||
|
closeIcon.onclick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
removeTab(i, item);
|
||||||
|
};
|
||||||
|
const wrapper = Tag("span", { class: "flex items-center" }, [labelNode, closeIcon]);
|
||||||
|
buttonChildren.push(wrapper);
|
||||||
|
} else {
|
||||||
|
buttonChildren.push(labelNode);
|
||||||
|
}
|
||||||
const button = Tag("button", {
|
const button = Tag("button", {
|
||||||
class: () => ui("tab", isActive() ? "tab-active" : ""),
|
class: () => {
|
||||||
|
const isActive = activeIndex() === i;
|
||||||
|
return ui("tab", isActive ? "tab-active" : "");
|
||||||
|
},
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!val(item.disabled)) {
|
if (!val(item.disabled)) {
|
||||||
@@ -1849,21 +1888,24 @@
|
|||||||
item.onclick();
|
item.onclick();
|
||||||
activeIndex(i);
|
activeIndex(i);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
title: item.tip || ""
|
||||||
const label = val(item.label);
|
}, buttonChildren);
|
||||||
if (label instanceof Node) {
|
|
||||||
button.replaceChildren(label);
|
|
||||||
} else {
|
|
||||||
button.textContent = String(label);
|
|
||||||
}
|
|
||||||
elements.push(button);
|
elements.push(button);
|
||||||
|
let contentNode;
|
||||||
|
const rawContent = val(item.content);
|
||||||
|
if (typeof rawContent === "function") {
|
||||||
|
contentNode = rawContent();
|
||||||
|
} else if (rawContent instanceof Node) {
|
||||||
|
contentNode = rawContent;
|
||||||
|
} else {
|
||||||
|
contentNode = document.createTextNode(String(rawContent));
|
||||||
|
}
|
||||||
|
const inner = Tag("div", { class: "tab-content-inner" }, contentNode);
|
||||||
const panel = Tag("div", {
|
const panel = Tag("div", {
|
||||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||||
style: () => isActive() ? "display: block" : "display: none"
|
style: () => activeIndex() === i ? "display: block" : "display: none"
|
||||||
}, [
|
}, inner);
|
||||||
Tag("div", { class: "tab-content-inner" }, () => val(item.content))
|
|
||||||
]);
|
|
||||||
elements.push(panel);
|
elements.push(panel);
|
||||||
}
|
}
|
||||||
return elements;
|
return elements;
|
||||||
@@ -1899,7 +1941,7 @@
|
|||||||
!isFirst ? Tag("hr", { class: () => prevCompleted() ? "bg-primary" : "" }) : null,
|
!isFirst ? Tag("hr", { class: () => prevCompleted() ? "bg-primary" : "" }) : null,
|
||||||
Tag("div", { class: "timeline-start" }, [() => renderSlot(item.title)]),
|
Tag("div", { class: "timeline-start" }, [() => renderSlot(item.title)]),
|
||||||
Tag("div", { class: "timeline-middle" }, [
|
Tag("div", { class: "timeline-middle" }, [
|
||||||
() => item.icon ? getIcon(item.icon) : getIcon(iconMap[itemType] || iconMap.success)
|
() => item.icon ? getIcon2(item.icon) : getIcon2(iconMap[itemType] || iconMap.success)
|
||||||
]),
|
]),
|
||||||
Tag("div", { class: "timeline-end timeline-box shadow-sm" }, [() => renderSlot(item.detail)]),
|
Tag("div", { class: "timeline-end timeline-box shadow-sm" }, [() => renderSlot(item.detail)]),
|
||||||
!isLast ? Tag("hr", { class: () => isCompleted() ? "bg-primary" : "" }) : null
|
!isLast ? Tag("hr", { class: () => isCompleted() ? "bg-primary" : "" }) : null
|
||||||
@@ -1942,7 +1984,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const ToastComponent = () => {
|
const ToastComponent = () => {
|
||||||
const closeIcon = getIcon("icon-[lucide--x]");
|
const closeIcon = getIcon2("icon-[lucide--x]");
|
||||||
const el = Tag("div", {
|
const el = Tag("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`
|
||||||
}, [
|
}, [
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -15,51 +15,91 @@ export const Tabs = (props) => {
|
|||||||
const itemsSignal = typeof items === "function" ? items : () => items || [];
|
const itemsSignal = typeof items === "function" ? items : () => items || [];
|
||||||
const activeIndex = $(0);
|
const activeIndex = $(0);
|
||||||
|
|
||||||
|
// Sincroniza con active:true solo cuando cambia la lista de items
|
||||||
Watch(() => {
|
Watch(() => {
|
||||||
const idx = itemsSignal().findIndex(it => val(it.active) === true);
|
const list = itemsSignal();
|
||||||
if (idx !== -1 && idx !== activeIndex()) activeIndex(idx);
|
const idx = list.findIndex(it => val(it.active) === true);
|
||||||
|
if (idx !== -1 && activeIndex() !== idx) {
|
||||||
|
activeIndex(idx);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Contenedor principal con las clases de DaisyUI
|
const removeTab = (indexToRemove, item) => {
|
||||||
|
if (item.onClose) item.onClose();
|
||||||
|
const currentItems = itemsSignal();
|
||||||
|
const newItems = currentItems.filter((_, idx) => idx !== indexToRemove);
|
||||||
|
const isWritableSignal = typeof items === "function" && !items._isComputed;
|
||||||
|
if (!isWritableSignal) {
|
||||||
|
console.warn("Tabs: items must be a writable signal to support closable tabs");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
items(newItems);
|
||||||
|
if (newItems.length === 0) return;
|
||||||
|
let newActive = activeIndex();
|
||||||
|
if (indexToRemove < newActive) newActive--;
|
||||||
|
else if (indexToRemove === newActive) newActive = Math.min(newActive, newItems.length - 1);
|
||||||
|
activeIndex(newActive);
|
||||||
|
};
|
||||||
|
|
||||||
return Tag("div", { ...rest, class: ui('tabs', className) }, () => {
|
return Tag("div", { ...rest, class: ui('tabs', className) }, () => {
|
||||||
const list = itemsSignal();
|
const list = itemsSignal();
|
||||||
const elements = [];
|
const elements = [];
|
||||||
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
for (let i = 0; i < list.length; i++) {
|
||||||
const item = list[i];
|
const item = list[i];
|
||||||
const isActive = () => activeIndex() === i;
|
|
||||||
|
// --- Botón ---
|
||||||
// Botón (tab)
|
const label = val(item.label);
|
||||||
|
const labelNode = label instanceof Node ? label : document.createTextNode(String(label));
|
||||||
|
const buttonChildren = [];
|
||||||
|
|
||||||
|
if (item.closable) {
|
||||||
|
const closeIcon = getIcon("icon-[lucide--x]");
|
||||||
|
closeIcon.classList.add("w-3.5", "h-3.5", "ml-2", "cursor-pointer", "hover:opacity-70");
|
||||||
|
closeIcon.onclick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
removeTab(i, item);
|
||||||
|
};
|
||||||
|
const wrapper = Tag("span", { class: "flex items-center" }, [labelNode, closeIcon]);
|
||||||
|
buttonChildren.push(wrapper);
|
||||||
|
} else {
|
||||||
|
buttonChildren.push(labelNode);
|
||||||
|
}
|
||||||
|
|
||||||
const button = Tag("button", {
|
const button = Tag("button", {
|
||||||
class: () => ui("tab", isActive() ? "tab-active" : ""),
|
class: () => {
|
||||||
|
const isActive = activeIndex() === i;
|
||||||
|
return ui("tab", isActive ? "tab-active" : "");
|
||||||
|
},
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!val(item.disabled)) {
|
if (!val(item.disabled)) {
|
||||||
if (item.onclick) item.onclick();
|
if (item.onclick) item.onclick();
|
||||||
activeIndex(i);
|
activeIndex(i);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
title: item.tip || ""
|
||||||
// Asignar etiqueta (puede ser texto o nodo)
|
}, buttonChildren);
|
||||||
const label = val(item.label);
|
|
||||||
if (label instanceof Node) {
|
|
||||||
button.replaceChildren(label);
|
|
||||||
} else {
|
|
||||||
button.textContent = String(label);
|
|
||||||
}
|
|
||||||
elements.push(button);
|
elements.push(button);
|
||||||
|
|
||||||
// Contenido (tab-content) - borde exterior estático
|
// --- Panel ---
|
||||||
|
let contentNode;
|
||||||
|
const rawContent = val(item.content);
|
||||||
|
if (typeof rawContent === "function") {
|
||||||
|
contentNode = rawContent();
|
||||||
|
} else if (rawContent instanceof Node) {
|
||||||
|
contentNode = rawContent;
|
||||||
|
} else {
|
||||||
|
contentNode = document.createTextNode(String(rawContent));
|
||||||
|
}
|
||||||
|
|
||||||
|
const inner = Tag("div", { class: "tab-content-inner" }, contentNode);
|
||||||
const panel = Tag("div", {
|
const panel = Tag("div", {
|
||||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||||
style: () => isActive() ? "display: block" : "display: none"
|
style: () => activeIndex() === i ? "display: block" : "display: none"
|
||||||
}, [
|
}, inner);
|
||||||
// Contenedor interno con animación
|
|
||||||
Tag("div", { class: "tab-content-inner" }, () => val(item.content))
|
|
||||||
]);
|
|
||||||
elements.push(panel);
|
elements.push(panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return elements;
|
return elements;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -58,6 +58,10 @@
|
|||||||
--border: 1px;
|
--border: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Agrupamos los selectores normales de CSS */
|
/* Agrupamos los selectores normales de CSS */
|
||||||
.input, .select, .textarea {
|
.input, .select, .textarea {
|
||||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user