diff --git a/app/components/Doc.tsx b/app/components/Doc.tsx index 68010205..a351224c 100644 --- a/app/components/Doc.tsx +++ b/app/components/Doc.tsx @@ -1,6 +1,23 @@ +import * as React from 'react' import { FaEdit } from 'react-icons/fa' +import { marked } from 'marked' +import markedAlert from 'marked-alert' +import { gfmHeadingId, getHeadingList } from 'marked-gfm-heading-id' import { DocTitle } from '~/components/DocTitle' import { Markdown } from '~/components/Markdown' +import { Toc } from './Toc' +import { twMerge } from 'tailwind-merge' + +type DocProps = { + title: string + content: string + repo: string + branch: string + filePath: string + shouldRenderToc?: boolean + colorFrom?: string + colorTo?: string +} export function Doc({ title, @@ -8,33 +25,69 @@ export function Doc({ repo, branch, filePath, -}: { - title: string - content: string - repo: string - branch: string - filePath: string -}) { + shouldRenderToc = false, + colorFrom, + colorTo, +}: DocProps) { + const { markup, headings } = React.useMemo(() => { + const markup = marked.use( + { gfm: true }, + gfmHeadingId(), + markedAlert() + )(content) as string + + const headings = getHeadingList() + + return { markup, headings } + }, [content]) + + const isTocVisible = shouldRenderToc && headings && headings.length > 1 + return ( -
- {title ? {title} : null} -
-
-
-
- -
-
-
-
- +
+
- Edit on GitHub - + {title ? {title} : null} +
+
+
+
+ +
+
+
+ +
+
+ + {isTocVisible && ( +
+ +
+ )}
-
) } diff --git a/app/components/DocsLayout.tsx b/app/components/DocsLayout.tsx index ad909667..004cfec2 100644 --- a/app/components/DocsLayout.tsx +++ b/app/components/DocsLayout.tsx @@ -562,7 +562,7 @@ export function DocsLayout({
{children} @@ -601,7 +601,7 @@ export function DocsLayout({
-
+
diff --git a/app/components/Markdown.tsx b/app/components/Markdown.tsx index d46905cd..ef76af3b 100644 --- a/app/components/Markdown.tsx +++ b/app/components/Markdown.tsx @@ -206,33 +206,41 @@ const getHighlighter = cache(async (language: string, themes: string[]) => { return highlighter }) -export function Markdown({ code }: { code: string }) { - const jsx = React.useMemo(() => { - const markup = marked.use( - { gfm: true }, - gfmHeadingId(), - markedAlert() - )(code) as string - - const options: HTMLReactParserOptions = { - replace: (domNode) => { - if (domNode instanceof Element && domNode.attribs) { - const replacer = markdownComponents[domNode.name] - if (replacer) { - return React.createElement( - replacer, - attributesToProps(domNode.attribs), - domToReact(domNode.children, options) - ) - } - } +const options: HTMLReactParserOptions = { + replace: (domNode) => { + if (domNode instanceof Element && domNode.attribs) { + const replacer = markdownComponents[domNode.name] + if (replacer) { + return React.createElement( + replacer, + attributesToProps(domNode.attribs), + domToReact(domNode.children, options) + ) + } + } + + return + }, +} + +type MarkdownProps = { rawContent?: string; htmlMarkup?: string } - return - }, +export function Markdown({ rawContent, htmlMarkup }: MarkdownProps) { + return React.useMemo(() => { + if (rawContent) { + const markup = marked.use( + { gfm: true }, + gfmHeadingId(), + markedAlert() + )(rawContent) as string + + return parse(markup, options) } - return parse(markup, options) - }, [code]) + if (htmlMarkup) { + return parse(htmlMarkup, options) + } - return jsx + return null + }, [rawContent, htmlMarkup]) } diff --git a/app/components/Toc.tsx b/app/components/Toc.tsx new file mode 100644 index 00000000..bf529659 --- /dev/null +++ b/app/components/Toc.tsx @@ -0,0 +1,59 @@ +import * as React from 'react' +import { twMerge } from 'tailwind-merge' +import { HeadingData } from 'marked-gfm-heading-id' +import { useLocation } from '@tanstack/react-router' + +const headingLevels: Record = { + 1: 'pl-2', + 2: 'pl-2', + 3: 'pl-6', + 4: 'pl-10', + 5: 'pl-14', + 6: 'pl-16', +} + +type TocProps = { + headings: HeadingData[] + colorFrom?: string + colorTo?: string +} + +export function Toc({ headings, colorFrom, colorTo }: TocProps) { + const location = useLocation() + + const [hash, setHash] = React.useState('') + + React.useEffect(() => { + setHash(location.hash) + }, [location]) + + return ( + + ) +} diff --git a/app/routes/$libraryId/$version.docs.$.tsx b/app/routes/$libraryId/$version.docs.$.tsx index de31e616..951a91c5 100644 --- a/app/routes/$libraryId/$version.docs.$.tsx +++ b/app/routes/$libraryId/$version.docs.$.tsx @@ -44,6 +44,9 @@ function Docs() { repo={library.repo} branch={branch} filePath={filePath} + colorFrom={library.colorFrom} + colorTo={library.colorTo} + shouldRenderToc /> ) } diff --git a/app/routes/$libraryId/$version.docs.framework.$framework.$.tsx b/app/routes/$libraryId/$version.docs.framework.$framework.$.tsx index dd68d322..0df92261 100644 --- a/app/routes/$libraryId/$version.docs.framework.$framework.$.tsx +++ b/app/routes/$libraryId/$version.docs.framework.$framework.$.tsx @@ -50,6 +50,9 @@ function Docs() { repo={library.repo} branch={branch} filePath={filePath} + colorFrom={library.colorFrom} + colorTo={library.colorTo} + shouldRenderToc /> ) } diff --git a/app/routes/$libraryId/$version.docs.framework.$framework.examples.$.tsx b/app/routes/$libraryId/$version.docs.framework.$framework.examples.$.tsx index a9b8af7f..d0f0d9c4 100644 --- a/app/routes/$libraryId/$version.docs.framework.$framework.examples.$.tsx +++ b/app/routes/$libraryId/$version.docs.framework.$framework.examples.$.tsx @@ -9,7 +9,7 @@ import { seo } from '~/utils/seo' import { capitalize, slugToTitle } from '~/utils/utils' export const Route = createFileRoute( - '/$libraryId/$version/docs/framework/$framework/examples/$', + '/$libraryId/$version/docs/framework/$framework/examples/$' )({ head: ({ params }) => { const library = getLibrary(params.libraryId) @@ -17,10 +17,10 @@ export const Route = createFileRoute( return { meta: seo({ title: `${capitalize(params.framework)} ${library.name} ${slugToTitle( - params._splat || '', + params._splat || '' )} Example | ${library.name} Docs`, description: `An example showing how to implement ${slugToTitle( - params._splat || '', + params._splat || '' )} in ${capitalize(params.framework)} using ${library.name}.`, }), } diff --git a/app/routes/$libraryId/route.tsx b/app/routes/$libraryId/route.tsx index 4dfc9147..bd34aa43 100644 --- a/app/routes/$libraryId/route.tsx +++ b/app/routes/$libraryId/route.tsx @@ -15,8 +15,8 @@ export const Route = createFileRoute('/$libraryId')({ .map( (framework) => `${framework.charAt(0).toUpperCase()}${framework.slice( - 1, - )} ${library.name.replace('TanStack ', '')}`, + 1 + )} ${library.name.replace('TanStack ', '')}` ) .join(', ')}` : '', diff --git a/app/routes/_libraries/blog.$.tsx b/app/routes/_libraries/blog.$.tsx index bb0658b9..06ad7598 100644 --- a/app/routes/_libraries/blog.$.tsx +++ b/app/routes/_libraries/blog.$.tsx @@ -66,7 +66,7 @@ export default function BlogPost() { const blogContent = `_by ${formatAuthors(authors)} on ${format( new Date(published || 0), - 'MMM dd, yyyy', + 'MMM dd, yyyy' )}._ ${content}` diff --git a/app/routes/_libraries/blog.index.tsx b/app/routes/_libraries/blog.index.tsx index cacc280d..085aef5a 100644 --- a/app/routes/_libraries/blog.index.tsx +++ b/app/routes/_libraries/blog.index.tsx @@ -116,7 +116,7 @@ function BlogIndex() {
- +
diff --git a/app/routes/_libraries/dedicated-support.tsx b/app/routes/_libraries/dedicated-support.tsx index c613d599..13250811 100644 --- a/app/routes/_libraries/dedicated-support.tsx +++ b/app/routes/_libraries/dedicated-support.tsx @@ -22,7 +22,7 @@ export const Route = createFileRoute('/_libraries/dedicated-support')({ let indices = shuffle( Array.from({ length: teamMembers.length - 1 }) .fill(0) - .map((_, i) => i + 1), + .map((_, i) => i + 1) ) return indices diff --git a/app/routes/_libraries/index.tsx b/app/routes/_libraries/index.tsx index 7e9e67f9..e6b19816 100644 --- a/app/routes/_libraries/index.tsx +++ b/app/routes/_libraries/index.tsx @@ -190,7 +190,7 @@ function Index() { params className={twMerge( `border-4 border-transparent rounded-lg shadow-lg p-4 md:p-8 text-white transition-all bg-white dark:bg-gray-900 dark:border dark:border-gray-800`, - library.cardStyles, + library.cardStyles )} style={{ zIndex: i, @@ -204,7 +204,7 @@ function Index() { return ( - ), + ) )}
diff --git a/app/routes/_libraries/table.$version.index.tsx b/app/routes/_libraries/table.$version.index.tsx index 0e64174a..b8d928a3 100644 --- a/app/routes/_libraries/table.$version.index.tsx +++ b/app/routes/_libraries/table.$version.index.tsx @@ -312,7 +312,7 @@ export default function TableVersionIndex() { 'TicketMaster', 'Comcast Business', 'Nozzle.io', - ], + ] ) .map((d, i) => ( diff --git a/tailwind.config.cjs b/tailwind.config.cjs index 1ac4dc8a..e61293d3 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -20,6 +20,9 @@ module.exports = { inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.04)', none: 'none', }, + aria: { + current: 'current="location"', + }, colors: { // red: { // default: '#FF4255',