From 7a7599b1162434c355cbf8c8d5aedebeb135435a Mon Sep 17 00:00:00 2001 From: natxocc Date: Wed, 25 Mar 2026 18:15:56 +0100 Subject: [PATCH] new Components --- src/sigpro-ui.js | 164 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 157 insertions(+), 7 deletions(-) diff --git a/src/sigpro-ui.js b/src/sigpro-ui.js index ff54140..0394d57 100644 --- a/src/sigpro-ui.js +++ b/src/sigpro-ui.js @@ -48,6 +48,23 @@ export const UI = ($, defaultLang = "es") => { "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAB2AAAAdgFOeyYIAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAMxJREFUOI3t0bFKwlEUBvBfmmBEr1APIDZJ9AJJQyAIvkGP0C4uQruza+DUmuIc9AC9gBG4Nmpkw/8IB3Vw1w8u95zvnvPde77LEeUUV9HAF67QRA2nmMf5A+o4x3cWOsMYy8j7WMX6jaYbLBL/mAWe8RcHm1ihs8G94gVKQQzwlAouMcQo8p/Y28HdYpYFZmsi0MVdxD1MdrxsC500wijdvgtbI1AYtDbxMwkuFAZmE1uYwkkSqOIaHyHcxEU0vUXNPSqKr37fZ6xDwD9DPS0OyHjQHQAAAABJRU5ErkJggg=="; const iconMail = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAC4SURBVDiNxdIxagJRFIXhLzLFBNJYaJslSEylWOhq3IorMGQ16SyjYCFiZWU5pTaDFvOUyTAZ8RHID69555577oXLf/OEGaY4R3g/4IhORHg3eOXYYvSAeRQ8OWQYYoNPvDQYnxUr7zBB1grCAv3QbIlxjXmAb7Txhq+rkFUKq9NUU8vcJiizwDtOWGEdmvTKqT+61H0GXsP7jSxpEGF/R1e3wkO0FBeVRnhTSBTneBB3yvOI4D/mAnvrIwKM5s4AAAAAAElFTkSuQmCC"; + const iconInfo = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAASVJREFUOI190r0uhFEQBuBnVxaF2PUTCkFchV0SV6BQi0rEbShFlCqNktJP0Iqf3i3YVSlXVEQozojP8e2+ySSTed+ZMzNnKnpjCFPhv+C9j/YPlnCBV3TCujhHq19iFftoYxOjBa4esTb2QvsP+7jFWJ9HxnEXRf5gGU9Z8gKucBl+sUgHTahE8AJnOCoIT/AcmhmsF7gtrGINBqWFFWcmLXMUhzjIuEbk1GA+2i/DNh4wUsK1MVfFV2GUHJO4xlsPHr8j1Eu44bAcDek2agP4lDZaxWMm3MEKbrL4hjT/8U+gJc00nglnw4qYkL5xMW9rTzqSvEiefI/dMrIaRTrSPzcKXCNinUguPeUfNKWj6kqH9Bz+aVnbvb6PtKTp8F/wUSb6Bu5YN5n7ff0kAAAAAElFTkSuQmCC"; + const iconSuccess = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAQtJREFUOI2F0jFOAlEQBuAPImoFqyTa6TEEbfUihruYDYfwCAg3UDsTY20na0VjgqUWWuxgHsuy/skk82bmn/fPm9eyHXs4Cn+Br4baNZxjhk8UYUtMMWwitjHGHNfoJrlexObIo3YDY9zjoOGSQzxEkzVc4O0fctqkwCANzkJiE9LmI9ytDrvKB+tWGQnylIAsOB04VcrfdluO55CeYo6THfygVUne4jX8S1zho1LTDu7fCL2KxCe8oF8zUqb8G51VYGrzEffD6jDCJA0MY6bqnHXoK9d4Vk3kyk/S1KSPR9zUJdvRpAiJWZLLIlYEufYrrzBQ7nyJ97ClcuYN2dX1pejgOPwFvuuKfgHXiDR+HL1j1AAAAABJRU5ErkJggg=="; + const iconError = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAARZJREFUOI2V0j1KQ1EQBeDPp4lWRiMoKVyAK9AoiLgJGytxD9oJNhKyDyvBnw2IugC3YGKVRk1KRbR48yC5vjzwwIHL3DPnzp2ZGdMxj9U4D/BZoZ3ANu4wQj84xC3aVYkZuujhCItjd42I9dAJ7R908YDlikeaeAyTCezgpST5IJia9LFVlA0nOMd7It4IjuMttKeFQR17uKooPcUV9lHL0ArX0T8MPqLa1hx+MDNFWDX7LHLV4/VGiWghmGJJvhu1WXzLO5rhORGeYRf3SfwQNVwWgbZ8SZqJcD04jhX5GDfTsjryJUlN0uQnXJRdZmHSx7H8nwWWItaP5NJVLrCFG3mTXoNDXJeVPW185E1ai/MAX2WiX9S3NSPYbj+uAAAAAElFTkSuQmCC"; + const iconWarning = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAARJJREFUOI2l0r8uRFEQBvAfu9glwUYiUaxHUEl0VDpKeq+wpZBINAqFRHgTKg0tCSqVhmKDEM1u/Esodm725rq7iC+ZzMnM982ZmXP4JwpdchWsYBrXeMkj9XQQV3GEi+BMYR63v+mqiDPUUrEaTiP3I1ZxEOcySnE+jFxXVPEQPimWiCYzOdCbKbCFPe1Z+8PgBvvBycVMCIdSsY2wBEPBmcnrYBtraKRib2EJGljHjswLLuI8Z6SS9hLTl15iIR08wZLv2AzLYjk0YATP8n9lVWbrgUJohosYxCdG8Zghdvp5ldCUi6hrPd0VjvGEVzTxEYLkogGMYQ67uEtvcgKzGA8y9IV/D9/Evdb89Q7d/Q1fB8U0mpUmzV0AAAAASUVORK5CYII="; + const iconLeft = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABfSURBVDiNY2AY8oCZSHWxDAwMEgwMDHfJsaSAgYHhH9QQsjT/Z2BgKKe75gQGiLMLCSlkwiHOSI6t6ADmhYoBN6SIARIeidgkiUlIxxkYGB4xMDB8YmBguE6JSwYpAACvLRHTKwPjZgAAAABJRU5ErkJggg=="; + const iconRight = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABNSURBVDiN3dAxCoAwFATRh3fU2oAHiDbi5Y1F2jT+gKLbzyy7/DYjUo8g4cTWI8koOF6XrOqc5ifDDVGJthfsj8OLujtHYJgwR+GP5QKMxA9/SolDQgAAAABJRU5ErkJggg=="; + const iconLLeft = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABlSURBVDiN3ZLBDUBAEEUfmtCchA5woUMlOO1FCQrAwbqwf8eFhHd7mfzJn2Tg82TGvABywAmPUgOLD4XcDK9AJ/y5cOlrNsIvpCdPDL/FUbkX/t6Slv3+SjgQf6QBmIAZGAP+FzZJViOd89x8pAAAAABJRU5ErkJggg=="; + const iconRRight = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABmSURBVDiN3dGxCoAgEMbxfz1dL1BTREJzmUv08trgDYcg6VCD3/YD7zvkoLmMgFEegLmmwAAecOJVvNeUWCAAt7IHjt9LThkyiRf9qC8oCom70u0BuDL+bngj/tNm/JqJePucW8wDvGYdzT0nMUkAAAAASUVORK5CYII="; + // --- UTILITY FUNCTIONS --- /** IF */ @@ -450,7 +467,7 @@ export const UI = ($, defaultLang = "es") => { placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha..."), $value: displayValue, readonly: true, - icon: $.html("img", { src: iconCalendar, class: "w-4 h-4 opacity-40" }), + icon: $.html("img", { src: iconCalendar, class: "opacity-40" }), onclick: (e) => { e.stopPropagation(); isOpen(!isOpen()); @@ -468,15 +485,31 @@ export const UI = ($, defaultLang = "es") => { [ $.html("div", { class: "flex justify-between items-center mb-4 gap-1" }, [ $.html("div", { class: "flex gap-0.5" }, [ - $.html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) }, "<<"), - $.html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) }, "<"), + $.html( + "button", + { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) }, + $.html("img", { src: iconLLeft, class: "opacity-40" }), + ), + $.html( + "button", + { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) }, + $.html("img", { src: iconLeft, class: "opacity-40" }), + ), ]), - $.html("span", { class: "text-xs font-bold uppercase flex-1 text-center" }, [ - () => internalDate().toLocaleString("es-ES", { month: "long", year: "numeric" }), + $.html("span", { class: "font-bold uppercase flex-1 text-center" }, [ + () => internalDate().toLocaleString("es-ES", { month: "short", year: "numeric" }), ]), $.html("div", { class: "flex gap-0.5" }, [ - $.html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) }, ">"), - $.html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) }, ">>"), + $.html( + "button", + { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) }, + $.html("img", { src: iconRight, class: "opacity-40" }), + ), + $.html( + "button", + { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) }, + $.html("img", { src: iconRRight, class: "opacity-40" }), + ), ]), ]), @@ -896,6 +929,123 @@ export const UI = ($, defaultLang = "es") => { $.html("span", { class: joinClass("indicator-item badge", props.badgeClass) }, props.badge), ]); + /** RATING */ + ui.Rating = (props) => { + const { $value, count = 5, name = `rating-${Math.random().toString(36).slice(2, 7)}`, mask = "mask-star", readonly = false, ...rest } = props; + + return $.html( + "div", + { + ...rest, + class: () => `rating ${val(readonly) ? "pointer-events-none" : ""} ${props.class || ""}`, + }, + Array.from({ length: val(count) }, (_, i) => { + const ratingValue = i + 1; + return $.html("input", { + type: "radio", + name: name, + class: `mask ${mask}`, + "aria-label": `${ratingValue} star`, + checked: () => Math.round(val($value)) === ratingValue, + onchange: () => { + if (!val(readonly) && typeof $value === "function") { + $value(ratingValue); + } + }, + }); + }), + ); + }; + + /** ALERT */ + ui.Alert = (props, children) => { + const { type = "info", soft = true, ...rest } = props; + const icons = { + info: iconInfo, + success: iconSuccess, + warning: iconWarning, + error: iconError, + }; + + const typeClass = () => { + const t = val(type); + const map = { + info: "alert-info", + success: "alert-success", + warning: "alert-warning", + error: "alert-error", + }; + return map[t] || t; + }; + + const content = children || props.message; + + return $.html( + "div", + { + ...rest, + role: "alert", + class: () => `alert ${typeClass()} ${val(soft) ? "alert-soft" : ""} ${props.class || ""}`, + }, + [ + $.html("img", { + src: icons[val(type)] || icons.info, + class: "w-4 h-4 object-contain", + alt: val(type), + }), + $.html("div", { class: "flex-1" }, [$.html("span", {}, [typeof content === "function" ? content() : content])]), + props.actions ? $.html("div", { class: "flex-none" }, [typeof props.actions === "function" ? props.actions() : props.actions]) : null, + ], + ); + }; + + /** TIMELINE */ + ui.Timeline = (props) => { + const { items = [], vertical = true, compact = false, ...rest } = props; + + const icons = { + info: iconInfo, + success: iconSuccess, + warning: iconWarning, + error: iconError, + }; + + return $.html( + "ul", + { + ...rest, + class: () => + `timeline ${val(vertical) ? "timeline-vertical" : "timeline-horizontal"} ${val(compact) ? "timeline-compact" : ""} ${props.class || ""}`, + }, + [ + ui.For( + items, + (item, i) => { + const isFirst = i === 0; + const isLast = i === val(items).length - 1; + const itemType = item.type || "success"; + const renderSlot = (content) => (typeof content === "function" ? content() : content); + + return $.html("li", { class: "flex-1" }, [ + !isFirst ? $.html("hr", { class: item.completed ? "bg-primary" : "" }) : null, + $.html("div", { class: "timeline-start" }, [renderSlot(item.title)]), + $.html("div", { class: "timeline-middle" }, [ + $.html("img", { + src: icons[itemType] || item.icon || icons.success, + class: "w-4 h-4 object-contain mx-1", + alt: itemType, + }), + ]), + $.html("div", { class: "timeline-end timeline-box shadow-sm" }, [renderSlot(item.detail)]), + !isLast ? $.html("hr", { class: item.completed ? "bg-primary" : "" }) : null, + ]); + }, + (item, i) => item.id || i, + ), + ], + ); + }; + /** TOAST */ ui.Toast = (message, type = "alert-success", duration = 3500) => { let container = document.getElementById("sigpro-toast-container");