diff --git a/.gitignore b/.gitignore index fc6b291..dcaea37 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ dist-ssr *.sw? # Output -scripts/output/*.json \ No newline at end of file +scripts/output/*.json +coverage \ No newline at end of file diff --git a/src/pages/Explorer.tsx b/src/pages/Explorer.tsx index a198501..ead845e 100644 --- a/src/pages/Explorer.tsx +++ b/src/pages/Explorer.tsx @@ -19,8 +19,14 @@ import { type Edge, } from '@xyflow/react'; import dagre from '@dagrejs/dagre'; -import { courseList, courses, prereqEdges } from '@/data/loadCourses'; +import { + courseList, + coreCourseList, + courses, + prereqEdges, +} from '@/data/loadCourses'; import { useExplorerStore } from '@/store/explorerStore'; +import type { Course } from '@/types/course'; import CourseNode from '@/components/CourseNode'; import type { CourseNodeData } from '@/components/CourseNode'; import CourseDetailPanel from '@/components/CourseDetailPanel'; @@ -28,14 +34,10 @@ import CourseDetailPanel from '@/components/CourseDetailPanel'; const NODE_W = 180; const NODE_H = 60; -// Only draw edges where both endpoints are in our course set; SYSC courses that -// appear in COMP 3004's prereqs are not nodes yet (see TODO #3 above). -const knownCodes = new Set(courseList.map((c) => c.code)); -const visibleEdges = prereqEdges.filter( - (e) => knownCodes.has(e.from) && knownCodes.has(e.to), -); - -function computeLayout(): { +function computeLayout( + visibleCourses: Course[], + visibleEdges: { from: string; to: string }[], +): { nodes: Node[]; edges: Edge[]; } { @@ -43,7 +45,7 @@ function computeLayout(): { g.setDefaultEdgeLabel(() => ({})); g.setGraph({ rankdir: 'TB', ranksep: 80, nodesep: 40 }); - for (const course of courseList) { + for (const course of visibleCourses) { g.setNode(course.code, { width: NODE_W, height: NODE_H }); } @@ -55,7 +57,7 @@ function computeLayout(): { dagre.layout(g); - const nodes: Node[] = courseList.map((course) => { + const nodes: Node[] = visibleCourses.map((course) => { const pos = g.node(course.code); return { id: course.code, @@ -78,11 +80,35 @@ function computeLayout(): { const nodeTypes: NodeTypes = { courseNode: CourseNode }; export default function Explorer() { - const { selectedCourse, highlightedSet, setSelectedCourse } = - useExplorerStore(); + const { + selectedCourse, + highlightedSet, + setSelectedCourse, + showAllCourses, + toggleShowAllCourse, + } = useExplorerStore(); + + // The single source of truth for "what's shown" — later filter modes + // (reachable-on-click, department/year, search) should derive their own + // visible set the same way, upstream of layout. + const visibleCourses = showAllCourses ? courseList : coreCourseList; + const visibleCodes = useMemo( + () => new Set(visibleCourses.map((c) => c.code)), + [visibleCourses], + ); + const visibleEdges = useMemo( + () => + prereqEdges.filter( + (e) => visibleCodes.has(e.from) && visibleCodes.has(e.to), + ), + [visibleCodes], + ); - // Layout is derived entirely from static import-time data; deps array is empty. - const { nodes, edges: layoutEdges } = useMemo(() => computeLayout(), []); + // Layout is recomputed whenever the visible course set changes. + const { nodes, edges: layoutEdges } = useMemo( + () => computeLayout(visibleCourses, visibleEdges), + [visibleCourses, visibleEdges], + ); // Re-derive edge styles when selection changes. const edges = useMemo( @@ -107,6 +133,8 @@ export default function Explorer() { return (
+ setSelectedCourse(null)} diff --git a/src/store/explorerStore.ts b/src/store/explorerStore.ts index f259594..aa93872 100644 --- a/src/store/explorerStore.ts +++ b/src/store/explorerStore.ts @@ -6,6 +6,8 @@ interface ExplorerState { selectedCourse: string | null; highlightedSet: Set; setSelectedCourse: (code: string | null) => void; + showAllCourses: boolean; + toggleShowAllCourse: () => void; } export const useExplorerStore = create((set) => ({ @@ -23,4 +25,7 @@ export const useExplorerStore = create((set) => ({ ...getDescendants(code, prereqEdges), ]), }), + showAllCourses: false, + toggleShowAllCourse: () => + set((state) => ({ showAllCourses: !state.showAllCourses })), }));