Rebuild all components
All checks were successful
Deploy Docs to Synology / deploy (push) Successful in 4s

This commit is contained in:
2026-04-21 18:00:17 +02:00
parent d900659d88
commit 16afea2768
67 changed files with 1820 additions and 2132 deletions

View File

@@ -1,22 +1,15 @@
// components/Collapse.js // components/Accordion.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
import { Collapse } from "./Collapse.js";
export const Collapse = (props, children) => { export const Accordion = (props) => {
const { class: className, title, name, open, ...rest } = props; const name = props.name || `accordion-${Math.random().toString(36).slice(2, 9)}`;
return Tag("div", { class: `space-y-2 ${props.class ?? ''}` },
return Tag("div", { props.items.map(item => Collapse({
...rest, ...item,
class: `collapse collapse-arrow bg-base-200 ${className || ''}`.trim() name,
}, [ type: "radio",
Tag("input", { class: item.class
type: name ? "radio" : "checkbox", }, item.children))
name: name, );
checked: () => typeof open === "function" ? open() : open,
onchange: (e) => {
if (typeof open === "function") open(e.target.checked);
}
}),
Tag("div", { class: "collapse-title text-xl font-medium" }, title),
Tag("div", { class: "collapse-content" }, children)
]);
}; };

View File

@@ -2,11 +2,6 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Alert = (props, children) => { export const Alert = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { ...props, class: `alert ${props.class ?? ''}` }, children);
return Tag("div", {
...rest,
role: "alert",
class: className || undefined
}, children);
}; };

View File

@@ -2,20 +2,19 @@
import { $, Tag, For, Watch } from "sigpro"; import { $, Tag, For, Watch } from "sigpro";
export const Autocomplete = (props) => { export const Autocomplete = (props) => {
const { class: className, items = [], value, onselect, placeholder, ...rest } = props; const query = $("");
const query = $(() => {
const v = typeof value === "function" ? value() : value;
return v || "";
});
const isOpen = $(false); const isOpen = $(false);
const cursor = $(-1); const cursor = $(-1);
const filteredItems = $([]); const filteredItems = $([]);
Watch(() => {
const v = typeof props.value === "function" ? props.value() : props.value;
return v || "";
}, (newVal) => query(newVal));
Watch(() => { Watch(() => {
const q = String(query()).toLowerCase(); const q = String(query()).toLowerCase();
const allItems = typeof items === "function" ? items() : items; const allItems = typeof props.items === "function" ? props.items() : props.items;
const filtered = q const filtered = q
? allItems.filter((item) => ? allItems.filter((item) =>
(typeof item === "string" ? item : item.label).toLowerCase().includes(q) (typeof item === "string" ? item : item.label).toLowerCase().includes(q)
@@ -28,8 +27,8 @@ export const Autocomplete = (props) => {
const display = typeof item === "string" ? item : item.label; const display = typeof item === "string" ? item : item.label;
const actual = typeof item === "string" ? item : item.value; const actual = typeof item === "string" ? item : item.value;
query(display); query(display);
if (typeof value === "function") value(actual); if (typeof props.value === "function") props.value(actual);
onselect?.(item); props.onselect?.(item);
isOpen(false); isOpen(false);
cursor(-1); cursor(-1);
}; };
@@ -51,22 +50,22 @@ export const Autocomplete = (props) => {
} }
}; };
return Tag("div", { class: `relative w-full ${className || ''}`.trim() }, [ return Tag("div", { class: `relative w-full ${props.class ?? ''}` }, [
Tag("label", { class: "input input-bordered w-full" }, [ Tag("label", { class: "input input-bordered w-full" }, [
Tag("span", { class: "icon-[lucide--search]" }), Tag("span", { class: "icon-[lucide--search]" }),
Tag("input", { Tag("input", {
...rest, ...props,
type: "text", type: "text",
class: "grow", class: "grow",
value: query, value: query,
placeholder: placeholder || "Buscar...", placeholder: props.placeholder || "Buscar...",
onfocus: () => isOpen(true), onfocus: () => isOpen(true),
onblur: () => setTimeout(() => isOpen(false), 150), onblur: () => setTimeout(() => isOpen(false), 150),
onkeydown: handleKeyDown, onkeydown: handleKeyDown,
oninput: (e) => { oninput: (e) => {
const newVal = e.target.value; const newVal = e.target.value;
query(newVal); query(newVal);
if (typeof value === "function") value(newVal); if (typeof props.value === "function") props.value(newVal);
isOpen(true); isOpen(true);
cursor(-1); cursor(-1);
} }
@@ -87,7 +86,7 @@ export const Autocomplete = (props) => {
]), ]),
(item, idx) => (typeof item === "string" ? item : item.value) + idx (item, idx) => (typeof item === "string" ? item : item.value) + idx
), ),
() => filteredItems().length === 0 && Tag("li", { class: "p-2 text-center opacity-50" }, "Sin resultados") () => filteredItems().length === 0 ? Tag("li", { class: "flex justify-center p-4 opacity-50" }, Tag("span", { class: "icon-[lucide--search-x] text-2xl" })) : null
]) ])
]); ]);
}; };

View File

@@ -2,10 +2,6 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Badge = (props, children) => { export const Badge = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("span", { ...props, class: `badge ${props.class ?? ''}` }, children);
return Tag("span", {
...rest,
class: className || undefined
}, children);
}; };

View File

@@ -1,9 +1,6 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Button = (props, children) => { export const Button = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("button", { return Tag("button", { ...props, class: `btn ${props.class ?? ''}` }, children);
...rest,
class: `btn ${className || ''}`.trim()
}, children);
}; };

View File

@@ -1,15 +1,14 @@
// components/Calendar.js // components/Calendar.js
import { $, Tag, Watch } from "sigpro"; import { $, Tag } from "sigpro";
export const Calendar = (props) => { export const Calendar = (props) => {
const { value, range = false, hour = false, onChange, class: className = "" } = props;
const internalDate = $(new Date()); const internalDate = $(new Date());
const hoverDate = $(null); const hoverDate = $(null);
const startHour = $(0); const startHour = $(0);
const endHour = $(0); const endHour = $(0);
const isRangeMode = () => { const isRangeMode = () => {
const r = typeof range === "function" ? range() : range; const r = typeof props.range === "function" ? props.range() : props.range;
return r === true; return r === true;
}; };
@@ -24,8 +23,7 @@ export const Calendar = (props) => {
}; };
const getCurrentValue = () => { const getCurrentValue = () => {
const v = value; return typeof props.value === "function" ? props.value() : props.value;
return typeof v === "function" ? v() : v;
}; };
const selectDate = (date) => { const selectDate = (date) => {
@@ -37,9 +35,9 @@ export const Calendar = (props) => {
const newValue = { const newValue = {
start: dateStr, start: dateStr,
end: null, end: null,
...(hour && { startHour: startHour() }), ...(props.hour && { startHour: startHour() }),
}; };
onChange?.(newValue); props.onChange?.(newValue);
} else { } else {
const start = current.start; const start = current.start;
let newValue; let newValue;
@@ -48,15 +46,15 @@ export const Calendar = (props) => {
} else { } else {
newValue = { start, end: dateStr }; newValue = { start, end: dateStr };
} }
if (hour) { if (props.hour) {
newValue.startHour = current.startHour !== undefined ? current.startHour : startHour(); newValue.startHour = current.startHour !== undefined ? current.startHour : startHour();
newValue.endHour = endHour(); newValue.endHour = endHour();
} }
onChange?.(newValue); props.onChange?.(newValue);
} }
} else { } else {
const newValue = hour ? `${dateStr}T${String(startHour()).padStart(2, "0")}:00:00` : dateStr; const newValue = props.hour ? `${dateStr}T${String(startHour()).padStart(2, "0")}:00:00` : dateStr;
onChange?.(newValue); props.onChange?.(newValue);
} }
}; };
@@ -88,7 +86,7 @@ export const Calendar = (props) => {
]); ]);
}; };
return Tag("div", { class: `p-4 bg-base-100 border border-base-300 shadow-2xl rounded-box w-80 select-none ${className}`.trim() }, [ return Tag("div", { class: `p-4 bg-base-100 border border-base-300 shadow-2xl rounded-box w-80 select-none ${props.class ?? ''}`.trim() }, [
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) }, Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) },
@@ -162,7 +160,7 @@ export const Calendar = (props) => {
} }
]), ]),
hour ? Tag("div", { class: "mt-3 pt-2 border-t border-base-300" }, [ props.hour ? Tag("div", { class: "mt-3 pt-2 border-t border-base-300" }, [
isRangeMode() isRangeMode()
? Tag("div", { class: "flex gap-4" }, [ ? Tag("div", { class: "flex gap-4" }, [
HourSlider({ value: startHour, onChange: (h) => startHour(h) }), HourSlider({ value: startHour, onChange: (h) => startHour(h) }),

View File

@@ -2,33 +2,21 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Card = (props, children) => { export const Card = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `card ${props.class ?? ''}` }, children);
...rest,
class: `card ${className || ''}`.trim()
}, children);
}; };
export const CardTitle = (props, children) => { export const CardTitle = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `card-title ${props.class ?? ''}` }, children);
...rest,
class: `card-title ${className || ''}`.trim()
}, children);
}; };
export const CardBody = (props, children) => { export const CardBody = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `card-body ${props.class ?? ''}` }, children);
...rest,
class: `card-body ${className || ''}`.trim()
}, children);
}; };
export const CardActions = (props, children) => { export const CardActions = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `card-actions ${props.class ?? ''}` }, children);
...rest,
class: `card-actions ${className || ''}`.trim()
}, children);
}; };

View File

@@ -2,17 +2,11 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Carousel = (props, children) => { export const Carousel = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `carousel ${props.class ?? ''}` }, children);
...rest,
class: `carousel ${className || ''}`.trim()
}, children);
}; };
export const CarouselItem = (props, children) => { export const CarouselItem = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `carousel-item ${props.class ?? ''}` }, children);
...rest,
class: `carousel-item ${className || ''}`.trim()
}, children);
}; };

View File

@@ -2,41 +2,41 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Chat = (props, children) => { export const Chat = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `chat ${props.class ?? ''}` }, children);
...rest,
class: `chat ${className || ''}`.trim()
}, children);
}; };
export const ChatImage = (props, children) => { export const ChatImage = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `chat-image avatar ${props.class ?? ''}` },
...rest, Tag("div", { class: "w-10 rounded-full" },
class: `chat-image ${className || ''}`.trim() typeof children === "string" ? Tag("img", { src: children, alt: "avatar" }) : children
}, children); )
);
}; };
export const ChatHeader = (props, children) => { export const ChatHeader = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `chat-header ${props.class ?? ''}` }, children);
...rest,
class: `chat-header ${className || ''}`.trim()
}, children);
}; };
export const ChatFooter = (props, children) => { export const ChatFooter = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `chat-footer ${props.class ?? ''}` }, children);
...rest,
class: `chat-footer ${className || ''}`.trim()
}, children);
}; };
export const ChatBubble = (props, children) => { export const ChatBubble = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `chat-bubble ${props.class ?? ''}` }, children);
...rest, };
class: `chat-bubble ${className || ''}`.trim()
}, children); export const ChatMessage = (props) => {
const { position = "start", avatar, header, message, footer, bubbleClass, ...rest } = props;
return Chat({ ...rest, class: `chat-${position} ${props.class ?? ''}` }, [
avatar && ChatImage(avatar),
header && ChatHeader(header),
ChatBubble({ class: bubbleClass }, message),
footer && ChatFooter(footer)
]);
}; };

