Skip to content

Commit

Permalink
menu
Browse files Browse the repository at this point in the history
  • Loading branch information
abernier committed Sep 5, 2024
1 parent db13b4c commit 7c4814a
Show file tree
Hide file tree
Showing 12 changed files with 275 additions and 178 deletions.
21 changes: 21 additions & 0 deletions src/app/[...slug]/Burger.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use client'

import Icon from '@/components/Icon'
import cn from '@/lib/cn'
import { ComponentProps } from 'react'
import { useMenu } from './MenuContext'

export function Burger({ className }: ComponentProps<'button'>) {
const [menuOpen, setMenuOpen] = useMenu()

return (
<button
className={cn(className, 'flex size-9 items-center justify-center')}
type="button"
aria-label="Menu"
onClick={() => setMenuOpen(!menuOpen)}
>
{menuOpen ? <Icon icon="close" /> : <Icon icon="menu" />}
</button>
)
}
96 changes: 16 additions & 80 deletions src/app/[...slug]/Menu.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,28 @@
'use client'

import Icon from '@/components/Icon'
import { useLockBodyScroll } from '@/hooks/useLockBodyScroll'
import { Nav } from '@/components/Nav'
import cn from '@/lib/cn'
import * as React from 'react'
import { ComponentProps, ElementRef, useEffect, useRef } from 'react'
import { useDocs } from './DocsContext'
import { useMenu } from './MenuContext'

