Skip to content
This repository has been archived by the owner on Aug 27, 2024. It is now read-only.

Commit

Permalink
Add navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
anli5005 committed Dec 30, 2023
1 parent 9936a09 commit cf4eb8a
Show file tree
Hide file tree
Showing 16 changed files with 320 additions and 17 deletions.
22 changes: 14 additions & 8 deletions app/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { allPages } from 'contentlayer/generated'
import { notFound } from 'next/navigation'
import { useMDXComponent } from 'next-contentlayer/hooks'
import { mdxComponents } from '@/components/mdx'
import { Card } from '@/components/Card'
import { Prose } from '@/components/Prose'
import { MenuItemActivator } from '@/app/menu'

export async function generateStaticParams() {
return allPages.map(page => ({
Expand All @@ -14,14 +17,17 @@ export default function Page({ params }: { params: { slug: string } }) {
if (!page) notFound()

const MDXContent = useMDXComponent(page.body.code)
const content = <MDXContent components={mdxComponents} />

if (page.customLayout) return <>
<MenuItemActivator item={page.activeMenuItem ?? null} />
{content}
</>

return <div className="card">
<div className="mb-4 w-fit">
<h1 className="text-3xl font-bold mb-0.5">{page.title}</h1>
<div className="bg-gradient-to-r from-cyan-500 to-purple-500 h-1" />
</div>
<div className="prose prose-neutral dark:prose-invert max-w-none prose-h1:mb-4 prose-h2:mt-6 prose-h2:mb-2 prose-a:text-cyan-500 prose-a:dark:text-cyan-400 prose-p:my-2 prose-pre:my-2 prose-pre:border prose-pre:border-transparent prose-pre:dark:border-white/10 prose-blockquote:my-2">
return <Card title={<h1>{page.title}</h1>}>
<MenuItemActivator item={page.activeMenuItem ?? null} />
<Prose>
<MDXContent components={mdxComponents} />
</div>
</div>
</Prose>
</Card>
}
18 changes: 13 additions & 5 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "@/styles/global.css"
import "prism-themes/themes/prism-atom-dark.css"
import { Menu, MenuContextProvider } from "./menu"

export const metadata = {
title: 'Next.js',
Expand All @@ -14,12 +15,19 @@ export default function RootLayout({
return (
<html lang="en">
<body>
<div className="container mx-auto px-4 py-8 md:px-8 md:py-16">
<h1 className="text-6xl md:text-8xl w-fit font-bold text-transparent bg-clip-text bg-gradient-to-br from-cyan-700 to-purple-700 dark:from-cyan-400 dark:to-purple-400">CIS 1951</h1>
<div className="mt-8">
{children}
<MenuContextProvider>
<div className="md:container mx-auto md:px-8 md:py-16">
<h1 className="hidden md:block md:text-8xl w-fit font-bold text-transparent bg-clip-text bg-gradient-to-br from-cyan-700 to-purple-700 dark:from-cyan-400 dark:to-purple-400">CIS 1951</h1>
<div className="md:mt-8 md:flex flex-nowrap gap-4">
<div className="flex flex-col items-center bg-neutral-50 dark:bg-neutral-800 md:bg-transparent dark:md:bg-transparent pt-4 md:pt-0">
<h1 className="text-center mb-3 md:hidden text-3xl w-fit font-bold text-transparent bg-clip-text bg-gradient-to-br from-cyan-700 to-purple-700 dark:from-cyan-400 dark:to-purple-400">CIS 1951</h1>
<div className="px-4 md:px-0 w-full"><Menu /></div>
<div className="w-full rounded-t-2xl h-4 bg-white dark:bg-black mt-4 border-t border-neutral-200 dark:border-neutral-700 md:hidden" />
</div>
<div className="container mx-auto md:w-full md:basis-full p-4 pt-1 md:p-0 md:pt-0">{children}</div>
</div>
</div>
</div>
</MenuContextProvider>
</body>
</html>
)
Expand Down
116 changes: 116 additions & 0 deletions app/menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"use client"

import Link from "next/link"
import { usePathname } from "next/navigation"
import { ReactNode, createContext, useContext, useEffect, useState } from "react"

export type MenuItemProps = {
id: string
title: string
icon: string
href: string
}

export const menuItems: MenuItemProps[] = [
{
id: "home",
title: "Home",
icon: "🏠",
href: "/",
},
{
id: "syllabus",
title: "Syllabus",
icon: "📄",
href: "/syllabus",
},
{
id: "codestyle",
title: "Style Guide",
icon: "💅",
href: "/codestyle",
},
{
id: "resources",
title: "Resources",
icon: "🧑‍💻",
href: "/resources",
},
]

export type MenuCoordinator = {
activeItem: string | null
setActiveItem: (id: string | null) => void
}

const MenuContext = createContext<MenuCoordinator | null>(null)

export function MenuContextProvider({ children }: { children: ReactNode }) {
const pathname = usePathname()
const [activeState, setActiveState] = useState<{ item: string | null, pathname: string }>({ item: null, pathname })

useEffect(() => {
if (activeState.pathname !== pathname) {
setActiveState({ item: null, pathname })
}
}, [pathname])

return <MenuContext.Provider value={{
activeItem: activeState.item,
setActiveItem(item) {
setActiveState({ item, pathname })
}
}}>
{children}
</MenuContext.Provider>
}

export function useMenuContext(): MenuCoordinator {
const context = useContext(MenuContext)
if (!context) throw new Error("useMenuContext must be called inside a MenuContextProvider")

return context
}

export type MenuItemActivatorProps = {
item: string | null
}

export function MenuItemActivator({ item }: MenuItemActivatorProps) {
const context = useMenuContext()
useEffect(() => {
context.setActiveItem(item)
}, [])

return null
}

function MenuItem({ id, title, icon, href }: MenuItemProps) {
const context = useMenuContext()
const isActive = id === context.activeItem

let className = "block px-3 py-2 rounded-xl group relative bg-opacity-0"
if (isActive) {
className += " bg-gradient-to-br from-cyan-700 to-purple-700 text-white"
} else {
className += " transition-[background-color] active:bg-neutral-200 dark:active:bg-neutral-700 md:dark:active:bg-neutral-800"
}

let containerClassName = "flex gap-3 transition-[margin-left] justify-center text-center md:text-left"
if (!isActive) containerClassName += " md:group-hover:ml-1"

return <Link className={className} href={href}>
<div className={containerClassName}>
<div className="w-4">{icon}</div>
<div className="md:grow line-clamp-1 overflow-hidden overflow-ellipsis">{title}</div>
<div className="opacity-40 hidden sm:block"></div>
</div>
<div className="absolute pointer-events-none inset-0 rounded-xl border border-black/30 dark:border-white/30 opacity-0 group-hover:opacity-100" />
</Link>
}

export function Menu() {
return <div className="w-full md:w-64 grid grid-cols-2 md:flex flex-col gap-0.5 md:gap-1">
{menuItems.map(item => <MenuItem key={item.id} {...item} />)}
</div>
}
20 changes: 20 additions & 0 deletions components/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ReactNode } from "react"

export type CardProps = {
title?: ReactNode
children: ReactNode
margin?: boolean
}

export function Card({ title, children, margin = false }: CardProps) {
let className = "rounded-xl p-4 bg-neutral-50 border border-neutral-200 dark:border-neutral-700 dark:bg-neutral-800"
if (margin) className += " mb-4"

return <div className={className}>
{title && <div className="mb-4 w-fit text-3xl font-bold">
{title}
<div className="bg-gradient-to-r from-cyan-500 to-purple-500 h-1 mt-0.5" />
</div>}
{children}
</div>
}
11 changes: 11 additions & 0 deletions components/Prose.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ReactNode } from "react"

export type ProseProps = {
children: ReactNode
}

export function Prose({ children }: ProseProps) {
return <div className="prose prose-neutral dark:prose-invert max-w-none prose-h1:mb-4 prose-h2:mt-6 prose-h2:mb-2 prose-p:my-2 prose-pre:my-2 prose-pre:border prose-pre:border-transparent prose-pre:dark:border-white/10 prose-blockquote:my-2">
{children}
</div>
}
62 changes: 62 additions & 0 deletions components/StaffGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
export type StaffMemberProps = {
name: string
section?: "501" | "502"
flavorText?: string
pennkey: string
school: "sas" | "seas" | "wharton" // Sorry, forgot what Penn Medicine has
pronouns: string
github?: string
website?: string
avatar?: string
}

function StaffMember({
name,
flavorText,
section,
pennkey,
school,
pronouns,
github,
website,
avatar,
}: StaffMemberProps) {
const links = [
github && {
text: "GitHub",
href: `https://github.com/${github}`,
},
website && {
text: "Website",
href: website,
}
].filter(link => link)

return <div className="flex gap-3 items-center">
<div className="bg-neutral-200 dark:bg-neutral-600 aspect-square w-28 rounded-full overflow-hidden shrink-0">
{avatar && <img className="block w-full h-full object-cover" src={avatar} />}
</div>
<div>
<h4 className="font-bold m-0 leading-tight">
{name}
{section && <span className="font-medium opacity-70"> ({section})</span>}
</h4>
{flavorText && <div className="italic opacity-60 text-sm">{flavorText}</div>}
<div className="text-sm mt-2"><span className="font-medium">Email</span>: <span className="font-mono">{pennkey}@{school}</span></div>
<div className="text-sm"><span className="font-medium">Pronouns</span>: {pronouns}</div>
{links.length > 0 && <div className="text-sm flex gap-2">
{links.map(({ text, href }, index) => <a key={index} href={href} className="link" target="_blank" rel="noopener noreferrer">{text}</a>)}
</div>}
</div>
</div>
}

export type StaffGridProps = {
members: StaffMemberProps[]
}

export function StaffGrid({ members }: StaffGridProps) {
return <div className="grid lg:grid-cols-2 xl:grid-cols-3 my-4 gap-4">
{members.map((member, index) => <StaffMember key={index} {...member} />)}
</div>
}
6 changes: 6 additions & 0 deletions components/mdx.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { MDXComponents } from "mdx/types";
import Link from "next/link";
import { Card } from "./Card";
import { Prose } from "./Prose";
import { StaffGrid } from "./StaffGrid";

export const mdxComponents: MDXComponents = {
a: ({ href, ...props }) => {
Expand All @@ -12,4 +15,7 @@ export const mdxComponents: MDXComponents = {
return <a {...props} />
}
},
Card: Card,
Prose: Prose,
StaffGrid: StaffGrid,
}
6 changes: 6 additions & 0 deletions content/pages/codestyle.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: Code Style Guide
activeMenuItem: codestyle
---

Make it readable
48 changes: 46 additions & 2 deletions content/pages/index.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,51 @@
---
title: Home
activeMenuItem: home
customLayout: true
---

Hello, world!
<Card title={<h2>Welcome</h2>} margin>
<Prose>
*\[insert course description here\]*
</Prose>
</Card>

[Syllabus](/~cis1951/syllabus)
<Card title={<h2>Staff</h2>} margin>
<h3 className="font-bold text-xl">Instructors</h3>
<StaffGrid members={[
{
name: "Jordan Hawkman",
section: "501",
pennkey: "jhawk24",
school: "seas",
pronouns: "he/him",
},
{
name: "Anthony Li",
section: "501",
flavorText: "OvercomplicatedView",
pennkey: "antli",
school: "seas",
pronouns: "he/him",
github: "anli5005",
website: "https://anli.dev",
},
{
name: "Yuying Fan",
section: "502",
pennkey: "yuyingf",
school: "seas",
pronouns: "she/her",
},
]} />

<h3 className="mt-4 text-xl font-bold">TAs</h3>
<StaffGrid members={[
{
name: "TBD",
pennkey: "tbd",
school: "tbd",
pronouns: "TBD",
},
]} />
</Card>
6 changes: 6 additions & 0 deletions content/pages/resources.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: Resources
activeMenuItem: resources
---

They exist
1 change: 1 addition & 0 deletions content/pages/syllabus.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Syllabus
activeMenuItem: syllabus
---

## Grading
Expand Down
2 changes: 2 additions & 0 deletions contentlayer.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export const Page = defineDocumentType(() => ({
contentType: 'mdx',
fields: {
title: { type: 'string', required: true },
activeMenuItem: { type: 'string', required: false },
customLayout: { type: 'boolean', required: false },
},
computedFields: {
slug: {
Expand Down
Loading

0 comments on commit cf4eb8a

Please sign in to comment.