View File

@@ -1,19 +1,4 @@
// components/Checkbox.js // components/Checkbox.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Checkbox = (props) => { export const Checkbox = (props) => Tag("input", { ...props, type: "checkbox", class: `checkbox ${props.class ?? ''}` });
const { class: className, label, ...rest } = props;
const inputEl = Tag("input", {
...rest,
type: "checkbox",
class: className || undefined
});
if (!label) return inputEl;
return Tag("label", { class: "label cursor-pointer justify-start gap-3" }, [
inputEl,
Tag("span", { class: "label-text" }, label)
]);
};

View File

@@ -2,20 +2,6 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Collapse = (props, children) => { export const Collapse = (props, children) => {
const { class: className, open, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { ...props, class: `collapse ${props.class ?? ''}` }, children);
return Tag("div", {
...rest,
class: `collapse ${className || ''}`.trim(),
tabindex: 0
}, [
Tag("input", {
type: "checkbox",
checked: () => typeof open === "function" ? open() : open,
onchange: (e) => {
if (typeof open === "function") open(e.target.checked);
}
}),
...(Array.isArray(children) ? children : [children])
]);
}; };

View File

@@ -2,7 +2,6 @@
import { $, Tag, If } from "sigpro"; import { $, Tag, If } from "sigpro";
export const Colorpicker = (props) => { export const Colorpicker = (props) => {
const { class: className, value, label, ...rest } = props;
const isOpen = $(false); const isOpen = $(false);
const palette = [ const palette = [
@@ -17,67 +16,53 @@ export const Colorpicker = (props) => {
]; ];
const getColor = () => { const getColor = () => {
const v = value; const v = props.value;
return (typeof v === "function" ? v() : v) || "#000000"; return (typeof v === "function" ? v() : v) || "#000000";
}; };
return Tag("div", { class: `relative w-fit ${className || ''}`.trim() }, [ return Tag("div", { class: `relative w-fit ${props.class ?? ''}` }, [
Tag( Tag("button", {
"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",
type: "button", onclick: (e) => { e.stopPropagation(); isOpen(!isOpen()); },
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", ...props
onclick: (e) => { }, [
e.stopPropagation(); Tag("div", {
isOpen(!isOpen()); class: "size-5 rounded-sm shadow-inner border border-black/10 shrink-0",
}, style: () => `background-color: ${getColor()}`
...rest, }),
}, props.label ? Tag("span", { class: "opacity-80" }, props.label) : null
[ ]),
Tag("div", {
class: "size-5 rounded-sm shadow-inner border border-black/10 shrink-0",
style: () => `background-color: ${getColor()}`,
}),
label ? Tag("span", { class: "opacity-80" }, label) : null,
],
),
If(isOpen, () => If(isOpen, () =>
Tag( Tag("div", {
"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",
{ onclick: (e) => e.stopPropagation()
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(), Tag("div", { class: "grid grid-cols-8 gap-1" },
}, palette.map(c =>
[ Tag("button", {
Tag( type: "button",
"div", style: `background-color: ${c}`,
{ class: "grid grid-cols-8 gap-1" }, class: () => {
palette.map((c) => const active = getColor().toLowerCase() === c.toLowerCase();
Tag("button", { return `size-6 rounded-sm cursor-pointer transition-all hover:scale-125 hover:z-10 active:scale-95 outline-none border border-black/5 p-0 min-h-0 ${active ? "ring-2 ring-offset-1 ring-primary z-10 scale-110" : ""}`;
type: "button", },
style: `background-color: ${c}`, onclick: () => {
class: () => { if (typeof props.value === "function") props.value(c);
const active = getColor().toLowerCase() === c.toLowerCase(); isOpen(false);
return `size-6 rounded-sm cursor-pointer transition-all hover:scale-125 hover:z-10 active:scale-95 outline-none border border-black/5 p-0 min-h-0 }
${active ? "ring-2 ring-offset-1 ring-primary z-10 scale-110" : ""}`; })
}, )
onclick: () => { )
if (typeof value === "function") value(c); ])
isOpen(false);
},
}),
),
),
],
),
), ),
If(isOpen, () => If(isOpen, () =>
Tag("div", { Tag("div", {
class: "fixed inset-0 z-[100]", class: "fixed inset-0 z-[100]",
onclick: () => isOpen(false), onclick: () => isOpen(false)
}), })
), )
]); ]);
}; };

View File

@@ -3,35 +3,33 @@ import { $, Tag, If, Watch } from "sigpro";
import { Calendar } from "./Calendar.js"; import { Calendar } from "./Calendar.js";
export const Datepicker = (props) => { export const Datepicker = (props) => {
const { class: className, value, range, placeholder, hour = false, ...rest } = props;
const isOpen = $(false); const isOpen = $(false);
const isRangeMode = () => { const isRangeMode = () => {
const r = typeof range === "function" ? range() : range; const r = typeof props.range === "function" ? props.range() : props.range;
return r === true; return r === true;
}; };
const displayValue = $(""); const displayValue = $("");
Watch(() => { Watch(() => {
const v = typeof value === "function" ? value() : value; const v = typeof props.value === "function" ? props.value() : props.value;
if (!v) { if (!v) {
displayValue(""); displayValue("");
return; return;
} }
let text = ""; let text = "";
if (typeof v === "string") { if (typeof v === "string") {
text = (hour && v.includes("T")) ? v.replace("T", " ") : v; text = (props.hour && v.includes("T")) ? v.replace("T", " ") : v;
} else if (v.start && v.end) { } else if (v.start && v.end) {
const startStr = hour && v.startHour !== undefined const startStr = props.hour && v.startHour !== undefined
? `${v.start} ${String(v.startHour).padStart(2, "0")}:00` ? `${v.start} ${String(v.startHour).padStart(2, "0")}:00`
: v.start; : v.start;
const endStr = hour && v.endHour !== undefined const endStr = props.hour && v.endHour !== undefined
? `${v.end} ${String(v.endHour).padStart(2, "0")}:00` ? `${v.end} ${String(v.endHour).padStart(2, "0")}:00`
: v.end; : v.end;
text = `${startStr} - ${endStr}`; text = `${startStr} - ${endStr}`;
} else if (v.start) { } else if (v.start) {
const startStr = hour && v.startHour !== undefined const startStr = props.hour && v.startHour !== undefined
? `${v.start} ${String(v.startHour).padStart(2, "0")}:00` ? `${v.start} ${String(v.startHour).padStart(2, "0")}:00`
: v.start; : v.start;
text = `${startStr}...`; text = `${startStr}...`;
@@ -40,7 +38,7 @@ export const Datepicker = (props) => {
}); });
const handleCalendarChange = (newValue) => { const handleCalendarChange = (newValue) => {
if (typeof value === "function") value(newValue); if (typeof props.value === "function") props.value(newValue);
if (!isRangeMode() || (newValue?.end !== undefined && newValue?.end !== null)) { if (!isRangeMode() || (newValue?.end !== undefined && newValue?.end !== null)) {
isOpen(false); isOpen(false);
} }
@@ -51,16 +49,16 @@ export const Datepicker = (props) => {
isOpen(!isOpen()); isOpen(!isOpen());
}; };
return Tag("div", { class: `relative w-full ${className || ''}`.trim() }, [ return Tag("div", { class: `relative w-full ${props.class ?? ''}` }, [
Tag("label", { class: "input input-bordered w-full", onclick: toggleOpen }, [ Tag("label", { class: "input input-bordered w-full", onclick: toggleOpen }, [
Tag("span", { class: "icon-[lucide--calendar]" }), Tag("span", { class: "icon-[lucide--calendar]" }),
Tag("input", { Tag("input", {
...rest, ...props,
type: "text", type: "text",
class: "grow", class: "grow",
value: displayValue, value: displayValue,
readonly: true, readonly: true,
placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha...") placeholder: props.placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha...")
}) })
]), ]),
@@ -70,9 +68,9 @@ export const Datepicker = (props) => {
onclick: (e) => e.stopPropagation() onclick: (e) => e.stopPropagation()
}, [ }, [
Calendar({ Calendar({
value, value: props.value,
range: isRangeMode(), range: isRangeMode(),
hour, hour: props.hour,
onChange: handleCalendarChange onChange: handleCalendarChange
}) })
]) ])

4
components/Divider.js Normal file
View File

@@ -0,0 +1,4 @@
// components/Collapse.js
import { Tag } from "sigpro";
export const Divider = (props) => Tag("div", { ...props, class: `divider ${props.class ?? ''}` });

View File

@@ -1,36 +1,31 @@
// components/Drawer.js // components/Drawer.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Drawer = (props) => { export const Drawer = (props, children) => {
const { class: className, id, open, content, children, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { ...props, class: `drawer ${props.class ?? ''}` }, children);
const drawerId = id || `drawer-${Math.random().toString(36).slice(2, 9)}`; };
return Tag("div", { export const Sidebar = (props) => {
...rest, const id = props.id || `drawer-${Math.random().toString(36).slice(2, 9)}`;
class: `drawer ${className || ''}`.trim() return Tag("div", { ...props, class: `drawer ${props.class ?? ''}` }, [
}, [
Tag("input", { Tag("input", {
id: drawerId, id,
type: "checkbox", type: "checkbox",
class: "drawer-toggle", class: "drawer-toggle",
checked: () => typeof open === "function" ? open() : open, checked: () => (typeof props.open === "function" ? props.open() : props.open),
onchange: (e) => { onchange: (e) => typeof props.open === "function" && props.open(e.target.checked)
if (typeof open === "function") open(e.target.checked);
}
}), }),
Tag("div", { class: "drawer-content" }, children), Tag("div", { class: "drawer-content" }, props.children),
Tag("div", { class: "drawer-side" }, [ Tag("div", { class: "drawer-side" }, [
Tag("label", { Tag("label", {
for: drawerId, for: id,
class: "drawer-overlay", class: "drawer-overlay",
onclick: () => { onclick: () => typeof props.open === "function" && props.open(false)
if (typeof open === "function") open(false);
}
}), }),
Tag("div", { class: "min-h-full bg-base-200 w-80 p-4" }, [ Tag("div", { class: "min-h-full bg-base-200 w-80 p-4" },
typeof content === "function" ? content() : content typeof props.content === "function" ? props.content() : props.content
]) )
]) ])
]); ]);
}; };

View File

@@ -13,20 +13,12 @@ if (typeof window !== 'undefined' && !window.__dropdownHandlerRegistered) {
window.__dropdownHandlerRegistered = true; window.__dropdownHandlerRegistered = true;
} }
export const Dropdown = (props) => { export const Dropdown = (props) => Tag("details", {
const { class: className, children, ...rest } = props; ...props,
class: `dropdown ${props.class ?? ''}`,
return Tag("details", { onclick: (e) => {
...rest, const details = e.currentTarget;
class: `dropdown ${className || ''}`.trim(), if (currentOpen && currentOpen !== details) currentOpen.open = false;
onclick: (e) => { setTimeout(() => { currentOpen = details.open ? details : null; }, 0);
const details = e.currentTarget; }
if (currentOpen && currentOpen !== details) { }, props.children);
currentOpen.open = false;
}
setTimeout(() => {
currentOpen = details.open ? details : null;
}, 0);
}
}, children);
};

View File

@@ -2,10 +2,6 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Fab = (props, children) => { export const Fab = (props, children) => {
const { class: className, position = "bottom-6 right-6", ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { ...props, class: `fab ${props.class ?? ''}` }, children);
return Tag("div", {
...rest,
class: `absolute ${position} flex flex-col-reverse items-end gap-3 z-[100] ${className || ''}`.trim()
}, children);
}; };

View File

@@ -1,14 +1,10 @@
// components/Fieldset.js // components/Fieldset.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Fieldset = (props, children) => { export const Fieldset = (props, children) => Tag("fieldset", {
const { class: className, legend, ...rest } = props; ...props,
class: `fieldset ${props.class ?? ''}`
return Tag("fieldset", { }, [
...rest, props.legend ? Tag("legend", { class: "fieldset-legend" }, props.legend) : null,
class: `fieldset ${className || ''}`.trim() children
}, [ ]);
legend ? Tag("legend", { class: "fieldset-legend" }, legend) : null,
children
]);
};

View File

@@ -2,42 +2,31 @@
import { $, Tag, If, For } from "sigpro"; import { $, Tag, If, For } from "sigpro";
export const Fileinput = (props) => { export const Fileinput = (props) => {
const { class: className, max = 2, accept = "*", onselect, ...rest } = props;
const selectedFiles = $([]); const selectedFiles = $([]);
const isDragging = $(false); const isDragging = $(false);
const error = $(null); const error = $(null);
const MAX_BYTES = max * 1024 * 1024; const maxBytes = (props.max || 2) * 1024 * 1024;
const handleFiles = (files) => { const handleFiles = (files) => {
const fileList = Array.from(files); const fileList = Array.from(files);
error(null); error(null);
const oversized = fileList.find((f) => f.size > MAX_BYTES); if (fileList.find(f => f.size > maxBytes)) {
if (oversized) { error(`Máx ${props.max || 2}MB`);
error(`Máx ${max}MB`);
return; return;
} }
selectedFiles([...selectedFiles(), ...fileList]); selectedFiles([...selectedFiles(), ...fileList]);
onselect?.(selectedFiles()); props.onselect?.(selectedFiles());
}; };
const removeFile = (index) => { const removeFile = (idx) => {
const updated = selectedFiles().filter((_, i) => i !== index); const updated = selectedFiles().filter((_, i) => i !== idx);
selectedFiles(updated); selectedFiles(updated);
onselect?.(updated); props.onselect?.(updated);
}; };
return Tag("div", { return Tag("div", { ...props, class: `fieldset w-full p-0 ${props.class ?? ''}` }, [
...rest,
class: `fieldset w-full p-0 ${className || ''}`.trim()
}, [
Tag("label", { Tag("label", {
class: () => ` class: () => `relative flex items-center justify-between w-full h-12 px-4 border-2 border-dashed rounded-lg cursor-pointer transition-all duration-200 ${isDragging() ? "border-primary bg-primary/10" : "border-base-content/20 bg-base-100 hover:bg-base-200"}`,
relative flex items-center justify-between w-full h-12 px-4
border-2 border-dashed rounded-lg cursor-pointer
transition-all duration-200
${isDragging() ? "border-primary bg-primary/10" : "border-base-content/20 bg-base-100 hover:bg-base-200"}
`,
ondragover: (e) => { e.preventDefault(); isDragging(true); }, ondragover: (e) => { e.preventDefault(); isDragging(true); },
ondragleave: () => isDragging(false), ondragleave: () => isDragging(false),
ondrop: (e) => { e.preventDefault(); isDragging(false); handleFiles(e.dataTransfer.files); } ondrop: (e) => { e.preventDefault(); isDragging(false); handleFiles(e.dataTransfer.files); }
@@ -45,19 +34,17 @@ export const Fileinput = (props) => {
Tag("div", { class: "flex items-center gap-3 w-full" }, [ Tag("div", { class: "flex items-center gap-3 w-full" }, [
Tag("span", { class: "icon-[lucide--upload]" }), Tag("span", { class: "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 ${props.max || 2}MB`)
]), ]),
Tag("input", { Tag("input", {
type: "file", type: "file",
multiple: true, multiple: true,
accept, accept: props.accept || "*",
class: "hidden", class: "hidden",
onchange: (e) => handleFiles(e.target.files) onchange: (e) => handleFiles(e.target.files)
}) })
]), ]),
() => error() && Tag("span", { class: "text-[10px] text-error mt-1 px-1 font-medium" }, error()), () => error() && Tag("span", { class: "text-[10px] text-error mt-1 px-1 font-medium" }, error()),
If(() => selectedFiles().length > 0, () => If(() => selectedFiles().length > 0, () =>
Tag("ul", { class: "mt-2 space-y-1" }, [ Tag("ul", { class: "mt-2 space-y-1" }, [
For(selectedFiles, (file, idx) => For(selectedFiles, (file, idx) =>

55
components/FilterList.js Normal file
View File

@@ -0,0 +1,55 @@
// components/FilterList.js
import { $, Tag, For, Watch } from "sigpro";
export const FilterList = (props) => {
const { items, filterKeys = ["label"], placeholder = "Filtrar...", renderItem, onSelect, class: className } = props;
const filterText = $("");
const filteredItems = $([]);
const updateFiltered = () => {
const q = String(filterText()).toLowerCase();
// items puede ser función, señal o array plano
const allItems = typeof items === "function" ? items() : items || [];
if (!q) {
filteredItems([...allItems]);
return;
}
const filtered = allItems.filter(item =>
filterKeys.some(key => {
const val = typeof item === "string" ? item : item[key];
return String(val || "").toLowerCase().includes(q);
})
);
filteredItems(filtered);
};
// Ejecutar inmediatamente al montar y luego reactivamente cuando cambie items o filterText
updateFiltered();
Watch(() => {
// Dependencias: items (evaluado) y filterText
const it = typeof items === "function" ? items() : items;
return { it, ft: filterText() };
}, () => updateFiltered());
return Tag("div", { class: `filter-list flex flex-col ${className ?? ''}` }, [
Tag("div", { class: "p-2 border-b border-base-300" }, [
Tag("label", { class: "input input-sm input-bordered flex items-center gap-2 w-full" }, [
Tag("span", { class: "icon-[lucide--search] opacity-50" }),
Tag("input", {
type: "text",
class: "grow",
placeholder,
value: filterText,
oninput: (e) => filterText(e.target.value),
ref: (el) => { if (el && props.autoFocus) el.focus(); }
})
])
]),
Tag("div", { class: "max-h-60 overflow-y-auto" }, [
For(filteredItems, (item, idx) =>
renderItem(item, idx, () => onSelect?.(item))
)
])
]);
};

View File

@@ -1,7 +1,14 @@
// components/Icon.js // components/Icon.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Icon = (iconClass) => { export const Icon = (props, children) => {
if (!iconClass) return null; if (typeof props === "string") {
return Tag("span", { class: iconClass }); if (props.includes("icon-") || props.startsWith("lucide-")) {
}; return Tag("span", { class: props }, children);
}
return Tag("span", { class: "icon" }, props);
}
if (!props) return null;
const { class: className, ...rest } = props;
return Tag("span", { ...rest, class: className }, children);
};

View File

@@ -2,13 +2,9 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Indicator = (props, children) => { export const Indicator = (props, children) => {
const { value, class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { ...props, class: `indicator ${props.class ?? ''}` }, [
return Tag("div", { props.value ? Tag("span", { class: `indicator-item badge ${props.class ?? ''}` }, props.value) : null,
...rest,
class: "indicator"
}, [
value ? Tag("span", { class: `indicator-item badge ${className || ''}`.trim() }, value) : null,
children children
]); ]);
}; };

View File

@@ -1,10 +1,4 @@
// components/Input.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Input = (props) => { export const Input = (props) => Tag("input", { ...props, class: `input ${props.class ?? ''}` });
const { class: className, type = "text", ...rest } = props;
return Tag("input", {
...rest,
type,
class: `input ${className || ''}`.trim()
});
};

View File

@@ -2,13 +2,6 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Kbd = (props, children) => { export const Kbd = (props, children) => {
if (typeof props === "string" || typeof props === "number") { children === undefined && (children = props, props = {});
children = props; return Tag("kbd", { ...props, class: `kbd ${props.class ?? ''}` }, children);
props = {};
}
const { class: className, ...rest } = props;
return Tag("kbd", {
...rest,
class: `kbd ${className || ''}`.trim()
}, children);
}; };

View File

@@ -1,11 +0,0 @@
// components/List.js
import { Tag } from "sigpro";
export const List = (props, children) => {
const { class: className, ...rest } = props;
return Tag("ul", {
...rest,
class: `list ${className || ''}`.trim()
}, children);
};

View File

@@ -1,10 +1,7 @@
// components/Loading.js // components/Loading.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Spinner = (props) => { export const Loading = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("span", { return Tag("span", { ...props, class: `loading loading-spinner ${props.class ?? ''}` }, children);
...rest,
class: `loading loading-spinner ${className || ''}`.trim()
});
}; };

View File

@@ -1,11 +1,32 @@
// components/Menu.js // components/Menu.js
import { Tag } from "sigpro"; import { Tag, For } from "sigpro";
export const Menu = (props, children) => { export const Menu = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("ul", { ...props, class: `menu ${props.class ?? ''}` }, children);
};
return Tag("ul", { export const MenuItems = (props) => {
...rest, const { items, keyFn = (item, idx) => item.id ?? idx } = props;
class: `menu ${className || ''}`.trim() const itemsSignal = typeof items === "function" ? items : () => items || [];
}, children);
const renderItem = (item) => {
if (item.children) {
return Tag("li", {}, [
Tag("details", {}, [
Tag("summary", {}, item.label),
Tag("ul", {}, MenuItems({ items: item.children }))
])
]);
}
return Tag("li", {}, Tag("a", {
href: item.href,
onclick: item.onclick ? (e) => {
if (!item.href) e.preventDefault();
item.onclick(e);
} : null
}, item.label));
};
return For(itemsSignal, renderItem, keyFn);
}; };

View File

@@ -2,31 +2,28 @@
import { Tag, Watch } from "sigpro"; import { Tag, Watch } from "sigpro";
export const Modal = (props) => { export const Modal = (props) => {
const { class: className, open, title, children, ...rest } = props;
let dialogRef = null; let dialogRef = null;
Watch(() => { Watch(() => {
const isOpen = typeof open === "function" ? open() : open; const isOpen = typeof props.open === "function" ? props.open() : props.open;
if (!dialogRef) return; if (!dialogRef) return;
isOpen ? dialogRef.showModal() : dialogRef.close(); isOpen ? dialogRef.showModal() : dialogRef.close();
}); });
const close = () => { const close = () => typeof props.open === "function" && props.open(false);
if (typeof open === "function") open(false);
};
return Tag("dialog", { return Tag("dialog", {
...rest, ...props,
ref: el => dialogRef = el, ref: el => dialogRef = el,
class: `modal ${className || ''}`.trim(), class: `modal ${props.class ?? ''}`,
onclose: close, onclose: close,
oncancel: close oncancel: close
}, [ }, [
Tag("div", { class: "modal-box" }, [ Tag("div", { class: "modal-box" }, [
title && Tag("h3", { class: "text-lg font-bold" }, title), props.title && Tag("h3", { class: "text-lg font-bold" }, props.title),
children, props.children,
Tag("div", { class: "modal-action" }, [ Tag("div", { class: "modal-action" }, [
props.actions || Button({ onclick: close }, "Cerrar") props.actions || Tag("button", { class: "btn", onclick: close }, "Cerrar")
]) ])
]), ]),
Tag("form", { method: "dialog", class: "modal-backdrop" }, [ Tag("form", { method: "dialog", class: "modal-backdrop" }, [

View File

@@ -2,10 +2,6 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Navbar = (props, children) => { export const Navbar = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { ...props, class: `navbar ${props.class ?? ''}` }, children);
return Tag("div", {
...rest,
class: `navbar ${className || ''}`.trim()
}, children);
}; };

View File

@@ -1,18 +1,18 @@
// components/RadialProgress.js // components/Radial.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const RadialProgress = (props) => { export const Radial = (props, children) => {
const { class: className, value, max = 100, children, ...rest } = props; children === undefined && (children = props, props = {});
const percentage = value != null ? (value / max) * 100 : 0; const percentage = props.value != null ? (props.value / (props.max || 100)) * 100 : 0;
const style = `--value: ${percentage}; --max: 100;`; const style = `--value: ${percentage}; --max: 100;`;
return Tag("div", { return Tag("div", {
...rest, ...props,
class: `radial-progress ${className || ''}`.trim(), class: `radial-progress ${props.class ?? ''}`,
style: style, style: style,
role: "progressbar", role: "progressbar",
"aria-valuenow": value, "aria-valuenow": props.value,
"aria-valuemin": 0, "aria-valuemin": 0,
"aria-valuemax": max "aria-valuemax": props.max || 100
}, children || `${Math.round(percentage)}%`); }, children || `${Math.round(percentage)}%`);
}; };

View File

@@ -1,19 +1,4 @@
// components/Radio.js // components/Radio.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Radio = (props) => { export const Radio = (props) => Tag("input", { ...props, type: "radio", class: `radio ${props.class ?? ''}` });
const { class: className, label, ...rest } = props;
const radioEl = Tag("input", {
...rest,
type: "radio",
class: `radio ${className || ''}`.trim()
});
if (!label) return radioEl;
return Tag("label", { class: "label cursor-pointer justify-start gap-3" }, [
radioEl,
Tag("span", { class: "label-text" }, label)
]);
};

View File

@@ -1,12 +1,4 @@
// components/Range.js // components/Range.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Range = (props) => { export const Range = (props) => Tag("input", { ...props, type: "range", class: `range ${props.class ?? ''}` });
const { class: className, ...rest } = props;
return Tag("input", {
...rest,
type: "range",
class: `range ${className || ''}`.trim()
});
};

View File

@@ -2,23 +2,19 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Rating = (props, children) => { export const Rating = (props, children) => {
const { class: className, count, mask = "mask-star", value, onchange, ...rest } = props; children === undefined && (children = props, props = {});
const name = `rating-${Math.random().toString(36).slice(2, 7)}`; const name = `rating-${Math.random().toString(36).slice(2, 7)}`;
return Tag("div", { return Tag("div", { ...props, class: `rating ${props.class ?? ''}` }, children || Array.from({ length: props.count || 5 }, (_, i) => {
...rest,
class: `rating ${className || ''}`.trim()
}, children || Array.from({ length: count || 5 }, (_, i) => {
const starValue = i + 1; const starValue = i + 1;
return Tag("input", { return Tag("input", {
type: "radio", type: "radio",
name, name,
class: `mask ${mask}`, class: `mask ${props.mask || "mask-star"}`,
checked: () => typeof value === "function" ? value() === starValue : value === starValue, checked: () => typeof props.value === "function" ? props.value() === starValue : props.value === starValue,
onchange: () => { onchange: () => {
if (onchange) onchange(starValue); if (props.onchange) props.onchange(starValue);
else if (typeof value === "function") value(starValue); else if (typeof props.value === "function") props.value(starValue);
} }
}); });
})); }));

View File

@@ -2,24 +2,24 @@
import { Tag, For } from "sigpro"; import { Tag, For } from "sigpro";
export const Select = (props, children) => { export const Select = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("select", { return Tag("select", { ...props, class: `select ${props.class ?? ''}` }, children);
...rest,
class: `select ${className || ''}`.trim()
}, children);
}; };
export const Options = (props) => { export const Options = (props) => {
const { items, placeholder, placeholderDisabled = true, ...rest } = props; const placeholderOption = props.placeholder
? Tag("option", { disabled: props.placeholderDisabled ?? true, selected: true }, props.placeholder)
const itemArray = typeof items === "function" ? items() : (items || []); : null;
return [ const dynamicOptions = For(
placeholder && Tag("option", { disabled: placeholderDisabled, selected: true }, placeholder), () => [...(typeof props.items === "function" ? props.items() : props.items || [])],
For(itemArray, (item) => { (item) => {
const val = typeof item === "string" ? item : item.value; const val = typeof item === "string" ? item : item.value;
const label = typeof item === "string" ? item : item.label; const label = typeof item === "string" ? item : item.label;
return Tag("option", { value: val, ...rest }, label); return Tag("option", { value: val }, label);
}) },
]; props.keyFn || ((item) => (typeof item === "string" ? item : item.value))
);
return placeholderOption ? [placeholderOption, dynamicOptions] : dynamicOptions;
}; };

View File

@@ -1,19 +1,12 @@
// components/Skeleton.js // components/Skeleton.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Skeleton = (props) => { export const Skeleton = (props) => Tag("div", { ...props, class: `skeleton ${props.class ?? ''}` });
const { class: className, ...rest } = props;
return Tag("div", {
...rest,
class: `skeleton ${className || ''}`.trim()
});
};
export const SkeletonText = (props) => { export const SkeletonText = (props) => {
const { class: className, lines = 3, ...rest } = props; return Tag("div", { ...props, class: "space-y-2" },
return Tag("div", { ...rest, class: "space-y-2" }, Array.from({ length: props.lines || 3 }, () =>
Array.from({ length: lines }, (_, i) => Tag("div", { class: `skeleton h-4 w-full ${props.class ?? ''}` })
Tag("div", { class: `skeleton h-4 w-full ${className || ''}`.trim() })
) )
); );
}; };

View File

@@ -2,9 +2,6 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Stack = (props, children) => { export const Stack = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `stack ${props.class ?? ''}` }, children);
...rest,
class: `stack ${className || ''}`.trim()
}, children);
}; };

View File

@@ -2,26 +2,19 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Stats = (props, children) => { export const Stats = (props, children) => {
const { class: className, vertical = false, ...rest } = props; children === undefined && (children = props, props = {});
const direction = vertical ? "stats-vertical" : "stats-horizontal"; const direction = props.vertical ? "stats-vertical" : "stats-horizontal";
return Tag("div", { return Tag("div", { ...props, class: `stats ${direction} ${props.class ?? ''}`.trim() }, children);
...rest,
class: `stats ${direction} ${className || ''}`.trim()
}, children);
}; };
export const Stat = (props) => { export const Stat = (props, children) => {
const { class: className, label, value, desc, icon, actions, children, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { ...props, class: `stat ${props.class ?? ''}` }, [
if (children !== undefined) { props.icon && Tag("div", { class: "stat-figure" }, props.icon),
return Tag("div", { ...rest, class: `stat ${className || ''}`.trim() }, children); props.label && Tag("div", { class: "stat-title" }, props.label),
} props.value && Tag("div", { class: "stat-value" }, props.value),
props.desc && Tag("div", { class: "stat-desc" }, props.desc),
return Tag("div", { ...rest, class: `stat ${className || ''}`.trim() }, [ props.actions && Tag("div", { class: "stat-actions" }, props.actions),
icon && Tag("div", { class: "stat-figure" }, icon), children
label && Tag("div", { class: "stat-title" }, label),
value && Tag("div", { class: "stat-value" }, value),
desc && Tag("div", { class: "stat-desc" }, desc),
actions && Tag("div", { class: "stat-actions" }, actions)
]); ]);
}; };

View File

@@ -2,18 +2,11 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Steps = (props, children) => { export const Steps = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("ul", { return Tag("ul", { ...props, class: `steps ${props.class ?? ''}` }, children);
...rest,
class: `steps ${className || ''}`.trim()
}, children);
}; };
export const Step = (props, children) => { export const Step = (props, children) => {
const { class: className, dataContent, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("li", { return Tag("li", { ...props, class: `step ${props.class ?? ''}`, "data-content": props.dataContent }, children);
...rest,
class: `step ${className || ''}`.trim(),
"data-content": dataContent
}, children);
}; };

View File

@@ -2,20 +2,13 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Swap = (props) => { export const Swap = (props) => {
const { class: className, value, on, off, ...rest } = props; return Tag("label", { ...props, class: `swap ${props.class ?? ''}` }, [
return Tag("label", {
...rest,
class: `swap ${className || ''}`.trim()
}, [
Tag("input", { Tag("input", {
type: "checkbox", type: "checkbox",
checked: () => typeof value === "function" ? value() : value, checked: () => typeof props.value === "function" ? props.value() : props.value,
onchange: (e) => { onchange: (e) => typeof props.value === "function" && props.value(e.target.checked)
if (typeof value === "function") value(e.target.checked);
}
}), }),
Tag("div", { class: "swap-on" }, on), Tag("div", { class: "swap-on" }, props.on),
Tag("div", { class: "swap-off" }, off) Tag("div", { class: "swap-off" }, props.off)
]); ]);
}; };

View File

@@ -1,10 +1,31 @@
// components/Table.js // components/Table.js
import { Tag } from "sigpro"; import { Tag, For } from "sigpro";
export const Table = (props, children) => { export const Table = (props, children) => {
const { class: className, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("table", { return Tag("table", { ...props, class: `table ${props.class ?? ''}` }, children);
...rest, };
class: `table ${className || ''}`.trim()
}, children); export const Rows = (props) => {
const itemArray = typeof props.items === "function" ? props.items() : (props.items || []);
const thead = props.header !== false && props.columns?.some(col => col.label) ?
Tag("thead", {},
Tag("tr", {},
props.columns.map(col => Tag("th", { class: col.class }, col.label))
)
) : null;
const tbody = Tag("tbody", {}, [
For(itemArray, (item, idx) =>
Tag("tr", {},
props.columns.map(col => {
const content = col.render ? col.render(item, idx) : item[col.key];
return Tag("td", { class: col.class }, content);
})
)
, props.keyFn || ((item, idx) => item.id ?? idx))
]);
return [thead, tbody];
}; };

View File

@@ -1,75 +1,49 @@
// components/Tabs.js // components/Tabs.js
import { Tag, $, Watch } from "sigpro"; import { Tag, For } from "sigpro";
export const Tabs = (props) => { export const Tabs = (props, children) => {
const { items, class: className, onTabClose, ...rest } = props; children === undefined && (children = props, props = {});
const itemsSignal = typeof items === "function" ? items : () => items || []; return Tag("div", { ...props, class: `tabs ${props.class ?? ''}` }, children);
const activeIndex = $(0); };
Watch(() => { export const Tab = (props, children) => {
const list = itemsSignal(); children === undefined && (children = props, props = {});
const idx = list.findIndex(it => { return Tag("a", { ...props, role: "tab", class: `tab ${props.class ?? ''}` }, children);
const active = it.active; };
return typeof active === "function" ? active() : active;
});
if (idx !== -1 && activeIndex() !== idx) activeIndex(idx);
});
const removeTab = (idx, item) => { export const TabContent = (props, children) => {
item.onClose?.(); children === undefined && (children = props, props = {});
onTabClose?.(item, idx); return Tag("div", { ...props, class: `tab-content ${props.class ?? ''}` }, children);
const current = itemsSignal(); };
if (typeof items !== "function" || items._isComputed) return;
const newItems = current.filter((_, i) => i !== idx);
items(newItems);
if (newItems.length) {
let newIdx = activeIndex();
if (idx < newIdx) newIdx--;
else if (idx === newIdx) newIdx = Math.min(newIdx, newItems.length - 1);
activeIndex(newIdx);
}
};
return Tag("div", { ...rest, class: `tabs ${className || ''}`.trim() }, () => { export const TabClose = (props) => Tag("a", { ...props, role: "tab", class: `tab ${props.class ?? ''}` }, [
const list = itemsSignal(); Tag("span", { class: "flex items-center" }, [
const elements = []; props.label,
for (let i = 0; i < list.length; i++) { Tag("span", {
const item = list[i]; class: "icon-[lucide--x] w-3.5 h-3.5 ml-2 cursor-pointer hover:opacity-70",
const label = typeof item.label === "function" ? item.label() : item.label; onclick: (e) => { e.stopPropagation(); props.onClose?.(e); }
const closable = typeof item.closable === "function" ? item.closable() : item.closable; })
])
]);
const btnContent = closable export const TabItems = (props) => {
? Tag("span", { class: "flex items-center" }, [ const items = typeof props.items === "function" ? props.items : () => props.items || [];
label, return For(
Tag("span", { items,
class: "icon-[lucide--x] w-3.5 h-3.5 ml-2 cursor-pointer hover:opacity-70", (item, idx) => {
onclick: (e) => { e.stopPropagation(); removeTab(i, item); } const TabComp = item.closable ? TabClose : Tab;
}) return [
]) TabComp({
: label; ...item,
class: () => props.activeIndex() === idx ? `tab-active ${item.class ?? ''}` : item.class,
const tabBtn = Tag("button", { onclick: (e) => { e.preventDefault(); props.activeIndex(idx); item.onclick?.(e); },
class: () => `tab ${activeIndex() === i ? 'tab-active' : ''}`, onClose: () => props.onClose?.(idx, item)
onclick: (e) => { }),
e.preventDefault(); TabContent({
const disabled = typeof item.disabled === "function" ? item.disabled() : item.disabled; style: () => `display: ${props.activeIndex() === idx ? "block" : "none"};`
if (!disabled) { }, typeof item.content === "function" ? item.content() : item.content)
item.onclick?.(); ];
activeIndex(i); },
} (item, idx) => item.id ?? idx
} );
}, btnContent);
elements.push(item.tip ? Tag("div", { class: "tooltip", "data-tip": item.tip }, tabBtn) : tabBtn);
const content = typeof item.content === "function" ? item.content() : item.content;
elements.push(
Tag("div", {
class: "tab-content bg-base-100 border-base-300 p-6",
style: () => `display: ${activeIndex() === i ? 'block' : 'none'}`
}, content)
);
}
return elements;
});
}; };

View File

@@ -1,19 +0,0 @@
// components/TextRotate.js
import { Tag } from "sigpro";
export const TextRotate = (props) => {
const { class: className, words, ...rest } = props;
const wordsArray = Array.isArray(words)
? words
: (typeof words === 'string' ? words.split(',') : []);
return Tag("span", {
...rest,
class: `text-rotate ${className || ''}`.trim()
}, [
Tag("span", {},
wordsArray.map(word => Tag("span", {}, word))
)
]);
};

View File

@@ -1,9 +1,4 @@
// components/Textarea.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Textarea = (props) => { export const Textarea = (props) => Tag("textarea", { ...props, class: `textarea ${props.class ?? ''}` });
const { class: className, ...rest } = props;
return Tag("textarea", {
...rest,
class: `textarea ${className || ''}`.trim()
});
};

12
components/Textrotate.js Normal file
View File

@@ -0,0 +1,12 @@
// components/Textrotate.js
import { Tag } from "sigpro";
export const TextRotate = (props) => {
const wordsArray = Array.isArray(props.words)
? props.words
: (typeof props.words === 'string' ? props.words.split(',') : []);
return Tag("span", { ...props, class: `text-rotate ${props.class ?? ''}` }, [
Tag("span", {}, wordsArray.map(word => Tag("span", {}, word)))
]);
};

View File

@@ -2,9 +2,11 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Timeline = (props, children) => { export const Timeline = (props, children) => {
const { class: className, vertical = true, compact = false, ...rest } = props; children === undefined && (children = props, props = {});
const vertical = props.vertical !== false;
const compact = props.compact === true;
return Tag("ul", { return Tag("ul", {
...rest, ...props,
class: `timeline ${vertical ? 'timeline-vertical' : 'timeline-horizontal'} ${compact ? 'timeline-compact' : ''} ${className || ''}`.trim() class: `timeline ${vertical ? 'timeline-vertical' : 'timeline-horizontal'} ${compact ? 'timeline-compact' : ''} ${props.class ?? ''}`.trim()
}, children); }, children);
}; };

View File

@@ -3,11 +3,10 @@ import { Tag, Mount } from "sigpro";
export const Toast = (message, type = "alert-success", duration = 3500) => { export const 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 = Tag("div", { container = Tag("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);
} }
@@ -16,7 +15,6 @@ export const Toast = (message, type = "alert-success", duration = 3500) => {
container.appendChild(toastHost); container.appendChild(toastHost);
let timeoutId; let timeoutId;
const close = () => { const close = () => {
clearTimeout(timeoutId); clearTimeout(timeoutId);
const el = toastHost.firstElementChild; const el = toastHost.firstElementChild;
@@ -52,10 +50,6 @@ export const Toast = (message, type = "alert-success", duration = 3500) => {
}; };
const instance = Mount(ToastComponent, toastHost); const instance = Mount(ToastComponent, toastHost);
if (duration > 0) timeoutId = setTimeout(close, duration);
if (duration > 0) {
timeoutId = setTimeout(close, duration);
}
return close; return close;
}; };

View File

@@ -1,19 +1,4 @@
// components/Toggle.js // components/Toggle.js
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Toggle = (props) => { export const Toggle = (p) => Tag("input", { ...p, type: "checkbox", class: `toggle ${p.class ?? ''}` });
const { class: className, label, ...rest } = props;
const inputEl = Tag("input", {
...rest,
type: "checkbox",
class: `toggle ${className || ''}`.trim()
});
if (!label) return inputEl;
return Tag("label", { class: "label cursor-pointer justify-start gap-3" }, [
inputEl,
Tag("span", { class: "label-text" }, label)
]);
};

View File

@@ -2,10 +2,6 @@
import { Tag } from "sigpro"; import { Tag } from "sigpro";
export const Tooltip = (props, children) => { export const Tooltip = (props, children) => {
const { class: className, tip, ...rest } = props; children === undefined && (children = props, props = {});
return Tag("div", { return Tag("div", { ...props, class: `tooltip ${props.class ?? ''}`, "data-tip": props.tip }, children);
...rest,
class: `tooltip ${className || ''}`.trim(),
"data-tip": tip
}, children);
}; };

84
components/_Base.js Normal file
View File

@@ -0,0 +1,84 @@
import { $, Tag } from "sigpro";
import { Tag } from "sigpro";
export const Alert = (props, children) => Tag("div", { ...props, class: `alert ${props.class ?? ''}` }, children);
export const Badge = (props, children) => Tag("span", { ...props, class: `badge ${props.class ?? ''}` }, children);
export const Button = (props, children) => Tag("button", { ...props, class: `btn ${props.class ?? ''}` }, children);
export const Card = (props, children) => Tag("div", { ...props, class: `card ${props.class ?? ''}` }, children);
export const CardTitle = (props, children) => Tag("div", { ...props, class: `card-title ${props.class ?? ''}` }, children);
export const CardBody = (props, children) => Tag("div", { ...props, class: `card-body ${props.class ?? ''}` }, children);
export const CardActions = (props, children) => Tag("div", { ...props, class: `card-actions ${props.class ?? ''}` }, children);
export const Carousel = (props, children) => Tag("div", { ...props, class: `carousel ${props.class ?? ''}` }, children);
export const CarouselItem = (props, children) => Tag("div", { ...props, class: `carousel-item ${props.class ?? ''}` }, children);
export const Chat = (props, children) => Tag("div", { ...props, class: `chat ${props.class ?? ''}` }, children);
export const ChatBubble = (props, children) => Tag("div", { ...props, class: `chat-bubble ${props.class ?? ''}` }, children);
export const Checkbox = (props) => Tag("input", { ...props, type: "checkbox", class: `checkbox ${props.class ?? ''}` });
export const Collapse = (props, children) => Tag("div", { ...props, class: `collapse ${props.class ?? ''}` }, children);
export const Divider = (props) => Tag("div", { ...props, class: `divider ${props.class ?? ''}` });
export const Drawer = (props, children) => Tag("div", { ...props, class: `drawer ${props.class ?? ''}` }, children);
export const Dropdown = (props, children) => Tag("details", { ...props, class: `dropdown ${props.class ?? ''}` }, children);
export const Fab = (props, children) => Tag("div", { ...props, class: `fab ${props.class ?? ''}` }, children);
export const Fieldset = (props, children) => Tag("fieldset", { ...props, class: `fieldset ${props.class ?? ''}` }, children);
export const Indicator = (props, children) => Tag("div", { ...props, class: `indicator ${props.class ?? ''}` }, children);
export const Input = (props) => Tag("input", { ...props, class: `input ${props.class ?? ''}` });
export const Kbd = (props, children) => Tag("kbd", { ...props, class: `kbd ${props.class ?? ''}` }, children);
export const Label = (props, children) => Tag("label", { ...props, class: `label ${props.class ?? ''}` }, children);
export const List = (props, children) => Tag("ul", { ...props, class: `list ${props.class ?? ''}` }, children);
export const Menu = (props, children) => Tag("ul", { ...props, class: `menu ${props.class ?? ''}` }, children);
export const Modal = (props, children) => Tag("dialog", { ...props, class: `modal ${props.class ?? ''}` }, children);
export const Navbar = (props, children) => Tag("div", { ...props, class: `navbar ${props.class ?? ''}` }, children);
export const Radio = (props) => Tag("input", { ...props, type: "radio", class: `radio ${props.class ?? ''}` });
export const Range = (props) => Tag("input", { ...props, type: "range", class: `range ${props.class ?? ''}` });
export const Rating = (props, children) => Tag("div", { ...props, class: `rating ${props.class ?? ''}` }, children);
export const Select = (props, children) => Tag("select", { ...props, class: `select ${props.class ?? ''}` }, children);
export const Skeleton = (props) => Tag("div", { ...props, class: `skeleton ${props.class ?? ''}` });
export const Stack = (props, children) => Tag("div", { ...props, class: `stack ${props.class ?? ''}` }, children);
export const Stat = (props, children) => Tag("div", { ...props, class: `stat ${props.class ?? ''}` }, children);
export const Stats = (props, children) => Tag("div", { ...props, class: `stats ${props.class ?? ''}` }, children);
export const Steps = (props, children) => Tag("ul", { ...props, class: `steps ${props.class ?? ''}` }, children);
export const Step = (props, children) => Tag("li", { ...props, class: `step ${props.class ?? ''}` }, children);
export const Swap = (props, children) => Tag("label", { ...props, class: `swap ${props.class ?? ''}` }, children);
export const Table = (props, children) => Tag("table", { ...props, class: `table ${props.class ?? ''}` }, children);
export const Tab = (props, children) => Tag("a", { ...props, role: "tab", class: `tab ${props.class ?? ''}` }, children);
export const Tabs = (props, children) => Tag("div", { ...props, class: `tabs ${props.class ?? ''}` }, children);
export const TabContent = (props, children) => Tag("div", { ...props, class: `tab-content ${props.class ?? ''}` }, children);
export const Textarea = (props) => Tag("textarea", { ...props, class: `textarea ${props.class ?? ''}` });
export const Timeline = (props, children) => Tag("ul", { ...props, class: `timeline ${props.class ?? ''}` }, children);
export const Toast = (props, children) => Tag("div", { ...props, class: `toast ${props.class ?? ''}` }, children);
export const Toggle = (props) => Tag("input", { ...props, type: "checkbox", class: `toggle ${props.class ?? ''}` });
export const Tooltip = (props, children) => Tag("div", { ...props, class: `tooltip ${props.class ?? ''}` }, children);
// Complex
export const TabClose = (props) => Tag("a", { ...props, role: "tab", class: `tab ${props.class ?? ''}` }, [
Tag("span", { class: "flex items-center" }, [
p.label,
Tag("span", {
class: "icon-[lucide--x] w-3.5 h-3.5 ml-2 cursor-pointer hover:opacity-70",
onclick: (e) => { e.stopPropagation(); p.onClose?.(e); }
})
])
]);
export const TabItems = (props) => {
const items = typeof p.items === "function" ? p.items : () => p.items || [];
return For(
items,
(item, idx) => {
const TabComp = item.closable ? TabClose : Tab;
return [
TabComp({
label: item.label,
class: () => p.activeIndex() === idx ? "tab-active" : "",
onclick: (e) => { e.preventDefault(); p.activeIndex(idx); },
onClose: () => p.onClose?.(idx, item)
}),
TabContent({
style: () => `display: ${p.activeIndex() === idx ? "block" : "none"};`
}, typeof item.content === "function" ? item.content() : item.content)
];
},
(item, idx) => item.id ?? idx
);
};

View File

@@ -11,6 +11,7 @@
--color-black: #000; --color-black: #000;
--color-white: #fff; --color-white: #fff;
--spacing: 0.25rem; --spacing: 0.25rem;
--container-xs: 20rem;
--container-md: 28rem; --container-md: 28rem;
--container-3xl: 48rem; --container-3xl: 48rem;
--container-5xl: 64rem; --container-5xl: 64rem;
@@ -1411,6 +1412,36 @@
} }
} }
} }
.validator-hint {
@layer daisyui.l1.l2.l3 {
visibility: hidden;
margin-top: calc(0.25rem * 2);
font-size: 0.75rem;
}
}
.validator {
@layer daisyui.l1.l2.l3 {
&:user-valid, &:has(:user-valid) {
&, &:focus, &:checked, &[aria-checked="true"], &:focus-within {
--input-color: var(--color-success);
}
}
&:user-invalid, &:has(:user-invalid), &[aria-invalid]:not([aria-invalid="false"]), &:has([aria-invalid]:not([aria-invalid="false"])) {
&, &:focus, &:checked, &[aria-checked="true"], &:focus-within {
--input-color: var(--color-error);
}
& ~ .validator-hint {
visibility: visible;
color: var(--color-error);
}
}
}
&:user-invalid, &:has(:user-invalid), &[aria-invalid]:not([aria-invalid="false"]), &:has([aria-invalid]:not([aria-invalid="false"])) {
& ~ .validator-hint {
display: revert-layer;
}
}
}
.collapse { .collapse {
visibility: collapse; visibility: collapse;
} }
@@ -1595,6 +1626,27 @@
} }
} }
} }
.toast {
@layer daisyui.l1.l2.l3 {
position: fixed;
inset-inline-start: auto;
inset-inline-end: calc(0.25rem * 4);
top: auto;
bottom: calc(0.25rem * 4);
display: flex;
flex-direction: column;
gap: calc(0.25rem * 2);
background-color: transparent;
translate: var(--toast-x, 0) var(--toast-y, 0);
width: max-content;
max-width: calc(100vw - 2rem);
& > * {
@media (prefers-reduced-motion: no-preference) {
animation: toast 0.25s ease-out;
}
}
}
}
.toggle { .toggle {
@layer daisyui.l1.l2.l3 { @layer daisyui.l1.l2.l3 {
border: var(--border) solid currentColor; border: var(--border) solid currentColor;
@@ -3120,6 +3172,9 @@
.top-0 { .top-0 {
top: calc(var(--spacing) * 0); top: calc(var(--spacing) * 0);
} }
.top-1\/2 {
top: calc(1 / 2 * 100%);
}
.top-2 { .top-2 {
top: calc(var(--spacing) * 2); top: calc(var(--spacing) * 2);
} }
@@ -4341,6 +4396,19 @@
mask-size: 100% 100%; mask-size: 100% 100%;
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233a14 14 0 0 0 6.392 6.384'/%3E%3C/svg%3E"); --svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233a14 14 0 0 0 6.392 6.384'/%3E%3C/svg%3E");
} }
.icon-\[lucide--search-x\] {
display: inline-block;
width: 1em;
height: 1em;
background-color: currentColor;
-webkit-mask-image: var(--svg);
mask-image: var(--svg);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='m13.5 8.5l-5 5m0-5l5 5'/%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21l-4.3-4.3'/%3E%3C/g%3E%3C/svg%3E");
}
.icon-\[lucide--search\] { .icon-\[lucide--search\] {
display: inline-block; display: inline-block;
width: 1em; width: 1em;
@@ -4900,6 +4968,9 @@
.max-w-md { .max-w-md {
max-width: var(--container-md); max-width: var(--container-md);
} }
.max-w-xs {
max-width: var(--container-xs);
}
.min-w-\[4rem\] { .min-w-\[4rem\] {
min-width: 4rem; min-width: 4rem;
} }
@@ -4944,6 +5015,10 @@
--tw-translate-x: 100%; --tw-translate-x: 100%;
translate: var(--tw-translate-x) var(--tw-translate-y); translate: var(--tw-translate-x) var(--tw-translate-y);
} }
.-translate-y-1\/2 {
--tw-translate-y: calc(calc(1 / 2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.translate-y-2 { .translate-y-2 {
--tw-translate-y: calc(var(--spacing) * 2); --tw-translate-y: calc(var(--spacing) * 2);
translate: var(--tw-translate-x) var(--tw-translate-y); translate: var(--tw-translate-x) var(--tw-translate-y);

1096
dist/sigpro-ui.esm.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

1096
dist/sigpro-ui.js vendored

File diff suppressed because it is too large Load Diff

2
dist/sigpro-ui.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/sigpro.min.css vendored

File diff suppressed because one or more lines are too long

View File

@@ -149,15 +149,20 @@ const DynamicDemo = () => {
}; };
return Div({ class: 'flex flex-col gap-4 w-full' }, [ return Div({ class: 'flex flex-col gap-4 w-full' }, [
Select({ Select({
items: [ class: 'select-bordered w-full',
{ value: 'all', label: 'All items' }, value: filterType,
{ value: 'fruits', label: 'Fruits' }, onchange: (e) => filterType(e.target.value)
{ value: 'vegetables', label: 'Vegetables' } }, [
], Options({
value: filterType, items: [
onchange: (e) => filterType(e.target.value) { value: 'all', label: 'All items' },
}), { value: 'fruits', label: 'Fruits' },
{ value: 'vegetables', label: 'Vegetables' }
]
})
]),
Autocomplete({ Autocomplete({
items: () => allItems[filterType()], items: () => allItems[filterType()],
value: selected, value: selected,
@@ -165,6 +170,7 @@ const DynamicDemo = () => {
}) })
]); ]);
}; };
Mount(DynamicDemo, '#demo-dynamic'); Mount(DynamicDemo, '#demo-dynamic');
``` ```

View File

@@ -69,7 +69,7 @@ const LoadingDemo = () => {
isSaving(false); isSaving(false);
}, },
}, },
[Spinner({ value: isSaving }), "Save Changes"], [If(isSaving, ()=>Loading()), "Save Changes"],
); );
}; };
Mount(LoadingDemo, "#demo-loading"); Mount(LoadingDemo, "#demo-loading");

