|
|
|
|
@@ -2,7 +2,7 @@
|
|
|
|
|
* SigPro UI - daisyUI v5 & Tailwind v4 Plugin
|
|
|
|
|
* Provides a set of reactive functional components, flow control and i18n.
|
|
|
|
|
*/
|
|
|
|
|
import { $, $if, $for, $watch, $html, $mount } from 'sigpro';
|
|
|
|
|
import { $, $if, $for, $watch, $html, $mount } from "sigpro";
|
|
|
|
|
|
|
|
|
|
// --- I18N CORE ---
|
|
|
|
|
const i18n = {
|
|
|
|
|
@@ -34,13 +34,13 @@ const iconShow =
|
|
|
|
|
const iconHide =
|
|
|
|
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAEDSURBVDiN1dK/K8VhFAbwD+VLGSxKcu9guSQ/Zils/gNkuaX4BxRZDTdklYU/QAaDlEVGGwu2Kz/uVbKJzWDwfuv1+jHz1Km3c85znuf0Hv4jxnD2W8MItnCJ5xAX2MQcHsOQL+jEAapYQD9aQwxiDy+B3JKSe1DHCpqQYQ0PeMJOpDyAmyAAirjGbDRwFYcoYCZSzjGP+8B1gqXEUT2QxyPlqaRnGceNeENzUswwil1MBocbSU9DCAXUUI6K25HtIo5QSVaooitP9OEO65iIbE+HXSvBVRbeNZQSR9pxGil3o83HNw5hEbfYR0dKFki5ci+u8OrzIQ1/R8xx7ocL+9t4B0HPOVXjoptxAAAAAElFTkSuQmCC";
|
|
|
|
|
const iconClose =
|
|
|
|
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAESSURBVDiNtdO7LoRBHAXwXyhVG5ddEq9AgbXiskS1dOJS6DyDRKLmFRCvQY9aIi6FnpDsrkuiQCWKmS/5dmW/VXCSyWTm/GfmnPzP8A8oYBc3eEMd59iKXCZW8YpDzCOHHszgAE9Ya3V4EY8YzXhgAndYaib68YxiO4kYRg290Bk3t/GAvbiuII/7uJ7CGG5RxSCGcJrceh2LEkzGwnIctTgnGMdFWtZ7IimFcrykirkmrkvokI7WVn1lcD9wpdFCKfVyYmE2xRdFC4mCY2ykCgaEfp/gTGhbX4pfx1FaQUEIyW/bWBUC1oAFIUgjGYdLWgQpwTJesC/4z6Eb00JG6lhpJzGPHVziEx/CZ9qM3N/iGy1pNoTrsd1eAAAAAElFTkSuQmCC";
|
|
|
|
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABcSURBVDiN3dIxDoAwCIXhL563g3bSm+hlq4O6GFNbO+k/EV54QIDfsSBk9IA5ZxCQEG+0eGi5BqDHivEhV2xSXXwy2EdOR3xLV+ta0/26wvSm+KTYpPmMzY/0QTZeZR2f+FxhRQAAAABJRU5ErkJggg==";
|
|
|
|
|
const iconCalendar =
|
|
|
|
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAACLSURBVDiN7dO9CQJBFEXhb38K0FwQrMNEVpuwB0NjrcYabECsQk0sQ1mTF4zIjrgmBh54MMx998AEwzOrmC5e8gJjbDHCJO7PHYI0v2JT4Ig9DljGwq5DkOZTLOCOMoIhBpknpHmFWx3ldaaUo6oTc2/ab7rl+508f8GvCC5oenTn4tM1cWg/nBNmD4fBH/Kfvt2TAAAAAElFTkSuQmCC";
|
|
|
|
|
const iconLock =
|
|
|
|
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAWQAAAFkBqp2phgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAACQSURBVDiN7dKxDcJQDATQJ0YgXQQ1bAgDEIZBETPQwjakIjRQ8CMSyR8SiZKTrvHZd/r+JsYSNZrEI1ZR4ywzfElcJ55xwiITOECNTVDf4jDGoEEZ1Etcxxg8pmjRDiahb7BH20uKKPVUkVmL+YjQArdI+PT2bO9Pd/A34O71Rd9QeN/LAFUSckfUscWuG3oCgP8nrDH6T5AAAAAASUVORK5CYII=";
|
|
|
|
|
const iconAbc =
|
|
|
|
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAB2AAAAdgFOeyYIAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAMRJREFUOI3t0bFKAmAUBeAPURD1HQwUTKPJEmzQoSWQcKpVfIuWdvU9WnqNhsYWBx0a2lvLSMKGbvQ7SO564HA497/3cu/92SPFAS5QDN9CftviDhZYYRpNPtH/rzATOsQT6jhCFzmc4DTJL6AX067hPiimuAr95RglzMJ/4AyyUXSMw3iEauhN6C0eUEMFAyzTFZ7xiOvwL3jbsPYSr3hPg3dB/o43SVYY+TnsPPwXztMG5SDr39dGM8kr4RKNDdPtJL4BNXEmsdKC+S4AAAAASUVORK5CYII=";
|
|
|
|
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADFSURBVDiN7dCxSoIBFAXgr1BbgmgSB5ubxKAHaAkcgnBpySVaDET3WhzcpQfoHZojawgX0ZZcfAWDSDdBoeUKP/8ojZ7tnnPv4dzDFv+KZzwl5jf84B354C4wwjdeUV4vl7DCEsXgxmhigDpOMcMVjoKr7cTyI/ZxiE90wmCB4zi+RRatZOxd7OEavxHtBmvjIV5wH2a59N8ZXIZQisMCzkL/wgGq6EYffXzgHHNo4y5h+oBGlLjEBJVUiVP0cJJOtMUG+APtfyYzbH7eVgAAAABJRU5ErkJggg==";
|
|
|
|
|
const icon123 =
|
|
|
|
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAB2AAAAdgFOeyYIAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAMxJREFUOI3t0bFKwlEUBvBfmmBEr1APIDZJ9AJJQyAIvkGP0C4uQruza+DUmuIc9AC9gBG4Nmpkw/8IB3Vw1w8u95zvnvPde77LEeUUV9HAF67QRA2nmMf5A+o4x3cWOsMYy8j7WMX6jaYbLBL/mAWe8RcHm1ihs8G94gVKQQzwlAouMcQo8p/Y28HdYpYFZmsi0MVdxD1MdrxsC500wijdvgtbI1AYtDbxMwkuFAZmE1uYwkkSqOIaHyHcxEU0vUXNPSqKr37fZ6xDwD9DPS0OyHjQHQAAAABJRU5ErkJggg==";
|
|
|
|
|
const iconMail =
|
|
|
|
|
@@ -61,6 +61,8 @@ 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=";
|
|
|
|
|
const iconUpload =
|
|
|
|
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADNSURBVDiNndOxTgJRFIThz41ZDMFKqH0DLSRSq4lQ0RifUcMzUJlYQKjtLcHVSimBggPRNSzs/sk0kzPnTHEvxZyHKnGJD3yhXSWcYRnKwvvH0Y7wEG/4wQI1XOEek6LLF3FtiDoGoXp4WcxsSXILHjFCH/Nf/jy8ER6KGuTZNNhJvkFpEpygUyHbRi1BFy8VFryilyANlSVFerxn6N36IRVyG0PNEtdbkbmBU8zwdOCSJp4xRWNj3sWS5YGaRvM/f6GBa5ztafCJMb5hBQQ/MMwXLnnZAAAAAElFTkSuQmCC";
|
|
|
|
|
|
|
|
|
|
// --- UI COMPONENTS ---
|
|
|
|
|
|
|
|
|
|
@@ -114,7 +116,7 @@ export const Input = (props) => {
|
|
|
|
|
disabled: () => val(props.disabled),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const leftIcon = icon ? icon : iconsByType[type] ? $html("img", { src: iconsByType[type], class: "w-5 h-5 opacity-50", alt: type }) : null;
|
|
|
|
|
const leftIcon = icon ? icon : iconsByType[type] ? $html("img", { src: iconsByType[type], class: "opacity-50", alt: type }) : null;
|
|
|
|
|
|
|
|
|
|
return $html(
|
|
|
|
|
"label",
|
|
|
|
|
@@ -155,6 +157,117 @@ export const Input = (props) => {
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** FILEINPUT */
|
|
|
|
|
export const Fileinput = (props) => {
|
|
|
|
|
const { tooltip, max = 2, accept = "*", onSelect } = props;
|
|
|
|
|
|
|
|
|
|
const selectedFiles = $([]);
|
|
|
|
|
const isDragging = $(false);
|
|
|
|
|
const error = $(null);
|
|
|
|
|
const MAX_BYTES = max * 1024 * 1024;
|
|
|
|
|
|
|
|
|
|
const handleFiles = (files) => {
|
|
|
|
|
const fileList = Array.from(files);
|
|
|
|
|
error(null);
|
|
|
|
|
const oversized = fileList.find((f) => f.size > MAX_BYTES);
|
|
|
|
|
|
|
|
|
|
if (oversized) {
|
|
|
|
|
error(`Máx ${max}MB`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selectedFiles([...selectedFiles(), ...fileList]);
|
|
|
|
|
onSelect?.(selectedFiles());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const removeFile = (index) => {
|
|
|
|
|
const updated = selectedFiles().filter((_, i) => i !== index);
|
|
|
|
|
selectedFiles(updated);
|
|
|
|
|
onSelect?.(updated);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return $html("fieldset", { class: "fieldset w-full p-0" }, [
|
|
|
|
|
$html(
|
|
|
|
|
"div",
|
|
|
|
|
{
|
|
|
|
|
class: () => `w-full ${tooltip ? "tooltip tooltip-top before:z-50 after:z-50" : ""}`,
|
|
|
|
|
"data-tip": tooltip,
|
|
|
|
|
},
|
|
|
|
|
[
|
|
|
|
|
$html(
|
|
|
|
|
"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
|
|
|
|
|
${isDragging() ? "border-primary bg-primary/10" : "border-base-content/20 bg-base-100 hover:bg-base-200"}
|
|
|
|
|
`,
|
|
|
|
|
ondragover: (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
isDragging(true);
|
|
|
|
|
},
|
|
|
|
|
ondragleave: () => isDragging(false),
|
|
|
|
|
ondrop: (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
isDragging(false);
|
|
|
|
|
handleFiles(e.dataTransfer.files);
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
[
|
|
|
|
|
$html("div", { class: "flex items-center gap-3 w-full" }, [
|
|
|
|
|
$html("img", { src: iconUpload, class: "w-5 h-5 opacity-50 shrink-0" }),
|
|
|
|
|
$html("span", { class: "text-sm opacity-70 truncate grow text-left" }, "Arrastra o selecciona archivos..."),
|
|
|
|
|
$html("span", { class: "text-[10px] opacity-40 shrink-0" }, `Máx ${max}MB`),
|
|
|
|
|
]),
|
|
|
|
|
$html("input", {
|
|
|
|
|
type: "file",
|
|
|
|
|
multiple: true,
|
|
|
|
|
accept: accept,
|
|
|
|
|
class: "hidden",
|
|
|
|
|
onchange: (e) => handleFiles(e.target.files),
|
|
|
|
|
}),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
() => (error() ? $html("span", { class: "text-[10px] text-error mt-1 px-1 font-medium" }, error()) : null),
|
|
|
|
|
|
|
|
|
|
$if(
|
|
|
|
|
() => selectedFiles().length > 0,
|
|
|
|
|
() =>
|
|
|
|
|
$html("ul", { class: "mt-2 space-y-1" }, [
|
|
|
|
|
$for(
|
|
|
|
|
selectedFiles,
|
|
|
|
|
(file, index) =>
|
|
|
|
|
$html("li", { class: "flex items-center justify-between p-1.5 pl-3 text-xs bg-base-200/50 rounded-md border border-base-300" }, [
|
|
|
|
|
$html("div", { class: "flex items-center gap-2 truncate" }, [
|
|
|
|
|
$html("span", { class: "opacity-50" }, "📄"),
|
|
|
|
|
$html("span", { class: "truncate font-medium max-w-[200px]" }, file.name),
|
|
|
|
|
$html("span", { class: "text-[9px] opacity-40" }, `(${(file.size / 1024).toFixed(0)} KB)`),
|
|
|
|
|
]),
|
|
|
|
|
$html(
|
|
|
|
|
"button",
|
|
|
|
|
{
|
|
|
|
|
type: "button",
|
|
|
|
|
class: "btn btn-ghost btn-xs btn-circle",
|
|
|
|
|
onclick: (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
removeFile(index);
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
[$html("img", { src: iconClose, class: "w-3 h-3 opacity-70" })],
|
|
|
|
|
),
|
|
|
|
|
]),
|
|
|
|
|
(file) => file.name + file.lastModified,
|
|
|
|
|
),
|
|
|
|
|
]),
|
|
|
|
|
),
|
|
|
|
|
]);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** SELECT */
|
|
|
|
|
export const Select = (props) => {
|
|
|
|
|
const { label, options, value, ...rest } = props;
|
|
|
|
|
@@ -277,11 +390,13 @@ export const Autocomplete = (props) => {
|
|
|
|
|
|
|
|
|
|
/** DATEPICKER */
|
|
|
|
|
export const Datepicker = (props) => {
|
|
|
|
|
const { value, range, label, placeholder, ...rest } = props;
|
|
|
|
|
const { value, range, label, placeholder, hour = false, ...rest } = props;
|
|
|
|
|
|
|
|
|
|
const isOpen = $(false);
|
|
|
|
|
const internalDate = $(new Date());
|
|
|
|
|
const hoverDate = $(null);
|
|
|
|
|
const startHour = $(0);
|
|
|
|
|
const endHour = $(0);
|
|
|
|
|
const isRangeMode = () => val(range) === true;
|
|
|
|
|
|
|
|
|
|
const now = new Date();
|
|
|
|
|
@@ -300,16 +415,29 @@ export const Datepicker = (props) => {
|
|
|
|
|
|
|
|
|
|
if (isRangeMode()) {
|
|
|
|
|
if (!current?.start || (current.start && current.end)) {
|
|
|
|
|
if (typeof value === "function") value({ start: dateStr, end: null });
|
|
|
|
|
if (typeof value === "function") {
|
|
|
|
|
value({
|
|
|
|
|
start: dateStr,
|
|
|
|
|
end: null,
|
|
|
|
|
...(hour && { startHour: startHour() }),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const start = current.start;
|
|
|
|
|
if (typeof value === "function") {
|
|
|
|
|
value(dateStr < start ? { start: dateStr, end: start } : { start, end: dateStr });
|
|
|
|
|
const newValue = dateStr < start ? { start: dateStr, end: start } : { start, end: dateStr };
|
|
|
|
|
if (hour) {
|
|
|
|
|
newValue.startHour = current.startHour || startHour();
|
|
|
|
|
newValue.endHour = current.endHour || endHour();
|
|
|
|
|
}
|
|
|
|
|
value(newValue);
|
|
|
|
|
}
|
|
|
|
|
isOpen(false);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (typeof value === "function") value(dateStr);
|
|
|
|
|
if (typeof value === "function") {
|
|
|
|
|
value(hour ? `${dateStr}T${String(startHour()).padStart(2, "0")}:00:00` : dateStr);
|
|
|
|
|
}
|
|
|
|
|
isOpen(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
@@ -317,9 +445,21 @@ export const Datepicker = (props) => {
|
|
|
|
|
const displayValue = $(() => {
|
|
|
|
|
const v = val(value);
|
|
|
|
|
if (!v) return "";
|
|
|
|
|
if (typeof v === "string") return v;
|
|
|
|
|
if (v.start && v.end) return `${v.start} - ${v.end}`;
|
|
|
|
|
if (v.start) return `${v.start}...`;
|
|
|
|
|
if (typeof v === "string") {
|
|
|
|
|
if (hour && v.includes("T")) {
|
|
|
|
|
return v.replace("T", " ");
|
|
|
|
|
}
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
if (v.start && v.end) {
|
|
|
|
|
const startStr = hour && v.startHour ? `${v.start} ${String(v.startHour).padStart(2, "0")}:00` : v.start;
|
|
|
|
|
const endStr = hour && v.endHour ? `${v.end} ${String(v.endHour).padStart(2, "0")}:00` : v.end;
|
|
|
|
|
return `${startStr} - ${endStr}`;
|
|
|
|
|
}
|
|
|
|
|
if (v.start) {
|
|
|
|
|
const startStr = hour && v.startHour ? `${v.start} ${String(v.startHour).padStart(2, "0")}:00` : v.start;
|
|
|
|
|
return `${startStr}...`;
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@@ -333,6 +473,30 @@ export const Datepicker = (props) => {
|
|
|
|
|
internalDate(new Date(d.getFullYear() + y, d.getMonth(), 1));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Componente de selector de hora
|
|
|
|
|
const HourSlider = ({ value, onChange }) => {
|
|
|
|
|
return $html("div", { class: "flex-1" }, [
|
|
|
|
|
$html("div", { class: "flex gap-2 items-center" }, [
|
|
|
|
|
$html("input", {
|
|
|
|
|
type: "range",
|
|
|
|
|
min: 0,
|
|
|
|
|
max: 23,
|
|
|
|
|
value: value,
|
|
|
|
|
class: "range range-xs flex-1",
|
|
|
|
|
oninput: (e) => {
|
|
|
|
|
const newHour = parseInt(e.target.value);
|
|
|
|
|
onChange(newHour);
|
|
|
|
|
const currentVal = val(value);
|
|
|
|
|
if (!isRangeMode() && currentVal && typeof currentVal === "string" && currentVal.includes("-")) {
|
|
|
|
|
value(currentVal.split("T")[0] + "T" + String(newHour).padStart(2, "0") + ":00:00");
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
$html("span", { class: "text-sm font-mono min-w-[48px] text-center" }, () => String(val(value)).padStart(2, "0") + ":00"),
|
|
|
|
|
]),
|
|
|
|
|
]);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return $html("div", { class: "relative w-full" }, [
|
|
|
|
|
Input({
|
|
|
|
|
label,
|
|
|
|
|
@@ -410,7 +574,7 @@ export const Datepicker = (props) => {
|
|
|
|
|
class: () => {
|
|
|
|
|
const v = val(value);
|
|
|
|
|
const h = hoverDate();
|
|
|
|
|
const isStart = typeof v === "string" ? v === dStr : v?.start === dStr;
|
|
|
|
|
const isStart = typeof v === "string" ? v.split("T")[0] === dStr : v?.start === dStr;
|
|
|
|
|
const isEnd = v?.end === dStr;
|
|
|
|
|
let inRange = false;
|
|
|
|
|
|
|
|
|
|
@@ -441,6 +605,45 @@ export const Datepicker = (props) => {
|
|
|
|
|
return nodes;
|
|
|
|
|
},
|
|
|
|
|
]),
|
|
|
|
|
|
|
|
|
|
// Selector de hora - modo rango con dos sliders
|
|
|
|
|
hour
|
|
|
|
|
? $html("div", { class: "mt-3 pt-2 border-t border-base-300" }, [
|
|
|
|
|
isRangeMode()
|
|
|
|
|
? $html("div", { class: "flex gap-4" }, [
|
|
|
|
|
HourSlider({
|
|
|
|
|
value: startHour,
|
|
|
|
|
onChange: (newHour) => {
|
|
|
|
|
startHour(newHour);
|
|
|
|
|
const currentVal = val(value);
|
|
|
|
|
if (currentVal?.start) {
|
|
|
|
|
value({ ...currentVal, startHour: newHour });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
HourSlider({
|
|
|
|
|
value: endHour,
|
|
|
|
|
onChange: (newHour) => {
|
|
|
|
|
endHour(newHour);
|
|
|
|
|
const currentVal = val(value);
|
|
|
|
|
if (currentVal?.end) {
|
|
|
|
|
value({ ...currentVal, endHour: newHour });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
])
|
|
|
|
|
: HourSlider({
|
|
|
|
|
value: startHour,
|
|
|
|
|
onChange: (newHour) => {
|
|
|
|
|
startHour(newHour);
|
|
|
|
|
const currentVal = val(value);
|
|
|
|
|
if (currentVal && typeof currentVal === "string" && currentVal.includes("-")) {
|
|
|
|
|
value(currentVal.split("T")[0] + "T" + String(newHour).padStart(2, "0") + ":00:00");
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
])
|
|
|
|
|
: null,
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
@@ -583,10 +786,7 @@ export const Range = (props) => {
|
|
|
|
|
|
|
|
|
|
if (!label && !tooltip) return rangeEl;
|
|
|
|
|
|
|
|
|
|
const layout = $html("div", { class: "flex flex-col gap-2" }, [
|
|
|
|
|
label ? $html("span", { class: "label-text" }, label) : null,
|
|
|
|
|
rangeEl
|
|
|
|
|
]);
|
|
|
|
|
const layout = $html("div", { class: "flex flex-col gap-2" }, [label ? $html("span", { class: "label-text" }, label) : null, rangeEl]);
|
|
|
|
|
|
|
|
|
|
return tooltip ? $html("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout;
|
|
|
|
|
};
|
|
|
|
|
@@ -885,7 +1085,9 @@ export const Rating = (props) => {
|
|
|
|
|
const { value, count = 5, mask = "mask-star", readonly = false, ...rest } = props;
|
|
|
|
|
const ratingGroup = `rating-${Math.random().toString(36).slice(2, 7)}`;
|
|
|
|
|
|
|
|
|
|
return $html("div", {
|
|
|
|
|
return $html(
|
|
|
|
|
"div",
|
|
|
|
|
{
|
|
|
|
|
...rest,
|
|
|
|
|
class: () => `rating ${val(readonly) ? "pointer-events-none" : ""} ${props.class || ""}`,
|
|
|
|
|
},
|
|
|
|
|
@@ -904,7 +1106,8 @@ export const Rating = (props) => {
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}));
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** ALERT */
|
|
|
|
|
@@ -1075,10 +1278,7 @@ export const Toast = (message, type = "alert-success", duration = 3500) => {
|
|
|
|
|
{
|
|
|
|
|
class: `alert alert-soft ${type} shadow-lg transition-all duration-300 translate-x-10 opacity-0 pointer-events-auto`,
|
|
|
|
|
},
|
|
|
|
|
[
|
|
|
|
|
$html("span", typeof message === "function" ? message : () => message),
|
|
|
|
|
Button({ class: "btn-xs btn-circle btn-ghost", onclick: close }, "✕"),
|
|
|
|
|
],
|
|
|
|
|
[$html("span", typeof message === "function" ? message : () => message), Button({ class: "btn-xs btn-circle btn-ghost", onclick: close }, "✕")],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
requestAnimationFrame(() => el.classList.remove("translate-x-10", "opacity-0"));
|
|
|
|
|
@@ -1110,10 +1310,40 @@ export const UI = (defaultLang = "es") => {
|
|
|
|
|
|
|
|
|
|
// Create UI object with all components
|
|
|
|
|
const ui = {
|
|
|
|
|
Locale, tt, Button, Input, Select, Autocomplete, Datepicker, Colorpicker,
|
|
|
|
|
Checkbox, Radio, Range, Modal, Dropdown, Accordion, Tabs, Badge,
|
|
|
|
|
Tooltip, Navbar, Menu, Drawer, Fieldset, List, Stack, Stat, Swap, Indicator,
|
|
|
|
|
Rating, Alert, Timeline, Fab, Toast, Loading, Table
|
|
|
|
|
Locale,
|
|
|
|
|
tt,
|
|
|
|
|
Button,
|
|
|
|
|
Input,
|
|
|
|
|
Select,
|
|
|
|
|
Autocomplete,
|
|
|
|
|
Datepicker,
|
|
|
|
|
Colorpicker,
|
|
|
|
|
Checkbox,
|
|
|
|
|
Radio,
|
|
|
|
|
Range,
|
|
|
|
|
Modal,
|
|
|
|
|
Dropdown,
|
|
|
|
|
Accordion,
|
|
|
|
|
Tabs,
|
|
|
|
|
Badge,
|
|
|
|
|
Tooltip,
|
|
|
|
|
Navbar,
|
|
|
|
|
Menu,
|
|
|
|
|
Drawer,
|
|
|
|
|
Fieldset,
|
|
|
|
|
List,
|
|
|
|
|
Stack,
|
|
|
|
|
Stat,
|
|
|
|
|
Swap,
|
|
|
|
|
Indicator,
|
|
|
|
|
Rating,
|
|
|
|
|
Alert,
|
|
|
|
|
Timeline,
|
|
|
|
|
Fab,
|
|
|
|
|
Toast,
|
|
|
|
|
Loading,
|
|
|
|
|
Fileinput,
|
|
|
|
|
Table
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Inject all components into window
|
|
|
|
|
@@ -1122,7 +1352,7 @@ export const UI = (defaultLang = "es") => {
|
|
|
|
|
value: ui[key],
|
|
|
|
|
writable: false,
|
|
|
|
|
configurable: true,
|
|
|
|
|
enumerable: true
|
|
|
|
|
enumerable: true,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|