splitpanes
Version:
A Vue.js reliable, simple and touch-ready panes splitter / resizer
2 lines (1 loc) • 10.3 kB
JavaScript
(function(h,s){typeof exports=="object"&&typeof module<"u"?s(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],s):(h=typeof globalThis<"u"?globalThis:h||self,s(h.splitpanes={},h.Vue))})(this,function(h,s){"use strict";const U={__name:"splitpanes",props:{horizontal:{type:Boolean,default:!1},pushOtherPanes:{type:Boolean,default:!0},maximizePanes:{type:Boolean,default:!0},rtl:{type:Boolean,default:!1},firstSplitter:{type:Boolean,default:!1}},emits:["ready","resize","resized","pane-click","pane-maximize","pane-add","pane-remove","splitter-click","splitter-dblclick"],setup(N,{emit:x}){const w=x,u=N,y=s.useSlots(),l=s.ref([]),M=s.computed(()=>l.value.reduce((e,n)=>(e[~~n.id]=n)&&e,{})),m=s.computed(()=>l.value.length),S=s.ref(null),P=s.ref(!1),d=s.ref({mouseDown:!1,dragging:!1,activeSplitter:null,cursorOffset:0}),p=s.ref({splitter:null,timeoutId:null}),k=s.computed(()=>({[`splitpanes splitpanes--${u.horizontal?"horizontal":"vertical"}`]:!0,"splitpanes--dragging":d.value.dragging})),E=()=>{document.addEventListener("mousemove",c,{passive:!1}),document.addEventListener("mouseup",g),"ontouchstart"in window&&(document.addEventListener("touchmove",c,{passive:!1}),document.addEventListener("touchend",g))},B=()=>{document.removeEventListener("mousemove",c,{passive:!1}),document.removeEventListener("mouseup",g),"ontouchstart"in window&&(document.removeEventListener("touchmove",c,{passive:!1}),document.removeEventListener("touchend",g))},_=(e,n)=>{const t=e.target.closest(".splitpanes__splitter");if(t){const{left:i,top:a}=t.getBoundingClientRect(),{clientX:o,clientY:r}="ontouchstart"in window&&e.touches?e.touches[0]:e;d.value.cursorOffset=u.horizontal?r-a:o-i}E(),d.value.mouseDown=!0,d.value.activeSplitter=n},c=e=>{d.value.mouseDown&&(e.preventDefault(),d.value.dragging=!0,requestAnimationFrame(()=>{X(H(e)),v("resize",{event:e},!0)}))},g=e=>{d.value.dragging&&(window.getSelection().removeAllRanges(),v("resized",{event:e},!0)),d.value.mouseDown=!1,d.value.activeSplitter=null,setTimeout(()=>{d.value.dragging=!1,B()},100)},C=(e,n)=>{"ontouchstart"in window&&(e.preventDefault(),p.value.splitter===n?(clearTimeout(p.value.timeoutId),p.value.timeoutId=null,A(e,n),p.value.splitter=null):(p.value.splitter=n,p.value.timeoutId=setTimeout(()=>p.value.splitter=null,500))),d.value.dragging||v("splitter-click",{event:e,index:n},!0)},A=(e,n)=>{if(v("splitter-dblclick",{event:e,index:n},!0),u.maximizePanes){let t=0;l.value=l.value.map((i,a)=>(i.size=a===n?i.max:i.min,a!==n&&(t+=i.min),i)),l.value[n].size-=t,v("pane-maximize",{event:e,index:n,pane:l.value[n]}),v("resized",{event:e,index:n},!0)}},F=(e,n)=>{v("pane-click",{event:e,index:M.value[n].index,pane:M.value[n]})},H=e=>{const n=S.value.getBoundingClientRect(),{clientX:t,clientY:i}="ontouchstart"in window&&e.touches?e.touches[0]:e;return{x:t-(u.horizontal?0:d.value.cursorOffset)-n.left,y:i-(u.horizontal?d.value.cursorOffset:0)-n.top}},G=e=>{e=e[u.horizontal?"y":"x"];const n=S.value[u.horizontal?"clientHeight":"clientWidth"];return u.rtl&&!u.horizontal&&(e=n-e),e*100/n},X=e=>{const n=d.value.activeSplitter;let t={prevPanesSize:q(n),nextPanesSize:R(n),prevReachedMinPanes:0,nextReachedMinPanes:0};const i=0+(u.pushOtherPanes?0:t.prevPanesSize),a=100-(u.pushOtherPanes?0:t.nextPanesSize),o=Math.max(Math.min(G(e),a),i);let r=[n,n+1],f=l.value[r[0]]||null,z=l.value[r[1]]||null;const T=f.max<100&&o>=f.max+t.prevPanesSize,ae=z.max<100&&o<=100-(z.max+R(n+1));if(T||ae){T?(f.size=f.max,z.size=Math.max(100-f.max-t.prevPanesSize-t.nextPanesSize,0)):(f.size=Math.max(100-z.max-t.prevPanesSize-R(n+1),0),z.size=z.max);return}if(u.pushOtherPanes){const j=Y(t,o);if(!j)return;({sums:t,panesToResize:r}=j),f=l.value[r[0]]||null,z=l.value[r[1]]||null}f!==null&&(f.size=Math.min(Math.max(o-t.prevPanesSize-t.prevReachedMinPanes,f.min),f.max)),z!==null&&(z.size=Math.min(Math.max(100-o-t.nextPanesSize-t.nextReachedMinPanes,z.min),z.max))},Y=(e,n)=>{const t=d.value.activeSplitter,i=[t,t+1];return n<e.prevPanesSize+l.value[i[0]].min&&(i[0]=V(t).index,e.prevReachedMinPanes=0,i[0]<t&&l.value.forEach((a,o)=>{o>i[0]&&o<=t&&(a.size=a.min,e.prevReachedMinPanes+=a.min)}),e.prevPanesSize=q(i[0]),i[0]===void 0)?(e.prevReachedMinPanes=0,l.value[0].size=l.value[0].min,l.value.forEach((a,o)=>{o>0&&o<=t&&(a.size=a.min,e.prevReachedMinPanes+=a.min)}),l.value[i[1]].size=100-e.prevReachedMinPanes-l.value[0].min-e.prevPanesSize-e.nextPanesSize,null):n>100-e.nextPanesSize-l.value[i[1]].min&&(i[1]=W(t).index,e.nextReachedMinPanes=0,i[1]>t+1&&l.value.forEach((a,o)=>{o>t&&o<i[1]&&(a.size=a.min,e.nextReachedMinPanes+=a.min)}),e.nextPanesSize=R(i[1]-1),i[1]===void 0)?(e.nextReachedMinPanes=0,l.value.forEach((a,o)=>{o<m.value-1&&o>=t+1&&(a.size=a.min,e.nextReachedMinPanes+=a.min)}),l.value[i[0]].size=100-e.prevPanesSize-R(i[0]-1),null):{sums:e,panesToResize:i}},q=e=>l.value.reduce((n,t,i)=>n+(i<e?t.size:0),0),R=e=>l.value.reduce((n,t,i)=>n+(i>e+1?t.size:0),0),V=e=>[...l.value].reverse().find(t=>t.index<e&&t.size>t.min)||{},W=e=>l.value.find(t=>t.index>e+1&&t.size>t.min)||{},I=()=>{var n;const e=Array.from(((n=S.value)==null?void 0:n.children)||[]);for(const t of e){const i=t.classList.contains("splitpanes__pane"),a=t.classList.contains("splitpanes__splitter");!i&&!a&&(t.remove(),console.warn("Splitpanes: Only <pane> elements are allowed at the root of <splitpanes>. One of your DOM nodes was removed."))}},L=(e,n,t=!1)=>{const i=e-1,a=document.createElement("div");a.classList.add("splitpanes__splitter"),t||(a.onmousedown=o=>_(o,i),typeof window<"u"&&"ontouchstart"in window&&(a.ontouchstart=o=>_(o,i)),a.onclick=o=>C(o,i+1)),a.ondblclick=o=>A(o,i+1),n.parentNode.insertBefore(a,n)},J=e=>{e.onmousedown=void 0,e.onclick=void 0,e.ondblclick=void 0,e.remove()},b=()=>{var t;const e=Array.from(((t=S.value)==null?void 0:t.children)||[]);for(const i of e)i.className.includes("splitpanes__splitter")&&J(i);let n=0;for(const i of e)i.className.includes("splitpanes__pane")&&(!n&&u.firstSplitter?L(n,i,!0):n&&L(n,i),n++)},K=({uid:e,...n})=>{const t=M.value[e];for(const[i,a]of Object.entries(n))t[i]=a},Q=e=>{var t;let n=-1;Array.from(((t=S.value)==null?void 0:t.children)||[]).some(i=>(i.className.includes("splitpanes__pane")&&n++,i.isSameNode(e.el))),l.value.splice(n,0,{...e,index:n}),l.value.forEach((i,a)=>i.index=a),P.value&&s.nextTick(()=>{b(),O({addedPane:l.value[n]}),v("pane-add",{pane:l.value[n]})})},Z=e=>{const n=l.value.findIndex(i=>i.id===e);l.value[n].el=null;const t=l.value.splice(n,1)[0];l.value.forEach((i,a)=>i.index=a),s.nextTick(()=>{b(),v("pane-remove",{pane:t}),O({removedPane:{...t}})})},O=(e={})=>{!e.addedPane&&!e.removedPane?ne():l.value.some(n=>n.givenSize!==null||n.min||n.max<100)?ie(e):ee(),P.value&&v("resized")},ee=()=>{const e=100/m.value;let n=0;const t=[],i=[];for(const a of l.value)a.size=Math.max(Math.min(e,a.max),a.min),n-=a.size,a.size>=a.max&&t.push(a.id),a.size<=a.min&&i.push(a.id);n>.1&&D(n,t,i)},ne=()=>{let e=100;const n=[],t=[];let i=0;for(const o of l.value)e-=o.size,o.givenSize!==null&&i++,o.size>=o.max&&n.push(o.id),o.size<=o.min&&t.push(o.id);let a=100;if(e>.1){for(const o of l.value)o.givenSize===null&&(o.size=Math.max(Math.min(e/(m.value-i),o.max),o.min)),a-=o.size;a>.1&&D(a,n,t)}},ie=({addedPane:e,removedPane:n}={})=>{let t=100/m.value,i=0;const a=[],o=[];((e==null?void 0:e.givenSize)??null)!==null&&(t=(100-e.givenSize)/(m.value-1));for(const r of l.value)i-=r.size,r.size>=r.max&&a.push(r.id),r.size<=r.min&&o.push(r.id);if(!(Math.abs(i)<.1)){for(const r of l.value)(e==null?void 0:e.givenSize)!==null&&(e==null?void 0:e.id)===r.id||(r.size=Math.max(Math.min(t,r.max),r.min)),i-=r.size,r.size>=r.max&&a.push(r.id),r.size<=r.min&&o.push(r.id);i>.1&&D(i,a,o)}},D=(e,n,t)=>{let i;e>0?i=e/(m.value-n.length):i=e/(m.value-t.length),l.value.forEach((a,o)=>{if(e>0&&!n.includes(a.id)){const r=Math.max(Math.min(a.size+i,a.max),a.min),f=r-a.size;e-=f,a.size=r}else if(!t.includes(a.id)){const r=Math.max(Math.min(a.size+i,a.max),a.min),f=r-a.size;e-=f,a.size=r}}),Math.abs(e)>.1&&s.nextTick(()=>{P.value&&console.warn("Splitpanes: Could not resize panes correctly due to their constraints.")})},v=(e,n=void 0,t=!1)=>{const i=(n==null?void 0:n.index)??d.value.activeSplitter??null;w(e,{...n,...i!==null&&{index:i},...t&&i!==null&&{prevPane:l.value[i-(u.firstSplitter?1:0)],nextPane:l.value[i+(u.firstSplitter?0:1)]},panes:l.value.map(a=>({min:a.min,max:a.max,size:a.size}))})};s.watch(()=>u.firstSplitter,()=>b()),s.onMounted(()=>{I(),b(),O(),v("ready"),P.value=!0}),s.onBeforeUnmount(()=>P.value=!1);const te=()=>{var e;return s.h("div",{ref:S,class:k.value},(e=y.default)==null?void 0:e.call(y))};return s.provide("panes",l),s.provide("indexedPanes",M),s.provide("horizontal",s.computed(()=>u.horizontal)),s.provide("requestUpdate",K),s.provide("onPaneAdd",Q),s.provide("onPaneRemove",Z),s.provide("onPaneClick",F),(e,n)=>(s.openBlock(),s.createBlock(s.resolveDynamicComponent(te)))}},$={__name:"pane",props:{size:{type:[Number,String]},minSize:{type:[Number,String],default:0},maxSize:{type:[Number,String],default:100}},setup(N){var _;const x=N,w=s.inject("requestUpdate"),u=s.inject("onPaneAdd"),y=s.inject("horizontal"),l=s.inject("onPaneRemove"),M=s.inject("onPaneClick"),m=(_=s.getCurrentInstance())==null?void 0:_.uid,S=s.inject("indexedPanes"),P=s.computed(()=>S.value[m]),d=s.ref(null),p=s.computed(()=>{const c=isNaN(x.size)||x.size===void 0?0:parseFloat(x.size);return Math.max(Math.min(c,E.value),k.value)}),k=s.computed(()=>{const c=parseFloat(x.minSize);return isNaN(c)?0:c}),E=s.computed(()=>{const c=parseFloat(x.maxSize);return isNaN(c)?100:c}),B=s.computed(()=>{var c;return`${y.value?"height":"width"}: ${(c=P.value)==null?void 0:c.size}%`});return s.watch(()=>p.value,c=>w({uid:m,size:c})),s.watch(()=>k.value,c=>w({uid:m,min:c})),s.watch(()=>E.value,c=>w({uid:m,max:c})),s.onMounted(()=>{u({id:m,el:d.value,min:k.value,max:E.value,givenSize:x.size===void 0?null:p.value,size:p.value})}),s.onBeforeUnmount(()=>l(m)),(c,g)=>(s.openBlock(),s.createElementBlock("div",{ref_key:"paneEl",ref:d,class:"splitpanes__pane",onClick:g[0]||(g[0]=C=>s.unref(M)(C,c._.uid)),style:s.normalizeStyle(B.value)},[s.renderSlot(c.$slots,"default")],4))}};h.Pane=$,h.Splitpanes=U,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})});