New Fileinput
File input with Drag&Drop Datepicker with hour Range
This commit is contained in:
298
ui/sigpro-ui.js
298
ui/sigpro-ui.js
@@ -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;
|
||||
@@ -164,7 +277,7 @@ export const Select = (props) => {
|
||||
{
|
||||
...rest,
|
||||
class: joinClass("select select-bordered w-full", props.class || props.class),
|
||||
value: value
|
||||
value: value,
|
||||
},
|
||||
$for(
|
||||
() => val(options) || [],
|
||||
@@ -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 = $(12);
|
||||
const endHour = $(18);
|
||||
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,
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -535,7 +738,7 @@ export const Checkbox = (props) => {
|
||||
...rest,
|
||||
type: "checkbox",
|
||||
class: () => (val(toggle) ? "toggle" : "checkbox"),
|
||||
checked: value
|
||||
checked: value,
|
||||
});
|
||||
|
||||
const layout = $html("label", { class: "label cursor-pointer justify-start gap-3" }, [
|
||||
@@ -578,15 +781,12 @@ export const Range = (props) => {
|
||||
type: "range",
|
||||
class: joinClass("range", props.class),
|
||||
value: value,
|
||||
disabled: () => val(props.disabled)
|
||||
disabled: () => val(props.disabled),
|
||||
});
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -656,7 +856,7 @@ export const Grid = (props) => {
|
||||
onContextMenu: (e) => on?.onContextMenu?.(e),
|
||||
onColumnResized: (e) => on?.onColumnResized?.(e),
|
||||
onColumnMoved: (e) => on?.onColumnMoved?.(e),
|
||||
onRowDataUpdated: (e) => on?.onRowDataUpdated?.(e)
|
||||
onRowDataUpdated: (e) => on?.onRowDataUpdated?.(e),
|
||||
};
|
||||
|
||||
gridApi = createGrid(container, gridOptions);
|
||||
@@ -682,7 +882,7 @@ export const Grid = (props) => {
|
||||
gridApi = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -732,7 +932,7 @@ export const Accordion = (props, children) => {
|
||||
$html("input", {
|
||||
type: name ? "radio" : "checkbox",
|
||||
name: name,
|
||||
checked: open
|
||||
checked: open,
|
||||
}),
|
||||
$html("div", { class: "collapse-title text-xl font-medium" }, title),
|
||||
$html("div", { class: "collapse-content" }, children),
|
||||
@@ -877,7 +1077,7 @@ export const Swap = (props) =>
|
||||
$html("label", { class: joinClass("swap", props.class || props.class) }, [
|
||||
$html("input", {
|
||||
type: "checkbox",
|
||||
checked: props.value
|
||||
checked: props.value,
|
||||
}),
|
||||
$html("div", { class: "swap-on" }, props.on),
|
||||
$html("div", { class: "swap-off" }, props.off),
|
||||
@@ -895,7 +1095,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 || ""}`,
|
||||
},
|
||||
@@ -914,7 +1116,8 @@ export const Rating = (props) => {
|
||||
}
|
||||
},
|
||||
});
|
||||
}));
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
/** ALERT */
|
||||
@@ -1085,10 +1288,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"));
|
||||
@@ -1120,10 +1320,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, Grid, Dropdown, Accordion, Tabs, Badge,
|
||||
Tooltip, Navbar, Menu, Drawer, Fieldset, List, Stack, Stat, Swap, Indicator,
|
||||
Rating, Alert, Timeline, Fab, Toast, Loading
|
||||
Locale,
|
||||
tt,
|
||||
Button,
|
||||
Input,
|
||||
Select,
|
||||
Autocomplete,
|
||||
Datepicker,
|
||||
Colorpicker,
|
||||
Checkbox,
|
||||
Radio,
|
||||
Range,
|
||||
Modal,
|
||||
Grid,
|
||||
Dropdown,
|
||||
Accordion,
|
||||
Tabs,
|
||||
Badge,
|
||||
Tooltip,
|
||||
Navbar,
|
||||
Menu,
|
||||
Drawer,
|
||||
Fieldset,
|
||||
List,
|
||||
Stack,
|
||||
Stat,
|
||||
Swap,
|
||||
Indicator,
|
||||
Rating,
|
||||
Alert,
|
||||
Timeline,
|
||||
Fab,
|
||||
Toast,
|
||||
Loading,
|
||||
Fileinput,
|
||||
};
|
||||
|
||||
// Inject all components into window
|
||||
@@ -1132,7 +1362,7 @@ export const UI = (defaultLang = "es") => {
|
||||
value: ui[key],
|
||||
writable: false,
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
enumerable: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user