UNPKG

react-split-pane

Version:

React split-pane component with hooks and TypeScript

3 lines (2 loc) 9.67 kB
import{jsx as e}from"react/jsx-runtime";import{forwardRef as n,useState as t,useRef as r,useEffect as i,useCallback as o,useMemo as s,Children as a}from"react";function c(...e){return e.filter(Boolean).join(" ")}const u=n(function(n,t){const{className:r,style:i,children:o,defaultSize:s,size:a,minSize:u,maxSize:l,...d}=n,p={position:"relative",outline:"none",overflow:"auto",flex:"none",...i},m=c("split-pane-pane",r);return e("div",{ref:t,className:m,style:p,"data-pane":"true",...d,children:o})});function l(e,n="polite"){if("undefined"==typeof document)return;const t=document.createElement("div");t.setAttribute("role","status"),t.setAttribute("aria-live",n),t.setAttribute("aria-atomic","true"),t.style.position="absolute",t.style.left="-10000px",t.style.width="1px",t.style.height="1px",t.style.overflow="hidden",t.textContent=e,document.body.appendChild(t),setTimeout(()=>{document.body.removeChild(t)},1e3)}function d(e){return 1e3>e?Math.round(e)+" pixels":Math.round(e/10)/100+" thousand pixels"}function p(n){const{direction:t,index:r,isDragging:i,disabled:o,onPointerDown:s,onKeyDown:a,className:u,style:l,currentSize:d,minSize:p,maxSize:m,children:z}=n,h="horizontal"===t?"vertical":"horizontal",f={...{flex:"none",position:"relative",userSelect:"none",touchAction:"none",..."horizontal"===t?{width:"1px",cursor:o?"default":"col-resize"}:{height:"1px",cursor:o?"default":"row-resize"},...i&&{cursor:"horizontal"===t?"col-resize":"row-resize"}},...l},v=c("split-pane-divider",t,i&&"dragging",u),y=function(e,n){return`${"horizontal"===n?"vertical":"horizontal"} divider ${e+1}`}(r,t),S=function(e){return`Use ${"horizontal"===e?"left and right arrow keys":"up and down arrow keys"} to resize. Hold Shift for larger steps. Press Home or End to minimize or maximize.`}(t);return e("div",{role:"separator","aria-orientation":h,"aria-label":y,"aria-valuenow":d,"aria-valuemin":p,"aria-valuemax":void 0===m||m===1/0?void 0:m,"aria-description":S,tabIndex:o?-1:0,className:v,style:f,onPointerDown:o?void 0:s,onKeyDown:o?void 0:a,"data-divider-index":r,children:z})}function m(e,n){if("number"==typeof e)return e;if(e.endsWith("%")){return parseFloat(e)/100*n}if(e.endsWith("px"))return parseFloat(e);const t=parseFloat(e);return isNaN(t)?0:t}function z(e,n,t){return Math.min(Math.max(e,n),t)}function h(e,n,t,r,i){const o=[...e],s=e[n]??0,a=e[n+1]??0,c=r[n+1]??0,u=i[n+1]??1/0;let l=z(s+t,r[n]??0,i[n]??1/0),d=z(a-t,c,u);const p=a-d,m=Math.min(Math.abs(l-s),Math.abs(p));return t>0?(l=s+m,d=a-m):(l=s-m,d=a+m),o[n]=l,o[n+1]=d,o}function f(e){const{direction:n,sizes:s,minSizes:a,maxSizes:c,snapPoints:u=[],snapTolerance:l=10,step:d,onResizeStart:p,onResize:m,onResizeEnd:z}=e,[f,v]=t(!1),[y,S]=t(s),g=r(null),x=r(null),w=r(!0),E=r(null),b=r(y);b.current=y;const D=r(z);D.current=z;const P=r(s);i(()=>{f||JSON.stringify(s)===JSON.stringify(P.current)||(P.current=s,S(s))},[s,f]),i(()=>(w.current=!0,()=>{w.current=!1,x.current&&(cancelAnimationFrame(x.current),x.current=null)}),[]);const A=o((e,t)=>{if(!g.current||!w.current)return;const{dividerIndex:r,startPosition:i,startSizes:o,pointerType:s}=g.current;let p=("horizontal"===n?e:t)-i;d&&(p=function(e,n){return n>0?Math.round(e/n)*n:e}(p,d));let z=h(o,r,p,a,c);u.length>0&&(z=z.map(e=>function(e,n,t){for(const r of n)if(t>=Math.abs(e-r))return r;return e}(e,u,l))),S(z),m&&m(z,{sizes:z,source:"pointer",pointerType:s})},[n,d,a,c,u,l,m]),R=o(e=>{g.current&&e.pointerId===g.current.pointerId&&(e.preventDefault(),E.current={x:e.clientX,y:e.clientY},x.current||(x.current=requestAnimationFrame(()=>{x.current=null,w.current&&E.current&&A(E.current.x,E.current.y)})))},[A]),T=o(e=>{if(!g.current||e.pointerId!==g.current.pointerId)return;const{element:n,pointerId:t,pointerType:r}=g.current;n?.hasPointerCapture?.(t)&&n.releasePointerCapture(t),x.current&&(cancelAnimationFrame(x.current),x.current=null),v(!1);const i=b.current,o=D.current;o&&o(i,{sizes:i,source:"pointer",pointerType:r}),g.current=null},[]),k=o(e=>t=>{t.preventDefault();const r="horizontal"===n?t.clientX:t.clientY,i=t.currentTarget;i.setPointerCapture(t.pointerId);const o=t.pointerType;g.current={dividerIndex:e,startPosition:r,startSizes:y,pointerId:t.pointerId,pointerType:o,element:i},v(!0),p&&p({sizes:y,source:"pointer",pointerType:o,originalEvent:t.nativeEvent})},[n,y,p]);return i(()=>{if(f)return document.addEventListener("pointermove",R),document.addEventListener("pointerup",T),document.addEventListener("pointercancel",T),()=>{document.removeEventListener("pointermove",R),document.removeEventListener("pointerup",T),document.removeEventListener("pointercancel",T)}},[f,R,T]),{isDragging:f,currentSizes:y,handlePointerDown:k}}const v=10,y=50;function S(e){const{direction:n,sizes:t,minSizes:i,maxSizes:s,step:a=v,largeStep:c=y,onResize:u,onResizeEnd:p}=e,m=r(null);return{handleKeyDown:o(e=>r=>{if(!("horizontal"===n?["ArrowLeft","ArrowRight"]:["ArrowUp","ArrowDown"]).includes(r.key)&&"Home"!==r.key&&"End"!==r.key&&"Escape"!==r.key)return;if(r.preventDefault(),"Escape"===r.key){if(m.current){const e=m.current;m.current=null,u&&u(e,{sizes:e,source:"keyboard",originalEvent:r.nativeEvent}),p&&p(e,{sizes:e,source:"keyboard",originalEvent:r.nativeEvent}),l("Pane sizes restored")}return}null===m.current&&(m.current=[...t]);let o=0,f=[...t],v="arrow";switch(r.key){case"ArrowLeft":case"ArrowUp":o=-(r.shiftKey?c:a);break;case"ArrowRight":case"ArrowDown":o=r.shiftKey?c:a;break;case"Home":{v="home";const n=e+1,r=i[e]??0,o=(t[e]??0)+(t[n]??0);f[e]=r,f[n]=o-r;break}case"End":{v="end";const n=e,r=e+1,o=(t[n]??0)+(t[r]??0);f[n]=Math.min(s[n]??1/0,o-(i[r]??0)),f[r]=o-f[n];break}}if(0!==o&&(f=h(t,e,o,i,s)),f=f.map((e,n)=>z(e,i[n]??0,s[n]??1/0)),u&&u(f,{sizes:f,source:"keyboard",originalEvent:r.nativeEvent}),p&&p(f,{sizes:f,source:"keyboard",originalEvent:r.nativeEvent}),"home"===v)l(`Pane ${e+1} minimized to ${d(f[e]??0)}`);else if("end"===v)l(`Pane ${e+1} maximized to ${d(f[e]??0)}`);else{const n=o>0?e:e+1;l(`Pane ${n+1} resized to ${d(f[n]??0)}`)}},[n,t,i,s,a,c,u,p])}}function g(n){const{direction:l="horizontal",resizable:d=!0,snapPoints:z,snapTolerance:h=10,step:v,onResizeStart:y,onResize:g,onResizeEnd:x,className:w,style:E,divider:b,dividerStyle:D,dividerClassName:P,dividerSize:A=1,children:R}=n,T=r(null),[k,I]=t(0),N=r(0),M=s(()=>a.toArray(R).filter(e=>"object"==typeof e&&null!==e&&"props"in e).map(e=>({props:e.props,size:e.props.size,defaultSize:e.props.defaultSize,minSize:e.props.minSize??0,maxSize:e.props.maxSize??1/0})),[R]),$=M.length,L=r(!1);2>$&&!L.current&&(L.current=!0,console.warn(`SplitPane requires at least 2 Pane children. Received ${$}.`));const{minSizes:C,maxSizes:F}=s(()=>{if(0===k)return{minSizes:Array($).fill(0),maxSizes:Array($).fill(1/0)};const e=[],n=[];return M.forEach(t=>{e.push(m(t.minSize,k)),n.push(t.maxSize===1/0?1/0:m(t.maxSize,k))}),{minSizes:e,maxSizes:n}},[k,$,M]),K=o(e=>{if(0===e)return Array($).fill(0);const n=e-A*($-1),t=M.map(e=>{const t=e.size??e.defaultSize;return void 0!==t?m(t,n):null}),r=t.reduce((e,n)=>e+(n??0),0),i=t.filter(e=>null===e).length,o=i>0?(n-r)/i:0;return t.map(e=>null===e?o:e)},[$,M,A]),[O,H]=t(()=>K(k));i(()=>{if(0===k)return;if(!M.some(e=>void 0!==e.size))return;const e=K(k);H(n=>n.length===e.length&&n.every((n,t)=>n===e[t])?n:e)},[k,M,K]);const J=o(e=>{const n=N.current;if(N.current=e,0===e)return;const t=M.some(e=>void 0!==e.size),r=e-A*($-1);H(i=>i.every(e=>0===e)||i.length!==$?K(e):n>0&&n!==e?t?K(e):function(e,n){const t=e.reduce((e,n)=>e+n,0);if(0===t){const t=n/e.length;return e.map(()=>t)}return e.map(e=>e/t*n)}(i,r):0===n?K(e):i)},[$,M,K,A]),X=r(0);i(()=>{const e=T.current;if(!e)return;const n=new ResizeObserver(e=>{const n=e[0];n&&(e=>{const n=Math.round("horizontal"===l?e.width:e.height);n>0&&n!==X.current&&(X.current=n,I(n),J(n))})(n.contentRect)});return n.observe(e),()=>{n.disconnect()}},[l,J]);const Y=o((e,n)=>{H(e),g?.(e,n)},[g]),{isDragging:j,currentSizes:U,handlePointerDown:q}=f({direction:l,sizes:O,minSizes:C,maxSizes:F,snapPoints:z,snapTolerance:h,step:v,onResizeStart:y,onResize:Y,onResizeEnd:x}),{handleKeyDown:W}=S({direction:l,sizes:U,minSizes:C,maxSizes:F,step:v,onResize:Y,onResizeEnd:x}),B=o(e=>n=>{const t={...n,pointerId:1,pointerType:"mouse",nativeEvent:n.nativeEvent};q(e)(t)},[q]),G=o(e=>n=>{const t=n.touches[0];if(!t)return;const r={...n,clientX:t.clientX,clientY:t.clientY,pointerId:t.identifier,pointerType:"touch",nativeEvent:n.nativeEvent};q(e)(r)},[q]),Q=o(()=>{},[]),V={display:"flex",flexDirection:"horizontal"===l?"row":"column",height:"100%",width:"100%",overflow:"hidden",position:"relative",...E},Z=c("split-pane",l,w);return e("div",{ref:T,className:Z,style:V,children:k>0&&(()=>{if(2>$)return null;const n=[];return M.forEach((t,r)=>{const i=U[r]??0,o={..."horizontal"===l?{width:i+"px",height:"100%"}:{height:i+"px",width:"100%"},...t.props.style};if(n.push(e(u,{...t.props,style:o,children:t.props.children},"pane-"+r)),$-1>r){const t=C[r],o=F[r];n.push(e(b??p,{direction:l,index:r,isDragging:j,disabled:!d,onPointerDown:q(r),onMouseDown:B(r),onTouchStart:G(r),onTouchEnd:Q,onKeyDown:W(r),className:P,style:D,currentSize:i,minSize:t,maxSize:o===1/0?void 0:o},"divider-"+r))}}),n})()})}function x(e){const{key:n,storage:r=localStorage,debounce:o=300}=e,[s,a]=t(()=>{try{const e=r.getItem(n);return e?JSON.parse(e):[]}catch{return[]}});return i(()=>{if(0===s.length)return;const e=setTimeout(()=>{try{r.setItem(n,JSON.stringify(s))}catch(e){console.warn("Failed to persist pane sizes:",e)}},o);return()=>clearTimeout(e)},[s,n,r,o]),[s,a]}export{p as Divider,u as Pane,g as SplitPane,S as useKeyboardResize,x as usePersistence,f as useResizer}; //# sourceMappingURL=index.js.map