UNPKG

vue-tree-dnd

Version:

Sortable drag-n-drop tree structure for Vue3 with no dependencies

273 lines (272 loc) 10.3 kB
import { isRef as U, isReactive as W, isProxy as Y, toRaw as J, defineComponent as F, inject as E, computed as S, watch as x, resolveComponent as K, openBlock as g, createElementBlock as V, normalizeStyle as Q, createElementVNode as Z, createBlock as L, resolveDynamicComponent as ee, unref as k, createVNode as G, createCommentVNode as q, Fragment as H, renderList as _, withDirectives as te, withModifiers as $, vShow as oe, ref as I, provide as D, onMounted as re, onUnmounted as ne } from "vue"; const R = (v) => { const o = [], n = (i, d = []) => { o.push({ ...i, __vue_dnd_tree_ancestors: d }); for (const u of i.children) n(u, [...d, i.id]); }; return v.forEach((i) => { n(i); }), o; }; function de(v) { const o = (n) => Array.isArray(n) ? n.map((i) => o(i)) : U(n) || W(n) || Y(n) ? o(J(n)) : n && typeof n == "object" ? Object.keys(n).reduce((i, d) => (i[d] = o(n[d]), i), {}) : n; return o(v); } const ae = (v, o, n) => Math.min(Math.max(v, o), n), ie = { key: 0, style: { display: "flex", "flex-direction": "row", "align-items": "center", opacity: "0.3", "pointer-events": "none" } }, A = /* @__PURE__ */ F({ __name: "TreeNode", props: { item: {}, component: {}, ancestors: {}, depth: {}, deltaX: {}, isGhost: { type: Boolean }, locked: { type: Boolean } }, setup(v) { var M, X, B; const o = v; if (!o.item) throw new Error("item is required"); if (typeof ((M = o.item) == null ? void 0 : M.id) != "string" && typeof ((X = o.item) == null ? void 0 : X.id) != "number" || o.item.id === "") throw new Error("item.id is required"); if (!Array.isArray((B = o.item) == null ? void 0 : B.children)) throw new Error("item.children array is required"); if (!("expanded" in o.item)) throw new Error("item.expanded is required"); if (!o.component) throw new Error("component is required"); const n = E("setExpanded", () => { throw new Error("setExpanded has not been provided"); }), i = (e) => { n(e, o.item.id); }, d = E("dropTarget"), u = E("dragItem"), y = E("dragstart"), j = (e, s, l) => { if (y === void 0) throw new Error("VueTreeDnd has not been provided"); y(e, s, l); }, T = E("dragover"), N = (e, s) => { if (T === void 0) throw new Error("VueTreeDnd has not been provided"); T(e, s); }, p = S(() => { if ((u == null ? void 0 : u.value) == null) return []; const e = u.value.id; if (o.item.children.filter((l) => l.id !== e).length > 0 && o.item.expanded) return [{ id: e, targetId: o.item.id, position: "FIRST_CHILD", ghostIndent: o.depth + 1 }]; const s = (l) => o.depth - (o.ancestors.length - l); return [ ...o.ancestors.map((l, t) => ({ id: e, targetId: l, position: "RIGHT", ghostIndent: s(t) })), { id: e, targetId: o.item.id, position: "RIGHT", ghostIndent: o.depth }, { id: e, targetId: o.item.id, position: "LAST_CHILD", ghostIndent: o.depth + 1 } ]; }), m = S(() => { const e = Math.min(...p.value.map((l) => l.ghostIndent)), s = Math.max(...p.value.map((l) => l.ghostIndent)); return ae(o.deltaX, e, s); }), C = E("setDropProposal"), f = (e) => { if (C === void 0) throw new Error("VueTreeDnd has not been provided"); C(e); }; x([d, p, m], () => { if (!(d === void 0 || (d == null ? void 0 : d.value) === null) && d.value === o.item.id) { const e = p.value.find((s) => s.ghostIndent === m.value); if (e == null) throw new Error(`Could not find impliedMoveMutation for ghostIndent ${m.value}`); f(e); } }); const w = { position: "absolute", opacity: 0, pointerEvents: "none" }, b = S(() => { var e; return ((e = u == null ? void 0 : u.value) == null ? void 0 : e.id) === o.item.id && !o.isGhost ? w : {}; }); return (e, s) => { var t; const l = K("TreeNode", !0); return g(), V("a", { href: "javascript:;", style: Q([{ color: "inherit", "text-decoration": "none" }, b.value]) }, [ Z("div", { style: { display: "flex", "flex-direction": "row", "align-items": "center" }, onDragover: s[0] || (s[0] = (r) => N(r, e.item.id)) }, [ (g(), L(ee(e.component), { item: e.item, depth: e.depth, expanded: e.item.expanded, onSetExpanded: i }, null, 40, ["item", "depth", "expanded"])) ], 32), k(d) === e.item.id && !e.isGhost && k(u) !== void 0 ? (g(), V("div", ie, [ G(l, { item: k(u), component: e.component, ancestors: [], "drop-target": k(d), depth: m.value, "delta-x": e.deltaX, "is-ghost": !0, locked: e.locked }, null, 8, ["item", "component", "drop-target", "depth", "delta-x", "locked"]) ])) : q("", !0), (g(!0), V(H, null, _(((t = e.item) == null ? void 0 : t.children) || [], (r, a) => { var c, h, O; return te((g(), L(l, { key: r.id, item: r, component: e.component, ancestors: a === e.item.children.length - 1 || a === e.item.children.length - 2 && ((c = k(u)) == null ? void 0 : c.id) === ((O = (h = e.item) == null ? void 0 : h.children[e.item.children.length - 1]) == null ? void 0 : O.id) ? [...e.ancestors, e.item.id] : [], "drop-target": k(d), depth: e.depth + 1, "delta-x": e.deltaX, "is-ghost": e.isGhost, locked: e.locked, draggable: !e.locked, onDragstart: $((z) => j(z, r.id, e.depth + 1), ["stop"]) }, null, 8, ["item", "component", "ancestors", "drop-target", "depth", "delta-x", "is-ghost", "locked", "draggable", "onDragstart"])), [ [oe, e.item.expanded] ]); }), 128)) ], 4); }; } }), se = { key: 0, style: { display: "flex", "flex-direction": "row", "align-items": "center", opacity: "0.3", "pointer-events": "none" } }, P = "__vue-dnd-tree-root__", ue = /* @__PURE__ */ F({ __name: "VueTreeDnd", props: { modelValue: {}, component: {}, locked: { type: Boolean } }, emits: ["move", "update:modelValue"], setup(v, { emit: o }) { const n = v, i = I([]), d = I([]), u = (t) => i.value.find((r) => r.id === t); x(() => n.modelValue, () => { i.value = R(n.modelValue), d.value = i.value.map(({ id: t }) => t); }, { immediate: !0, deep: !0 }), D("setExpanded", (t, r) => { const a = structuredClone(de(n.modelValue)), c = (h) => { h.id === r && (h.expanded = t), h.children !== void 0 && h.children.forEach(c); }; a.forEach(c), o("update:modelValue", a); }); const y = (t) => { const r = d.value.findIndex((a) => a === t); return r === 0 ? P : d.value[r - 1]; }, j = (t) => { if (t === P) return !1; const r = i.value.find((c) => c.id === t); if (r === void 0) throw new Error(`targetId ${t} not found in flatTreeNodes`); return r.__vue_dnd_tree_ancestors.some((c) => { var h; return !(((h = u(c)) == null ? void 0 : h.expanded) ?? !1); }); }, T = I(0), N = (t) => { if (t.preventDefault(), e.value === null) throw new Error("VueTreeDnd has not correctly set dataTransfer data (missing)"); const { initialX: r, initialDepth: a } = e.value, c = Math.round((+t.clientX - r) / 20); T.value = a + c; }; re(() => { document.addEventListener("dragover", N); }), ne(() => { document.removeEventListener("dragover", N); }); const p = I(null), m = S(() => i.value.find((t) => t.id === p.value)); D("dragItem", m); const C = S(() => m.value === void 0 ? /* @__PURE__ */ new Set() : new Set(R([m.value]).map((t) => t.id))), f = I(null), w = (t) => { if (C.value.has(t) || j(t)) { w(y(t)); return; } f.value = t; }; D("dropTarget", f), D("setDropTarget", w); const b = I(null), M = (t) => { b.value = t; }; D("setDropProposal", M), x(f, () => { if (f.value === P) { if (p.value === null) throw new Error("dragItemId.value is null"); M({ id: p.value, targetId: n.modelValue[0].id, position: "LEFT", ghostIndent: 0 }); } }); const X = () => { if (b.value == null) return; f.value = null, p.value = null; const { ghostIndent: t, ...r } = b.value; r.id !== r.targetId && o("move", r); }, B = new Image(), e = I(null), s = (t, r, a) => { t.dataTransfer !== null && (t.dataTransfer.dropEffect = "none", t.dataTransfer.effectAllowed = "none", t.dataTransfer.setDragImage(B, 0, 0)), setTimeout(() => { p.value = r, e.value = { initialX: t.clientX, initialDepth: a }, w(y(r)); }, 0); }; return D("dragstart", s), D("dragover", (t, r) => { t.preventDefault(); const a = y(r); if (p.value === r) { w(a); return; } w(t.offsetY < t.target.clientHeight / 2 ? a : r); }), (t, r) => (g(), V("div", { onDragend: X }, [ f.value === P && m.value !== void 0 ? (g(), V("div", se, [ G(A, { item: m.value, component: t.component, ancestors: [], depth: 0, "delta-x": T.value, "is-ghost": !0, locked: t.locked }, null, 8, ["item", "component", "delta-x", "locked"]) ])) : q("", !0), (g(!0), V(H, null, _(t.modelValue || [], (a) => (g(), L(A, { key: a.id, item: a, component: t.component, ancestors: [], depth: 0, "drop-target": f.value, "delta-x": T.value, "is-ghost": !1, locked: t.locked, draggable: !t.locked, onDragstart: $((c) => s(c, a.id, 0), ["stop"]) }, null, 8, ["item", "component", "drop-target", "delta-x", "locked", "draggable", "onDragstart"]))), 128)) ], 32)); } }); export { ue as default };