UNPKG

vue-tree-dnd

Version:

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

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