View File

@@ -8,26 +8,26 @@ Form input component with icons, password toggle, and validation. Use `Label()`
## Props ## Props
| Prop | Type | Default | Description | | Prop | Type | Default | Description |
| :----------- | :--------------------------- | :--------- | :----------------------------------------------- | | :------------ | :--------------------------- | :------- | :----------------------------------------------- |
| `type` | `string` | `'text'` | Input type (text, password, email, number, date) | | `type` | `string` | `'text'` | Input type (text, password, email, number, date) |
| `value` | `string \| Signal<string>` | `''` | Input value | | `value` | `string \| Signal<string>` | `''` | Input value |
| `placeholder`| `string` | `' '` | Placeholder text | | `placeholder` | `string` | `' '` | Placeholder text |
| `icon` | `string \| VNode \| Signal` | `-` | Icon displayed inside input | | `icon` | `string \| VNode \| Signal` | `-` | Icon displayed inside input |
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state | | `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) | | `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `oninput` | `function` | `-` | Input event handler | | `oninput` | `function` | `-` | Input event handler |
| `validate` | `function` | `-` | Validation function returning error message | | `validate` | `function` | `-` | Validation function returning error message |
## Styling ## Styling
Input supports all **daisyUI Input classes**: Input supports all **daisyUI Input classes**:
| Category | Keywords | Description | | Category | Keywords | Description |
| :--- | :--- | :--- | | :------- | :---------------------------------------------------------------------------------------------------------------- | :------------------- |
| Style | `input-bordered`, `input-ghost` | Input style variants | | Style | `input-bordered`, `input-ghost` | Input style variants |
| Color | `input-primary`, `input-secondary`, `input-accent`, `input-info`, `input-success`, `input-warning`, `input-error` | Input color variants | | Color | `input-primary`, `input-secondary`, `input-accent`, `input-info`, `input-success`, `input-warning`, `input-error` | Input color variants |
| Size | `input-xs`, `input-sm`, `input-md`, `input-lg` | Input size variants | | Size | `input-xs`, `input-sm`, `input-md`, `input-lg` | Input size variants |
## Live Examples ## Live Examples
@@ -42,15 +42,15 @@ Input supports all **daisyUI Input classes**:
```javascript ```javascript
const BasicDemo = () => { const BasicDemo = () => {
const name = $(''); const name = $("");
return Input({ return Input({
placeholder: 'Enter your name', placeholder: "Enter your name",
value: name, value: name,
oninput: (e) => name(e.target.value) oninput: (e) => name(e.target.value),
}); });
}; };
Mount(BasicDemo, '#demo-basic'); Mount(BasicDemo, "#demo-basic");
``` ```
### With Icon ### With Icon
@@ -64,16 +64,19 @@ Mount(BasicDemo, '#demo-basic');
```javascript ```javascript
const IconDemo = () => { const IconDemo = () => {
const email = $(''); const email = $("");
return Input({ return Label({ class: "input" }, [
type: 'email', Icon("✉️"),
icon: "✉️", Input({
value: email, class: "grow",
oninput: (e) => email(e.target.value) type: "email",
}); value: email,
oninput: (e) => email(e.target.value),
}),
]);
}; };
Mount(IconDemo, '#demo-icon'); Mount(IconDemo, "#demo-icon");
``` ```
### Password with Toggle ### Password with Toggle
@@ -87,15 +90,27 @@ Mount(IconDemo, '#demo-icon');
```javascript ```javascript
const PasswordDemo = () => { const PasswordDemo = () => {
const password = $(''); const password = $("");
const visible = $(false);
return Input({
type: 'password', return Label({ class: "input input-bordered w-full max-w-xs" }, [
value: password, Icon("icon-[lucide--lock]"),
oninput: (e) => password(e.target.value) Input({
}); type: () => (visible() ? "text" : "password"),
value: password,
placeholder: "Contraseña",
class: "grow",
oninput: (e) => password(e.target.value),
}),
Swap({
value: visible,
class: "swap-rotate",
on: Icon("icon-[lucide--eye]"),
off: Icon("icon-[lucide--eye-off]"),
}),
]);
}; };
Mount(PasswordDemo, '#demo-password'); Mount(PasswordDemo, "#demo-password");
``` ```
### With Floating Label ### With Floating Label
@@ -111,18 +126,20 @@ Wrap the input with `Label()` component:
```javascript ```javascript
const LabelDemo = () => { const LabelDemo = () => {
const email = $(''); const email = $("");
return Input({ return Label({ class: "floating-label" }, [
type: 'email', Span("Text floating"),
Input({
type: "email",
label: "Email", label: "Email",
floating: true,
value: email, value: email,
placeholder: ' ', placeholder: "Clic here",
oninput: (e) => email(e.target.value) oninput: (e) => email(e.target.value),
}); }),
]);
}; };
Mount(LabelDemo, '#demo-label'); Mount(LabelDemo, "#demo-label");
``` ```
### With Tooltip & label ### With Tooltip & label
@@ -138,18 +155,22 @@ Wrap the input with `Tooltip()` component:
```javascript ```javascript
const TooltipDemo = () => { const TooltipDemo = () => {
const username = $(''); const username = $("");
return Tooltip({ tip: 'Must be at least 3 characters' }, return Tooltip(
Input({ { tip: "Must be at least 3 characters" },
value: username, Label({ class: "input" }, [
label: "Username", Span({ class: "label" }, "User"),
placeholder: 'Username', Input({
oninput: (e) => username(e.target.value) value: username,
}) label: "Username",
placeholder: "Username",
oninput: (e) => username(e.target.value),
}),
]),
); );
}; };
Mount(TooltipDemo, '#demo-tooltip'); Mount(TooltipDemo, "#demo-tooltip");
``` ```
### Error State ### Error State
@@ -163,26 +184,27 @@ Mount(TooltipDemo, '#demo-tooltip');
```javascript ```javascript
const ErrorDemo = () => { const ErrorDemo = () => {
const email = $(''); const email = $("");
return Div({ class: 'w-full' }, [ return Div({ class: "form-control w-full max-w-xs" }, [
Input({ Label({ class: "label" }, Span({ class: "label-text" }, "Email")),
type: 'email', Div({ class: "relative w-full" }, [
value: email, Input({
placeholder: 'Enter your email', type: "email",
label: 'Email', value: email,
icon: 'icon-[lucide--mail]', placeholder: "mail@site.com",
validate: (value) => { class: "input input-bordered w-full pl-10 validator",
if (!value) return ''; required: true,
if (!value.includes('@')) return 'Email must contain @'; oninput: (e) => email(e.target.value)
if (!value.includes('.')) return 'Email must contain .'; }),
return ''; Span({ class: "absolute left-3 top-1/2 -translate-y-1/2 text-base-content/60" },
}, Icon("icon-[lucide--mail]")
oninput: (e) => email(e.target.value) )
}) ]),
Div({ class: "validator-hint hidden" }, "Enter a valid email address")
]); ]);
}; };
Mount(ErrorDemo, '#demo-error'); Mount(ErrorDemo, "#demo-error");
``` ```
### Disabled State ### Disabled State
@@ -197,11 +219,11 @@ Mount(ErrorDemo, '#demo-error');
```javascript ```javascript
const DisabledDemo = () => { const DisabledDemo = () => {
return Input({ return Input({
value: 'john.doe', value: "john.doe",
disabled: true disabled: true,
}); });
}; };
Mount(DisabledDemo, '#demo-disabled'); Mount(DisabledDemo, "#demo-disabled");
``` ```
### All Variants ### All Variants
@@ -215,33 +237,33 @@ Mount(DisabledDemo, '#demo-disabled');
```javascript ```javascript
const VariantsDemo = () => { const VariantsDemo = () => {
const text = $(''); const text = $("");
const number = $(0); const number = $(0);
return Div({ class: 'flex flex-col gap-4' }, [ return Div({ class: "flex flex-col gap-4" }, [
Input({ Input({
placeholder: 'Type something...', placeholder: "Type something...",
value: text, value: text,
oninput: (e) => text(e.target.value) oninput: (e) => text(e.target.value),
}), }),
Input({ Input({
type: 'number', type: "number",
value: number, value: number,
oninput: (e) => number(parseInt(e.target.value) || 0) oninput: (e) => number(parseInt(e.target.value) || 0),
}), }),
Input({ Input({
type: 'date', type: "date",
value: $('2024-01-01') value: $("2024-01-01"),
}), }),
Input({class: 'input-primary', value: "Primary"}), Input({ class: "input-primary", value: "Primary" }),
Input({class: 'input-secondary', value: "Secondary"}), Input({ class: "input-secondary", value: "Secondary" }),
Input({class: 'input-accent', value: "Accent"}), Input({ class: "input-accent", value: "Accent" }),
Input({class: 'input-ghost', value: "Ghost"}), Input({ class: "input-ghost", value: "Ghost" }),
Input({class: 'input-info', value: "Info"}), Input({ class: "input-info", value: "Info" }),
Input({class: 'input-success', value: "Success"}), Input({ class: "input-success", value: "Success" }),
Input({class: 'input-warning', value: "Warning"}), Input({ class: "input-warning", value: "Warning" }),
Input({class: 'input-error', value: "Error"}), Input({ class: "input-error", value: "Error" }),
]); ]);
}; };
Mount(VariantsDemo, '#demo-variants'); Mount(VariantsDemo, "#demo-variants");
``` ```

