@oscherbakov/react-flow-automated-layout 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ (function(M,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("react/jsx-runtime"),require("react"),require("@xyflow/react"),require("@dagrejs/dagre")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","@xyflow/react","@dagrejs/dagre"],n):(M=typeof globalThis<"u"?globalThis:M||self,n(M.reactFlowAutomatedLayout={},M.jsxRuntime,M.React,M.ReactFlow,M.dagre))})(this,function(M,n,k,B,rt){"use strict";const ot=k.createContext(void 0);function P(){const t=k.useContext(ot);if(t===void 0)throw new Error("useLayoutContext must be used within a LayoutProvider");return t}const st=t=>{switch(t){case"DOWN":return"TB";case"RIGHT":return"LR";case"UP":return"BT";case"LEFT":return"RL";default:return"TB"}},nt=t=>{switch(t){case"TB":return"DOWN";case"LR":return"RIGHT";case"BT":return"UP";case"RL":return"LEFT";default:return"DOWN"}},Et={enabled:!0,padding:{horizontal:80,vertical:48},respectHeaderHeight:!0,minWidth:200,minHeight:100},vt=t=>{switch(t){case"DOWN":return B.Position.Bottom;case"RIGHT":return B.Position.Right;case"UP":return B.Position.Top;case"LEFT":return B.Position.Left;default:return B.Position.Bottom}},Nt=t=>{switch(t){case"DOWN":return B.Position.Top;case"RIGHT":return B.Position.Left;case"UP":return B.Position.Bottom;case"LEFT":return B.Position.Right;default:return B.Position.Top}};function kt(t,r,e){if(e)return{nodes:t,edges:r};const s=t.filter(h=>!h.hidden),o=new Set(s.map(h=>h.id)),i=r.filter(h=>o.has(h.source)&&o.has(h.target));return{nodes:s,edges:i}}const Bt=172,Tt=36,V=async(t,r,e,s=0,o=50,i=50,h=Bt,a=Tt,d=!1)=>{const l=new rt.graphlib.Graph().setDefaultEdgeLabel(()=>({}));l.setGraph({rankdir:e,marginx:s,marginy:s,nodesep:o,ranksep:i,edgesep:Math.max(20,o/4),ranker:"tight-tree"});const c=d?t:t.filter(g=>!g.hidden);if(c.length===0)return{nodes:[],edges:[],width:0,height:0};c.forEach(g=>{var y,x;const f=Number((y=g.style)==null?void 0:y.width)||h,b=Number((x=g.style)==null?void 0:x.height)||a;l.setNode(g.id,{width:f,height:b})}),r.forEach(g=>{var v,E;const f=g.sourceHandle,b=g.targetHandle,y=!!((v=g.data)!=null&&v.isReciprocal),x=!!((E=g.data)!=null&&E.isSyntheticBridge);let w={};y?w={constraint:!1,minlen:1,weight:5}:x?w={constraint:!0,minlen:1,weight:4}:f==="right"&&b==="left"?w={constraint:!1,minlen:1}:f==="bottom"&&b==="top"?w={constraint:!0,minlen:1}:w={constraint:!0,minlen:2},l.setEdge(g.source,g.target,w)}),rt.layout(l);const u=l.graph().width||0,p=l.graph().height||0,N=vt(nt(e)),C=Nt(nt(e));return{nodes:c.map(g=>{var E;const f=l.node(g.id),{width:b,height:y}=f,x={...g,sourcePosition:N,targetPosition:C,selected:!1},w=f.x,v=f.y;switch((E=g.data)==null?void 0:E.positionType){case"center":x.position={x:w,y:v};break;case"topRight":x.position={x:w+b/2,y:v-y/2};break;case"bottomLeft":x.position={x:w-b/2,y:v+y/2};break;case"bottomRight":x.position={x:w+b/2,y:v+y/2};break;case"topLeft":default:x.position={x:w-b/2,y:v-y/2}}return x}),edges:r,width:u,height:p}},it={calculate:async(t,r,e)=>{const s=e.nodes||t,o=e.edges||r,h=e.dagreDirection||typeof e.direction=="string"&&(e.direction==="RIGHT"||e.direction==="LEFT"?"LR":"TB")||"TB",a=e.margin??(typeof e.padding=="number"?e.padding:0),d=e.nodeSpacing??50,l=e.layerSpacing??50,c=e.nodeWidth??172,u=e.nodeHeight??36,p=e.layoutHidden??!1;return V(s,o,h,a,d,l,c,u,p)}},ct={dagre:it};function St({initialDirection:t="DOWN",initialAlgorithm:r="layered",initialAutoLayout:e=!0,initialPadding:s=50,initialSpacing:o={node:50,layer:50},initialNodeDimensions:i={width:172,height:36},initialParentResizingOptions:h,includeHidden:a=!1,layoutEngines:d}){const[l,c]=k.useState(t),[u,p]=k.useState(r),[N,C]=k.useState(e),[L,g]=k.useState(!1),[f,b]=k.useState(a),[y,x]=k.useState({...ct,...d}),[w,v]=k.useState({}),[E,D]=k.useState(s),[G,m]=k.useState(o.node||150),[S,T]=k.useState(o.layer||180),[O,A]=k.useState(i.width||100),[_,W]=k.useState(i.height||100),[$,K]=k.useState({...Et,...h,enabled:e}),[j,R]=k.useState([]);return{direction:l,algorithm:u,autoLayout:N,layoutInProgress:L,layoutHidden:f,layoutEngines:y,layoutEngineOptions:w,padding:E,nodeSpacing:G,layerSpacing:S,nodeWidth:O,nodeHeight:_,parentResizingOptions:$,selectedNodes:j,setDirection:c,setAlgorithm:p,setAutoLayout:C,setLayoutInProgress:g,setLayoutHidden:b,setLayoutEngines:x,setLayoutEngineOptions:v,setPadding:D,setNodeSpacing:m,setLayerSpacing:T,setNodeWidth:A,setNodeHeight:W,setParentResizingOptionsState:K,setSelectedNodes:R}}function Mt({nodes:t,externalNodeIdWithNode:r,externalNodeParentIdMapWithChildIdSet:e,noParentKey:s="no-parent"}){const[o,i]=k.useState(new Map),[h,a]=k.useState(new Map),[d,l]=k.useState({}),[c,u]=k.useState(t.length),[p,N]=k.useState(!1),C=r||o,L=e||h,g=C.size;return k.useEffect(()=>{if(!r||!e){const f=new Map,b=new Map;t.forEach(y=>{var w;f.set(y.id,y);const x=y.parentId||s;b.has(x)||b.set(x,new Set),(w=b.get(x))==null||w.add(y.id)}),r||i(f),e||a(b)}},[t,r,e,s]),k.useEffect(()=>{t.length!==c&&u(t.length)},[t]),k.useEffect(()=>{C.size>0&&L.size>0&&!p&&N(!0)},[C,L,p]),k.useEffect(()=>{const f={};L.forEach((y,x)=>{f[x]=y.size});let b=!1;for(const y in f)if(!d.hasOwnProperty(y)||f[y]!==d[y]){b=!0;break}b&&l(f)},[L]),{nodeIdWithNode:C,nodeParentIdMapWithChildIdSet:L,numberOfNodes:g,nodesLength:c,childrenInitialized:p,parentChildStructure:d}}function Dt({nodes:t,edges:r,selectedNodes:e,layoutHidden:s,calculateLayout:o,calculateContainerLayout:i,updateNodes:h,updateEdges:a,setLayoutInProgress:d,setNodeSpacing:l,setLayerSpacing:c}){const u=B.useReactFlow(),p=k.useRef(null),N=k.useRef(null),C=k.useCallback(async(g=[],f=[])=>{p.current&&p.current.abort();const b=new AbortController;p.current=b;const y=g.length>0?g:t,x=f.length>0?f:r;if(y.length===0)return{nodes:y,edges:x};try{if(d(!0),b.signal.aborted)return{nodes:y,edges:x};const{nodes:w,edges:v}=kt(y,x,s),E=await o(w,v,e,b.signal);return b.signal.aborted?{nodes:y,edges:x}:(p.current===b&&(h?h(E.nodes):u!=null&&u.setNodes&&u.setNodes(E.nodes),a?a(E.edges):u!=null&&u.setEdges&&u.setEdges(E.edges),N.current&&(N.current.node!==void 0&&l(N.current.node),N.current.layer!==void 0&&c(N.current.layer),N.current=null)),E)}catch(w){return b.signal.aborted||console.error("Error applying layout:",w),{nodes:y,edges:x}}finally{p.current===b&&(d(!1),p.current=null)}},[t,r,e,o,h,a,u,s,d,l,c]),L=k.useCallback(async g=>{p.current&&p.current.abort();const f=new AbortController;if(p.current=f,t.length===0)return{nodes:t,edges:r};try{if(d(!0),f.signal.aborted)return{nodes:t,edges:r};const b=await i(g,t,r,f.signal);return f.signal.aborted?{nodes:t,edges:r}:(p.current===f&&(h?h(b.nodes):u!=null&&u.setNodes&&u.setNodes(b.nodes),a?a(b.edges):u!=null&&u.setEdges&&u.setEdges(b.edges)),b)}catch(b){return f.signal.aborted||console.error("Error applying container layout:",b),{nodes:t,edges:r}}finally{p.current===f&&(d(!1),p.current=null)}},[t,r,i,h,a,u,d]);return{applyLayout:C,applyContainerLayout:L,pendingSpacingUpdateRef:N}}const Ot=(t,r,e="no-parent")=>{const s=new Set,o=new Map;t.forEach((a,d)=>{const l=r.get(d);l&&o.set(d,{id:d,node:l,children:[],depth:0})});const i=(a,d)=>{if(s.has(a)||!t.has(a)||!r.get(a))return null;s.add(a);const c=o.get(a);c.depth=d;const u=t.get(a)||new Set;for(const p of u)if(t.has(p)&&r.get(p)){const C=i(p,d+1);C&&c.children.push(C)}return c},h=[];return t.forEach((a,d)=>{const l=r.get(d);if(l){const c=l.parentId;if(!c||c===e){const u=i(d,0);u&&h.push(u)}}}),h};function I(t,r,e="no-parent"){const s=[];let o=t;const i=new Set;for(;o&&o!==e;){if(i.has(o)){console.warn(`Cycle detected in ancestor path for node ${t}`);break}i.add(o),s.push(o);const h=r.get(o);if(!h||!h.parentId){o=e;break}o=h.parentId}return o===e&&s.push(e),s}function z(t,r,e,s="no-parent"){if(t===r)return t;const o=I(t,e,s),i=o.indexOf(r);return i===-1?null:i===0?t:o[i-1]||null}function U(t,r,e,s="no-parent"){const o=I(t,e,s),i=I(r,e,s),h=new Set(i);let a=null,d=-1;for(let p=0;p<o.length;p++)if(h.has(o[p])){a=o[p],d=p;break}if(!a)return{lca:null,sourceChild:null,targetChild:null};const l=i.indexOf(a),c=d>0?o[d-1]:t,u=l>0?i[l-1]:r;return{lca:a,sourceChild:c,targetChild:u}}const At=t=>JSON.parse(JSON.stringify(t)),at=(t,r,e,s,o)=>{const i=At(t);return i.source=r,i.target=e,i.id=s,i.data={...i.data,...o},i},zt=t=>{for(const r of t.values()){const e=new Map;r.forEach(o=>{const i=`${o.source}->${o.target}`,h=e.get(i)||[];h.push(o),e.set(i,h)});const s=new Set;r.forEach(o=>{const i=`${o.source}->${o.target}`,h=`${o.target}->${o.source}`;if(s.has(i)||s.has(h))return;const a=e.get(h);if(!a||a.length===0)return;[...e.get(i)||[],...a].forEach(l=>{l.data={...l.data,isReciprocal:!0}}),s.add(i),s.add(h)})}},mt=(t,r,e,s)=>{const o=new Map,i=new Map;t.forEach(a=>{const d=o.get(a.target)||[];d.push(a),o.set(a.target,d);const l=i.get(a.source)||[];l.push(a),i.set(a.source,l)});const h=new Set;i.forEach((a,d)=>{const l=o.get(d)||[];l.length!==0&&l.forEach(c=>{a.forEach(u=>{if(c.source===u.target)return;const{lca:p,sourceChild:N,targetChild:C}=U(c.source,u.target,r,s),L=U(c.source,d,r,s).lca,g=U(d,u.target,r,s).lca;if(!L||L!==g)return;const f=z(c.source,L,r,s),b=z(u.target,L,r,s),y=z(d,L,r,s);if(!f||!b||!y||f!==b||f===y)return;const x=f,w=z(c.source,x,r,s),v=z(u.target,x,r,s);if(!p||!N||!C||!w||!v||w===v)return;const E=`synthetic_bridge_${c.id}_${d}_${u.id}_${x}`;if(h.has(E))return;const D=at(c,w,v,E,{isTemporary:!0,isSyntheticBridge:!0,bridgeNode:d,bridgeBranch:y,anchorBranch:f,originalSource:c.source,originalTarget:u.target});e.has(x)||e.set(x,[]),e.get(x).push(D),h.add(E)})})})};function X(t,r,e="no-parent"){const s=new Map;for(const o of t){const{lca:i,sourceChild:h,targetChild:a}=U(o.source,o.target,r,e);if(i&&h&&a&&h!==a){const d=at(o,h,a,`temp_${o.id}_${i}`,{isTemporary:!0,originalSource:o.source,originalTarget:o.target});s.has(i)||s.set(i,[]),s.get(i).push(d)}}return mt(t,r,s,e),zt(s),s}const q=10,lt=(t,r)=>t.find(e=>e.id===r),ht=(t,r,e)=>{var a,d;const s=e.get(t);if(!s)return null;let o=s.position.x+(s.width||Number((a=s.style)==null?void 0:a.width)||172)/2,i=s.position.y+(s.height||Number((d=s.style)==null?void 0:d.height)||36)/2,h=s.parentId;for(;h&&h!==r;){const l=e.get(h);if(!l)break;o+=l.position.x,i+=l.position.y,h=l.parentId}return{x:o,y:i}},H=t=>{var r,e;return{width:t.width||Number((r=t.style)==null?void 0:r.width)||172,height:t.height||Number((e=t.style)==null?void 0:e.height)||36}},dt=t=>{const{width:r,height:e}=H(t);return{x:t.position.x+r/2,y:t.position.y+e/2}},Gt=(t,r)=>{if(t.length===0)return{nodes:t,width:0,height:0};let e=Number.POSITIVE_INFINITY,s=Number.POSITIVE_INFINITY,o=Number.NEGATIVE_INFINITY,i=Number.NEGATIVE_INFINITY;t.forEach(c=>{const{width:u,height:p}=H(c);e=Math.min(e,c.position.x),s=Math.min(s,c.position.y),o=Math.max(o,c.position.x+u),i=Math.max(i,c.position.y+p)});const h=r-e,a=r-s;t.forEach(c=>{c.position={x:c.position.x+h,y:c.position.y+a}});const d=o-e+r*2,l=i-s+r*2;return{nodes:t,width:d,height:l}},_t=(t,r,e,s)=>{if(!(t.length<2||s.size===0))if(r==="TB"||r==="BT"){const o=[...t].sort((i,h)=>i.position.x-h.position.x);o.forEach((i,h)=>{if(!s.has(i.id))return;let a=i.position.x+H(i).width;for(let d=h+1;d<o.length;d++){const l=o[d],{width:c}=H(l),u=a+e;l.position.x<u&&(l.position.x=u),a=l.position.x+c}})}else{const o=[...t].sort((i,h)=>i.position.y-h.position.y);o.forEach((i,h)=>{if(!s.has(i.id))return;let a=i.position.y+H(i).height;for(let d=h+1;d<o.length;d++){const l=o[d],{height:c}=H(l),u=a+e;l.position.y<u&&(l.position.y=u),a=l.position.y+c}})}},Ht=(t,r,e,s)=>{const o=new Map,i=new Map;r.forEach(a=>{const d=o.get(a.target)||[];d.push(a),o.set(a.target,d);const l=i.get(a.source)||[];l.push(a),i.set(a.source,l)});const h=new Map;return i.forEach((a,d)=>{const l=o.get(d)||[];l.length!==0&&l.forEach(c=>{a.forEach(u=>{if(c.source===u.target)return;const p=U(c.source,d,e,s).lca,N=U(d,u.target,e,s).lca;if(p!==t||N!==t)return;const C=z(c.source,t,e,s),L=z(u.target,t,e,s),g=z(d,t,e,s);if(!C||!L||!g||C!==L||C===g)return;const f=z(c.source,C,e,s),b=z(u.target,C,e,s);if(!f||!b||f===b)return;const y=`${C}:${g}:${f}:${b}`;h.set(y,{anchorContainerId:C,bridgeContainerId:g,sourceNodeId:f,targetNodeId:b})})})}),[...h.values()]},Ft=(t,r,e,s,o,i,h)=>{const a=Ht(r,o,i,h),d=new Set;return a.forEach(l=>{const c=lt(t,l.anchorContainerId),u=lt(t,l.bridgeContainerId);if(!c||!u)return;const p=ht(l.sourceNodeId,r,i),N=ht(l.targetNodeId,r,i);if(!p||!N)return;const{width:C,height:L}=H(u),{width:g,height:f}=H(c),b=dt(c),y=dt(u);if(e==="TB"||e==="BT"){const x=(p.y+N.y)/2;u.position.y=x-L/2;const w=y.x>=b.x,v=w?u.position.x-(c.position.x+g):c.position.x-(u.position.x+C),E=Math.max(s,v);u.position.x=w?c.position.x+g+E:c.position.x-C-E}else{const x=(p.x+N.x)/2;u.position.x=x-C/2;const w=y.y>=b.y,v=w?u.position.y-(c.position.y+f):c.position.y-(u.position.y+L),E=Math.max(s,v);u.position.y=w?c.position.y+f+E:c.position.y-L-E}d.add(u.id)}),d},ut=async(t,r,e,s,o,i=q,h=50,a=50,d=172,l=36,c=V,u=!1,p="no-parent")=>{const N=X(o,s,p),{updatedNodes:C}=await Y(t,r,e,s,o,i,h,a,d,l,c,u,N,p),L=s.get(t);if(!L)return{updatedNodes:C,updatedEdges:o};const{updatedNodes:g}=await ut(L.parentId||p,r,e,s,o,i,h,a,d,l,c,u,p);return{updatedNodes:[...g,...C],updatedEdges:o}},Y=async(t,r,e,s,o,i=q,h=50,a=50,d=172,l=36,c=V,u=!1,p=new Map,N="no-parent")=>{const C=e.get(t);if(!C||C.size===0)return{updatedNodes:[]};const L=[];if(C.forEach(E=>{const D=s.get(E);D&&L.push(D)}),L.length===0)return{updatedNodes:[]};let g=r;const f=s.get(t);f&&f.data.layoutDirection&&(g=f.data.layoutDirection);const b=p.get(t)||[],{nodes:y,edges:x}=await c(L,b,g,i,h,a,d,l,u);y.forEach(E=>{s.set(E.id,E)});const w=Ft(y,t,g,h,o,s,N);_t(y,g,h,w),y.forEach(E=>{s.set(E.id,E)});const v=Gt(y,i);return v.nodes.forEach(E=>{s.set(E.id,E)}),f&&v.width&&v.height&&Pt(f,v.width,v.height),{updatedNodes:[...v.nodes],udpatedParentNode:f||void 0}},Pt=(t,r,e)=>(t.style||(t.style={}),t.width=r,t.height=e,t.measured={width:r,height:e},t.style.width=r,t.style.height=e,t),Ut=async(t,r,e,s,o,i=q,h=50,a=50,d=172,l=36,c=V,u=!1,p="no-parent")=>{let N=[];const C=X(o,s,p),L=new Map;let g=0;const f=(y,x)=>{x>g&&(g=x);for(const w of y)L.has(x)||L.set(x,[]),L.get(x).push(w.id),w.children.length>0&&f(w.children,x+1)};f(t,0);for(let y=g;y>=0;y--){const x=L.get(y)||[],w=await Promise.all(x.map(v=>Y(v,r,e,s,o,i,h,a,d,l,c,u,C,p)));for(const{updatedNodes:v}of w)N=[...v,...N]}const{updatedNodes:b}=await Y(p,r,e,s,o,i,h,a,d,l,c,u,C,p);return N=[...b,...N],{updatedNodes:N,updatedEdges:o}},Vt=(t,r,e,s="no-parent")=>{const o=t.map(l=>l.id),i=t.map(l=>{var c;return((c=e.get(l.id))==null?void 0:c.parentId)||l.parentId}).filter(l=>!!l),a=Array.from(new Set([...o,...i])).filter(l=>r.has(l)),d=a.filter(l=>{const c=e.get(l);return c?!c.parentId||!a.includes(c.parentId):!0});return d.length===0?[s]:d},Yt=async(t,r,e)=>{const{dagreDirection:s,nodeParentIdMapWithChildIdSet:o,nodeIdWithNode:i,nodes:h,edges:a,margin:d,nodeSpacing:l,layerSpacing:c,nodeWidth:u,nodeHeight:p,layoutHidden:N=!1,noParentKey:C="no-parent"}=r,L=Vt(t,o,i,C);if(L.length===0)return{nodes:h,edges:a};const g=new Map,f=new Map;if(e!=null&&e.aborted)return{nodes:h,edges:a};(await Promise.all(L.map(async w=>e!=null&&e.aborted?{updatedNodes:[],updatedEdges:[]}:ut(w,s,o,i,a,d,l,c,u,p,void 0,N)))).forEach(({updatedNodes:w,updatedEdges:v})=>{w.forEach(E=>{g.set(E.id,E)}),v.forEach(E=>{f.set(E.id,E)})});const y=h.map(w=>g.has(w.id)?g.get(w.id):w),x=a.map(w=>f.has(w.id)?f.get(w.id):w);return{nodes:y,edges:x}},jt=(t,r,e,s,o,i,h,a,d=172,l=36,c=!1,u="no-parent")=>{const p=k.useCallback(async(C,L,g,f)=>{if(!(t[e]||t.dagre))return console.error(`Layout engine "${e}" not found`),{nodes:C,edges:L};const y=c?C:C.filter(S=>!S.hidden),x=new Set(y.map(S=>S.id)),w=c?L:L.filter(S=>x.has(S.source)&&x.has(S.target)),v=st(r),E=s.padding.horizontal;let D=[],G=[];if(g&&g.length>0){const S=c?g:g.filter(O=>!O.hidden),T=await Yt(S,{dagreDirection:v,nodeParentIdMapWithChildIdSet:o,nodeIdWithNode:i,nodes:y,edges:w,margin:E,nodeSpacing:h,layerSpacing:a,nodeWidth:d,nodeHeight:l,layoutHidden:c,noParentKey:u},f);D=T.nodes,G=T.edges}else{if(f!=null&&f.aborted)return{nodes:C,edges:L};const S=Ot(o,i,u),T=await Ut(S,v,o,i,w,E,h,a,d,l,void 0,c,u);D=T.updatedNodes,G=T.updatedEdges}return{nodes:C.map(S=>!c&&S.hidden?S:D.find(O=>O.id===S.id)||S),edges:G}},[e,r,t,s.padding.horizontal,o,i,h,a,d,l,c,u]),N=k.useCallback(async(C,L,g,f)=>{if(f!=null&&f.aborted)return{nodes:L,edges:g};const b=st(r),y=s.padding.horizontal,x=c?L:L.filter(T=>!T.hidden),w=new Set(x.map(T=>T.id)),v=c?g:g.filter(T=>w.has(T.source)&&w.has(T.target)),E=X(v,i,u),D=T=>{const O=[],A=o.get(T);if(A)for(const _ of A)o.has(_)&&O.push(...D(_));return O.push(T),O},G=D(C),m=new Map;for(const T of G){if(f!=null&&f.aborted)return{nodes:L,edges:g};const{updatedNodes:O,udpatedParentNode:A}=await Y(T,b,o,i,v,y,h,a,d,l,void 0,c,E,u);O.forEach(_=>m.set(_.id,_)),A&&m.set(A.id,A)}return{nodes:L.map(T=>m.get(T.id)??T),edges:g}},[r,s.padding.horizontal,o,i,h,a,d,l,c,u]);return{calculateLayout:p,calculateContainerLayout:N}};function It({children:t,initialDirection:r="DOWN",initialAlgorithm:e="layered",initialAutoLayout:s=!0,initialPadding:o=50,initialSpacing:i={node:50,layer:50},initialNodeDimensions:h={width:172,height:36},initialParentResizingOptions:a,includeHidden:d=!1,layoutEngines:l,updateNodes:c,updateEdges:u,nodeParentIdMapWithChildIdSet:p,nodeIdWithNode:N,noParentKey:C="no-parent",disableAutoLayoutEffect:L=!1}){const g=B.useNodes(),f=B.useEdges(),{direction:b,algorithm:y,autoLayout:x,layoutInProgress:w,layoutHidden:v,layoutEngines:E,layoutEngineOptions:D,padding:G,nodeSpacing:m,layerSpacing:S,nodeWidth:T,nodeHeight:O,parentResizingOptions:A,selectedNodes:_,setDirection:W,setAlgorithm:$,setAutoLayout:K,setLayoutInProgress:j,setLayoutHidden:R,setLayoutEngines:gt,setLayoutEngineOptions:qt,setPadding:Jt,setNodeSpacing:pt,setLayerSpacing:yt,setNodeWidth:Zt,setNodeHeight:Qt,setParentResizingOptionsState:xt,setSelectedNodes:wt}=St({initialDirection:r,initialAlgorithm:e,initialAutoLayout:s,initialPadding:o,initialSpacing:i,initialNodeDimensions:h,initialParentResizingOptions:a,includeHidden:d,layoutEngines:l}),{nodeIdWithNode:tt,nodeParentIdMapWithChildIdSet:bt,numberOfNodes:Wt,nodesLength:$t,childrenInitialized:Ct,parentChildStructure:Kt}=Mt({nodes:g,externalNodeIdWithNode:N,externalNodeParentIdMapWithChildIdSet:p,noParentKey:C}),Rt=k.useCallback(({nodes:F})=>{wt(F)},[wt]);B.useOnSelectionChange({onChange:Rt});const{calculateLayout:te,calculateContainerLayout:ee}=jt(E,b,y,A,bt,tt,m,S,T,O,v,C),{applyLayout:Lt,applyContainerLayout:re}=Dt({nodes:g,edges:f,selectedNodes:_,layoutHidden:v,calculateLayout:te,calculateContainerLayout:ee,updateNodes:c,updateEdges:u,setLayoutInProgress:j,setNodeSpacing:pt,setLayerSpacing:yt}),oe=k.useCallback((F,et)=>{gt(ie=>({...ie,[F]:et}))},[gt]),se=k.useCallback(F=>{xt(et=>({...et,...F,enabled:x}))},[x,xt]);k.useEffect(()=>{var F;L||tt.has((F=g[g.length-1])==null?void 0:F.id)&&Ct&&x&&Lt()},[Ct,x,b,Wt,m,S,Kt,$t]);const ne={direction:b,algorithm:y,autoLayout:x,layoutInProgress:w,padding:G,nodeSpacing:m,layerSpacing:S,nodeWidth:T,nodeHeight:O,layoutHidden:v,parentResizingOptions:A,layoutEngines:E,layoutEngineOptions:D,nodeParentIdMapWithChildIdSet:bt,nodeIdWithNode:tt,noParentKey:C,updateNodes:c,updateEdges:u,setDirection:W,setAlgorithm:$,setAutoLayout:K,setLayoutInProgress:j,setPadding:Jt,setNodeSpacing:pt,setLayerSpacing:yt,setNodeWidth:Zt,setNodeHeight:Qt,setLayoutHidden:R,setParentResizingOptions:se,setLayoutEngineOptions:qt,applyLayout:Lt,applyContainerLayout:re,clearLayoutCache:()=>{},registerLayoutEngine:oe};return n.jsx(ot.Provider,{value:ne,children:t})}const J=({compact:t=!1})=>{const{direction:r,setDirection:e,clearLayoutCache:s}=P(),o=i=>{e(i),s&&s()};return t?n.jsx(B.ControlButton,{onClick:()=>o(r==="DOWN"?"RIGHT":"DOWN"),title:`Switch to ${r==="DOWN"?"horizontal":"vertical"} layout`,children:n.jsx("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"2",fill:"none",children:r==="DOWN"?n.jsx("path",{d:"M4 12h16M16 6l6 6-6 6"}):n.jsx("path",{d:"M12 4v16M6 16l6 6 6-6"})})}):n.jsxs(n.Fragment,{children:[n.jsx(B.ControlButton,{onClick:()=>o("DOWN"),className:r==="DOWN"?"selected":"",title:"Top to Bottom",children:n.jsx("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"2",fill:"none",children:n.jsx("path",{d:"M12 4v16M6 16l6 6 6-6"})})}),n.jsx(B.ControlButton,{onClick:()=>o("RIGHT"),className:r==="RIGHT"?"selected":"",title:"Left to Right",children:n.jsx("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"2",fill:"none",children:n.jsx("path",{d:"M4 12h16M16 6l6 6-6 6"})})}),n.jsx(B.ControlButton,{onClick:()=>o("LEFT"),className:r==="LEFT"?"selected":"",title:"Right to Left",children:n.jsx("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"2",fill:"none",children:n.jsx("path",{d:"M20 12H4M8 6L2 12l6 6"})})}),n.jsx(B.ControlButton,{onClick:()=>o("UP"),className:r==="UP"?"selected":"",title:"Bottom to Top",children:n.jsx("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"2",fill:"none",children:n.jsx("path",{d:"M12 20V4M6 8l6-6 6 6"})})})]})},Z=({compact:t=!1})=>{const{nodeSpacing:r,layerSpacing:e,setNodeSpacing:s,setLayerSpacing:o,clearLayoutCache:i}=P(),[h,a]=k.useState(!1),d=c=>{s(c),i&&i()},l=c=>{o(c),i&&i()};return t?n.jsxs("div",{style:{position:"relative"},children:[n.jsx(B.ControlButton,{onClick:()=>a(!h),title:"Adjust node and layer spacing",children:n.jsxs("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"2",fill:"none",children:[n.jsx("rect",{x:"3",y:"3",width:"7",height:"7",rx:"1"}),n.jsx("rect",{x:"14",y:"3",width:"7",height:"7",rx:"1"}),n.jsx("rect",{x:"3",y:"14",width:"7",height:"7",rx:"1"}),n.jsx("rect",{x:"14",y:"14",width:"7",height:"7",rx:"1"}),n.jsx("path",{d:"M7 10v4M17 10v4M10 7h4M10 17h4"})]})}),h&&n.jsxs("div",{className:"react-flow-spacing-dropdown",style:{position:"absolute",right:"0",top:"100%",marginTop:"8px",background:"white",border:"1px solid #ddd",borderRadius:"4px",padding:"8px",boxShadow:"0 2px 10px rgba(0,0,0,0.1)",zIndex:10,minWidth:"200px"},children:[n.jsxs("div",{style:{marginBottom:"12px"},children:[n.jsxs("div",{style:{fontWeight:"bold",marginBottom:"4px"},children:["Node Spacing: ",r,"px"]}),n.jsx("div",{style:{display:"flex",gap:"4px"},children:[50,100,150,200].map(c=>n.jsx("button",{onClick:()=>d(c),style:{background:r===c?"#0041d0":"#f5f5f5",color:r===c?"white":"inherit",border:"1px solid #ddd",borderRadius:"4px",padding:"4px 8px",cursor:"pointer"},children:c},c))})]}),n.jsxs("div",{children:[n.jsxs("div",{style:{fontWeight:"bold",marginBottom:"4px"},children:["Layer Spacing: ",e,"px"]}),n.jsx("div",{style:{display:"flex",gap:"4px"},children:[50,100,150,200].map(c=>n.jsx("button",{onClick:()=>l(c),style:{background:e===c?"#0041d0":"#f5f5f5",color:e===c?"white":"inherit",border:"1px solid #ddd",borderRadius:"4px",padding:"4px 8px",cursor:"pointer"},children:c},c))})]}),n.jsx("div",{style:{marginTop:"12px",textAlign:"right"},children:n.jsx("button",{onClick:()=>a(!1),style:{background:"#f5f5f5",border:"1px solid #ddd",borderRadius:"4px",padding:"4px 8px",cursor:"pointer"},children:"Close"})})]})]}):n.jsxs("div",{className:"spacing-controls",children:[n.jsxs(B.ControlButton,{onClick:()=>d(Math.max(50,r-25)),title:"Decrease node spacing",children:[n.jsxs("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"2",fill:"none",children:[n.jsx("rect",{x:"3",y:"3",width:"7",height:"7",rx:"1"}),n.jsx("rect",{x:"14",y:"3",width:"7",height:"7",rx:"1"}),n.jsx("rect",{x:"3",y:"14",width:"7",height:"7",rx:"1"}),n.jsx("rect",{x:"14",y:"14",width:"7",height:"7",rx:"1"}),n.jsx("line",{x1:"7",y1:"10",x2:"7",y2:"14"}),n.jsx("line",{x1:"17",y1:"10",x2:"17",y2:"14"}),n.jsx("line",{x1:"10",y1:"7",x2:"14",y2:"7"}),n.jsx("line",{x1:"10",y1:"17",x2:"14",y2:"17"})]}),n.jsx("span",{style:{marginLeft:"4px"},children:"-"})]}),n.jsxs("div",{style:{display:"inline-flex",alignItems:"center",padding:"5px 10px",fontSize:"12px",background:"#f8f8f8",borderRadius:"4px",margin:"0 4px",border:"1px solid #ddd"},children:[r,"px"]}),n.jsxs(B.ControlButton,{onClick:()=>d(Math.min(300,r+25)),title:"Increase node spacing",children:[n.jsxs("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"2",fill:"none",children:[n.jsx("rect",{x:"3",y:"3",width:"7",height:"7",rx:"1"}),n.jsx("rect",{x:"14",y:"3",width:"7",height:"7",rx:"1"}),n.jsx("rect",{x:"3",y:"14",width:"7",height:"7",rx:"1"}),n.jsx("rect",{x:"14",y:"14",width:"7",height:"7",rx:"1"}),n.jsx("line",{x1:"7",y1:"10",x2:"7",y2:"14"}),n.jsx("line",{x1:"17",y1:"10",x2:"17",y2:"14"}),n.jsx("line",{x1:"10",y1:"7",x2:"14",y2:"7"}),n.jsx("line",{x1:"10",y1:"17",x2:"14",y2:"17"})]}),n.jsx("span",{style:{marginLeft:"4px"},children:"+"})]}),n.jsx("div",{style:{display:"inline-block",width:"1px",height:"24px",background:"#ddd",margin:"0 10px"}}),n.jsxs(B.ControlButton,{onClick:()=>l(Math.max(50,e-25)),title:"Decrease layer spacing",children:[n.jsxs("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"2",fill:"none",children:[n.jsx("line",{x1:"4",y1:"6",x2:"20",y2:"6"}),n.jsx("line",{x1:"4",y1:"12",x2:"20",y2:"12"}),n.jsx("line",{x1:"4",y1:"18",x2:"20",y2:"18"})]}),n.jsx("span",{style:{marginLeft:"4px"},children:"-"})]}),n.jsxs("div",{style:{display:"inline-flex",alignItems:"center",padding:"5px 10px",fontSize:"12px",background:"#f8f8f8",borderRadius:"4px",margin:"0 4px",border:"1px solid #ddd"},children:[e,"px"]}),n.jsxs(B.ControlButton,{onClick:()=>l(Math.min(300,e+25)),title:"Increase layer spacing",children:[n.jsxs("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"2",fill:"none",children:[n.jsx("line",{x1:"4",y1:"6",x2:"20",y2:"6"}),n.jsx("line",{x1:"4",y1:"12",x2:"20",y2:"12"}),n.jsx("line",{x1:"4",y1:"18",x2:"20",y2:"18"})]}),n.jsx("span",{style:{marginLeft:"4px"},children:"+"})]})]})},Q=({compact:t=!1})=>{const{autoLayout:r,setAutoLayout:e}=P(),s=()=>{e(!r)};return n.jsx(B.ControlButton,{onClick:s,className:r?"selected":"",title:r?"Disable automatic layout":"Enable automatic layout",style:{color:r?"green":"inherit"},children:t?n.jsxs("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"1.5",fill:"none",children:[n.jsx("rect",{x:"4",y:"4",width:"4",height:"4",rx:"1"}),n.jsx("rect",{x:"4",y:"16",width:"4",height:"4",rx:"1"}),n.jsx("rect",{x:"16",y:"10",width:"4",height:"4",rx:"1"}),n.jsxs(n.Fragment,{children:[n.jsx("path",{d:"M8 6c6 0 3 8 8 6"}),n.jsx("path",{d:"M8 18c3-3 2-8 6-6"})]})]}):n.jsxs(n.Fragment,{children:["Auto-Layout ",r?"ON":"OFF"]})})},ft=({compact:t=!1})=>{const{layoutInProgress:r,setLayoutInProgress:e,applyLayout:s}=P(),o=async()=>{if(!r)try{e(!0),await s()}catch(i){console.error("Error applying layout:",i)}};return n.jsxs(B.ControlButton,{onClick:o,title:"Apply layout",disabled:r,style:{color:r?"#999":"#000"},children:[n.jsxs("svg",{viewBox:"0 0 24 24",width:"16",height:"16",stroke:"currentColor",strokeWidth:"1.5",fill:"none",children:[n.jsx("rect",{x:"3",y:"3",width:"5",height:"5",rx:"1"}),n.jsx("rect",{x:"16",y:"3",width:"5",height:"5",rx:"1"}),n.jsx("rect",{x:"3",y:"16",width:"5",height:"5",rx:"1"}),n.jsx("rect",{x:"16",y:"16",width:"5",height:"5",rx:"1"}),n.jsx("path",{d:"M8 5.5h8M5.5 8v8M18.5 8v8M8 18.5h8"}),n.jsx("circle",{cx:"12",cy:"12",r:"3"}),n.jsx("path",{d:"M11 10.5v3l2-1.5z",fill:"currentColor"})]}),!t&&n.jsx("span",{style:{marginLeft:"4px"},children:"Apply Layout"})]})},Xt=({showDirectionControls:t=!0,showSpacingControls:r=!0,showAutoLayoutToggle:e=!0,showApplyLayoutButton:s=!0,standalone:o=!1,position:i="top-right"})=>{const h=P(),{autoLayout:a}=h;return o?n.jsxs(B.Controls,{position:i,showZoom:!1,showFitView:!1,showInteractive:!1,children:[s&&!a&&n.jsx(ft,{compact:!0}),t&&n.jsx(J,{compact:!0}),r&&n.jsx(Z,{compact:!0}),e&&n.jsx(Q,{compact:!0})]}):n.jsxs(n.Fragment,{children:[s&&!a&&n.jsx(ft,{compact:!0}),t&&n.jsx(J,{compact:!0}),r&&n.jsx(Z,{compact:!0}),e&&n.jsx(Q,{compact:!0})]})};M.AutoLayoutToggle=Q,M.DagreEngine=it,M.DirectionControls=J,M.LayoutControls=Xt,M.LayoutProvider=It,M.SpacingControls=Z,M.engines=ct,M.useLayoutContext=P,Object.defineProperty(M,Symbol.toStringTag,{value:"Module"})});
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ interface LayoutControlsProps {
3
+ showDirectionControls?: boolean;
4
+ showSpacingControls?: boolean;
5
+ showAutoLayoutToggle?: boolean;
6
+ showApplyLayoutButton?: boolean;
7
+ standalone?: boolean;
8
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
9
+ }
10
+ /**
11
+ * Layout controls component that provides UI for controlling layout options
12
+ * Can be used standalone or integrated with React Flow Controls
13
+ */
14
+ declare const LayoutControls: React.FC<LayoutControlsProps>;
15
+ export default LayoutControls;
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ interface ApplyLayoutButtonProps {
3
+ compact?: boolean;
4
+ }
5
+ declare const ApplyLayoutButton: React.FC<ApplyLayoutButtonProps>;
6
+ export default ApplyLayoutButton;
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ interface AutoLayoutToggleProps {
3
+ compact?: boolean;
4
+ }
5
+ declare const AutoLayoutToggle: React.FC<AutoLayoutToggleProps>;
6
+ export default AutoLayoutToggle;
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ interface DirectionControlsProps {
3
+ compact?: boolean;
4
+ }
5
+ declare const DirectionControls: React.FC<DirectionControlsProps>;
6
+ export default DirectionControls;
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ interface SpacingControlsProps {
3
+ compact?: boolean;
4
+ }
5
+ declare const SpacingControls: React.FC<SpacingControlsProps>;
6
+ export default SpacingControls;
@@ -0,0 +1,66 @@
1
+ import { Edge, Node } from '@xyflow/react';
2
+ export type LayoutDirection = "DOWN" | "RIGHT" | "LEFT" | "UP";
3
+ export type LayoutAlgorithm = "layered" | "mrtree";
4
+ export interface LayoutEngine {
5
+ calculate: (nodes: Node[], edges: Edge[], options: Record<string, any>) => Promise<{
6
+ nodes: Node[];
7
+ edges: Edge[];
8
+ width?: number;
9
+ height?: number;
10
+ }>;
11
+ }
12
+ export interface ParentResizingOptions {
13
+ enabled: boolean;
14
+ padding: {
15
+ horizontal: number;
16
+ vertical: number;
17
+ };
18
+ respectHeaderHeight: boolean;
19
+ minWidth?: number;
20
+ minHeight?: number;
21
+ }
22
+ export interface LayoutContextState {
23
+ direction: LayoutDirection;
24
+ algorithm: LayoutAlgorithm;
25
+ autoLayout: boolean;
26
+ layoutInProgress: boolean;
27
+ padding: number;
28
+ nodeSpacing: number;
29
+ layerSpacing: number;
30
+ nodeWidth: number;
31
+ nodeHeight: number;
32
+ layoutHidden: boolean;
33
+ parentResizingOptions: ParentResizingOptions;
34
+ layoutEngines: Record<string, LayoutEngine>;
35
+ layoutEngineOptions: Record<string, any>;
36
+ nodeParentIdMapWithChildIdSet: Map<string, Set<string>>;
37
+ nodeIdWithNode: Map<string, Node>;
38
+ noParentKey: string;
39
+ updateNodes?: (nodes: Node[]) => void;
40
+ updateEdges?: (edges: Edge[]) => void;
41
+ setDirection: (direction: LayoutDirection) => void;
42
+ setAlgorithm: (algorithm: LayoutAlgorithm) => void;
43
+ setAutoLayout: (auto: boolean) => void;
44
+ setLayoutInProgress: (inProgress: boolean) => void;
45
+ setPadding: (padding: number) => void;
46
+ setNodeSpacing: (spacing: number) => void;
47
+ setLayerSpacing: (spacing: number) => void;
48
+ setNodeWidth: (width: number) => void;
49
+ setNodeHeight: (height: number) => void;
50
+ setLayoutHidden: (hidden: boolean) => void;
51
+ setParentResizingOptions: (options: Partial<ParentResizingOptions>) => void;
52
+ setLayoutEngineOptions: (options: Record<string, any>) => void;
53
+ applyLayout: (nodes?: Node[], edges?: Edge[]) => Promise<{
54
+ nodes: Node[];
55
+ edges: Edge[];
56
+ }> | undefined;
57
+ applyContainerLayout: (containerId: string) => Promise<{
58
+ nodes: Node[];
59
+ edges: Edge[];
60
+ }>;
61
+ clearLayoutCache: () => void;
62
+ registerLayoutEngine: (name: string, engine: LayoutEngine) => void;
63
+ }
64
+ declare const LayoutContext: import("react").Context<LayoutContextState | undefined>;
65
+ export declare function useLayoutContext(): LayoutContextState;
66
+ export default LayoutContext;
@@ -0,0 +1,29 @@
1
+ import { ReactNode } from 'react';
2
+ import { Edge, Node } from '@xyflow/react';
3
+ import { LayoutAlgorithm, LayoutDirection, LayoutEngine, ParentResizingOptions, useLayoutContext } from './LayoutContext';
4
+ interface LayoutProviderProps {
5
+ children: ReactNode;
6
+ initialDirection?: LayoutDirection;
7
+ initialAlgorithm?: LayoutAlgorithm;
8
+ initialAutoLayout?: boolean;
9
+ initialPadding?: number;
10
+ initialSpacing?: {
11
+ node?: number;
12
+ layer?: number;
13
+ };
14
+ initialNodeDimensions?: {
15
+ width?: number;
16
+ height?: number;
17
+ };
18
+ initialParentResizingOptions?: Partial<ParentResizingOptions>;
19
+ includeHidden?: boolean;
20
+ layoutEngines?: Record<string, LayoutEngine>;
21
+ updateNodes?: (nodes: Node[]) => void;
22
+ updateEdges?: (edges: Edge[]) => void;
23
+ nodeParentIdMapWithChildIdSet?: Map<string, Set<string>>;
24
+ nodeIdWithNode?: Map<string, Node>;
25
+ noParentKey?: string;
26
+ disableAutoLayoutEffect?: boolean;
27
+ }
28
+ export declare function LayoutProvider({ children, initialDirection, initialAlgorithm, initialAutoLayout, initialPadding, initialSpacing, initialNodeDimensions, initialParentResizingOptions, includeHidden, layoutEngines: customEngines, updateNodes, updateEdges, nodeParentIdMapWithChildIdSet: externalNodeParentIdMapWithChildIdSet, nodeIdWithNode: externalNodeIdWithNode, noParentKey, disableAutoLayoutEffect, }: LayoutProviderProps): import("react/jsx-runtime").JSX.Element;
29
+ export { useLayoutContext };
@@ -0,0 +1,9 @@
1
+ import { Edge, Node } from "@xyflow/react";
2
+ import { Direction } from "./HierarchicalLayoutOrganizer";
3
+ export interface LayoutResult {
4
+ nodes: Node[];
5
+ edges: Edge[];
6
+ width: number;
7
+ height: number;
8
+ }
9
+ export declare const calculateLayoutWithDagre: (nodes: Node[], edges: Edge[], direction: Direction, margin?: number, nodeSpacing?: number, layerSpacing?: number, defaultNodeWidth?: number, defaultNodeHeight?: number, includeHidden?: boolean) => Promise<LayoutResult>;
@@ -0,0 +1,78 @@
1
+ import { Edge, Node } from "@xyflow/react";
2
+ import { TreeNode } from "../utils/treeUtils";
3
+ export type Direction = 'TB' | 'LR' | 'RL' | 'BT';
4
+ /**
5
+ * @function organizeLayoutRecursively
6
+ * Recursively organizes the layout of a container and its parent hierarchy.
7
+ * Starts with the specified node and works upward through the parent chain.
8
+ *
9
+ * @param parentNodeId - The parent node to start layout from
10
+ * @param direction - The direction of the layout
11
+ * @param nodeParentIdMapWithChildIdSet - A map of parent node ids to their set of child IDs
12
+ * @param nodeIdWithNode - A map of node ids to their nodes
13
+ * @param edges - The edges to layout
14
+ * @param margin - The margin to use for the layout
15
+ * @param nodeSpacing - Spacing between nodes (horizontal within same rank)
16
+ * @param layerSpacing - Spacing between layers (vertical between ranks)
17
+ * @param defaultNodeWidth - Default width for nodes without explicit width
18
+ * @param defaultNodeHeight - Default height for nodes without explicit height
19
+ * @param includeHidden - Whether to include hidden nodes in the layout
20
+ * @param noParentKey - Key used to represent nodes without a parent
21
+ * @returns Promise<{ updatedNodes: Node[], updatedEdges: Edge[] }>
22
+ */
23
+ export declare const organizeLayoutRecursively: (parentNodeId: string, direction: Direction, nodeParentIdMapWithChildIdSet: Map<string, Set<string>>, nodeIdWithNode: Map<string, Node>, edges: Edge[], margin?: number, nodeSpacing?: number, layerSpacing?: number, defaultNodeWidth?: number, defaultNodeHeight?: number, LayoutAlgorithm?: (nodes: Node[], edges: Edge[], direction: Direction, margin?: number, nodeSpacing?: number, layerSpacing?: number, defaultNodeWidth?: number, defaultNodeHeight?: number, includeHidden?: boolean) => Promise<import("./Dagre").LayoutResult>, includeHidden?: boolean, noParentKey?: string) => Promise<{
24
+ updatedNodes: Node[];
25
+ updatedEdges: Edge[];
26
+ }>;
27
+ /**
28
+ * @function layoutSingleContainer
29
+ * Organizes the layout of a single container and its immediate child nodes.
30
+ * By default, it uses the Dagre layout algorithm or the provided algorithm to calculate the positions of the nodes.
31
+ * The function updates the dimensions of the parent node and its child nodes.
32
+ * It also updates the edges of the layout.
33
+ *
34
+ * @param parentNodeId - The parent node to layout
35
+ * @param direction - The direction of the layout
36
+ * @param nodeParentIdMapWithChildIdSet - A map of parent node ids to their set of child IDs
37
+ * @param nodeIdWithNode - A map of node ids to their nodes
38
+ * @param edges - The edges to layout
39
+ * @param margin - The margin to use for the layout
40
+ * @param nodeSpacing - Spacing between nodes
41
+ * @param layerSpacing - Spacing between layers
42
+ * @param defaultNodeWidth - Default width for nodes without explicit width
43
+ * @param defaultNodeHeight - Default height for nodes without explicit height
44
+ * @param LayoutAlgorithm - The layout algorithm to use
45
+ * @param includeHidden - Whether to include hidden nodes in the layout
46
+ * @returns Promise<{ updatedNodes: Node[], udpatedParentNode?: Node }>
47
+ */
48
+ export declare const layoutSingleContainer: (parentNodeId: string, defaultDirection: Direction, nodeParentIdMapWithChildIdSet: Map<string, Set<string>>, nodeIdWithNode: Map<string, Node>, _originalEdges: Edge[], margin?: number, nodeSpacing?: number, layerSpacing?: number, defaultNodeWidth?: number, defaultNodeHeight?: number, LayoutAlgorithm?: (nodes: Node[], edges: Edge[], direction: Direction, margin?: number, nodeSpacing?: number, layerSpacing?: number, defaultNodeWidth?: number, defaultNodeHeight?: number, includeHidden?: boolean) => Promise<import("./Dagre").LayoutResult>, includeHidden?: boolean, temporaryEdgesByParent?: Map<string, Edge[]>, noParentKey?: string) => Promise<{
49
+ updatedNodes: Node[];
50
+ udpatedParentNode?: Node;
51
+ }>;
52
+ export declare const fixParentNodeDimensions: (node: Node, width: number, height: number) => Node;
53
+ /**
54
+ * @function organizeLayoutByTreeDepth
55
+ * Processes parent nodes in order from deepest to shallowest, ensuring proper layout calculation.
56
+ * This function traverses the parent tree and calls layoutSingleContainer for each parent,
57
+ * starting with the deepest children and working up to the root node(s).
58
+ * Finally processes the custom noParentKey node to handle root-level elements.
59
+ *
60
+ * @param parentTree - The tree structure of parent nodes
61
+ * @param direction - The direction of the layout
62
+ * @param nodeParentIdMapWithChildIdSet - A map of parent node ids to their set of child IDs
63
+ * @param nodeIdWithNode - A map of node ids to their nodes
64
+ * @param edges - The edges to layout
65
+ * @param margin - The margin to use for the layout
66
+ * @param nodeSpacing - Spacing between nodes (horizontal within same rank)
67
+ * @param layerSpacing - Spacing between layers (vertical between ranks)
68
+ * @param defaultNodeWidth - Default width for nodes without explicit width
69
+ * @param defaultNodeHeight - Default height for nodes without explicit height
70
+ * @param LayoutAlgorithm - The layout algorithm to use
71
+ * @param includeHidden - Whether to include hidden nodes in the layout
72
+ * @param noParentKey - Key used to represent nodes without a parent
73
+ * @returns Promise<{ updatedNodes: Node[], updatedEdges: Edge[] }>
74
+ */
75
+ export declare const organizeLayoutByTreeDepth: (parentTree: TreeNode[], direction: Direction, nodeParentIdMapWithChildIdSet: Map<string, Set<string>>, nodeIdWithNode: Map<string, Node>, edges: Edge[], margin?: number, nodeSpacing?: number, layerSpacing?: number, defaultNodeWidth?: number, defaultNodeHeight?: number, LayoutAlgorithm?: (nodes: Node[], edges: Edge[], direction: Direction, margin?: number, nodeSpacing?: number, layerSpacing?: number, defaultNodeWidth?: number, defaultNodeHeight?: number, includeHidden?: boolean) => Promise<import("./Dagre").LayoutResult>, includeHidden?: boolean, noParentKey?: string) => Promise<{
76
+ updatedNodes: Node[];
77
+ updatedEdges: Edge[];
78
+ }>;
@@ -0,0 +1,9 @@
1
+ import { LayoutEngine } from '../context/LayoutContext';
2
+ /**
3
+ * Adapter for the existing Dagre implementation.
4
+ * This wraps the original calculateLayoutWithDagre function to match the LayoutEngine interface
5
+ */
6
+ export declare const DagreEngine: LayoutEngine;
7
+ export declare const engines: {
8
+ dagre: LayoutEngine;
9
+ };
@@ -0,0 +1,36 @@
1
+ import { Node, Edge } from '@xyflow/react';
2
+ import { LayoutDirection, LayoutEngine } from '../context/LayoutContext';
3
+ export interface LayoutConfig {
4
+ dagreDirection: string;
5
+ nodeParentIdMapWithChildIdSet: Map<string, Set<string>>;
6
+ nodeIdWithNode: Map<string, Node>;
7
+ nodes: Node[];
8
+ edges: Edge[];
9
+ margin: number;
10
+ nodeSpacing: number;
11
+ layerSpacing: number;
12
+ nodeWidth: number;
13
+ nodeHeight: number;
14
+ layoutHidden?: boolean;
15
+ noParentKey?: string;
16
+ }
17
+ /**
18
+ * Process selected node IDs for layout calculations (refactored to use LayoutConfig)
19
+ */
20
+ export declare const processSelectedNodes: (selectedNodes: Node[], config: LayoutConfig, signal?: AbortSignal) => Promise<{
21
+ nodes: Node[];
22
+ edges: Edge[];
23
+ }>;
24
+ /**
25
+ * Hook for layout calculation functionality
26
+ */
27
+ export declare const useLayoutCalculation: (layoutEngines: Record<string, LayoutEngine>, direction: LayoutDirection, algorithm: string, parentResizingOptions: any, nodeParentIdMapWithChildIdSet: Map<string, Set<string>>, nodeIdWithNode: Map<string, Node>, nodeSpacing: number, layerSpacing: number, nodeWidth?: number, nodeHeight?: number, layoutHidden?: boolean, noParentKey?: string) => {
28
+ calculateLayout: (nodes: Node[], edges: Edge[], selectedNodes?: Node[], signal?: AbortSignal) => Promise<{
29
+ nodes: Node[];
30
+ edges: Edge[];
31
+ }>;
32
+ calculateContainerLayout: (containerId: string, nodes: Node[], edges: Edge[], signal?: AbortSignal) => Promise<{
33
+ nodes: Node[];
34
+ edges: Edge[];
35
+ }>;
36
+ };
@@ -0,0 +1,35 @@
1
+ import { Edge, Node } from '@xyflow/react';
2
+ interface UseLayoutOperationsProps {
3
+ nodes: Node[];
4
+ edges: Edge[];
5
+ selectedNodes: Node[];
6
+ layoutHidden: boolean;
7
+ calculateLayout: (nodes: Node[], edges: Edge[], selectedNodes: Node[], signal: AbortSignal) => Promise<{
8
+ nodes: Node[];
9
+ edges: Edge[];
10
+ }>;
11
+ calculateContainerLayout: (containerId: string, nodes: Node[], edges: Edge[], signal?: AbortSignal) => Promise<{
12
+ nodes: Node[];
13
+ edges: Edge[];
14
+ }>;
15
+ updateNodes?: (nodes: Node[]) => void;
16
+ updateEdges?: (edges: Edge[]) => void;
17
+ setLayoutInProgress: (inProgress: boolean) => void;
18
+ setNodeSpacing: (spacing: number) => void;
19
+ setLayerSpacing: (spacing: number) => void;
20
+ }
21
+ export declare function useLayoutOperations({ nodes, edges, selectedNodes, layoutHidden, calculateLayout, calculateContainerLayout, updateNodes, updateEdges, setLayoutInProgress, setNodeSpacing, setLayerSpacing }: UseLayoutOperationsProps): {
22
+ applyLayout: (inputNodes?: Node[], inputEdges?: Edge[]) => Promise<{
23
+ nodes: Node[];
24
+ edges: Edge[];
25
+ }>;
26
+ applyContainerLayout: (containerId: string) => Promise<{
27
+ nodes: Node[];
28
+ edges: Edge[];
29
+ }>;
30
+ pendingSpacingUpdateRef: import("react").RefObject<{
31
+ node?: number;
32
+ layer?: number;
33
+ } | null>;
34
+ };
35
+ export {};
@@ -0,0 +1,50 @@
1
+ import { Node } from '@xyflow/react';
2
+ import { LayoutAlgorithm, LayoutDirection, LayoutEngine, ParentResizingOptions } from '../context/LayoutContext';
3
+ interface UseLayoutStateProps {
4
+ initialDirection?: LayoutDirection;
5
+ initialAlgorithm?: LayoutAlgorithm;
6
+ initialAutoLayout?: boolean;
7
+ initialPadding?: number;
8
+ initialSpacing?: {
9
+ node?: number;
10
+ layer?: number;
11
+ };
12
+ initialNodeDimensions?: {
13
+ width?: number;
14
+ height?: number;
15
+ };
16
+ initialParentResizingOptions?: Partial<ParentResizingOptions>;
17
+ includeHidden?: boolean;
18
+ layoutEngines?: Record<string, LayoutEngine>;
19
+ }
20
+ export declare function useLayoutState({ initialDirection, initialAlgorithm, initialAutoLayout, initialPadding, initialSpacing, initialNodeDimensions, initialParentResizingOptions, includeHidden, layoutEngines: customEngines, }: UseLayoutStateProps): {
21
+ direction: LayoutDirection;
22
+ algorithm: LayoutAlgorithm;
23
+ autoLayout: boolean;
24
+ layoutInProgress: boolean;
25
+ layoutHidden: boolean;
26
+ layoutEngines: Record<string, LayoutEngine>;
27
+ layoutEngineOptions: Record<string, any>;
28
+ padding: number;
29
+ nodeSpacing: number;
30
+ layerSpacing: number;
31
+ nodeWidth: number;
32
+ nodeHeight: number;
33
+ parentResizingOptions: ParentResizingOptions;
34
+ selectedNodes: Node[];
35
+ setDirection: import("react").Dispatch<import("react").SetStateAction<LayoutDirection>>;
36
+ setAlgorithm: import("react").Dispatch<import("react").SetStateAction<LayoutAlgorithm>>;
37
+ setAutoLayout: import("react").Dispatch<import("react").SetStateAction<boolean>>;
38
+ setLayoutInProgress: import("react").Dispatch<import("react").SetStateAction<boolean>>;
39
+ setLayoutHidden: import("react").Dispatch<import("react").SetStateAction<boolean>>;
40
+ setLayoutEngines: import("react").Dispatch<import("react").SetStateAction<Record<string, LayoutEngine>>>;
41
+ setLayoutEngineOptions: import("react").Dispatch<import("react").SetStateAction<Record<string, any>>>;
42
+ setPadding: import("react").Dispatch<import("react").SetStateAction<number>>;
43
+ setNodeSpacing: import("react").Dispatch<import("react").SetStateAction<number>>;
44
+ setLayerSpacing: import("react").Dispatch<import("react").SetStateAction<number>>;
45
+ setNodeWidth: import("react").Dispatch<import("react").SetStateAction<number>>;
46
+ setNodeHeight: import("react").Dispatch<import("react").SetStateAction<number>>;
47
+ setParentResizingOptionsState: import("react").Dispatch<import("react").SetStateAction<ParentResizingOptions>>;
48
+ setSelectedNodes: import("react").Dispatch<import("react").SetStateAction<Node[]>>;
49
+ };
50
+ export {};
@@ -0,0 +1,16 @@
1
+ import { Node } from '@xyflow/react';
2
+ interface UseNodeMapsProps {
3
+ nodes: Node[];
4
+ externalNodeIdWithNode?: Map<string, Node>;
5
+ externalNodeParentIdMapWithChildIdSet?: Map<string, Set<string>>;
6
+ noParentKey?: string;
7
+ }
8
+ export declare function useNodeMaps({ nodes, externalNodeIdWithNode, externalNodeParentIdMapWithChildIdSet, noParentKey }: UseNodeMapsProps): {
9
+ nodeIdWithNode: Map<string, Node>;
10
+ nodeParentIdMapWithChildIdSet: Map<string, Set<string>>;
11
+ numberOfNodes: number;
12
+ nodesLength: number;
13
+ childrenInitialized: boolean;
14
+ parentChildStructure: Record<string, number>;
15
+ };
16
+ export {};
@@ -0,0 +1,9 @@
1
+ import { LayoutProvider, useLayoutContext } from './context/LayoutProvider';
2
+ import LayoutControls from './components/LayoutControls';
3
+ import DirectionControls from './components/controls/DirectionControls';
4
+ import SpacingControls from './components/controls/SpacingControls';
5
+ import AutoLayoutToggle from './components/controls/AutoLayoutToggle';
6
+ import { DagreEngine, engines } from './engines';
7
+ import type { LayoutDirection, LayoutAlgorithm, LayoutEngine, LayoutContextState, ParentResizingOptions } from './context/LayoutContext';
8
+ export { LayoutProvider, LayoutControls, useLayoutContext, DirectionControls, SpacingControls, AutoLayoutToggle, DagreEngine, engines, };
9
+ export type { LayoutDirection, LayoutAlgorithm, LayoutEngine, LayoutContextState, ParentResizingOptions };
@@ -0,0 +1,13 @@
1
+ import { Node } from "@xyflow/react";
2
+ /**
3
+ * Filters selected node IDs to only include parent nodes and removes redundant parent selections
4
+ * (parents that would include changes from other selected parents)
5
+ *
6
+ * @param selectedNodeIds Array of node IDs that were selected
7
+ * @param nodeParentIdMapWithChildIdSet Map of parent IDs to their set of child IDs
8
+ * @param nodeIdWithNode Map of node IDs to nodes
9
+ * @param noParentKey Key used to represent nodes without a parent
10
+ * @returns Array of filtered parent node IDs that should be processed
11
+ */
12
+ declare const filterSelectedParentNodes: (selectedNodes: Node[], nodeParentIdMapWithChildIdSet: Map<string, Set<string>>, nodeIdWithNode: Map<string, Node>, noParentKey?: string) => string[];
13
+ export default filterSelectedParentNodes;
@@ -0,0 +1,36 @@
1
+ import { Edge, Node, Position } from '@xyflow/react';
2
+ import { LayoutDirection } from '../context/LayoutContext';
3
+ import { Direction } from '../core/HierarchicalLayoutOrganizer';
4
+ /**
5
+ * Convert direction from our API format to the format expected by layout engines
6
+ */
7
+ export declare const convertDirection: (dir: LayoutDirection) => Direction;
8
+ export declare const convertDirectionToLayout: (dir: Direction) => LayoutDirection;
9
+ /**
10
+ * Update node handle positions based on current direction
11
+ */
12
+ export declare const updateHandlePositions: (nodes: Node[], direction: LayoutDirection) => Node[];
13
+ /**
14
+ * Default parent resizing options
15
+ */
16
+ export declare const DEFAULT_PARENT_RESIZING_OPTIONS: {
17
+ enabled: boolean;
18
+ padding: {
19
+ horizontal: number;
20
+ vertical: number;
21
+ };
22
+ respectHeaderHeight: boolean;
23
+ minWidth: number;
24
+ minHeight: number;
25
+ };
26
+ export declare const getSourcePosition: (direction: LayoutDirection) => Position;
27
+ export declare const getTargetPosition: (direction: LayoutDirection) => Position;
28
+ /**
29
+ * Utility to filter visible nodes and edges based on the layoutHidden flag.
30
+ * If layoutHidden is false, only visible nodes/edges are included.
31
+ * If layoutHidden is true, all nodes/edges are included.
32
+ */
33
+ export declare function filterVisibleNodesAndEdges(nodes: Node[], edges: Edge[], layoutHidden: boolean): {
34
+ nodes: Node[];
35
+ edges: Edge[];
36
+ };
@@ -0,0 +1,6 @@
1
+ import { Edge, Node } from "@xyflow/react";
2
+ /**
3
+ * Process all edges once to create a global temporary edges map
4
+ * This replaces all edge processing complexity
5
+ */
6
+ export declare function createGlobalTemporaryEdgesMap(edges: Edge[], nodeIdWithNode: Map<string, Node>, noParentKey?: string): Map<string, Edge[]>;
@@ -0,0 +1,54 @@
1
+ import { Node } from '@xyflow/react';
2
+ /**
3
+ * Tree node representing a node in the parent-child hierarchy
4
+ */
5
+ export interface TreeNode {
6
+ id: string;
7
+ node: Node;
8
+ children: TreeNode[];
9
+ depth: number;
10
+ }
11
+ /**
12
+ * Builds a tree structure from parent-child relationships in the node maps.
13
+ * The tree only includes parent nodes (nodes that have children).
14
+ * The tree starts with root parent nodes (parent nodes without parents) at depth 0.
15
+ *
16
+ * @param nodeParentIdMapWithChildIdSet Map of parent IDs to their set of child IDs
17
+ * @param nodeIdWithNode Map of node IDs to node objects
18
+ * @param noParentKey Key used to represent nodes without a parent
19
+ * @returns An array of TreeNode objects representing root parent nodes, each containing its hierarchy
20
+ */
21
+ export declare const buildNodeTree: (nodeParentIdMapWithChildIdSet: Map<string, Set<string>>, nodeIdWithNode: Map<string, Node>, noParentKey?: string) => TreeNode[];
22
+ /**
23
+ * Gets all nodes at the specified depth in the tree
24
+ *
25
+ * @param tree The tree structure to search
26
+ * @param targetDepth The depth to find nodes at
27
+ * @returns Array of nodes at the specified depth
28
+ */
29
+ export declare const getNodesAtDepth: (tree: TreeNode[], targetDepth: number) => Node[];
30
+ /**
31
+ * Finds the maximum depth in the tree
32
+ *
33
+ * @param tree The tree structure to analyze
34
+ * @returns The maximum depth in the tree
35
+ */
36
+ export declare const getMaxTreeDepth: (tree: TreeNode[]) => number;
37
+ /**
38
+ * Get the path from a node to the root (including the node itself)
39
+ */
40
+ export declare function getAncestorPath(nodeId: string, nodeIdWithNode: Map<string, Node>, noParentKey?: string): string[];
41
+ /**
42
+ * Returns the first child under the given ancestor on the path to the node.
43
+ * If the node itself is the direct child under the ancestor, returns the node id.
44
+ * Returns null if the ancestor is not on the node's ancestor path.
45
+ */
46
+ export declare function getFirstChildUnderAncestor(nodeId: string, ancestorId: string, nodeIdWithNode: Map<string, Node>, noParentKey?: string): string | null;
47
+ /**
48
+ * Enhanced LCA function that returns both the LCA and the immediate children
49
+ */
50
+ export declare function findLCAWithChildren(sourceNodeId: string, targetNodeId: string, nodeIdWithNode: Map<string, Node>, noParentKey?: string): {
51
+ lca: string | null;
52
+ sourceChild: string | null;
53
+ targetChild: string | null;
54
+ };
package/dist/vite.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>