104 lines
2.9 KiB
TypeScript
104 lines
2.9 KiB
TypeScript
import type { OrgNode } from '@/types/org'
|
|
|
|
export interface FlowNode {
|
|
id: string
|
|
type: 'orgNode'
|
|
position: { x: number; y: number }
|
|
data: {
|
|
node: OrgNode
|
|
level: number
|
|
}
|
|
}
|
|
|
|
export interface FlowEdge {
|
|
id: string
|
|
source: string
|
|
target: string
|
|
sourceHandle?: string
|
|
targetHandle?: string
|
|
type: 'straight' | 'smoothstep'
|
|
}
|
|
|
|
export function generateNodeLayout(nodes: OrgNode[]): { nodes: FlowNode[]; edges: FlowEdge[] } {
|
|
const flowNodes: FlowNode[] = []
|
|
const flowEdges: FlowEdge[] = []
|
|
|
|
let nodeCounter = 0
|
|
const levelPositions: Map<number, number> = new Map() // Track position within each level
|
|
|
|
function processNode(
|
|
node: OrgNode,
|
|
level: number,
|
|
parentId: string | null,
|
|
parentX: number = 400,
|
|
parentY: number = 100
|
|
): string {
|
|
const nodeId = `node-${nodeCounter++}`
|
|
|
|
// Hierarchical positioning: levels go top to bottom
|
|
const yPosition = 100 + (level - 1) * 200 // 200px between levels
|
|
|
|
// Get current position for this level
|
|
let xPosition: number
|
|
if (level === 1) {
|
|
// Root nodes: center them horizontally
|
|
const rootIndex = flowNodes.filter(n => n.data.level === 1).length
|
|
const rootSpacing = 350
|
|
const totalRootWidth = (nodes.length - 1) * rootSpacing
|
|
xPosition = 400 + rootIndex * rootSpacing - totalRootWidth / 2
|
|
} else {
|
|
// Child nodes: position relative to parent
|
|
const currentLevelCount = levelPositions.get(level) || 0
|
|
levelPositions.set(level, currentLevelCount + 1)
|
|
|
|
// Calculate children positions spread around parent
|
|
const parentNode = flowNodes.find(n => n.id === parentId)
|
|
if (parentNode) {
|
|
const siblingCount = node.children?.length || 1
|
|
const parentChildren = flowNodes.filter(n =>
|
|
flowEdges.some(e => e.source === parentId && e.target === n.id)
|
|
).length
|
|
|
|
const childSpacing = Math.max(250, 400 / Math.max(siblingCount, 1))
|
|
const totalWidth = (siblingCount - 1) * childSpacing
|
|
xPosition = parentX + (parentChildren * childSpacing) - (totalWidth / 2)
|
|
} else {
|
|
xPosition = parentX + (currentLevelCount * 300)
|
|
}
|
|
}
|
|
|
|
flowNodes.push({
|
|
id: nodeId,
|
|
type: 'orgNode',
|
|
position: { x: xPosition, y: yPosition },
|
|
data: {
|
|
node,
|
|
level
|
|
}
|
|
})
|
|
|
|
// Add edge from parent - center to center diagonal
|
|
if (parentId) {
|
|
flowEdges.push({
|
|
id: `edge-${parentId}-${nodeId}`,
|
|
source: parentId,
|
|
target: nodeId,
|
|
type: 'straight'
|
|
})
|
|
}
|
|
|
|
// Process children
|
|
node.children.forEach((child, index) => {
|
|
processNode(child, level + 1, nodeId, xPosition, yPosition)
|
|
})
|
|
|
|
return nodeId
|
|
}
|
|
|
|
// Process all root nodes
|
|
nodes.forEach((node, index) => {
|
|
processNode(node, 1, null)
|
|
})
|
|
|
|
return { nodes: flowNodes, edges: flowEdges }
|
|
} |