done with distributive services
- duplicated side bar still exists
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
// sample code from https://docsearch.algolia.com/docs/docsearch
|
||||
|
||||
import { DocSearch } from '@docsearch/react';
|
||||
import { useThemeConfig } from 'nextra-theme-docs'
|
||||
|
||||
import '@docsearch/css';
|
||||
|
||||
@@ -11,6 +12,7 @@ function AlgoliaSearch() {
|
||||
appId={process.env.NEXT_SEARCH_ALGOLIA_APP_ID || 'NKGLZZZUBC'}
|
||||
indexName={process.env.NEXT_SEARCH_ALGOLIA_INDEX_NAME || 'notenextra_trance_0'}
|
||||
apiKey={process.env.NEXT_SEARCH_ALGOLIA_API_KEY || '727b389a61e862e590dfab9ce9df31a2'}
|
||||
theme={useThemeConfig().darkMode ? 'dark' : 'light'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
188
components/navbar.client.tsx
Normal file
188
components/navbar.client.tsx
Normal file
@@ -0,0 +1,188 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
MenuItem as _MenuItem,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItems
|
||||
} from '@headlessui/react'
|
||||
import cn from 'clsx'
|
||||
import { Anchor, Button } from 'nextra/components'
|
||||
import { useFSRoute } from 'nextra/hooks'
|
||||
import { ArrowRightIcon, MenuIcon } from 'nextra/icons'
|
||||
import type { MenuItem } from 'nextra/normalize-pages'
|
||||
import type { FC, ReactNode } from 'react'
|
||||
import { setMenu, useConfig, useMenu, useThemeConfig } from 'nextra-theme-docs'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { normalizePages } from 'nextra/normalize-pages'
|
||||
import { PageMapItem } from 'nextra'
|
||||
|
||||
const classes = {
|
||||
link: cn(
|
||||
'x:text-sm x:contrast-more:text-gray-700 x:contrast-more:dark:text-gray-100 x:whitespace-nowrap',
|
||||
'x:text-gray-600 x:hover:text-black x:dark:text-gray-400 x:dark:hover:text-gray-200',
|
||||
'x:ring-inset x:transition-colors'
|
||||
)
|
||||
}
|
||||
|
||||
const NavbarMenu: FC<{
|
||||
menu: MenuItem
|
||||
children: ReactNode
|
||||
}> = ({ menu, children }) => {
|
||||
const routes = Object.fromEntries(
|
||||
(menu.children || []).map(route => [route.name, route])
|
||||
)
|
||||
return (
|
||||
<Menu>
|
||||
<MenuButton
|
||||
className={({ focus }) =>
|
||||
cn(
|
||||
classes.link,
|
||||
'x:items-center x:flex x:gap-1.5 x:cursor-pointer',
|
||||
focus && 'x:nextra-focus'
|
||||
)
|
||||
}
|
||||
>
|
||||
{children}
|
||||
<ArrowRightIcon
|
||||
height="14"
|
||||
className="x:*:origin-center x:*:transition-transform x:*:rotate-90"
|
||||
/>
|
||||
</MenuButton>
|
||||
<MenuItems
|
||||
transition
|
||||
className={cn(
|
||||
'x:focus-visible:nextra-focus',
|
||||
'nextra-scrollbar x:motion-reduce:transition-none',
|
||||
// From https://headlessui.com/react/menu#adding-transitions
|
||||
'x:origin-top x:transition x:duration-200 x:ease-out x:data-closed:scale-95 x:data-closed:opacity-0',
|
||||
'x:border x:border-black/5 x:dark:border-white/20',
|
||||
'x:z-30 x:rounded-md x:py-1 x:text-sm x:shadow-lg',
|
||||
'x:backdrop-blur-md x:bg-nextra-bg/70',
|
||||
// headlessui adds max-height as style, use !important to override
|
||||
'x:max-h-[min(calc(100vh-5rem),256px)]!'
|
||||
)}
|
||||
anchor={{ to: 'bottom', gap: 10, padding: 16 }}
|
||||
>
|
||||
{Object.entries(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- fixme
|
||||
(menu.items as Record<string, { title: string; href?: string }>) || {}
|
||||
).map(([key, item]) => (
|
||||
<_MenuItem
|
||||
key={key}
|
||||
as={Anchor}
|
||||
href={item.href || routes[key]?.route}
|
||||
className={({ focus }) =>
|
||||
cn(
|
||||
'x:block x:py-1.5 x:transition-colors x:ps-3 x:pe-9',
|
||||
focus
|
||||
? 'x:text-gray-900 x:dark:text-gray-100'
|
||||
: 'x:text-gray-600 x:dark:text-gray-400'
|
||||
)
|
||||
}
|
||||
>
|
||||
{item.title}
|
||||
</_MenuItem>
|
||||
))}
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
|
||||
const isMenu = (page: any): page is MenuItem => page.type === 'menu'
|
||||
|
||||
export const ClientNavbar: FC<{
|
||||
pageMap: PageMapItem[]
|
||||
children: ReactNode
|
||||
className?: string
|
||||
}> = ({ pageMap, children, className }) => {
|
||||
|
||||
const { topLevelNavbarItems } = normalizePages({
|
||||
list: pageMap,
|
||||
route: usePathname()
|
||||
})
|
||||
|
||||
// filter out titles for elements in topLevelNavbarItems with non empty route
|
||||
const existingCourseNames = new Set(
|
||||
topLevelNavbarItems.filter(
|
||||
item => !('href' in item)
|
||||
).map(item => item.title)
|
||||
)
|
||||
// filter out elements in topLevelNavbarItems with url but have title in existingCourseNames
|
||||
const filteredTopLevelNavbarItems = topLevelNavbarItems.filter(item => !('href' in item && existingCourseNames.has(item.title)))
|
||||
|
||||
// or even better, remove all old doc page generated links
|
||||
// this don't make sense because it will destroy the navbar structure highlight for children pages
|
||||
// const filteredTopLevelNavbarItems = topLevelNavbarItems.filter(item => !('firstChildRoute' in item))
|
||||
|
||||
// const items = topLevelNavbarItems
|
||||
// use filteredTopLevelNavbarItems to generate items
|
||||
const items = filteredTopLevelNavbarItems
|
||||
|
||||
const themeConfig = useThemeConfig()
|
||||
|
||||
const pathname = useFSRoute()
|
||||
const menu = useMenu()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
'x:flex x:gap-4 x:overflow-x-auto nextra-scrollbar x:py-1.5 x:max-md:hidden',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{items.map((page, _index, arr) => {
|
||||
if ('display' in page && page.display === 'hidden') return
|
||||
if (isMenu(page)) {
|
||||
return (
|
||||
<NavbarMenu key={page.name} menu={page}>
|
||||
{page.title}
|
||||
</NavbarMenu>
|
||||
)
|
||||
}
|
||||
const href =
|
||||
// If it's a directory
|
||||
('frontMatter' in page ? page.route : page.firstChildRoute) ||
|
||||
page.href ||
|
||||
page.route
|
||||
|
||||
const isCurrentPage =
|
||||
href === pathname ||
|
||||
(pathname.startsWith(page.route + '/') &&
|
||||
arr.every(item => !('href' in item) || item.href !== pathname)) ||
|
||||
undefined
|
||||
|
||||
return (
|
||||
<Anchor
|
||||
href={href}
|
||||
key={page.name}
|
||||
className={cn(
|
||||
classes.link,
|
||||
'x:aria-[current]:font-medium x:aria-[current]:subpixel-antialiased x:aria-[current]:text-current'
|
||||
)}
|
||||
aria-current={isCurrentPage}
|
||||
>
|
||||
{page.title}
|
||||
</Anchor>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{themeConfig.search && (
|
||||
<div className="x:max-md:hidden">{themeConfig.search}</div>
|
||||
)}
|
||||
|
||||
{children}
|
||||
|
||||
<Button
|
||||
aria-label="Menu"
|
||||
className={({ active }) =>
|
||||
cn('nextra-hamburger x:md:hidden', active && 'x:bg-gray-400/20')
|
||||
}
|
||||
onClick={() => setMenu(prev => !prev)}
|
||||
>
|
||||
<MenuIcon height="24" className={cn({ open: menu })} />
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
170
components/navbar.tsx
Normal file
170
components/navbar.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
// customized navbar component, modified from https://github.com/shuding/nextra/blob/c8238813e1ba425cdd72783d57707b0ff3ca52ea/examples/custom-theme/app/_components/navbar.tsx#L9
|
||||
|
||||
// Rebuild from source code https://github.com/shuding/nextra/tree/c8238813e1ba425cdd72783d57707b0ff3ca52ea/packages/nextra-theme-docs/src/components/navbar
|
||||
|
||||
'use client'
|
||||
|
||||
import { usePathname } from 'next/navigation'
|
||||
import type { PageMapItem } from 'nextra'
|
||||
import { Anchor } from 'nextra/components'
|
||||
import { normalizePages } from 'nextra/normalize-pages'
|
||||
import type { FC, ReactNode } from 'react'
|
||||
|
||||
import cn from 'clsx'
|
||||
// eslint-disable-next-line no-restricted-imports -- since we don't need `newWindow` prop
|
||||
import NextLink from 'next/link'
|
||||
import { DiscordIcon, GitHubIcon } from 'nextra/icons'
|
||||
import { ClientNavbar } from './navbar.client'
|
||||
|
||||
// export const Navbar: FC<{ pageMap: PageMapItem[] }> = ({ pageMap }) => {
|
||||
// const pathname = usePathname()
|
||||
// const { topLevelNavbarItems } = normalizePages({
|
||||
// list: pageMap,
|
||||
// route: pathname
|
||||
// })
|
||||
// return (
|
||||
// <ul
|
||||
// style={{
|
||||
// display: 'flex',
|
||||
// listStyleType: 'none',
|
||||
// padding: 20,
|
||||
// gap: 20,
|
||||
// background: 'lightcoral',
|
||||
// margin: 0
|
||||
// }}
|
||||
// >
|
||||
// {filteredTopLevelNavbarItems.map(item => {
|
||||
|
||||
// const route = item.route || ('href' in item ? item.href! : '')
|
||||
// return (
|
||||
// <li key={route}>
|
||||
// <Anchor href={route} style={{ textDecoration: 'none' }}>
|
||||
// {item.title}
|
||||
// </Anchor>
|
||||
// </li>
|
||||
// )
|
||||
// })}
|
||||
// </ul>
|
||||
// )
|
||||
// }
|
||||
|
||||
|
||||
/* TODO: eslint typescript-sort-keys/interface: error */
|
||||
|
||||
interface NavbarProps {
|
||||
/**
|
||||
* Page map.
|
||||
*/
|
||||
pageMap: PageMapItem[]
|
||||
/**
|
||||
* Extra content after the last icon.
|
||||
*/
|
||||
children?: ReactNode
|
||||
/**
|
||||
* Specifies whether the logo should have a link or provides the URL for the logo's link.
|
||||
* @default true
|
||||
*/
|
||||
logoLink?: string | boolean
|
||||
/**
|
||||
* Logo of the website.
|
||||
*/
|
||||
logo: ReactNode
|
||||
/**
|
||||
* URL of the project homepage.
|
||||
*/
|
||||
projectLink?: string
|
||||
/**
|
||||
* Icon of the project link.
|
||||
* @default <GitHubIcon />
|
||||
*/
|
||||
projectIcon?: ReactNode
|
||||
/**
|
||||
* URL of the chat link.
|
||||
*/
|
||||
chatLink?: string
|
||||
/**
|
||||
* Icon of the chat link.
|
||||
* @default <DiscordIcon />
|
||||
*/
|
||||
chatIcon?: ReactNode
|
||||
/**
|
||||
* CSS class name.
|
||||
*/
|
||||
className?: string
|
||||
/**
|
||||
* Aligns navigation links to the specified side.
|
||||
* @default 'right'
|
||||
*/
|
||||
align?: 'left' | 'right'
|
||||
}
|
||||
|
||||
// Fix compiler error
|
||||
// Expression type `JSXElement` cannot be safely reordered
|
||||
const defaultGitHubIcon = (
|
||||
<GitHubIcon height="24" aria-label="Project repository" />
|
||||
)
|
||||
const defaultChatIcon = <DiscordIcon width="24" />
|
||||
|
||||
export const Navbar: FC<NavbarProps> = ({
|
||||
pageMap,
|
||||
children,
|
||||
logoLink = true,
|
||||
logo,
|
||||
projectLink,
|
||||
projectIcon = defaultGitHubIcon,
|
||||
chatLink,
|
||||
chatIcon = defaultChatIcon,
|
||||
className,
|
||||
align = 'right'
|
||||
}) => {
|
||||
const logoClass = cn(
|
||||
'x:flex x:items-center',
|
||||
align === 'left' ? 'x:max-md:me-auto' : 'x:me-auto'
|
||||
)
|
||||
return (
|
||||
<header
|
||||
className={cn(
|
||||
'nextra-navbar x:sticky x:top-0 x:z-30 x:w-full x:bg-transparent x:print:hidden',
|
||||
'x:max-md:[.nextra-banner:not([class$=hidden])~&]:top-(--nextra-banner-height)'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'nextra-navbar-blur',
|
||||
'x:absolute x:-z-1 x:size-full',
|
||||
'nextra-border x:border-b',
|
||||
'x:backdrop-blur-md x:bg-nextra-bg/70'
|
||||
)}
|
||||
/>
|
||||
<nav
|
||||
style={{ height: 'var(--nextra-navbar-height)' }}
|
||||
className={cn(
|
||||
'x:mx-auto x:flex x:max-w-(--nextra-content-width) x:items-center x:gap-4 x:pl-[max(env(safe-area-inset-left),1.5rem)] x:pr-[max(env(safe-area-inset-right),1.5rem)]',
|
||||
'x:justify-end',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{logoLink ? (
|
||||
<NextLink
|
||||
href={typeof logoLink === 'string' ? logoLink : '/'}
|
||||
className={cn(
|
||||
logoClass,
|
||||
'x:transition-opacity x:focus-visible:nextra-focus x:hover:opacity-75'
|
||||
)}
|
||||
aria-label="Home page"
|
||||
>
|
||||
{logo}
|
||||
</NextLink>
|
||||
) : (
|
||||
<div className={logoClass}>{logo}</div>
|
||||
)}
|
||||
<ClientNavbar pageMap={pageMap}
|
||||
className={align === 'left' ? 'x:me-auto' : ''}>
|
||||
{projectLink && <Anchor href={projectLink}>{projectIcon}</Anchor>}
|
||||
{chatLink && <Anchor href={chatLink}>{chatIcon}</Anchor>}
|
||||
{children}
|
||||
</ClientNavbar>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
@@ -26,180 +26,173 @@ export default {
|
||||
},
|
||||
/* Load link with relative path */
|
||||
Math3200_link: {
|
||||
title: 'Math 3200',
|
||||
title: 'Math3200',
|
||||
type: 'page',
|
||||
href: '/Math3200'
|
||||
},
|
||||
Math429_link: {
|
||||
title: 'Math 429',
|
||||
title: 'Math429',
|
||||
type: 'page',
|
||||
href: '/Math429'
|
||||
},
|
||||
Math4111_link: {
|
||||
title: 'Math 4111',
|
||||
title: 'Math4111',
|
||||
type: 'page',
|
||||
href: '/Math4111'
|
||||
},
|
||||
Math4121_link: {
|
||||
title: 'Math 4121',
|
||||
title: 'Math4121',
|
||||
type: 'page',
|
||||
href: '/Math4121'
|
||||
},
|
||||
Math4201_link: {
|
||||
title: 'Math 4201',
|
||||
title: 'Math4201',
|
||||
type: 'page',
|
||||
href: '/Math4201'
|
||||
},
|
||||
Math416_link: {
|
||||
title: 'Math 416',
|
||||
title: 'Math416',
|
||||
type: 'page',
|
||||
href: '/Math416'
|
||||
},
|
||||
Math401_link: {
|
||||
title: 'Math 401',
|
||||
title: 'Math401',
|
||||
type: 'page',
|
||||
href: '/Math401'
|
||||
},
|
||||
CSE332S_link: {
|
||||
title: 'CSE 332S',
|
||||
title: 'CSE332S',
|
||||
type: 'page',
|
||||
href: '/CSE332S'
|
||||
},
|
||||
CSE347_link: {
|
||||
title: 'CSE 347',
|
||||
title: 'CSE347',
|
||||
type: 'page',
|
||||
href: '/CSE347'
|
||||
},
|
||||
CSE442T_link: {
|
||||
title: 'CSE 442T',
|
||||
title: 'CSE442T',
|
||||
type: 'page',
|
||||
href: '/CSE442T'
|
||||
},
|
||||
CSE5313_link: {
|
||||
title: 'CSE 5313',
|
||||
title: 'CSE5313',
|
||||
type: 'page',
|
||||
href: '/CSE5313'
|
||||
},
|
||||
CSE510_link: {
|
||||
title: 'CSE 510',
|
||||
title: 'CSE510',
|
||||
type: 'page',
|
||||
href: '/CSE510'
|
||||
},
|
||||
CSE559A_link: {
|
||||
title: 'CSE 559A',
|
||||
title: 'CSE559A',
|
||||
type: 'page',
|
||||
href: '/CSE559A'
|
||||
},
|
||||
CSE5519_link: {
|
||||
title: 'CSE 5519',
|
||||
title: 'CSE5519',
|
||||
type: 'page',
|
||||
href: '/CSE5519'
|
||||
},
|
||||
/* Math Courses Start */
|
||||
Math3200: {
|
||||
display: 'hidden',
|
||||
title: 'Math3200',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
Math429:{
|
||||
display: 'hidden',
|
||||
title: 'Math429',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
Math4111: {
|
||||
display: 'hidden',
|
||||
title: 'Math4111',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
Math4121: {
|
||||
display: 'hidden',
|
||||
title: 'Math4121',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
Math4201: {
|
||||
display: 'hidden',
|
||||
title: 'Math4201',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
Math416: {
|
||||
display: 'hidden',
|
||||
title: 'Math416',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
Math401: {
|
||||
display: 'hidden',
|
||||
title: 'Math401',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
/* Math Courses End */
|
||||
/* CSE Courses Start */
|
||||
CSE332S: {
|
||||
display: 'hidden',
|
||||
title: 'CSE332S',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
CSE347: {
|
||||
display: 'hidden',
|
||||
title: 'CSE347',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
CSE442T: {
|
||||
display: 'hidden',
|
||||
title: 'CSE442T',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
CSE5313: {
|
||||
display: 'hidden',
|
||||
title: 'CSE5313',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
CSE510: {
|
||||
display: 'hidden',
|
||||
title: 'CSE510',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
CSE559A: {
|
||||
display: 'hidden',
|
||||
title: 'CSE559A',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
CSE5519: {
|
||||
display: 'hidden',
|
||||
title: 'CSE5519',
|
||||
type: 'page',
|
||||
theme:{
|
||||
sidebar: false,
|
||||
timestamp: true,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/* eslint-env node */
|
||||
import { Footer, Layout, Navbar } from 'nextra-theme-docs'
|
||||
import { Footer, Layout} from 'nextra-theme-docs'
|
||||
import { Banner, Head } from 'nextra/components'
|
||||
import { getPageMap } from 'nextra/page-map'
|
||||
import 'nextra-theme-docs/style.css'
|
||||
import { SpeedInsights } from "@vercel/speed-insights/next"
|
||||
import { Analytics } from "@vercel/analytics/react"
|
||||
import { Navbar } from '../components/navbar'
|
||||
|
||||
export const metadata = {
|
||||
metadataBase: new URL('https://notenextra.trance-0.com'),
|
||||
@@ -27,8 +28,10 @@ export const metadata = {
|
||||
}
|
||||
|
||||
export default async function RootLayout({ children }) {
|
||||
const pageMap = await getPageMap()
|
||||
const navbar = (
|
||||
<Navbar
|
||||
pageMap={pageMap}
|
||||
logo={
|
||||
<>
|
||||
<svg width="32" height="32" viewBox="0 0 16 16">
|
||||
@@ -42,7 +45,6 @@ export default async function RootLayout({ children }) {
|
||||
projectLink="https://github.com/Trance-0/NoteNextra"
|
||||
/>
|
||||
)
|
||||
const pageMap = await getPageMap()
|
||||
return (
|
||||
<html lang="en" dir="ltr" suppressHydrationWarning>
|
||||
<Head color={{
|
||||
|
||||
@@ -57,7 +57,7 @@ http {
|
||||
}
|
||||
|
||||
location ~ ^/Math(.*)$ {
|
||||
proxy_pass http://notenextra-math/$1;
|
||||
proxy_pass http://notenextra-math/Math$1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
@@ -65,7 +65,7 @@ http {
|
||||
}
|
||||
|
||||
location ~ ^/CSE(.*)$ {
|
||||
proxy_pass http://notenextra-cse/$1;
|
||||
proxy_pass http://notenextra-cse/CSE$1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
Reference in New Issue
Block a user