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
JavaScript
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
};