Skip to content

Commit

Permalink
feat: Add table of contents
Browse files Browse the repository at this point in the history
  • Loading branch information
Nos43ratu committed Nov 27, 2024
1 parent 2c49e2d commit 8154a5d
Show file tree
Hide file tree
Showing 12 changed files with 430 additions and 263 deletions.
15 changes: 5 additions & 10 deletions app/components/Doc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,22 @@ import { FaEdit } from 'react-icons/fa'
import { DocTitle } from '~/components/DocTitle'
import { Markdown } from '~/components/Markdown'

export function Doc({
title,
content,
repo,
branch,
filePath,
}: {
type DocProps = {
title: string
content: string
repo: string
branch: string
filePath: string
}) {
} & ({ code: string; markup?: never } | { markup: string; code?: never })

export function Doc({ title, repo, branch, filePath, code, markup }: DocProps) {
return (
<div className="p-4 lg:p-6 overflow-auto w-full bg-white/70 dark:bg-black/30 m-8 rounded-xl">
{title ? <DocTitle>{title}</DocTitle> : null}
<div className="h-4" />
<div className="h-px bg-gray-500 opacity-20" />
<div className="h-4" />
<div className="prose prose-gray prose-sm prose-p:leading-7 dark:prose-invert max-w-none">
<Markdown code={content} />
<Markdown code={code} markup={markup} />
</div>
<div className="h-12" />
<div className="w-full h-px bg-gray-500 opacity-30" />
Expand Down
157 changes: 157 additions & 0 deletions app/components/DocContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import * as React from 'react'
import { FaTimes } from 'react-icons/fa'
import { twMerge } from 'tailwind-merge'
import { useLoaderData, useMatches } from '@tanstack/react-router'
import { marked } from 'marked'
import markedAlert from 'marked-alert'
import { gfmHeadingId, getHeadingList } from 'marked-gfm-heading-id'
import { Toc } from '~/components/Toc'
import { Doc } from '~/components/Doc'
import { Carbon } from '~/components/Carbon'
import { Partners } from '~/components/Partners'
import { useMenuConfig } from '~/components/DocsLayout'
import { DocPagination } from '~/components/DocPagination'
import { DocsCalloutBytes } from '~/components/DocsCalloutBytes'
import { DocsCalloutQueryGG } from '~/components/DocsCalloutQueryGG'
import { Library } from '~/libraries'
import { useLocalStorage } from '~/utils/useLocalStorage'

export function DocContent({
title,
content,
branch,
filePath,
library,
}: {
title: string
content: string
branch: string
filePath: string
library: Library
}) {
const matches = useMatches()
const [showBytes, setShowBytes] = useLocalStorage('showBytes', true)
const { config } = useLoaderData({ from: '/$libraryId/$version/docs' })
const menuConfig = useMenuConfig({
config,
frameworks: library.frameworks,
repo: library.repo,
})

const { markup, headings } = React.useMemo(() => {
const markup = marked.use(
{ gfm: true },
gfmHeadingId(),
markedAlert()
)(content) as string

const headings = getHeadingList()

return { markup, headings }
}, [content])

const isExample = matches.some((d) => d.pathname.includes('/examples/'))

return (
<>
<div
className={twMerge(
`max-w-full min-w-0 flex relative justify-center w-full min-h-[88dvh] lg:min-h-0`,
!isExample && 'mx-auto w-[900px]'
)}
>
<Doc
title={title}
repo={library.repo}
branch={branch}
markup={markup}
filePath={filePath}
/>
<DocPagination
config={menuConfig}
colorFrom={library.colorFrom}
colorTo={library.colorTo}
textColor={library.textColor}
/>
</div>
<div className="-ml-2 pl-2 w-64 hidden md:block sticky top-0 max-h-screen overflow-y-auto">
<div className="ml-auto flex flex-col space-y-4">
<Partners libraryId={library.id} repo={library.repo} />

{headings.length ? (
<Toc
headings={headings}
colorFrom={library.colorFrom}
colorTo={library.colorTo}
/>
) : null}

<div className="p-4 bg-white dark:bg-gray-900/30 border-b border-gray-500/20 shadow-xl divide-y divide-gray-500/20 flex flex-col border-t border-l rounded-l-lg">
{library.id === 'query' ? (
<DocsCalloutQueryGG />
) : (
<DocsCalloutBytes />
)}
</div>

<div className="bg-white dark:bg-gray-900/20 border-gray-500/20 shadow-xl flex flex-col border-t border-l border-b p-4 space-y-2 rounded-l-lg">
<Carbon />
<div
className="text-[.7rem] bg-gray-500 bg-opacity-10 py-1 px-2 rounded text-gray-500 italic
dark:bg-opacity-20 self-center opacity-50 hover:opacity-100 transition-opacity"
>
This ad helps to keep us from burning out and rage-quitting OSS
just *that* much more, so chill. 😉
</div>
</div>
</div>
</div>
{showBytes ? (
<div className="w-[300px] max-w-[350px] fixed md:hidden top-1/2 right-2 z-30 -translate-y-1/2 shadow-lg">
<div className="bg-white dark:bg-gray-900/30 border border-black/10 dark:border-white/10 p-4 md:p-6 rounded-lg">
{library.id === 'query' ? (
<DocsCalloutQueryGG />
) : (
<DocsCalloutBytes />
)}
<button
className="absolute top-0 right-0 p-2 hover:text-red-500 opacity:30 hover:opacity-100"
onClick={() => {
setShowBytes(false)
}}
>
<FaTimes />
</button>
</div>
</div>
) : (
<button
className="right-0 top-1/2 -translate-y-[50px] fixed lg:hidden"
onClick={() => {
setShowBytes(true)
}}
>
<div
className="origin-bottom-right -rotate-90 text-xs bg-white dark:bg-gray-800 border border-gray-100
hover:bg-rose-600 hover:text-white p-1 px-2 rounded-t-md shadow-md dark:border-0"
>
{library.id === 'query' ? (
<>
<strong>
<span role="img" aria-label="crystal ball">
&#128302;
</span>{' '}
Skip the docs?
</strong>
</>
) : (
<>
Subscribe to <strong>Bytes</strong>
</>
)}
</div>
</button>
)}
</>
)
}
75 changes: 75 additions & 0 deletions app/components/DocPagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import * as React from 'react'
import { Link, useMatches } from '@tanstack/react-router'
import { FaArrowLeft, FaArrowRight } from 'react-icons/fa'
import { last } from '~/utils/utils'
import { MenuItem } from '~/utils/config'

type DocPaginationProps = {
config: MenuItem[]
colorFrom: string
colorTo: string
textColor: string
}

export function DocPagination({
config,
colorFrom,
colorTo,
textColor,
}: DocPaginationProps) {
const matches = useMatches()

const flatMenu = React.useMemo(
() => config.flatMap((d) => d?.children),
[config]
)

const lastMatch = last(matches)
const docsMatch = matches.find((d) => d.pathname.includes('/docs'))

const relativePathname = lastMatch.pathname.replace(
docsMatch!.pathname + '/',
''
)

const index = flatMenu.findIndex((d) => d?.to === relativePathname)
const prevItem = flatMenu[index - 1]
const nextItem = flatMenu[index + 1]

return (
<div className="fixed flex items-center flex-wrap bottom-2 left-0 lg:left-[250px] z-10 right-0 text-xs md:text-sm px-1">
<div className="w-1/2 px-1 flex justify-end flex-wrap">
{prevItem ? (
<Link
to={prevItem.to}
params
className="py-1 px-2 bg-white/70 text-black dark:bg-gray-500/40 dark:text-white shadow-lg shadow-black/20 flex items-center justify-center backdrop-blur-sm z-20 rounded-lg overflow-hidden"
>
<div className="flex gap-2 items-center font-bold">
<FaArrowLeft />
{prevItem.label}
</div>
</Link>
) : null}
</div>
<div className="w-1/2 px-1 flex justify-start flex-wrap">
{nextItem ? (
<Link
to={nextItem.to}
params
className="py-1 px-2 bg-white/70 text-black dark:bg-gray-500/40 dark:text-white shadow-lg shadow-black/20 flex items-center justify-center backdrop-blur-sm z-20 rounded-lg overflow-hidden"
>
<div className="flex gap-2 items-center font-bold">
<span
className={`bg-gradient-to-r ${colorFrom} ${colorTo} bg-clip-text text-transparent`}
>
{nextItem.label}
</span>{' '}
<FaArrowRight className={textColor} />
</div>
</Link>
) : null}
</div>
</div>
)
}
Loading

0 comments on commit 8154a5d

Please sign in to comment.