export function Menu({
header,
nav,
children,
aside,
footer,
}: {
header: React.ReactNode
nav: React.ReactNode
children: React.ReactNode
aside: React.ReactNode
footer: React.ReactNode
}) {
const { doc } = useDocs()
export function Menu({ className, asPath }: ComponentProps<'dialog'> & { asPath: string }) {
const { doc, docs } = useDocs()

const [menuOpen, setMenuOpen] = useMenu()
useLockBodyScroll(menuOpen)
const [opened, setOpened] = useMenu()
const dialogRef = useRef<ElementRef<'dialog'>>(null)

React.useEffect(() => setMenuOpen(false), [setMenuOpen])

const NEXT_PUBLIC_LIBNAME = process.env.NEXT_PUBLIC_LIBNAME
useEffect(() => {
if (opened) {
dialogRef.current?.show()
} else {
dialogRef.current?.close()
}
}, [opened])

return (
<>
<header className="max-w-8xl bg-surface sticky top-0 z-40 mx-auto flex w-full flex-none border-b border-outline-variant/50 lg:z-50">
<div className="flex w-full items-center justify-between pr-2">
{header}
<button
className="flex size-9 items-center justify-center lg:hidden"
onClick={() => setMenuOpen((v) => !v)}
type="button"
aria-label="Menu"
>
<Icon icon="menu" />
</button>
</div>
</header>

<div className="max-w-8xl mx-auto w-full">
<div className="lg:flex">
<div
id="sidebar"
className={cn(
'fixed inset-0 z-40 h-full w-full flex-none lg:static lg:block lg:h-auto lg:w-60 lg:overflow-y-visible lg:pt-0 xl:w-72',
!menuOpen && 'hidden',
)}
>
<div
id="nav-wrapper"
className="scrolling-touch bg-surface relative z-10 mr-24 h-full overflow-hidden overflow-y-auto lg:sticky lg:top-16 lg:mr-0 lg:block lg:h-auto lg:bg-transparent"
>
<nav
id="nav"
className="sticky?lg:h-(screen-16) relative z-10 overflow-y-auto px-4 pb-10 pl-0 lg:pb-14 lg:text-sm"
>
{nav}
</nav>
</div>
<button
onClick={() => setMenuOpen(false)}
className={cn(
'bg-surface/70',
'fixed right-0 top-0 z-0 h-screen w-screen',
!menuOpen && 'hidden',
)}
/>
</div>
<div id="content-wrapper" className={cn('flex-auto', menuOpen && 'overflow-hidden')}>
<div className="flex w-full">
<main className="min-w-0 flex-auto px-4 pb-24 pt-8 sm:px-6 lg:pb-16 xl:px-8">
<div>{children}</div>

<footer>{footer}</footer>
</main>

<aside className="hidden w-64 flex-none pl-8 pr-8 xl:block xl:text-sm">{aside}</aside>
</div>
</div>
</div>
</div>
</>
<dialog ref={dialogRef} className={cn(className, 'bg-surface-dim/95 backdrop-blur-xl')}>
<Nav docs={docs} asPath={asPath} collapsible={false} />
</dialog>
)
}
16 changes: 15 additions & 1 deletion src/app/[...slug]/MenuContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use client'

import { useLockBodyScroll } from '@/hooks/useLockBodyScroll'
import { createRequiredContext } from '@/lib/createRequiredContext'
import { Dispatch, ReactNode, SetStateAction, useState } from 'react'
import { Dispatch, ElementRef, ReactNode, SetStateAction, useEffect, useRef, useState } from 'react'

export type Ctx = [boolean, Dispatch<SetStateAction<boolean>>]

Expand All @@ -12,5 +13,18 @@ export { hook as useMenu }
export function MenuContext({ children }: { children?: ReactNode }) {
const state = useState(false)

const [opened, setOpened] = state
const dialogRef = useRef<ElementRef<'dialog'>>(null)

useEffect(() => {
if (opened) {
dialogRef.current?.show()
} else {
dialogRef.current?.close()
}
}, [opened])

useLockBodyScroll(opened)

return <Provider value={state}>{children}</Provider>
}
103 changes: 62 additions & 41 deletions src/app/[...slug]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import { getData } from '@/utils/docs'
import Link from 'next/link'
import { PiDiscordLogoLight } from 'react-icons/pi'
import { VscGithubAlt } from 'react-icons/vsc'
import { Burger } from './Burger'
import { DocsContext } from './DocsContext'
import { Menu } from './Menu'
import { MenuContext } from './MenuContext'

export type Props = {
params: { slug: string[] }
Expand All @@ -29,8 +32,16 @@ export default async function Layout({ params, children }: Props) {
const NEXT_PUBLIC_LIBNAME = process.env.NEXT_PUBLIC_LIBNAME
const NEXT_PUBLIC_LIBNAME_SHORT = process.env.NEXT_PUBLIC_LIBNAME_SHORT

const nav = (
<Nav
docs={docs}
asPath={asPath}
collapsible
className="sticky top-[calc(var(--header-height)+theme(spacing.8))]"
/>
)
const header = (
<div className="flex h-16 items-center gap-[--rgrid-m] px-[--rgrid-m]">
<div className="flex h-[--header-height] items-center gap-[--rgrid-m] px-[--rgrid-m]">
<div className="flex items-center">
<Link href="/" aria-label="Poimandres Docs">
<span className="font-bold">
Expand All @@ -49,26 +60,26 @@ export default async function Layout({ params, children }: Props) {

<Search />

<div className="flex gap-0">
{process.env.GITHUB && (
<Link
href={process.env.GITHUB}
className="flex size-9 items-center justify-center"
target="_blank"
>
<VscGithubAlt />
</Link>
)}
{process.env.DISCORD && (
<Link
href={process.env.DISCORD}
className="flex size-9 items-center justify-center"
target="_blank"
>
<PiDiscordLogoLight />
</Link>
)}
<div className="flex">
{[
{ href: process.env.GITHUB, icon: <VscGithubAlt /> },
{ href: process.env.DISCORD, icon: <PiDiscordLogoLight /> },
].map(({ href, icon }) => (
<>
{href && (
<Link
href={href}
className={cn('hidden size-9 items-center justify-center lg:flex')}
target="_blank"
>
{icon}
</Link>
)}
</>
))}
{/* <ToggleTheme className="hidden size-9 items-center justify-center sm:flex" /> */}

<Burger className="lg:hidden" />
</div>
</div>
)
Expand Down Expand Up @@ -108,7 +119,7 @@ export default async function Layout({ params, children }: Props) {
)}

{(!!previousPage || !!nextPage) && (
<nav className="my-32">
<nav className="my-16 lg:my-32">
<div className="flex flex-col gap-6 sm:flex-row sm:justify-between">
{!!previousPage && (
<div className="">
Expand Down Expand Up @@ -145,31 +156,41 @@ export default async function Layout({ params, children }: Props) {
)}
</>
)
const nav = <Nav docs={docs} asPath={asPath} />
const toc = <Toc toc={doc.tableOfContents.filter(({ level }) => level > 0)} />

const toc = (
<Toc
toc={doc.tableOfContents.filter(({ level }) => level > 0)}
className="sticky top-[calc(var(--header-height)+theme(spacing.12))]"
/>
)

return (
<>
<DocsContext value={{ docs, doc }}>
{/* <MenuContext> */}
<div className="[--side-w:theme(spacing.72)]">
<header className="border-b border-outline-variant/50 bg-surface/95 backdrop-blur-xl">
{header}
</header>
<div className="lg:flex lg:gap-[--rgrid-g]">
<nav className="hidden lg:block lg:w-[--side-w] lg:flex-none">{nav}</nav>
<main
className={cn('lg:min-w-0 lg:flex-1 lg:pr-[--rgrid-m] xl:flex xl:gap-[--rgrid-g]')}
>
<article className="fooo lg:min-w-0 lg:flex-1">
{children}
{footer}
</article>
<aside className="hidden lg:flex-none xl:block xl:w-[--side-w]">{toc}</aside>
</main>
<MenuContext>
<div className="[--side-w:theme(spacing.72)]">
<header className="sticky top-0 z-10 border-b border-outline-variant/50 bg-surface/95 backdrop-blur-xl">
{header}
<Menu
asPath={asPath}
className="z-100 left-0 top-[--header-height] h-[calc(100dvh-var(--header-height))] w-full overflow-auto lg:hidden"
/>
</header>

<div className="lg:flex lg:gap-[--rgrid-g]">
<nav className="hidden lg:block lg:w-[--side-w] lg:flex-none">{nav}</nav>
<main
className={cn('lg:min-w-0 lg:flex-1 lg:pr-[--rgrid-m] xl:flex xl:gap-[--rgrid-g]')}
>
<article className="post-container lg:min-w-0 lg:flex-1">
{children}
{footer}
</article>
<aside className="hidden lg:flex-none xl:block xl:w-[--side-w]">{toc}</aside>
</main>
</div>
</div>
</div>
{/* </MenuContext> */}
</MenuContext>
</DocsContext>
</>
)
Expand Down
2 changes: 1 addition & 1 deletion src/app/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default async function Page({ params }: Props) {

return (
<>
<div className={cn('my-6 border-b', 'border-outline-variant/50')}>
<div className={cn('my-8 border-b', 'border-outline-variant/50')}>
<h1 className="text-5xl font-bold tracking-tighter">{doc.title}</h1>
{!!doc?.description?.length && (
<p className={cn('my-2 text-base leading-5', 'text-on-surface-variant/50')}>
Expand Down
12 changes: 6 additions & 6 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
/* Write your own custom base styles here */

:root {
@apply [--rgrid-m:theme(spacing.4)] lg:[--rgrid-m:theme(spacing.6)];
@apply [--rgrid-g:theme(spacing.4)] lg:[--rgrid-g:theme(spacing.12)];
@apply [--header-height:theme(spacing.16)];

scroll-behavior: smooth;
--header-height: 4rem; /* TODO: make this value dynamic */
scroll-margin-top: var(--header-height);
font-size: 17px;

@apply [--rgrid-m:theme(spacing.4)] lg:[--rgrid-m:theme(spacing.6)];
@apply [--rgrid-g:theme(spacing.4)] lg:[--rgrid-g:theme(spacing.12)];
}

body {
Expand All @@ -25,10 +25,10 @@ body {
/* Stop purging. */

main :target {
scroll-margin-top: theme('spacing.20'); /* <Search />'s h-16 + more spacing */
@apply scroll-m-[calc(var(--header-height)+theme(spacing.4))];
}

.fooo > * {
.post-container > * {
@apply me-auto ms-auto block max-w-[calc(100%-2*var(--rgrid-m))] lg:max-w-4xl;

&.sandpack {
Expand Down
15 changes: 15 additions & 0 deletions src/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ const icons = {
<line x1="3" y1="18" x2="21" y2="18" />
</svg>
),
close: (props: SVGProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
{...props}
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
),
enter: (props: SVGProps) => (
<svg
viewBox="0 0 24 24"
Expand Down
Loading

0 comments on commit 7c4814a

Please sign in to comment.