Improved node layout in the map interface
This commit is contained in:
@@ -24,48 +24,40 @@ export function generateNodeLayout(nodes: OrgNode[]): { nodes: FlowNode[]; edges
|
|||||||
const flowEdges: FlowEdge[] = []
|
const flowEdges: FlowEdge[] = []
|
||||||
|
|
||||||
let nodeCounter = 0
|
let nodeCounter = 0
|
||||||
const levelPositions: Map<number, number> = new Map() // Track position within each level
|
const nodeMinWidth = 280 // Minimum width needed for each node card
|
||||||
|
const siblingPadding = 50 // Padding between sibling nodes
|
||||||
|
|
||||||
|
// First, calculate the width requirements for each subtree from bottom up
|
||||||
|
function calculateSubtreeWidth(node: OrgNode): number {
|
||||||
|
if (node.children.length === 0) {
|
||||||
|
return nodeMinWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate total width needed for all children plus padding between them
|
||||||
|
const childrenWidths = node.children.map(child => calculateSubtreeWidth(child))
|
||||||
|
const totalChildrenWidth = childrenWidths.reduce((sum, width) => sum + width, 0)
|
||||||
|
const paddingWidth = Math.max(0, (node.children.length - 1) * siblingPadding)
|
||||||
|
const totalWidthWithPadding = totalChildrenWidth + paddingWidth
|
||||||
|
|
||||||
|
// Node needs at least its own width or the width of all its children with padding
|
||||||
|
return Math.max(nodeMinWidth, totalWidthWithPadding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate positions using the width requirements
|
||||||
function processNode(
|
function processNode(
|
||||||
node: OrgNode,
|
node: OrgNode,
|
||||||
level: number,
|
level: number,
|
||||||
parentId: string | null,
|
parentId: string | null,
|
||||||
parentX: number = 400,
|
availableX: number,
|
||||||
parentY: number = 100
|
availableWidth: number
|
||||||
): string {
|
): string {
|
||||||
const nodeId = `node-${nodeCounter++}`
|
const nodeId = `node-${nodeCounter++}`
|
||||||
|
|
||||||
// Hierarchical positioning: levels go top to bottom
|
// Y position based on level
|
||||||
const yPosition = 100 + (level - 1) * 200 // 200px between levels
|
const yPosition = 100 + (level - 1) * 200
|
||||||
|
|
||||||
// Get current position for this level
|
// X position centered within available width
|
||||||
let xPosition: number
|
const xPosition = availableX + availableWidth / 2 - nodeMinWidth / 2
|
||||||
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({
|
flowNodes.push({
|
||||||
id: nodeId,
|
id: nodeId,
|
||||||
@@ -77,7 +69,7 @@ export function generateNodeLayout(nodes: OrgNode[]): { nodes: FlowNode[]; edges
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add edge from parent - center to center diagonal
|
// Add edge from parent
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
flowEdges.push({
|
flowEdges.push({
|
||||||
id: `edge-${parentId}-${nodeId}`,
|
id: `edge-${parentId}-${nodeId}`,
|
||||||
@@ -87,17 +79,40 @@ export function generateNodeLayout(nodes: OrgNode[]): { nodes: FlowNode[]; edges
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process children
|
// Process children with proper spacing
|
||||||
node.children.forEach((child, index) => {
|
if (node.children.length > 0) {
|
||||||
processNode(child, level + 1, nodeId, xPosition, yPosition)
|
const childWidths = node.children.map(child => calculateSubtreeWidth(child))
|
||||||
})
|
const totalChildWidth = childWidths.reduce((sum, width) => sum + width, 0)
|
||||||
|
const paddingWidth = Math.max(0, (node.children.length - 1) * siblingPadding)
|
||||||
|
const totalWidthWithPadding = totalChildWidth + paddingWidth
|
||||||
|
|
||||||
|
let currentX = availableX + (availableWidth - totalWidthWithPadding) / 2
|
||||||
|
|
||||||
|
node.children.forEach((child, index) => {
|
||||||
|
const childWidth = childWidths[index]
|
||||||
|
processNode(child, level + 1, nodeId, currentX, childWidth)
|
||||||
|
currentX += childWidth + siblingPadding // Add padding after each child except the last
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return nodeId
|
return nodeId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate total width needed for all root nodes
|
||||||
|
const rootWidths = nodes.map(node => calculateSubtreeWidth(node))
|
||||||
|
const totalRootWidth = rootWidths.reduce((sum, width) => sum + width, 0)
|
||||||
|
const rootPaddingWidth = Math.max(0, (nodes.length - 1) * siblingPadding)
|
||||||
|
const totalRootWidthWithPadding = totalRootWidth + rootPaddingWidth
|
||||||
|
|
||||||
|
// Start positioning from the left, centered in viewport
|
||||||
|
const startX = Math.max(50, (1400 - totalRootWidthWithPadding) / 2) // Increased viewport assumption
|
||||||
|
let currentX = startX
|
||||||
|
|
||||||
// Process all root nodes
|
// Process all root nodes
|
||||||
nodes.forEach((node, index) => {
|
nodes.forEach((node, index) => {
|
||||||
processNode(node, 1, null)
|
const rootWidth = rootWidths[index]
|
||||||
|
processNode(node, 1, null, currentX, rootWidth)
|
||||||
|
currentX += rootWidth + siblingPadding // Add padding between root nodes too
|
||||||
})
|
})
|
||||||
|
|
||||||
return { nodes: flowNodes, edges: flowEdges }
|
return { nodes: flowNodes, edges: flowEdges }
|
||||||
|
|||||||
Reference in New Issue
Block a user