@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.
- package/README.md +752 -0
- package/dist/react-flow-automated-layout.es.js +1573 -0
- package/dist/react-flow-automated-layout.umd.js +1 -0
- package/dist/types/components/LayoutControls.d.ts +15 -0
- package/dist/types/components/controls/ApplyLayoutButton.d.ts +6 -0
- package/dist/types/components/controls/AutoLayoutToggle.d.ts +6 -0
- package/dist/types/components/controls/DirectionControls.d.ts +6 -0
- package/dist/types/components/controls/SpacingControls.d.ts +6 -0
- package/dist/types/context/LayoutContext.d.ts +66 -0
- package/dist/types/context/LayoutProvider.d.ts +29 -0
- package/dist/types/core/Dagre.d.ts +9 -0
- package/dist/types/core/HierarchicalLayoutOrganizer.d.ts +78 -0
- package/dist/types/engines/index.d.ts +9 -0
- package/dist/types/hooks/useLayoutCalculation.d.ts +36 -0
- package/dist/types/hooks/useLayoutOperations.d.ts +35 -0
- package/dist/types/hooks/useLayoutState.d.ts +50 -0
- package/dist/types/hooks/useNodeMaps.d.ts +16 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/utils/filterSelectedParentNodes.d.ts +13 -0
- package/dist/types/utils/layoutProviderUtils.d.ts +36 -0
- package/dist/types/utils/temporaryEdgeMapCreator.d.ts +6 -0
- package/dist/types/utils/treeUtils.d.ts +54 -0
- package/dist/vite.svg +1 -0
- package/package.json +73 -0
|
@@ -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,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>
|