UNPKG

@indielayer/ui

Version:

Indielayer UI Components with Tailwind CSS build for Vue 3

276 lines (275 loc) 9.76 kB
import { defineComponent as oe, ref as b, computed as ne, watch as se, onBeforeUnmount as ae, openBlock as d, createElementBlock as v, normalizeClass as x, unref as a, createVNode as k, normalizeStyle as ie, withCtx as le, createElementVNode as l, mergeProps as de, toHandlers as ce, renderSlot as S, toDisplayString as _, createBlock as h, createCommentVNode as w, Fragment as ue, renderList as me } from "vue"; import { useDropZone as pe } 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 fe } from "../../composables/useCommon.js"; import { useInteractive as O } from "../../composables/useInteractive.js"; import { useInputtable as V } from "../../composables/useInputtable.js"; import { useTheme as ve } from "../../composables/useTheme.js"; import { uploadIcon as he, fileIcon as ge, closeIcon as ye } from "../../common/icons.js"; import be from "../label/Label.vue.js"; import D from "../inputFooter/InputFooter.vue.js"; import xe from "../progress/Progress.vue.js"; import I from "../icon/Icon.vue.js"; const ke = ["id", "disabled", "name", "accept", "multiple", "readonly"], Se = { class: "text-secondary-500 dark:text-secondary-400" }, _e = { class: "flex items-center" }, we = ["src"], Ie = { class: "truncate flex-1 pr-2" }, Ve = { class: "font-medium truncate" }, $e = { class: "text-xs text-secondary-500" }, Ee = ["onClick"], Ne = { ...fe.validators(), variant: ["box"] }, Ce = { ...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" } }, Re = { name: "XUpload", validators: Ne }, Ze = /* @__PURE__ */ oe({ ...Re, props: Ce, emits: [...V.emits(), "upload", "remove"], setup(j, { expose: F, emit: q }) { const o = j, u = q, p = b(null), f = ne(() => !!o.action), s = b([]); se(s, (e) => { u("update:modelValue", e); }, { deep: !0, immediate: !0 }); const { focus: $, blur: P } = O(p); function g(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 T(e) { const r = e.target.files; g(r) && u("change", e); } function M(e) { const r = e.target.files; g(r) && (C(r), o.validateOnInput && !B.value && y(s.value), u("input", e)); } const E = b(null); function Z(e) { g(e) && C(e); } const { isOverDropZone: N } = pe(E, { onDrop: Z, multiple: o.multiple, preventDefaultForUnhandled: !1 }); function C(e) { s.value = e ? Array.from(e).map((t) => ({ file: t, completed: !f.value, progress: f.value ? 0 : 100, error: "" })) : [], f.value && X(); } 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 R(e) { const t = ["jpg", "jpeg", "png", "gif", "webp", "svg"]; return e.type.startsWith("image") || t.some((r) => e.name.endsWith(r)); } const U = []; function A(e) { const t = URL.createObjectURL(e); return U.push(t), t; } ae(() => { U.forEach((e) => URL.revokeObjectURL(e)); }); function H(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 W(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 te = i.loaded / i.total * 100; e.progress = te; } }), 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 X() { if (!o.action && !s.value) return; const e = []; for (let t = 0; t < s.value.length; t++) e.push(W(s.value[t])); await Promise.all(e), y(s.value); } function G() { m.value = "", B.value = !0, s.value = [], p.value && (p.value.value = ""); } const { errorInternal: m, hideFooterInternal: J, isInsideForm: K, inputListeners: Q, isFirstValidation: B, validate: y, setError: Y } = V(o, { focus: $, emit: u }), { styles: ee, classes: L, className: re } = ve("Upload", {}, o); return F({ focus: $, blur: P, reset: G, validate: y, setError: Y }), (e, t) => (d(), v("div", { class: x([ a(re), a(L).wrapper ]) }, [ k(be, { style: ie(a(ee)), disabled: e.disabled, required: e.required, "is-inside-form": a(K), label: e.label, tooltip: e.tooltip }, { default: le(() => [ l("input", de({ id: e.id, ref_key: "elRef", ref: p, type: "file", class: [ "sr-only", a(L).input ], disabled: e.disabled, name: e.name, accept: e.accept, multiple: e.multiple, readonly: e.readonly }, ce({ ...a(Q), input: M, change: T }, !0)), null, 16, ke), S(e.$slots, "box", { isOver: a(N) }, () => [ l("div", { ref_key: "dropZoneRef", ref: E, 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(N) }]) }, [ k(I, { icon: a(he) }, null, 8, ["icon"]), l("span", Se, _(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(J) ? w("", !0) : (d(), h(D, { 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(ue, null, me(s.value, (r, n) => (d(), v("div", { key: n, class: "border rounded-md p-2 bg-secondary-100 my-2" }, [ l("div", _e, [ l("div", { class: x(["rounded-md w-9 h-9 flex items-center justify-center mr-2 border", { "p-2 bg-white": !R(r.file) }]) }, [ R(r.file) ? (d(), v("img", { key: 0, src: A(r.file), class: "w-full h-full object-cover rounded-md" }, null, 8, we)) : (d(), h(I, { key: 1, icon: a(ge) }, null, 8, ["icon"])) ], 2), l("div", Ie, [ l("div", Ve, _(r.file.name), 1), l("div", $e, _(H(r.file.size)), 1) ]), l("button", { type: "button", class: "shrink-0", onClick: (c) => z(r.file) }, [ S(e.$slots, "removeIcon", {}, () => [ k(I, { icon: a(ye) }, null, 8, ["icon"]) ]) ], 8, Ee) ]), f.value ? (d(), h(xe, { key: 0, percentage: r.progress, class: "mt-1", color: r.error ? "warning" : "success" }, null, 8, ["percentage", "color"])) : w("", !0), r.error ? (d(), h(D, { key: 1, error: r.error }, null, 8, ["error"])) : w("", !0) ]))), 128)) ]) ], 2)); } }); export { Ze as default };