View File

@@ -1,22 +1,27 @@
# Select # Select
Dropdown select component with full DaisyUI styling, reactive items, and form integration. Dropdown select component with full DaisyUI styling and reactive options.
## Tag ## Tag
`Select` `Select`, `Options`
## Props ## Select Props
| Prop | Type | Default | Description | | Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- | | :--- | :--- | :--- | :--- |
| `label` | `string` | `-` | Label text above select |
| `items` | `Array<{value: string, label: string}> \| Signal<Array>` | `[]` | Array of items with value and label |
| `value` | `string \| Signal<string>` | `''` | Selected value | | `value` | `string \| Signal<string>` | `''` | Selected value |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) | | `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state | | `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
| `onchange` | `function` | `-` | Change event handler | | `onchange` | `function` | `-` | Change event handler |
## Options Props
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `items` | `Array<string \| {value, label}> \| Signal<Array>` | `[]` | Array of items (strings or objects) |
| `placeholder` | `string` | `-` | Optional placeholder option (disabled, selected) |
## Styling ## Styling
Select supports all **daisyUI Select classes**: Select supports all **daisyUI Select classes**:
@@ -44,17 +49,23 @@ Select supports all **daisyUI Select classes**:
const BasicDemo = () => { const BasicDemo = () => {
const selected = $('apple'); const selected = $('apple');
return Select({ return Div({ class: 'form-control w-full max-w-xs' }, [
label: 'Choose a fruit', Label({ class: 'label' }, Span({ class: 'label-text' }, 'Choose a fruit')),
items: [ Select({
{ value: 'apple', label: '🍎 Apple' }, class: 'select select-bordered w-full',
{ value: 'banana', label: '🍌 Banana' }, value: selected,
{ value: 'orange', label: '🍊 Orange' }, onchange: (e) => selected(e.target.value)
{ value: 'grape', label: '🍇 Grape' } }, [
], Options({
value: selected, items: [
onchange: (e) => selected(e.target.value) { value: 'apple', label: '🍎 Apple' },
}); { value: 'banana', label: '🍌 Banana' },
{ value: 'orange', label: '🍊 Orange' },
{ value: 'grape', label: '🍇 Grape' }
]
})
])
]);
}; };
Mount(BasicDemo, '#demo-basic'); Mount(BasicDemo, '#demo-basic');
``` ```
@@ -72,16 +83,22 @@ Mount(BasicDemo, '#demo-basic');
const ReactiveDemo = () => { const ReactiveDemo = () => {
const selected = $('small'); const selected = $('small');
return Div({ class: 'flex flex-col gap-4 w-full' }, [ return Div({ class: 'flex flex-col gap-4 w-full' }, [
Select({ Div({ class: 'form-control w-full max-w-xs' }, [
label: 'Select size', Label({ class: 'label' }, Span({ class: 'label-text' }, 'Select size')),
items: [ Select({
{ value: 'small', label: 'Small' }, class: 'select select-bordered w-full',
{ value: 'medium', label: 'Medium' }, value: selected,
{ value: 'large', label: 'Large' } onchange: (e) => selected(e.target.value)
], }, [
value: selected, Options({
onchange: (e) => selected(e.target.value) items: [
}), { value: 'small', label: 'Small' },
{ value: 'medium', label: 'Medium' },
{ value: 'large', label: 'Large' }
]
})
])
]),
Div({ class: 'alert alert-info' }, () => `You selected: ${selected()}`) Div({ class: 'alert alert-info' }, () => `You selected: ${selected()}`)
]); ]);
}; };
@@ -99,16 +116,22 @@ Mount(ReactiveDemo, '#demo-reactive');
```javascript ```javascript
const DisabledDemo = () => { const DisabledDemo = () => {
return Select({ return Div({ class: 'form-control w-full max-w-xs' }, [
label: 'Country (disabled)', Label({ class: 'label' }, Span({ class: 'label-text' }, 'Country (disabled)')),
items: [ Select({
{ value: 'mx', label: 'Mexico' }, class: 'select select-bordered w-full',
{ value: 'us', label: 'United States' }, value: 'mx',
{ value: 'ca', label: 'Canada' } disabled: true
], }, [
value: 'mx', Options({
disabled: true items: [
}); { value: 'mx', label: 'Mexico' },
{ value: 'us', label: 'United States' },
{ value: 'ca', label: 'Canada' }
]
})
])
]);
}; };
Mount(DisabledDemo, '#demo-disabled'); Mount(DisabledDemo, '#demo-disabled');
``` ```
@@ -139,24 +162,36 @@ const DynamicDemo = () => {
}; };
return Div({ class: 'flex flex-col gap-4 w-full' }, [ return Div({ class: 'flex flex-col gap-4 w-full' }, [
Select({ Div({ class: 'form-control w-full max-w-xs' }, [
label: 'Category', Label({ class: 'label' }, Span({ class: 'label-text' }, 'Category')),
items: [ Select({
{ value: 'fruits', label: 'Fruits' }, class: 'select select-bordered w-full',
{ value: 'vegetables', label: 'Vegetables' } value: category,
], onchange: (e) => {
value: category, category(e.target.value);
onchange: (e) => { selectedItem('');
category(e.target.value); }
selectedItem(''); }, [
} Options({
}), items: [
Select({ { value: 'fruits', label: 'Fruits' },
label: 'Item', { value: 'vegetables', label: 'Vegetables' }
items: () => items[category()] || [], ]
value: selectedItem, })
onchange: (e) => selectedItem(e.target.value) ])
}), ]),
Div({ class: 'form-control w-full max-w-xs' }, [
Label({ class: 'label' }, Span({ class: 'label-text' }, 'Item')),
Select({
class: 'select select-bordered w-full',
value: selectedItem,
onchange: (e) => selectedItem(e.target.value)
}, [
Options({
items: () => items[category()] || []
})
])
]),
() => selectedItem() ? Div({ class: 'alert alert-success' }, `Selected: ${selectedItem()}`) : null () => selectedItem() ? Div({ class: 'alert alert-success' }, `Selected: ${selectedItem()}`) : null
]); ]);
}; };
@@ -179,38 +214,53 @@ const VariantsDemo = () => {
const ghost = $(''); const ghost = $('');
return Div({ class: 'flex flex-col gap-4' }, [ return Div({ class: 'flex flex-col gap-4' }, [
Select({ Div({ class: 'form-control w-full max-w-xs' }, [
label: 'Primary Select', Label({ class: 'label' }, Span({ class: 'label-text' }, 'Primary Select')),
class: 'select-primary', Select({
items: [ class: 'select select-primary w-full',
{ value: 'option1', label: 'Option 1' }, value: primary,
{ value: 'option2', label: 'Option 2' }, onchange: (e) => primary(e.target.value)
{ value: 'option3', label: 'Option 3' } }, [
], Options({
value: primary, items: [
onchange: (e) => primary(e.target.value) { value: 'option1', label: 'Option 1' },
}), { value: 'option2', label: 'Option 2' },
Select({ { value: 'option3', label: 'Option 3' }
label: 'Secondary Select', ]
class: 'select-secondary', })
items: [ ])
{ value: 'option1', label: 'Option 1' }, ]),
{ value: 'option2', label: 'Option 2' } Div({ class: 'form-control w-full max-w-xs' }, [
], Label({ class: 'label' }, Span({ class: 'label-text' }, 'Secondary Select')),
value: secondary, Select({
onchange: (e) => secondary(e.target.value) class: 'select select-secondary w-full',
}), value: secondary,
Select({ onchange: (e) => secondary(e.target.value)
label: 'Ghost Select', }, [
class: 'select-ghost', Options({
items: [ items: [
{ value: '', label: 'Select an option' }, { value: 'option1', label: 'Option 1' },
{ value: 'opt1', label: 'Option 1' }, { value: 'option2', label: 'Option 2' }
{ value: 'opt2', label: 'Option 2' } ]
], })
value: ghost, ])
onchange: (e) => ghost(e.target.value) ]),
}) Div({ class: 'form-control w-full max-w-xs' }, [
Label({ class: 'label' }, Span({ class: 'label-text' }, 'Ghost Select')),
Select({
class: 'select select-ghost w-full',
value: ghost,
onchange: (e) => ghost(e.target.value)
}, [
Options({
placeholder: 'Select an option',
items: [
{ value: 'opt1', label: 'Option 1' },
{ value: 'opt2', label: 'Option 2' }
]
})
])
])
]); ]);
}; };
Mount(VariantsDemo, '#demo-variants'); Mount(VariantsDemo, '#demo-variants');

