import { defineComponent, h, ref } from "vue";
import { iconNode } from "../utils.js";
import GnButton from "./GnButton.js";
import GnBadge from "./GnBadge.js";
function fileType(file) {
const ext = file.name.split(".").pop();
return ext ? ext.slice(0, 6).toUpperCase() : "FILE";
}
function fileSize(file) {
if(!file.size) {
return "0 B";
}
const units = ["B", "KB", "MB", "GB"];
const index = Math.min(Math.floor(Math.log(file.size) / Math.log(1024)), units.length - 1);
const value = file.size / Math.pow(1024, index);
return `${value.toFixed(value >= 10 || index === 0 ? 0 : 1)} ${units[index]}`;
}
export default defineComponent({
name: "GnFileUpload",
props: {
modelValue: { type: Array, default: () => [] },
title: { type: String, default: "Upload files" },
description: { type: String, default: "Attach documents, archives or images." },
primary: { type: String, default: "Choose files" },
secondary: { type: String, default: "Images get thumbnails, other files show their type" },
badge: { type: String, default: "" },
multiple: { type: Boolean, default: true },
accept: { type: String, default: "" }
},
emits: ["update:modelValue", "change"],
setup(props, { emit, slots }) {
const urls = ref(new Map());
const setFiles = fileList => {
const files = Array.from(fileList || []);
emit("update:modelValue", files);
emit("change", files);
};
const remove = index => {
const files = props.modelValue.filter((_, itemIndex) => itemIndex !== index);
emit("update:modelValue", files);
emit("change", files);
};
const previewUrl = file => {
if(!file.type?.startsWith("image/")) {
return "";
}
if(!urls.value.has(file)) {
urls.value.set(file, URL.createObjectURL(file));
}
return urls.value.get(file);
};
return () => h("div", { class: "file-upload-panel" }, [
h("div", { class: "file-upload-form" }, [
h("div", { class: "file-upload-header" }, [
h("div", { class: "file-upload-heading" }, [
h("h3", { class: "file-upload-title" }, slots.title?.() || props.title),
h("p", { class: "file-upload-description" }, slots.description?.() || props.description)
]),
props.badge && h(GnBadge, { variant: "info" }, () => props.badge)
]),
h("label", { class: "file-upload-dropzone" }, [
h("span", { class: "file-upload-icon", "aria-hidden": "true" }, [iconNode("ph-cloud-arrow-up")]),
h("span", { class: "file-upload-body" }, [
h("span", { class: "file-upload-primary" }, props.primary),
h("span", { class: "file-upload-secondary" }, props.secondary)
]),
h("input", {
type: "file",
multiple: props.multiple,
accept: props.accept || undefined,
onChange: event => setFiles(event.target.files)
})
]),
h("div", { class: "file-upload-preview", hidden: !props.modelValue.length }, props.modelValue.map((file, index) => h("figure", {
class: "file-upload-preview-item"
}, [
h("button", {
class: "file-upload-preview-remove",
type: "button",
"aria-label": `Remove ${file.name}`,
onClick: () => remove(index)
}, [iconNode("ph-x")]),
h("div", { class: "file-upload-preview-visual" }, previewUrl(file)
? h("img", { src: previewUrl(file), alt: "" })
: h("span", { class: "file-upload-preview-type" }, fileType(file))
),
h("figcaption", {}, [
h("span", { class: "file-upload-preview-name" }, file.name),
h("span", { class: "file-upload-preview-meta" }, `${fileType(file)} / ${fileSize(file)}`)
])
]))),
slots.actions && h("div", { class: "file-upload-actions" }, slots.actions()),
!slots.actions && props.modelValue.length > 0 && h("div", { class: "file-upload-actions" }, [
h(GnButton, {
variant: "secondary",
size: "sm",
onClick: () => setFiles([])
}, () => "Reset")
])
])
]);
}
});