summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.tsx12
-rw-r--r--src/components/Graph.tsx49
-rw-r--r--src/global.d.ts2
-rw-r--r--src/hooks/useCourseData.ts14
-rw-r--r--src/main.tsx9
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>
+);