import { useFetch } from 'use-http'; import React, { useEffect, useRef, useState } from 'react'; import { Box, Container } from '@chakra-ui/react'; import { wireframeJSONToSVG } from 'roadmap-renderer'; import { GlobalHeader } from '../../components/global-header'; import { OpensourceBanner } from '../../components/opensource-banner'; import { Footer } from '../../components/footer'; import { getAllRoadmaps, getRoadmapById, RoadmapType } from '../../lib/roadmap'; import Helmet from '../../components/helmet'; import { RoadmapPageHeader } from '../../components/roadmap/roadmap-page-header'; import { ContentDrawer, isTopicDone, markTopicDone, markTopicPending } from '../../components/roadmap/content-drawer'; import { RoadmapError } from '../../components/roadmap/roadmap-error'; import { RoadmapLoader } from '../../components/roadmap/roadmap-loader'; import { removeSortingInfo } from '../../lib/renderer'; import { TeamsBanner } from '../../components/teams-banner'; import { ShareIcons } from '../../components/share-icons'; type RoadmapProps = { roadmap: RoadmapType; }; export function InteractiveRoadmapRenderer(props: RoadmapProps) { const { roadmap } = props; const { loading: isLoading, error: hasErrorLoading, get } = useFetch(); const roadmapRef = useRef(null); const [isRendering, setIsRendering] = useState(true); const [roadmapJson, setRoadmapJson] = useState(null); const [groupId, setGroupId] = useState(''); const [hasErrorRendering, setHasErrorRendering] = useState(false); useEffect(() => { if (!roadmap.jsonUrl) { return; } get(roadmap.jsonUrl) .then((roadmapJson) => { setRoadmapJson(roadmapJson); }) .catch((err) => { console.error(err); setHasErrorRendering(true); }); }, [get, roadmap.id, roadmap.jsonUrl]); // Event bindings for the roadmap interactivity useEffect(() => { function keydownListener(event: KeyboardEvent) { if (event.key.toLowerCase() === 'escape') { setGroupId(''); } } function getClosestGroupId(target: HTMLElement) { const targetGroup = target?.closest('g'); return targetGroup?.dataset?.groupId; } function clickListener(event: MouseEvent) { const groupId = getClosestGroupId(event.target as HTMLElement); if (!groupId) { return; } if (groupId.startsWith('ext_link:')) { window.open(`https://${groupId.replace('ext_link:', '')}`); return; } // e.g. 100-internet:how-does-the-internet-work // will be translated to `internet:how-does-the-internet-work` setGroupId(removeSortingInfo(groupId)); } function rightClickListener(event: MouseEvent) { const groupId = getClosestGroupId(event.target as HTMLElement); if (!groupId) { return; } event.preventDefault(); const normalizedGroupId = removeSortingInfo(groupId); if (isTopicDone(normalizedGroupId)) { markTopicPending(normalizedGroupId); } else { markTopicDone(normalizedGroupId); } } window.addEventListener('keydown', keydownListener); window.addEventListener('click', clickListener); window.addEventListener('contextmenu', rightClickListener) return () => { window.removeEventListener('keydown', keydownListener); window.removeEventListener('click', clickListener); }; }, []); useEffect(() => { if (!roadmapJson) { return; } setIsRendering(true); wireframeJSONToSVG(roadmapJson) .then((svgElement) => { const container: HTMLElement = roadmapRef.current!; if (!container) { return; } if (container.firstChild) { container.removeChild(container.firstChild); } container.appendChild(svgElement); }) .catch((err) => { setHasErrorRendering(true); }) .finally(() => { setIsRendering(false); }); }, [roadmapJson]); if (!roadmap.jsonUrl) { return null; } if (hasErrorLoading || hasErrorRendering) { return ; } let minHeight: string[] = []; if (roadmap.id === 'frontend') { minHeight = ['1265px', '2075px', '2710px', '2804px', '2804px']; } if (roadmap.id === 'backend') { minHeight = ['1310px', '2150px', '2170px', '2920px', '2920px', '2920px']; } if (roadmap.id === 'devops') { minHeight = ['1160px', '1920px', '2505px', '2591px', '2591px', '2591px']; } if (roadmap.id === 'vue') { minHeight = ['600px', '820px', '1340px', '1680px', '1750px', '1750px']; } if (roadmap.id === 'react') { minHeight = ['720px', '865px', '1340px', '1615px', '1615px', '1615px']; } if (roadmap.id === 'blockchain') { minHeight = ['780px', '1120px', '1770px', '2235px', '2235px', '2235px']; } if (roadmap.id === 'golang') { minHeight = ['590px', '1201px', '1201px', '1625px', '1625px', '1625px']; } if (roadmap.id === 'javascript') { minHeight = ['892px', '1835px', '1835px', '2475px', '2475px', '2475px']; } if (roadmap.id === 'nodejs') { minHeight = ['865px', '1855px', '1855px', '2500px', '2500px', '2500px']; } if (roadmap.id === 'qa') { minHeight = ['865px', '1610px', '1610px', '2200px', '2200px', '2200px']; } if (roadmap.id === 'design-system') { minHeight = ['915px', '1760px', '1880px', '2370px', '2370px', '2370px']; } if (roadmap.id === 'angular') { minHeight = ['925px', '1365px', '1740px', '2370px', '2370px', '2370px']; } if (roadmap.id === 'software-architect') { minHeight = ['685px', '1170px', '1470px', '1980px', '1980px', '1980px']; } if (roadmap.id === 'software-design-architecture') { minHeight = ['515px', '1230px', '1310px', '1765px', '1765px', '1765px']; } if (roadmap.id === 'aspnet-core') { minHeight = ['1278px', '2105px', '2748px', '2845px', '2845px', '2845px']; } if (roadmap.id === 'flutter') { minHeight = ['830px', '1534px', '1553px', '2093px', '2093px', '2093px']; } if (roadmap.id === 'computer-science') { minHeight = ['1222px', '1393px', '2288px', '3084px', '3084px', '3084px']; } if (roadmap.id === 'graphql') { minHeight = ['770px', '1261px', '1617px', '1712px', '1712px', '1712px']; } return ( {(isLoading || isRendering) && } setGroupId('')} /> { (!isLoading && !isRendering) && }
); } export default function InteractiveRoadmap(props: RoadmapProps) { const { roadmap } = props; return (