import { $, watch, h, mount, when, each, isFunc } from "./sigpro.js" export const val = val => typeof val === "function" ? val() : val; export const getBy = (item, field = 'label') => (item && typeof item === 'object') ? item[field] : item; export const cls = (...classes) => classes.filter(Boolean).join(' ').trim(); export const filterBy = (items, query, field = 'label') => { const q = String(val(query) || '').toLowerCase(); const list = (val(items) || []).map(i => typeof i === 'object' ? i : { label: i, value: i }); return !q ? list : list.filter(item => String(item[field] || '').toLowerCase().includes(q)); }; export const rand = (r) => `${r}-${Math.random().toString(36).slice(2, 9)}` export const hide = () => document.activeElement?.blur() const currentLocale = $("en"); export const lang = { es: { uploadFiles: "Arrastrar y soltar o click para seleccionar..." }, en: { uploadFiles: "Drag and drop or click to select" } }; export const setLocale = (locale) => { if (lang[locale]) currentLocale(locale) } export const t = t => () => lang[currentLocale()][t] || t; const c1 = (tag, cls) => (p) => h(tag, { ...p, class: `${cls} ${p?.class || ''}`.trim() }) const c2 = (tag, cls) => (p, c) => h(tag, { ...p, class: `${cls} ${p?.class || ''}`.trim() }, c) const ct = (tag, cls, type) => (p) => h(tag, { type, ...p, class: `${cls} ${p?.class || ''}`.trim() }) export const Alert = c2("div", "alert") export const Avatar = (p, c) => h("div", { class: "avatar" }, h('div', { class: p.class }, c)) export const AvatarGroup = c2("div", "avatar-group -space-x-6") export const Badge = c2("span", "badge") export const Breadcrumbs = c2("div", "breadcrumbs") export const Button = c2("button", "btn") export const Card = c2("div", "card") export const CardTitle = c2("div", "card-title") export const CardBody = c2("div", "card-body") export const CardActions = c2("div", "card-actions") export const Carousel = c2("div", "carousel") export const CarouselItem = c2("div", "carousel-item") export const Chat = c2("div", "chat") export const ChatBubble = c2("div", "chat-bubble") export const ChatFooter = c2("div", "chat-footer") export const ChatHeader = c2("div", "chat-header") export const ChatImage = (p, c) => h("div", { ...p, class: cls("chat-image avatar", p.class) }, h("div", { class: "w-10 rounded-full" }, typeof c === "string" ? h("img", { src: c, alt: "avatar" }) : c)) export const Checkbox = ct("input", "checkbox", "checkbox") export const Drawer = c2("div", "drawer") export const DrawerToggle = (p) => input({ ...p, type: 'checkbox', class: 'drawer-toggle', checked: () => val(p.checked), onchange: (e) => isFunc(p.checked) && p.checked(e.target.checked) }) export const DrawerContent = c2("div", "drawer-content") export const DrawerSide = c2("div", "drawer-side") export const DrawerOverlay = (p) => label({ ...p, for: p.for, class: cls('drawer-overlay', p.class) }) export const Divider = c1("div", "divider") export const Dropdown = c2("div", "dropdown") export const DropdownButton = (p, c) => (h('div', { ...p, tabindex: '0', role: 'button', class: cls('btn', p.class) }, c)) export const DropdownContent = (p, c) => (h('div', { ...p, tabindex: '0', class: cls('dropdown-content', p.class) }, c)) export const Fab = (p, c) => h("div", { class: "fab" }, [h('div', { tabindex: "0", role: "button", class: cls('btn', p.class) }, Icon({},p.icon)), c]) export const Fieldset = (p, c) => h("fieldset", { class: cls("fieldset", p.class) }, [h("legend", { class: "fieldset-legend" }, p.label), c]) export const Icon = (p, c) => h("span", { ...p, class: cls(c, p.class) }) export const Indicator = (p, c) => h("div", { ...p, class: cls("indicator", p.class) }, [p.value && h("span", { class: cls("indicator-item badge", p.class) }, p.value), c]) export const Kbd = c2("kbd", "kbd") export const List = c2("ul", "list") export const ListRows = (p) => () => (val(p.items) || []).map((item, idx) => h('li', { class: cls('list-row', p.class, item?.class) }, typeof p.render === 'function' ? p.render(item, idx) : item)) export const Loading = c2("span", "loading loading-spinner") export const Navbar = c2("div", "navbar") export const Progress = c1("progress", "progress") export const Radial = (p, c) => h("div", { class: cls("radial-progress", p.class), style: `--value:${val(p.value) ?? 0};`, role: "progressbar", "aria-valuenow": p.value }, c) export const Radio = ct("input", "radio", "radio") export const Range = ct("input", "range", "range") export const Rating = c2("div", "rating") export const RatingItems = (p) => [...Array(p.count)].map((_, i) => h('input', { class: cls('mask', p.class), name: p.name, type: 'radio', checked: () => val(p.value) === i, onchange: () => isFunc(p.value) ? p.value(i) : p.onchange?.(i) })) export const Skeleton = c1("div", "skeleton") export const SkeletonText = c1("span", "skeleton skeleton-text") export const Stack = c2("div", "stack") export const Stats = c2("div", "stats shadow") export const Steps = c2("ul", "steps") export const Step = (p, c) => h("li", { ...p, class: cls("step", p.class), "data-content": p.dataContent }, c) export const Swap = c2("label", "swap") export const SwapToggle = (p) => h('input', { type: 'checkbox', checked: () => val(p.value), onchange: (e) => isFunc(p.value) && p.value(e.target.checked), class: p.class }) export const SwapOn = c2("div", "swap-on") export const SwapOff = c2("div", "swap-off") export const Table = c2("table", "table") export const Textarea = c1("textarea", "textarea") export const Textrotate = (p, c) => h('span', { ...p, class: cls('text-rotate', p.class) }, h('span', {}, c)) export const Timeline = c2("ul", "timeline") export const Toggle = ct("input", "toggle", "checkbox") export const Tooltip = (p, c) => h("div", { ...p, class: cls("tooltip", p.class), "data-tip": p.tip }, c) export const Accordion = (p) => { const name = p.name || rand('acc') return each(p.items, (it) => { return h('div', { class: cls('collapse', p.class) }, [ h('input', { type: 'radio', name, checked: it.open || undefined }), it.title ? h('div', { class: cls("collapse-title", `${it.classTitle ?? ' font-semibold'}`) }, it.title) : null, it.content ? h('div', { class: cls("collapse-content text-sm", `${it.classContent ?? ' font-semibold'}`) }, it.content) : null, ]); }); }; export const Autocomplete = ({ items, value, onselect, placeholder = '...', ...props }) => { const query = $(val(value) || '') const filtered = $(() => filterBy(items, query())) const pick = (item) => { const display = getBy(item) const actual = typeof item === 'string' ? item : item.value query(display) if (isFunc(value)) value(actual) onselect?.(item) hide() } return Dropdown({ class: 'w-80' }, [ h('div', { tabindex: '0', role: 'button', class: 'w-full' }, Input({ ...props, placeholder, value: query, left: Icon({},'icon-[lucide--search]') })), DropdownContent({ class: 'p-2 bg-base-100 rounded-box shadow-xl w-full max-h-60 overflow-y-auto border border-base-300 z-50' }, h('ul', { class: 'menu flex-col flex-nowrap w-full p-0' }, [ each(filtered, (item) => h('li', {}, [h('a', { onmousedown: (e) => e.preventDefault(), onclick: () => pick(item) }, getBy(item))]), 'value'), () => filtered().length === 0 ? h('li', { class: 'p-4 opacity-50 text-center' }, 'Sin resultados') : null ]) ) ]) }; export const Calendar = (p) => { const internalDate = $(new Date()) const hoverDate = $(null) const startHour = $(0) const endHour = $(0) const now = new Date() const todayStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}` const fmt = d => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}` const rangeMode = () => val(p.range) === true const current = () => val(p.value) const selectDate = (date) => { const s = fmt(date) const v = current() if (rangeMode()) { if (!v?.start || (v.start && v.end)) { p.onChange?.({ start: s, end: null, ...(p.hour && { startHour: startHour() }) }) } else { const start = v.start const nv = s < start ? { start: s, end: start } : { start, end: s } if (p.hour) { nv.startHour = v.startHour ?? startHour(); nv.endHour = endHour() } p.onChange?.(nv) } } else { p.onChange?.(p.hour ? `${s}T${String(startHour()).padStart(2, '0')}:00:00` : s) } } const move = (m) => { const d = internalDate(); internalDate(new Date(d.getFullYear(), d.getMonth() + m, 1)) } const moveYear = (y) => { const d = internalDate(); internalDate(new Date(d.getFullYear() + y, d.getMonth(), 1)) } const HourSlider = ({ value: hVal, onChange: onH }) => h('div', { class: 'flex-1' }, [ h('div', { class: 'flex gap-2 items-center' }, [ h('input', { type: 'range', min: 0, max: 23, value: hVal, class: 'range range-xs flex-1', oninput: e => onH(+e.target.value) }), h('span', { class: 'text-sm font-mono min-w-[48px] text-center' }, () => String(val(hVal)).padStart(2, '0') + ':00') ]) ]) return h('div', { class: cls('p-4 bg-base-100 border border-base-300 shadow-2xl rounded-box w-80 select-none', p.class) }, [ h('div', { class: 'flex justify-between items-center mb-4 gap-1' }, [ h('div', { class: 'flex gap-0.5' }, [ h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => moveYear(-1) }, h('span', { class: 'icon-[lucide--chevrons-left]' })), h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => move(-1) }, h('span', { class: 'icon-[lucide--chevron-left]' })) ]), h('span', { class: 'font-bold uppercase flex-1 text-center' }, () => internalDate().toLocaleString('es-ES', { month: 'short', year: 'numeric' })), h('div', { class: 'flex gap-0.5' }, [ h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => move(1) }, h('span', { class: 'icon-[lucide--chevron-right]' })), h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => moveYear(1) }, h('span', { class: 'icon-[lucide--chevrons-right]' })) ]) ]), h('div', { class: 'grid grid-cols-7 gap-1', onmouseleave: () => hoverDate(null) }, [ ...['L', 'M', 'X', 'J', 'V', 'S', 'D'].map(d => h('div', { class: 'text-[10px] opacity-40 font-bold text-center' }, d)), () => { const d = internalDate(), y = d.getFullYear(), m = d.getMonth() const firstDay = new Date(y, m, 1).getDay() const offset = firstDay === 0 ? 6 : firstDay - 1 const dim = new Date(y, m + 1, 0).getDate() const cells = [] for (let i = 0; i < offset; i++) cells.push(h('div')) for (let i = 1; i <= dim; i++) { const date = new Date(y, m, i), ds = fmt(date) cells.push(h('button', { type: 'button', class: () => { const v = current(), h = hoverDate() const isStart = typeof v === 'string' ? v.split('T')[0] === ds : v?.start === ds const isEnd = v?.end === ds let inRange = false if (rangeMode() && v?.start) { const start = v.start if (!v.end && h) inRange = (ds > start && ds <= h) || (ds < start && ds >= h) else if (v.end) inRange = ds > start && ds < v.end } const base = 'btn btn-xs p-0 aspect-square min-h-0 h-auto font-normal relative' const st = isStart || isEnd ? 'btn-primary z-10' : inRange ? 'bg-primary/20 border-none rounded-none' : 'btn-ghost' const today = ds === todayStr ? 'ring-1 ring-primary ring-inset font-black text-primary' : '' return cls(base, st, today) }, onmouseenter: () => rangeMode() && hoverDate(ds), onclick: () => selectDate(date) }, i.toString())) } return cells } ]), p.hour ? h('div', { class: 'mt-3 pt-2 border-t border-base-300' }, rangeMode() ? h('div', { class: 'flex gap-4' }, [HourSlider({ value: startHour, onChange: h => startHour(h) }), HourSlider({ value: endHour, onChange: h => endHour(h) })]) : HourSlider({ value: startHour, onChange: h => startHour(h) }) ) : null ]) }; export const Colorpicker = (p) => { const current = () => val(p.value) || '#000000' return Dropdown({}, [ DropdownButton({ class: 'btn' }, [ h('div', { class: 'size-5 rounded-sm', style: () => `background-color: ${current()}` }), p.label && h('span', {}, p.label) ]), DropdownContent({ class: 'p-0' }, ColorPalette({ value: p.value, onchange: (c) => { isFunc(p.value) ? p.value(c) : p.onchange?.(c) } }) ) ]) }; export const ColorPalette = (p) => { const current = () => val(p.value) || '#000000' const palette = [ '#000', '#1A1A1A', '#333', '#4D4D4D', '#666', '#808080', '#B3B3B3', '#FFF', '#450a0a', '#7f1d1d', '#991b1b', '#b91c1c', '#dc2626', '#ef4444', '#f87171', '#fca5a5', '#431407', '#7c2d12', '#9a3412', '#c2410c', '#ea580c', '#f97316', '#fb923c', '#ffedd5', '#713f12', '#a16207', '#ca8a04', '#eab308', '#facc15', '#fde047', '#fef08a', '#fff9c4', '#064e3b', '#065f46', '#059669', '#10b981', '#34d399', '#4ade80', '#84cc16', '#d9f99d', '#082f49', '#075985', '#0284c7', '#0ea5e9', '#38bdf8', '#7dd3fc', '#22d3ee', '#cffafe', '#1e1b4b', '#312e81', '#4338ca', '#4f46e5', '#6366f1', '#818cf8', '#a5b4fc', '#e0e7ff', '#2e1065', '#4c1d95', '#6d28d9', '#7c3aed', '#8b5cf6', '#a855f7', '#d946ef', '#fae8ff' ] const pick = (c) => { isFunc(p.value) ? p.value(c) : p.onchange?.(c) hide() } return h('div', { class: cls('p-3 bg-base-100 rounded-box shadow w-64', p.class) }, h('div', { class: 'grid grid-cols-8 gap-1' }, palette.map(c => h('button', { type: 'button', style: `background-color: ${c}`, class: () => { const act = current().toLowerCase() === c.toLowerCase() 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 ${act ? 'ring-2 ring-offset-1 ring-primary z-10 scale-110' : ''}` }, onclick: () => { pick(c) } })) )) }; export const Datepicker = (p) => { const displayValue = $("") const rangeMode = () => val(p.range) === true watch(() => { const v = val(p.value) if (!v) return displayValue("") let text = "" if (typeof v === "string") { text = p.hour && v.includes("T") ? v.replace("T", " ") : v } else if (v.start && v.end) { const startStr = p.hour && v.startHour != null ? `${v.start} ${String(v.startHour).padStart(2, "0")}:00` : v.start const endStr = p.hour && v.endHour != null ? `${v.end} ${String(v.endHour).padStart(2, "0")}:00` : v.end text = `${startStr} - ${endStr}` } else if (v.start) { const startStr = p.hour && v.startHour != null ? `${v.start} ${String(v.startHour).padStart(2, "0")}:00` : v.start text = `${startStr}...` } displayValue(text) }) const handleChange = (val) => { if (isFunc(p.value)) p.value(val) else p.onChange?.(val) if (!rangeMode() || val?.end != null) hide() } return Dropdown({ class: cls('w-full', p.class) }, [ h('label', { tabindex: '0', role: 'button', class: 'input input-bordered flex items-center gap-2 cursor-pointer' }, [ h('span', { class: 'icon-[lucide--calendar] shrink-0' }), h('span', { class: () => `grow text-left truncate ${!displayValue() ? 'opacity-50' : ''}`, }, () => displayValue() || p.placeholder || (rangeMode() ? 'Seleccionar rango...' : 'Seleccionar fecha...')), () => displayValue() ? h('button', { type: 'button', class: 'btn btn-ghost btn-xs btn-circle -mr-2', onmousedown: (e) => { e.preventDefault() e.stopPropagation() if (isFunc(p.value)) p.value(null) else p.onChange?.(null) displayValue("") // Forzar limpieza visual inmediata } }, h('span', { class: 'icon-[lucide--x] opacity-50' })) : null ]), DropdownContent({ class: 'p-0' }, Calendar({ value: p.value, range: rangeMode(), hour: p.hour, onChange: handleChange }) ) ]) }; export const Fileinput = (p) => { const files = $([]) const drag = $(false) const error = $(null) const maxBytes = (p.max || 2) * 1024 * 1024 const process = (fileList) => { const arr = Array.from(fileList) error(null) if (arr.some(f => f.size > maxBytes)) { error(`Máx ${p.max || 2}MB`) return } const updated = [...files(), ...arr] files(updated) if (isFunc(p.onselect)) p.onselect(updated) else if (isFunc(p.value)) p.value(updated) } const remove = (idx) => { const updated = files().filter((_, i) => i !== idx) files(updated) if (isFunc(p.onselect)) p.onselect(updated) else if (isFunc(p.value)) p.value(updated) } return h('div', { class: cls('fieldset w-full p-0', p.class) }, [ h('label', { 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 ${drag() ? 'border-primary bg-primary/10' : 'border-base-content/20 bg-base-100 hover:bg-base-200'}`, ondragover: (e) => { e.preventDefault(); drag(true) }, ondragleave: () => drag(false), ondrop: (e) => { e.preventDefault(); drag(false); process(e.dataTransfer.files) } }, [ h('div', { class: 'flex items-center gap-3 w-full' }, [ h('span', { class: 'icon-[lucide--upload]' }), h('span', { class: 'text-sm opacity-70 truncate grow text-left' }, t("uploadFiles")), h('span', { class: 'text-[10px] opacity-40 shrink-0' }, `Máx ${p.max || 2}MB`) ]), h('input', { type: 'file', multiple: true, accept: p.accept || '*', class: 'hidden', onchange: (e) => process(e.target.files) }) ]), () => error() && h('span', { class: 'text-[10px] text-error mt-1 px-1 font-medium' }, error()), when(() => files().length > 0, () => h('ul', { class: 'mt-2 space-y-1' }, each(files, (file, idx) => h('li', { class: 'flex items-center justify-between p-1.5 pl-3 text-xs bg-base-200/50 rounded-md border border-base-300' }, [ h('div', { class: 'flex items-center gap-2 truncate' }, [ h('span', { class: 'opacity-50' }, '📄'), h('span', { class: 'truncate font-medium max-w-[200px]' }, file.name), h('span', { class: 'text-[9px] opacity-40' }, `(${(file.size / 1024).toFixed(0)} KB)`) ]), h('button', { type: 'button', class: 'btn btn-ghost btn-xs btn-circle', onclick: (e) => { e.preventDefault(); remove(idx) } }, h('span', { class: 'icon-[lucide--x]' })) ]) ) ) ) ]) }; export const Input = (p) => { const { label, icon, float, placeholder, value, left, right, rule, hint, content, ...rest } = p; const showPassword = $(false); const isPassword = p.type === 'password'; const pattern = rule ?? null; const inputType = () => isPassword ? (val(showPassword) ? 'text' : 'password') : (p.type || 'search'); return h("label", { class: float ? 'floating-label' : '' }, [ float ? h("span", {}, label) : null, h("label", { pattern: pattern, class: () => cls('input validator', p.class) }, [ label && !float ? h('span', { class: 'label' }, label) : null, left ?? null, h('input', { ...rest, type: inputType, class: 'grow', pattern: pattern, placeholder: placeholder || label || ' ', value: value }), right ?? null, isPassword ? Swap({ class: 'ml-2' }, [ SwapToggle({ value: showPassword, class: "swap-rotate" }), SwapOn({}, Icon({},'icon-[lucide--eye]')), SwapOff({}, Icon({},'icon-[lucide--eye-off]')), ]) : null ]), hint ? h('div', { class: "validator-hint" }, hint) : null, ]); }; export const Menu = (p) => { if (p.children !== undefined) return h('ul', { class: cls('menu', p.class), ...p }, p.children) const { items } = p const render = (item) => item.children ? h('li', {}, h('details', { open: item.open || undefined }, [ h('summary', {}, getBy(item)), h('ul', {}, each(() => val(item.children) || [], render)) ])) : h('li', {}, h('a', { href: item.href, onclick: item.onclick ? (e) => { if (!item.href) e.preventDefault(); item.onclick(e) } : null }, getBy(item))) return h('ul', { class: cls('menu', p.class) }, each(() => val(items) || [], render) ) }; export const Modal = (p, c) => { let dialogRef = null; watch(() => { const isOpen = val(p.open); if (!dialogRef) return; isOpen ? dialogRef.showModal() : dialogRef.close(); }); const close = () => isFunc(p.open) && p.open(false); return h("dialog", { ...p, ref: el => dialogRef = el, class: cls('modal', p.class), onclose: close, oncancel: close }, [ h("div", { class: "modal-box" }, [ p.title && h("h3", { class: "text-lg font-bold" }, p.title), c, h("div", { class: "modal-action" }, [ p.actions || Button({ class: 'btn', onclick: close }, 'Cerrar') ]) ]), h("form", { method: "dialog", class: "modal-backdrop" }, [ h("button", {}, "close") ]) ]); }; export const Select = (p, c) => { if (c !== undefined) return h('select', { class: cls('select', p.class), ...p }, c) const { label, float, placeholder, placeholderDisabled = true, value, left, right, hint, items, keyFn, ...rest } = p const opts = () => { const raw = val(items) || [] const ph = placeholder ? [{ disabled: placeholderDisabled, label: placeholder, value: '' }] : [] return [...ph, ...raw] } return h('label', { class: float ? 'floating-label' : '' }, [ float ? h('span', {}, label) : null, h('label', { class: cls('select', rest.class) }, [ (!float && label) ? h('span', { class: 'label' }, label) : null, left ?? null, h('select', { value: () => val(value), onchange: (e) => isFunc(value) ? value(e.target.value) : rest.onchange?.(e) }, each(opts, (item) => { const val = getBy(item, item.value !== undefined ? 'value' : undefined) const lab = getBy(item, 'label') return h('option', { value: val, disabled: item.disabled || undefined }, lab) }) ), right ?? null ]), hint ? h('div', { class: 'validator-hint' }, hint) : null ]) }; export const Stat = (p) => h('div', { ...p, class: cls('stat', p.class) }, [ p.title ? h('div', { class: 'stat-title' }, p.title) : null, p.value ? h('div', { class: 'stat-value' }, p.value) : null, p.desc ? h('div', { class: 'stat-desc' }, p.desc) : null ]); export const TableItems = ({ items, columns = [], header = true }) => { const head = header !== false && columns.some(c => c.label) ? h('thead', {}, h('tr', {}, columns.map(c => h('th', { class: c.class }, c.label)))) : null const body = h('tbody', {}, () => { const list = val(items) || [] return list.map((it, idx) => h('tr', {}, columns.map(c => { const v = c.render ? c.render(it, idx) : it[c.key]; return h('td', { class: c.class }, v) }))) }) return [head, body].filter(Boolean) }; export const Tabs = (p, c) => { if (!p.items) { const { class: className, ...rest } = p return h('div', { ...rest, class: cls('tabs', className) }, c) } const { items, activeIndex, onClose, class: className, ...rest } = p const get = x => (isFunc(x) ? x() : x) const closeH = onClose || (isFunc(items) ? (idx, item) => { const arr = val(items) const newArr = arr.filter((_, i) => i !== idx) items(newArr) if (activeIndex() >= newArr.length) activeIndex(Math.max(0, newArr.length - 1)) } : null) return h('div', { ...rest, class: cls('tabs', className) }, () => { const list = val(items) || [] return list.flatMap((it, idx) => { const isActive = () => activeIndex() === idx const button = h('button', { class: () => `tab ${isActive() ? 'tab-active' : ''} ${it.class || ''}`, onclick: (e) => { e.preventDefault(); activeIndex(idx); it.onclick?.(e) } }, [ getBy(it), it.closable ? h('span', { class: 'ml-1 inline-flex items-center justify-center w-4 h-4 rounded-full hover:bg-base-300 text-base-content/60 hover:text-base-content cursor-pointer', onclick: (e) => { e.stopPropagation(); closeH?.(idx, it) } }, h('span', { class: 'icon-[lucide--x] w-3 h-3' })) : null ]) const contentDiv = h('div', { class: 'tab-content bg-base-100 border-base-300 p-6', style: () => `display: ${isActive() ? 'block' : 'none'};` }, isFunc(it.content) ? it.content() : it.content) return [button, contentDiv] }) }) }; export const Toast = (message, type = "alert-success", duration = 3500) => { let container = document.getElementById("sigpro-toast-container"); if (!container) { container = h("div", { id: "sigpro-toast-container", class: "fixed top-0 right-0 z-[9999] p-4 flex flex-col items-end gap-2 pointer-events-none" }); document.body.appendChild(container); } const host = h("div", { style: "display: contents" }); container.appendChild(host); let closeFn, timer, enterTimer; const ToastComponent = () => { const visible = $(false); const leaving = $(false); closeFn = () => { if (leaving()) return; clearTimeout(timer); clearTimeout(enterTimer); leaving(true); setTimeout(() => { instance.destroy(); host.remove(); if (!container.hasChildNodes()) container.remove(); }, 300); }; enterTimer = setTimeout(() => visible(true), 0); const content = typeof message === 'function' ? val(message) : message; const msgNode = typeof content === 'string' ? h("span", {}, content) : content; return h("div", { class: () => { const base = `alert alert-soft ${type} shadow-lg transition-all duration-300 inline-flex w-auto whitespace-nowrap pointer-events-auto`; if (leaving()) return `${base} translate-x-full opacity-0`; if (visible()) return `${base} translate-x-0 opacity-100`; return `${base} translate-x-10 opacity-0`; } }, [ msgNode, h("button", { class: "btn btn-xs btn-circle btn-ghost", onclick: closeFn }, h("span", { class: "icon-[lucide--x]" })) ]); }; const instance = mount(ToastComponent, host); if (duration > 0) timer = setTimeout(closeFn, duration); return closeFn; };