* Selects the best anchor block for offset computation. * Prefers an upstream (predecessor) anchor over a downstream one because * upstream blocks keep their layer assignment when new blocks are inserted * after them, giving a stable offset. Downstream blocks shift to later * layers in the ideal
(
eligibleIds: string[],
needsLayoutSet: Set<string>,
edges: Edge[],
layoutPositions: Map<string, { x: number; y: number }>
)
| 124 | * layers in the ideal layout, producing a large incorrect offset. |
| 125 | */ |
| 126 | function selectBestAnchor( |
| 127 | eligibleIds: string[], |
| 128 | needsLayoutSet: Set<string>, |
| 129 | edges: Edge[], |
| 130 | layoutPositions: Map<string, { x: number; y: number }> |
| 131 | ): string | undefined { |
| 132 | const candidates = eligibleIds.filter((id) => !needsLayoutSet.has(id) && layoutPositions.has(id)) |
| 133 | if (candidates.length === 0) return undefined |
| 134 | if (candidates.length === 1) return candidates[0] |
| 135 | |
| 136 | const candidateSet = new Set(candidates) |
| 137 | |
| 138 | for (const edge of edges) { |
| 139 | if (needsLayoutSet.has(edge.target) && candidateSet.has(edge.source)) { |
| 140 | return edge.source |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | for (const edge of edges) { |
| 145 | if (needsLayoutSet.has(edge.source) && candidateSet.has(edge.target)) { |
| 146 | return edge.target |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | return candidates[0] |
| 151 | } |
| 152 | |
| 153 | /** |
| 154 | * Layouts a group of blocks (either root level or within a container). |