@indielayer/ui
Version:
Indielayer UI Components with Tailwind CSS build for Vue 3
271 lines (270 loc) • 9.62 kB
JavaScript
import { defineComponent as te, ref as b, computed as oe, watch as ne, openBlock as d, createElementBlock as v, normalizeClass as x, unref as a, createVNode as k, normalizeStyle as se, withCtx as ae, createElementVNode as l, mergeProps as ie, toHandlers as le, renderSlot as S, toDisplayString as _, createBlock as g, createCommentVNode as w, Fragment as de, renderList as ce } from "vue";
import { useDropZone as ue } from "../../node_modules/.pnpm/@vueuse_core@11.1.0_vue@3.5.10_typescript@5.2.2_/node_modules/@vueuse/core/index.js";
import { useCommon as me } from "../../composables/useCommon.js";
import { useInteractive as O } from "../../composables/useInteractive.js";
import { useInputtable as V } from "../../composables/useInputtable.js";
import { useTheme as pe } from "../../composables/useTheme.js";
import { uploadIcon as fe, fileIcon as ve, closeIcon as ge } from "../../common/icons.js";
import he from "../label/Label.vue.js";
import L from "../inputFooter/InputFooter.vue.js";
import ye from "../progress/Progress.vue.js";
import I from "../icon/Icon.vue.js";
const be = ["id", "disabled", "name", "accept", "multiple", "readonly"], xe = { class: "text-secondary-500 dark:text-secondary-400" }, ke = { class: "flex items-center" }, Se = ["src"], _e = { class: "truncate flex-1 pr-2" }, we = { class: "font-medium truncate" }, Ie = { class: "text-xs text-secondary-500" }, Ve = ["onClick"], $e = {
...me.validators(),
variant: ["box"]
}, Ne = {
...O.props(),
...V.props(),
placeholder: String,
accept: String,
multiple: Boolean,
maxFiles: [Number, String],
maxFileSize: [Number, String],
variant: {
type: String,
default: "box"
},
// URL to submit data to.
action: String,
// Additional HTTP headers
headers: Object,
method: {
type: String,
default: "POST"
},
withCredentials: Boolean,
fileFormDataName: {
type: String,
default: "file"
}
}, Ce = {
name: "XUpload",
validators: $e
}, Te = /* @__PURE__ */ te({
...Ce,
props: Ne,
emits: [...V.emits(), "upload", "remove"],
setup(j, { expose: F, emit: U }) {
const o = j, u = U, p = b(null), f = oe(() => !!o.action), s = b([]);
ne(s, (e) => {
u("update:modelValue", e);
}, { deep: !0, immediate: !0 });
const { focus: $, blur: q } = O(p);
function h(e) {
if (!e)
return !1;
if (!o.multiple && e.length > 1 || o.maxFiles && e.length > Number(o.maxFiles))
return m.value = "Too many files", !1;
if (o.maxFileSize) {
for (let t = 0; t < e.length; t++)
if (e[t].size > Number(o.maxFileSize))
return m.value = "File size is too large", !1;
}
if (o.accept) {
const t = o.accept.split(",").map((r) => r.trim());
for (let r = 0; r < e.length; r++) {
const n = e[r];
if (!t.some((c) => n.type === c || n.name.endsWith(c)))
return m.value = "Invalid file type", !1;
}
}
return !0;
}
function P(e) {
const r = e.target.files;
h(r) && u("change", e);
}
function T(e) {
const r = e.target.files;
h(r) && (E(r), o.validateOnInput && !D.value && y(s.value), u("input", e));
}
const N = b(null);
function M(e) {
h(e) && E(e);
}
const { isOverDropZone: C } = ue(N, {
onDrop: M,
multiple: o.multiple,
preventDefaultForUnhandled: !1
});
function E(e) {
s.value = e ? Array.from(e).map((t) => ({
file: t,
completed: !f.value,
progress: f.value ? 0 : 100,
error: ""
})) : [], f.value && W();
}
function Z(e) {
const t = s.value.findIndex((r) => r.file === e);
t !== -1 && (u("remove", s.value[t]), s.value.splice(t, 1));
}
function B(e) {
const t = ["jpg", "jpeg", "png", "gif", "webp", "svg"];
return e.type.startsWith("image") || t.some((r) => e.name.endsWith(r));
}
function z(e) {
return URL.createObjectURL(e);
}
function A(e) {
return e < 1024 ? `${e} B` : e < 1024 * 1024 ? `${(e / 1024).toFixed(2)} KB` : e < 1024 * 1024 * 1024 ? `${(e / 1024 / 1024).toFixed(2)} MB` : `${(e / 1024 / 1024 / 1024).toFixed(2)} GB`;
}
async function H(e) {
return new Promise((t, r) => {
if (!o.action)
r(new Error("No action provided"));
else {
const n = new XMLHttpRequest(), c = new FormData();
c.append(o.fileFormDataName, e.file), n.open(o.method, o.action, !0), o.headers && typeof o.headers == "object" && Object.keys(o.headers).forEach((i) => {
n.setRequestHeader(i, o.headers[i]);
}), n.upload.addEventListener("progress", (i) => {
if (i.lengthComputable) {
const re = i.loaded / i.total * 100;
e.progress = re;
}
}), n.addEventListener("load", () => {
if (n.status >= 200 && n.status < 300) {
e.completed = !0;
try {
e.response = JSON.parse(n.responseText);
} catch {
e.response = n.responseText;
}
t(n.responseText);
} else {
const i = `Upload failed. Status: ${n.status}`;
e.error = i, r(new Error(i));
}
}), n.addEventListener("error", () => {
e.error = "An error occurred during the upload.", r(new Error("An error occurred during the upload."));
}), n.send(c);
}
});
}
async function W() {
if (!o.action && !s.value)
return;
const e = [];
for (let t = 0; t < s.value.length; t++)
e.push(H(s.value[t]));
await Promise.all(e), y(s.value);
}
function X() {
m.value = "", D.value = !0, s.value = [], p.value && (p.value.value = "");
}
const {
errorInternal: m,
hideFooterInternal: G,
isInsideForm: J,
inputListeners: K,
isFirstValidation: D,
validate: y,
setError: Q
} = V(o, { focus: $, emit: u }), { styles: Y, classes: R, className: ee } = pe("Upload", {}, o);
return F({ focus: $, blur: q, reset: X, validate: y, setError: Q }), (e, t) => (d(), v("div", {
class: x([
a(ee),
a(R).wrapper
])
}, [
k(he, {
style: se(a(Y)),
disabled: e.disabled,
required: e.required,
"is-inside-form": a(J),
label: e.label,
tooltip: e.tooltip
}, {
default: ae(() => [
l("input", ie({
id: e.id,
ref_key: "elRef",
ref: p,
type: "file",
class: [
"sr-only",
a(R).input
],
disabled: e.disabled,
name: e.name,
accept: e.accept,
multiple: e.multiple,
readonly: e.readonly
}, le({
...a(K),
input: T,
change: P
}, !0)), null, 16, be),
S(e.$slots, "box", { isOver: a(C) }, () => [
l("div", {
ref_key: "dropZoneRef",
ref: N,
class: x(["border border-dashed rounded-md flex items-center justify-center gap-2 p-4 cursor-pointer transition-colors duration-500 hover:border-primary-500 dark:hover:border-primary-400", {
"border-primary-500 dark:border-primary-400": a(C)
}])
}, [
k(I, { icon: a(fe) }, null, 8, ["icon"]),
l("span", xe, _(e.placeholder), 1),
t[0] || (t[0] = l("span", { class: "text-xs text-secondary-400 dark:text-secondary-500" }, "or drag and drop", -1))
], 2)
]),
a(G) ? w("", !0) : (d(), g(L, {
key: 0,
error: a(m),
helper: e.helper
}, null, 8, ["error", "helper"]))
]),
_: 3
}, 8, ["style", "disabled", "required", "is-inside-form", "label", "tooltip"]),
S(e.$slots, "files", { files: s.value }, () => [
(d(!0), v(de, null, ce(s.value, (r, n) => (d(), v("div", {
key: n,
class: "border rounded-md p-2 bg-secondary-100 my-2"
}, [
l("div", ke, [
l("div", {
class: x(["rounded-md w-9 h-9 flex items-center justify-center mr-2 border", {
"p-2 bg-white": !B(r.file)
}])
}, [
B(r.file) ? (d(), v("img", {
key: 0,
src: z(r.file),
class: "w-full h-full object-cover rounded-md"
}, null, 8, Se)) : (d(), g(I, {
key: 1,
icon: a(ve)
}, null, 8, ["icon"]))
], 2),
l("div", _e, [
l("div", we, _(r.file.name), 1),
l("div", Ie, _(A(r.file.size)), 1)
]),
l("button", {
type: "button",
class: "shrink-0",
onClick: (c) => Z(r.file)
}, [
S(e.$slots, "removeIcon", {}, () => [
k(I, { icon: a(ge) }, null, 8, ["icon"])
])
], 8, Ve)
]),
f.value ? (d(), g(ye, {
key: 0,
percentage: r.progress,
class: "mt-1",
color: r.error ? "warning" : "success"
}, null, 8, ["percentage", "color"])) : w("", !0),
r.error ? (d(), g(L, {
key: 1,
error: r.error
}, null, 8, ["error"])) : w("", !0)
]))), 128))
])
], 2));
}
});
export {
Te as default
};