View File

@@ -10,7 +10,7 @@
href="//cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css"
/> />
<link href="./sigpro.css" rel="stylesheet" type="text/css" /> <link href="./sigpro-ui.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

2
docs/sigpro-ui.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3,10 +3,12 @@ import * as AlertModule from './components/Alert.js';
import * as AutocompleteModule from './components/Autocomplete.js'; import * as AutocompleteModule from './components/Autocomplete.js';
import * as BadgeModule from './components/Badge.js'; import * as BadgeModule from './components/Badge.js';
import * as ButtonModule from './components/Button.js'; import * as ButtonModule from './components/Button.js';
import * as CalendarModule from './components/Calendar.js';
import * as CardModule from './components/Card.js'; import * as CardModule from './components/Card.js';
import * as CarouselModule from './components/Carousel.js'; import * as CarouselModule from './components/Carousel.js';
import * as ChatModule from './components/Chat.js'; import * as ChatModule from './components/Chat.js';
import * as CheckboxModule from './components/Checkbox.js'; import * as CheckboxModule from './components/Checkbox.js';
import * as CollapseModule from './components/Collapse.js';
import * as ColorpickerModule from './components/Colorpicker.js'; import * as ColorpickerModule from './components/Colorpicker.js';
import * as DatepickerModule from './components/Datepicker.js'; import * as DatepickerModule from './components/Datepicker.js';
import * as DrawerModule from './components/Drawer.js'; import * as DrawerModule from './components/Drawer.js';
@@ -16,9 +18,9 @@ import * as FieldsetModule from './components/Fieldset.js';
import * as FileinputModule from './components/Fileinput.js'; import * as FileinputModule from './components/Fileinput.js';
import * as IconModule from './components/Icon.js'; import * as IconModule from './components/Icon.js';
import * as IndicatorModule from './components/Indicator.js'; import * as IndicatorModule from './components/Indicator.js';
import * as KdbModule from './components/Kdb.js';
import * as InputModule from './components/Input.js'; import * as InputModule from './components/Input.js';
import * as ListModule from './components/List.js'; import * as KdbModule from './components/Kdb.js';
// import * as ListModule from './components/List.js';
import * as LoadingModule from './components/Loading.js'; import * as LoadingModule from './components/Loading.js';
import * as MenuModule from './components/Menu.js'; import * as MenuModule from './components/Menu.js';
import * as ModalModule from './components/Modal.js'; import * as ModalModule from './components/Modal.js';
@@ -36,10 +38,9 @@ import * as SwapModule from './components/Swap.js';
import * as TableModule from './components/Table.js'; import * as TableModule from './components/Table.js';
import * as TabsModule from './components/Tabs.js'; import * as TabsModule from './components/Tabs.js';
import * as TextareaModule from './components/Textarea.js'; import * as TextareaModule from './components/Textarea.js';
import * as TextRotateModule from './components/TextRotate.js'; import * as TextrotateModule from './components/Textrotate.js';
import * as TimelineModule from './components/Timeline.js'; import * as TimelineModule from './components/Timeline.js';
import * as ToastModule from './components/Toast.js'; import * as ToastModule from './components/Toast.js';
import * as ToggleModule from './components/Toggle.js';
import * as TooltipModule from './components/Tooltip.js'; import * as TooltipModule from './components/Tooltip.js';
import { Locale, tt } from './utils.js'; import { Locale, tt } from './utils.js';
@@ -49,10 +50,12 @@ export const Components = {
...AutocompleteModule, ...AutocompleteModule,
...BadgeModule, ...BadgeModule,
...ButtonModule, ...ButtonModule,
...CalendarModule,
...CardModule, ...CardModule,
...CarouselModule, ...CarouselModule,
...ChatModule, ...ChatModule,
...CheckboxModule, ...CheckboxModule,
...CollapseModule,
...ColorpickerModule, ...ColorpickerModule,
...DatepickerModule, ...DatepickerModule,
...DrawerModule, ...DrawerModule,
@@ -62,9 +65,9 @@ export const Components = {
...FileinputModule, ...FileinputModule,
...IconModule, ...IconModule,
...IndicatorModule, ...IndicatorModule,
...KdbModule,
...InputModule, ...InputModule,
...ListModule, ...KdbModule,
// ...ListModule,
...LoadingModule, ...LoadingModule,
...MenuModule, ...MenuModule,
...ModalModule, ...ModalModule,
@@ -82,10 +85,9 @@ export const Components = {
...TableModule, ...TableModule,
...TabsModule, ...TabsModule,
...TextareaModule, ...TextareaModule,
...TextRotateModule, ...TextrotateModule,
...TimelineModule, ...TimelineModule,
...ToastModule, ...ToastModule,
...ToggleModule,
...TooltipModule ...TooltipModule
}; };

View File

@@ -44,17 +44,18 @@
}, },
"scripts": { "scripts": {
"clean": "rm -rf ./dist ./css/*.css ./docs/*.js ./docs/*.css", "clean": "rm -rf ./dist ./css/*.css ./docs/*.js ./docs/*.css",
"build:cssmin": "tailwindcss -i ./sigpro.css -o ./dist/sigpro.min.css --content './src/**/*.js' --minify", "build:css": "tailwindcss -i ./sigpro.css -o ./dist/sigpro-ui.css --content './src/**/*.js'",
"build:css": "tailwindcss -i ./sigpro.css -o ./dist/sigpro.css --content './src/**/*.js'", "build:cssmin": "tailwindcss -i ./sigpro.css -o ./dist/sigpro-ui.min.css --content './src/**/*.js' --minify",
"build:cssdocs": "tailwindcss -i ./sigpro.css -o ./docs/sigpro.css --content './src/**/*.js' --minify",
"build:js": "bun run build:js:iife && bun run build:js:esm", "build:js": "bun run build:js:iife && bun run build:js:esm",
"build:js:iife": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.js --format=iife --global-name=SigProUI && bun build ./index.js --bundle --outfile=./dist/sigpro-ui.min.js --format=iife --global-name=SigProUI --minify", "build:js:iife": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.js --format=iife --global-name=SigProUI",
"build:js:esm": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.esm.js --format=esm && bun build ./index.js --bundle --outfile=./dist/sigpro-ui.esm.min.js --format=esm --minify", "build:js:iife:min": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.min.js --format=iife --global-name=SigProUI --minify",
"build:jsdocs": "bun build ./index.js --bundle --outfile=./docs/sigpro-ui.min.js --format=iife --global-name=SigProUI --minify", "build:js:esm": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.esm.js --format=esm",
"build": "bun run clean && bun run build:css && bun run build:js && bun run build:jsdocs && bun run build:cssdocs && bun run build:cssmin", "build:js:esm:min": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.esm.min.js --format=esm --minify",
"copy:docs": "cp dist/sigpro-ui.min.css dist/sigpro-ui.min.js docs/",
"build": "bun run clean && bun run build:css && bun run build:cssmin && bun run build:js:iife && bun run build:js:iife:min && bun run build:js:esm && bun run build:js:esm:min && bun run copy:docs",
"prepublishOnly": "bun run build", "prepublishOnly": "bun run build",
"docs": "bun x serve docs" "docs": "bun x serve docs"
}, },
"type": "module", "type": "module",
"unpkg": "./dist/sigpro-ui.min.js" "unpkg": "./dist/sigpro-ui.min.js"
} }

View File

@@ -60,7 +60,7 @@
font-size: 14px; font-size: 14px;
} }
/* Agrupamos los selectores normales de CSS */
.input, .input,
.select, .select,
.textarea { .textarea {
@@ -79,7 +79,6 @@
&:focus { &:focus {
--focus-color: var(--color-primary); --focus-color: var(--color-primary);
/* Selectores que detectan la variante de color sin importar el prefijo */
&[class*="-secondary"] { &[class*="-secondary"] {
--focus-color: var(--color-secondary); --focus-color: var(--color-secondary);
} }