diff options
| author | Yuren Hao <97327730+YurenHao0426@users.noreply.github.com> | 2025-08-06 01:39:08 -0700 |
|---|---|---|
| committer | Yuren Hao <97327730+YurenHao0426@users.noreply.github.com> | 2025-08-06 01:39:08 -0700 |
| commit | 343978870774f81ecaa06d7f253020265f601fd2 (patch) | |
| tree | dbb414f3b4e840c7c52d79fa4443ad87ad93056a /src | |
| parent | 79ec5fd1f0188a88f937737185c542e644cf98e4 (diff) | |
feat: scaffold prereq graph project
Diffstat (limited to 'src')
| -rw-r--r-- | src/App.tsx | 12 | ||||
| -rw-r--r-- | src/components/Graph.tsx | 49 | ||||
| -rw-r--r-- | src/global.d.ts | 2 | ||||
| -rw-r--r-- | src/hooks/useCourseData.ts | 14 | ||||
| -rw-r--r-- | src/main.tsx | 9 |
5 files changed, 86 insertions, 0 deletions
diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..9687afc --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,12 @@ +import Graph from "./components/Graph"; +import { useCourseData } from "./hooks/useCourseData"; + +export default function App() { + const catalog = useCourseData("/data/catalog_2025_fall.json"); + return ( + <> + <h1 style={{ textAlign: "center" }}>UIUC Course Prerequisite Graph</h1> + <Graph catalog={catalog} /> + </> + ); +} diff --git a/src/components/Graph.tsx b/src/components/Graph.tsx new file mode 100644 index 0000000..5e81b38 --- /dev/null +++ b/src/components/Graph.tsx @@ -0,0 +1,49 @@ +import CytoscapeComponent from "react-cytoscapejs"; +import cytoscape from "cytoscape"; +import dagre from "cytoscape-dagre"; +import { useMemo } from "react"; + +cytoscape.use(dagre); + +type Catalog = Record<string, string[]>; + +export default function Graph({ catalog }: { catalog: Catalog }) { + const elements = useMemo(() => { + const els: any[] = []; + Object.keys(catalog).forEach(id => { + els.push({ data: { id } }); + catalog[id].forEach(p => + els.push({ + data: { id: `${p}->${id}`, source: p, target: id } + }) + ); + }); + return els; + }, [catalog]); + + return ( + <CytoscapeComponent + elements={elements} + stylesheet={[ + { + selector: "node", + style: { + "background-color": "#3182bd", + label: "data(id)", + color: "#fff" + } + }, + { + selector: "edge", + style: { + width: 2, + "target-arrow-shape": "triangle", + "curve-style": "bezier" + } + } + ]} + layout={{ name: "dagre", rankDir: "TB", nodeSep: 30, rankSep: 50 }} + style={{ width: "100%", height: "90vh" }} + /> + ); +} diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..d2d9941 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,2 @@ +declare module 'react-cytoscapejs'; +declare module 'cytoscape-dagre'; diff --git a/src/hooks/useCourseData.ts b/src/hooks/useCourseData.ts new file mode 100644 index 0000000..74281fa --- /dev/null +++ b/src/hooks/useCourseData.ts @@ -0,0 +1,14 @@ +import { useEffect, useState } from "react"; + +export function useCourseData(file = "/data/catalog_2025_fall.json") { + const [catalog, setCatalog] = useState<Record<string, string[]>>({}); + + useEffect(() => { + fetch(file) + .then(r => r.json()) + .then(setCatalog) + .catch(console.error); + }, [file]); + + return catalog; +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..3fd3a69 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,9 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; + +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( + <React.StrictMode> + <App /> + </React.StrictMode> +); |
