Add Fileinput component for file selection
This commit is contained in:
113
src/components/Fileinput.js
Normal file
113
src/components/Fileinput.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import { $, $html, $if, $for } from "sigpro";
|
||||||
|
import { iconUpload, iconClose } from "../core/icons.js";
|
||||||
|
|
||||||
|
/** 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,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user