({paths, pagesById})
| 165 | readonly paths: readonly WikipediaPageId[][]; |
| 166 | readonly pagesById: Record<WikipediaPageId, WikipediaPage>; |
| 167 | }> = ({paths, pagesById}) => { |
| 168 | const graphRef = useRef<SVGSVGElement | null>(null); |
| 169 | const simulationRef = useRef<d3.Simulation<GraphNode, GraphLink> | null>(null); |
| 170 | const graphSvgRef = useRef<d3.Selection<SVGGElement, unknown, null, undefined> | null>(null); |
| 171 | const zoomableRef = useRef<d3.Selection<Element, unknown, null, undefined> | null>(null); |
| 172 | const graphWrapperSizeRef = useRef<HTMLDivElement | null>(null); |
| 173 | const zoomRef = useRef<d3.ZoomBehavior<Element, unknown> | null>(null); |
| 174 | |
| 175 | const color = d3.scaleOrdinal(d3.schemeCategory10); |
| 176 | |
| 177 | const getGraphWidth = useCallback(() => { |
| 178 | if (!graphWrapperSizeRef.current) return 0; |
| 179 | return graphWrapperSizeRef.current.getBoundingClientRect().width; |
| 180 | }, [graphWrapperSizeRef]); |
| 181 | |
| 182 | const getGraphData = useCallback(() => { |
| 183 | const seenNodes = new Set<WikipediaPageId>(); |
| 184 | const nodesData: GraphNode[] = []; |
| 185 | const linksData: GraphLink[] = []; |
| 186 | |
| 187 | paths.forEach((path) => { |
| 188 | let previousPageId: WikipediaPageId | null = null; |
| 189 | path.forEach((currentPageId, i) => { |
| 190 | const currentPage = pagesById[currentPageId]; |
| 191 | if (!currentPage) { |
| 192 | // eslint-disable-next-line no-console |
| 193 | console.error(`Failed to find page with ID ${currentPageId} in pages dictionary`); |
| 194 | return; |
| 195 | } |
| 196 | |
| 197 | if (!seenNodes.has(currentPageId)) { |
| 198 | nodesData.push({id: currentPageId, title: currentPage.title, degree: i}); |
| 199 | seenNodes.add(currentPageId); |
| 200 | } |
| 201 | |
| 202 | if (previousPageId) { |
| 203 | const sourceNode = nodesData.find((n) => n.id === previousPageId); |
| 204 | const targetNode = nodesData.find((n) => n.id === currentPageId); |
| 205 | if (!sourceNode || !targetNode) { |
| 206 | // eslint-disable-next-line no-console |
| 207 | console.error('Failed to find source or target node'); |
| 208 | return; |
| 209 | } |
| 210 | linksData.push({source: sourceNode, target: targetNode}); |
| 211 | } |
| 212 | |
| 213 | previousPageId = currentPageId; |
| 214 | }); |
| 215 | }); |
| 216 | |
| 217 | return {nodesData, linksData}; |
| 218 | }, [pagesById, paths]); |
| 219 | |
| 220 | const resetGraph = useCallback(() => { |
| 221 | const graphWidth = getGraphWidth(); |
| 222 | |
| 223 | // Reset the center of the simulation force and restart it. |
| 224 | simulationRef.current?.force( |
nothing calls this directly
no test coverage detected