UNPKG

vuetify-editable-data-grid

Version:

A highly customizable and interactive editable data table component built with Vue 3 and Vuetify 3. This component supports inline editing, undo/redo functionality, column visibility filters, drag-to-fill (like Excel), and more.

468 lines (467 loc) 19.3 kB
import { ref as x, watch as fe, mergeModels as De, useModel as Ee, useTemplateRef as Ie, computed as ee, onMounted as Oe, onBeforeUnmount as Re, resolveComponent as c, resolveDirective as Se, createBlock as J, openBlock as k, withCtx as p, createVNode as d, renderSlot as te, withDirectives as ne, unref as V, createElementBlock as $, createTextVNode as P, toDisplayString as I, Fragment as K, renderList as re, withModifiers as q, mergeProps as X, createSlots as Ue, createElementVNode as ve, normalizeClass as de, createCommentVNode as F, resolveDynamicComponent as Ke, withKeys as Ae, vShow as Le } from "vue"; function Ne(s, O = {}) { const { deep: D = !1, capacity: f = 1 / 0, clone: C = (y) => JSON.parse(JSON.stringify(y)) } = O, w = x([]), m = x(-1), A = x(!1), T = x(!1), z = x(!1), L = () => { T.value = m.value > 0, z.value = m.value < w.value.length - 1; }, j = (y) => { const E = C(y); m.value < w.value.length - 1 && w.value.splice(m.value + 1), w.value.push(E), m.value++, w.value.length > f && (w.value.shift(), m.value--), L(); }, r = () => { T.value && (s.value = C(w.value[m.value]), m.value--, s.value = C(w.value[m.value]), A.value = !1, L()); }, v = () => { z.value && (A.value = !0, m.value++, s.value = C(w.value[m.value]), A.value = !1, L()); }; return fe( s, (y) => { if (A.value) return; const E = JSON.stringify(y), R = JSON.stringify(w.value[m.value]); E !== R && j(y); }, { deep: D, immediate: !0 } ), { history: w, pointer: m, canUndo: T, canRedo: z, undo: r, redo: v }; } function Be(s, O = {}) { const { minimumFractionDigits: D = 0, maximumFractionDigits: f = 2 } = O, C = typeof s == "string" ? parseFloat(s) : s; return new Intl.NumberFormat(void 0, { minimumFractionDigits: D, maximumFractionDigits: f }).format(C); } const Fe = (s, O) => { const D = s.__vccOpts || s; for (const [f, C] of O) D[f] = C; return D; }, Te = ["id", "data-row", "data-col", "onClick", "onDblclick"], ze = { key: 0, class: "error-caret" }, He = { __name: "DataGrid", props: /* @__PURE__ */ De({ headers: { type: Array, default: () => [] } }, { modelValue: { type: Array, default: () => [], required: !0 }, modelModifiers: {} }), emits: ["update:modelValue"], setup(s, { expose: O }) { const D = Ee(s, "modelValue"), f = x([]), C = Ie("formRef"); fe( () => s.headers, (t) => { f.value.length === 0 && t.length > 0 && (f.value = t.map((n) => n.key)); }, { immediate: !0 } ); const w = ee(() => s.headers.length === f.value.length), m = ee(() => s.headers.filter((t) => f.value.includes(t.key))), A = (t) => { f.value.includes(t.key) ? f.value = f.value.filter((n) => n !== t.key) : f.value = [...f.value, t.key]; }, { canUndo: T, canRedo: z, undo: L, redo: j } = Ne(D, { deep: !0 }), r = D, v = x(null), y = x(null), E = x(!1), R = x({ row: null, key: null }), G = x(""), N = ee(() => G.value ? r.value.filter( (t) => Object.values(t).some((n) => String(n).toLowerCase().includes(G.value.toLowerCase())) ) : r.value); function B(t) { var n; return (n = s.headers.find((l) => l.key === t)) == null ? void 0 : n.type; } function me(t) { switch (B(t)) { case "string": return "v-text-field"; case "number": case "currency": return "v-number-input"; case "select": return "v-autocomplete"; case "checkbox": return "v-checkbox"; default: return null; } } function H(t, n) { v.value = { row: t, key: n }; } function Z(t, n) { var l, o; return ((l = v.value) == null ? void 0 : l.row) === t && ((o = v.value) == null ? void 0 : o.key) === n; } function Y(t, n) { var l, o; return ((l = R.value) == null ? void 0 : l.row) === t && ((o = R.value) == null ? void 0 : o.key) === n; } function ye(t, n) { B(n) && (R.value = { row: t, key: n }); } const Q = x({}); function le(t, n) { var o; const l = (o = t.target) == null ? void 0 : o.id; l && C.value.errors.find((a) => { if (a.id === l) return Q.value[n] = a.errorMessages[0], !0; }), R.value = { row: null, key: null }; } function oe() { return !v.value || !y.value ? null : v.value.row !== y.value.row ? "vertical" : v.value.key !== y.value.key ? "horizontal" : null; } function pe(t, n) { if (!v.value || !y.value) return !1; const l = oe(); if (!l) return !1; if (l === "vertical" && n === v.value.key) { const [o, a] = [v.value.row, y.value.row]; return o < a && t > o && t <= a || o > a && t < o && t >= a; } if (l === "horizontal" && t === v.value.row) { const o = s.headers.map((g) => g.key), [a, M] = [ o.indexOf(v.value.key), o.indexOf(y.value.key) ].sort((g, S) => g - S), i = o.indexOf(n); return i > a && i <= M; } return !1; } function W() { E.value = !0; } function ae(t) { var o; if (!E.value || !v.value) return; const n = document.elementFromPoint(t.clientX, t.clientY), l = (o = n == null ? void 0 : n.closest) == null ? void 0 : o.call(n, "[data-row][data-col]"); l && (y.value = { row: Number(l.dataset.row), key: l.dataset.col }); } function ue() { E.value && v.value && y.value && ke(), E.value = !1, y.value = null; } function ke() { const t = v.value, n = y.value, l = oe(); if (l) { if (l === "vertical") { const o = t.key, a = r.value.indexOf(N.value[t.row]), M = r.value.indexOf(N.value[n.row]); if (a === -1 || M === -1) return; const i = r.value[a][o], [g, S] = [t.row, n.row].sort((_, h) => _ - h); for (let _ = g; _ <= S; _++) { const h = r.value.indexOf(N.value[_]); h !== -1 && (r.value[h][o] = i); } } if (l === "horizontal") { const o = t.row, a = r.value.indexOf(N.value[o]); if (a === -1) return; const M = r.value[a][t.key], i = s.headers.map((_) => _.key), [g, S] = [i.indexOf(t.key), i.indexOf(n.key)].sort((_, h) => _ - h); for (let _ = g + 1; _ <= S; _++) { const h = i[_]; r.value[a][h] = M; } } } } function se(t) { if (!v.value || R.value.row !== null) return; const { row: n, key: l } = v.value, o = N.value.length, a = m.value.map((i) => i.key), M = a.indexOf(l); if (t.key === "ArrowDown") { const i = Math.min(n + 1, o - 1); H(i, l); } else if (t.key === "ArrowUp") { const i = Math.max(n - 1, 0); H(i, l); } else if (t.key === "ArrowRight") { const i = Math.min(M + 1, a.length - 1), g = a[i]; H(n, a[i]), ie(n, g); } else if (t.key === "ArrowLeft") { const i = Math.max(M - 1, 0), g = a[i]; H(n, a[i]), ie(n, g); } } function ie(t, n) { const l = document.getElementById(`cell-${t}-${n}`); l && l.scrollIntoView({ behavior: "smooth", inline: "nearest", block: "nearest" }); } return console.time("Total Mounting time"), Oe(() => { window.addEventListener("mousemove", ae), window.addEventListener("mouseup", ue), window.addEventListener("keydown", se), window.addEventListener("keydown", (t) => { (t.ctrlKey || t.metaKey) && t.key === "z" && L(), (t.ctrlKey || t.metaKey) && t.key === "y" && j(); }), console.timeEnd("Total Mounting time"); }), Re(() => { window.removeEventListener("mousemove", ae), window.removeEventListener("mouseup", ue), window.removeEventListener("keydown", se); }), O({ formRef: C }), (t, n) => { const l = c("v-col"), o = c("v-text-field"), a = c("v-btn"), M = c("v-icon"), i = c("v-badge"), g = c("v-list-subheader"), S = c("v-checkbox-btn"), _ = c("v-list-item-action"), h = c("v-list-item"), _e = c("v-list"), we = c("v-menu"), ge = c("v-row"), be = c("v-tooltip"), Ce = c("v-checkbox"), xe = c("v-select"), Ve = c("AppPriceDisplay"), $e = c("v-data-table"), Me = c("v-form"), he = c("v-container"), ce = Se("tooltip"); return k(), J(he, { fluid: "", class: "pa-0" }, { default: p(() => [ d(ge, null, { default: p(() => [ d(l, { cols: "6", class: "d-flex align-center" }, { default: p(() => [ te(t.$slots, "header", {}, void 0, !0) ]), _: 3 }), d(l, { cols: "6", class: "d-flex justify-end" }, { default: p(() => [ te(t.$slots, "controls", {}, () => [ d(o, { modelValue: G.value, "onUpdate:modelValue": n[0] || (n[0] = (e) => G.value = e), label: "Search", density: "compact", "hide-details": "", class: "my-2", style: { "max-width": "300px" } }, null, 8, ["modelValue"]), ne(d(a, { disabled: !V(T), variant: "text", icon: "mdi-undo", onClick: V(L) }, null, 8, ["disabled", "onClick"]), [ [ce, "Undo", "bottom"] ]), ne(d(a, { variant: "text", icon: "mdi-redo", disabled: !V(z), onClick: V(j) }, null, 8, ["disabled", "onClick"]), [ [ce, "Redo", "bottom"] ]), d(we, { "offset-y": "", "close-on-content-click": !1, contained: "", height: "400" }, { activator: p(({ props: e }) => [ d(a, X(e, { icon: "", variant: "text" }), { default: p(() => [ d(i, { "model-value": !w.value, dot: "", color: "mpBlue" }, { default: p(() => [ d(M, null, { default: p(() => n[2] || (n[2] = [ P("mdi-filter-variant") ])), _: 1 }) ]), _: 1 }, 8, ["model-value"]) ]), _: 2 }, 1040) ]), default: p(() => [ d(_e, { selected: f.value, "onUpdate:selected": n[1] || (n[1] = (e) => f.value = e), "select-strategy": "leaf" }, { default: p(() => [ d(g, { class: "font-weight-large" }, { default: p(() => [ P(I(t.$t("products.create.bulk-edit.filter-btn")), 1) ]), _: 1 }), (k(!0), $(K, null, re(s.headers, (e) => (k(), J(h, { key: e.key, title: e.title, value: e.key, disabled: e.permanent }, { prepend: p(({ isSelected: U }) => [ d(_, { start: "" }, { default: p(() => [ d(S, { "model-value": U, onClick: q((u) => A(e), ["stop"]) }, null, 8, ["model-value", "onClick"]) ]), _: 2 }, 1024) ]), _: 2 }, 1032, ["title", "value", "disabled"]))), 128)) ]), _: 1 }, 8, ["selected"]) ]), _: 1 }) ], !0) ]), _: 3 }) ]), _: 3 }), te(t.$slots, "prepend", {}, void 0, !0), d(Me, { ref_key: "formRef", ref: C }, { default: p(() => [ d($e, { headers: m.value, items: N.value, class: "custom-grid elevation-2 mt-4", "hide-default-footer": "", density: "compact" }, Ue({ _: 2 }, [ re(m.value, (e) => ({ name: `item.${e.key}`, fn: p(({ item: U, index: u }) => [ ve("div", { id: `cell-${u}-${e.key}`, class: de(["cell", { selected: Z(u, e.key), fillZone: pe(u, e.key), nonEditable: !B(e.key), [`text-${e.align || "center"}`]: !0 }]), "data-row": u, "data-col": e.key, onClick: (b) => H(u, e.key), onDblclick: (b) => ye(u, e.key) }, [ Q.value[`${u}-${e.key}`] ? (k(), J(be, { key: 0, activator: "parent", location: "top", "content-class": "error-tooltip" }, { default: p(() => [ P(I(Q.value[`${u}-${e.key}`]), 1) ]), _: 2 }, 1024)) : F("", !0), e.type === "checkbox" ? (k(), $(K, { key: 1 }, [ d(Ce, X({ modelValue: V(r)[u][e.key], "onUpdate:modelValue": (b) => V(r)[u][e.key] = b, "hide-details": "", variant: "plain", density: "compact", class: "d-flex justify-center align-center edit-input", autofocus: "" }, e.props), null, 16, ["modelValue", "onUpdate:modelValue"]), !e.hideHandle && Z(u, e.key) && B(e.key) ? (k(), $("div", { key: 0, class: "handle", onMousedown: q(W, ["stop", "prevent"]) }, null, 32)) : F("", !0) ], 64)) : e.type === "select" ? (k(), $(K, { key: 2 }, [ d(xe, X({ modelValue: V(r)[u][e.key], "onUpdate:modelValue": (b) => V(r)[u][e.key] = b, items: e.items, "item-title": e.itemTitle, "item-value": e.itemValue, "hide-details": "", variant: "plain", density: "compact" }, e.props), null, 16, ["modelValue", "onUpdate:modelValue", "items", "item-title", "item-value"]), !e.hideHandle && Z(u, e.key) && B(e.key) ? (k(), $("div", { key: 0, class: "handle", onMousedown: q(W, ["stop", "prevent"]) }, null, 32)) : F("", !0) ], 64)) : (k(), $(K, { key: 3 }, [ ne(ve("div", { class: de(["w-100", { show: Y(u, e.key) }]) }, [ (k(), J(Ke(me(e.key)), X({ modelValue: V(r)[u][e.key], "onUpdate:modelValue": (b) => V(r)[u][e.key] = b, class: "edit-input", variant: "plain", density: "compact", autofocus: "", "hide-details": "" }, e.props, { onBlur: (b) => le(b, `${u}-${e.key}`), onKeydown: Ae((b) => le(b, `${u}-${e.key}`), ["enter"]) }), { append: p(({ isValid: b }) => [ b.value ? F("", !0) : (k(), $("div", ze)) ]), _: 2 }, 1040, ["modelValue", "onUpdate:modelValue", "onBlur", "onKeydown"])) ], 2), [ [Le, Y(u, e.key)] ]), Y(u, e.key) ? F("", !0) : (k(), $(K, { key: 0 }, [ e.type === "number" ? (k(), $(K, { key: 0 }, [ P(I(e.prefix) + " " + I(U[e.key] ? V(Be)(U[e.key]) : U[e.key]) + " " + I(e.suffix), 1) ], 64)) : e.type === "currency" ? (k(), J(Ve, { key: 1, price: U[e.key], currency: e.currency_code, class: "text-no-wrap" }, null, 8, ["price", "currency"])) : (k(), $(K, { key: 2 }, [ P(I(e.prefix) + " " + I(U[e.key]) + " " + I(e.suffix), 1) ], 64)), !e.hideHandle && Z(u, e.key) && B(e.key) ? (k(), $("div", { key: 3, class: "handle", onMousedown: q(W, ["stop", "prevent"]) }, null, 32)) : F("", !0) ], 64)) ], 64)) ], 42, Te) ]) })) ]), 1032, ["headers", "items"]) ]), _: 1 }, 512) ]), _: 3 }); }; } }, Je = /* @__PURE__ */ Fe(He, [["__scopeId", "data-v-6cc7b667"]]), je = { install(s) { s.component("EditableDataGrid", Je); } }; export { Je as EditableDataGrid, je as default, Ne as useRefHistory };