Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: apply components to mdx #638

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/wet-keys-work.md
@@ -0,0 +1,7 @@
---
'nextra': patch
'nextra-theme-blog': patch
'nextra-theme-docs': patch
---

fix missing mdx components
9 changes: 3 additions & 6 deletions packages/nextra-theme-blog/src/article-layout.tsx
@@ -1,6 +1,5 @@
import React, { ReactNode } from 'react'
import Meta from './meta'
import MDXTheme from './mdx-theme'
import { useBlogContext } from './blog-context'
import { BasicLayout } from './basic-layout'
import { getParent } from './utils/parent'
Expand All @@ -11,11 +10,9 @@ export const ArticleLayout = ({ children }: { children: ReactNode }) => {
return (
<BasicLayout>
<Meta {...opts.meta} back={back} config={config} />
<MDXTheme>
{children}
{config.postFooter}
{config.comments}
</MDXTheme>
{children}
{config.postFooter}
{config.comments}
</BasicLayout>
)
}
11 changes: 9 additions & 2 deletions packages/nextra-theme-blog/src/blog-context.tsx
@@ -1,4 +1,9 @@
import React, { ReactElement, useContext, createContext, ReactNode } from 'react'
import React, {
ReactElement,
useContext,
createContext,
ReactNode
} from 'react'
import { LayoutProps } from './types'
import { isValidDate } from './utils/date'

Expand All @@ -12,7 +17,9 @@ export const BlogProvider = ({
const { date } = opts.meta

if (date && !isValidDate(date)) {
throw new Error(`Invalid date "${date}". Provide date in "YYYY/M/D", "YYYY/M/D H:m", "YYYY-MM-DD", "[YYYY-MM-DD]T[HH:mm]" or "[YYYY-MM-DD]T[HH:mm:ss.SSS]Z" format.`)
throw new Error(
`Invalid date "${date}". Provide date in "YYYY/M/D", "YYYY/M/D H:m", "YYYY-MM-DD", "[YYYY-MM-DD]T[HH:mm]" or "[YYYY-MM-DD]T[HH:mm:ss.SSS]Z" format.`
)
}
return (
<BlogContext.Provider value={{ config, opts }}>
Expand Down
46 changes: 17 additions & 29 deletions packages/nextra-theme-blog/src/index.tsx
@@ -1,4 +1,4 @@
import React, { ReactElement, ReactNode, FC } from 'react'
import React, { ReactElement, ReactNode, FC, PropsWithChildren } from 'react'
import { ThemeProvider } from 'next-themes'
import type { PageOpts } from 'nextra'
import type { LayoutProps, NextraBlogTheme } from './types'
Expand All @@ -7,7 +7,8 @@ import { ArticleLayout } from './article-layout'
import { PostsLayout } from './posts-layout'
import { PageLayout } from './page-layout'
import { DEFAULT_CONFIG } from './constants'
import { useRouter } from 'next/router'
import { components } from './mdx-theme'
import { Components } from '@mdx-js/react/lib'

const layoutMap = {
post: ArticleLayout,
Expand All @@ -34,26 +35,16 @@ const BlogLayout = ({
</BlogProvider>
)
}

const nextraPageContext: {
[key: string]: {
Content: FC
pageOpts: PageOpts
themeConfig: NextraBlogTheme
}
} = {}

function Layout(props: any) {
const { route } = useRouter()
const context = nextraPageContext[route]
if (!context) throw new Error(`No content found for ${route}.`)

const extendedConfig = { ...DEFAULT_CONFIG, ...context.themeConfig }

interface Props {
pageOpts: PageOpts
themeConfig: NextraBlogTheme
}
function Layout({ children, themeConfig, pageOpts }: PropsWithChildren<Props>) {
const extendedConfig = { ...DEFAULT_CONFIG, ...themeConfig }
return (
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<BlogLayout config={extendedConfig} opts={context.pageOpts}>
<context.Content {...props} />
<BlogLayout config={extendedConfig} opts={pageOpts}>
{children}
</BlogLayout>
</ThemeProvider>
)
Expand All @@ -63,18 +54,15 @@ function Layout(props: any) {
// stable layout. We then put the actual content into a global store and use
// the route to identify it.
export default function withLayout(
route: string,
Content: FC,
MdxContent: FC<{ components: Components }>,
pageOpts: PageOpts,
themeConfig: NextraBlogTheme
) {
nextraPageContext[route] = {
Content,
pageOpts,
themeConfig
}

return Layout
return (
<Layout pageOpts={pageOpts} themeConfig={themeConfig}>
<MdxContent components={components} />
</Layout>
)
}

export { useBlogContext } from './blog-context'
Expand Down
15 changes: 3 additions & 12 deletions packages/nextra-theme-blog/src/mdx-theme.tsx
Expand Up @@ -7,11 +7,10 @@ import React, {
useEffect,
useState
} from 'react'
import { MDXProvider } from '@mdx-js/react'
import Link from 'next/link'
import ReactDOM from 'react-dom'
import { useBlogContext } from './blog-context'

import { Components } from '@mdx-js/react/lib'
export const HeadingContext = createContext<
React.RefObject<HTMLHeadingElement | null>
>(React.createRef())
Expand Down Expand Up @@ -42,9 +41,7 @@ const createHeaderLink =
return (
<Tag className={`subheading-${Tag}`} {...props}>
<span className="subheading-anchor -mt-8" id={id} />
<a href={`#${id}`}>
{children}
</a>
<a href={`#${id}`}>{children}</a>
</Tag>
)
}
Expand Down Expand Up @@ -80,7 +77,7 @@ const Pre = ({ children }: { children?: ReactNode }): ReactElement => {
</div>
)
}
const components = {
export const components: Components = {
h1: H1,
h2: createHeaderLink('h2'),
h3: createHeaderLink('h3'),
Expand All @@ -90,9 +87,3 @@ const components = {
a: A,
pre: Pre
}

const MDXTheme = ({ children }: { children: ReactNode }): ReactElement => {
return <MDXProvider components={components}>{children}</MDXProvider>
}

export default MDXTheme
3 changes: 1 addition & 2 deletions packages/nextra-theme-blog/src/page-layout.tsx
@@ -1,13 +1,12 @@
import React, { ReactNode } from 'react'
import { BasicLayout } from './basic-layout'
import MDXTheme from './mdx-theme'
import Nav from './nav'

export const PageLayout = ({ children }: { children: ReactNode }) => {
return (
<BasicLayout>
<Nav />
<MDXTheme>{children}</MDXTheme>
{children}
</BasicLayout>
)
}
8 changes: 2 additions & 6 deletions packages/nextra-theme-blog/src/posts-layout.tsx
Expand Up @@ -3,7 +3,6 @@ import { useRouter } from 'next/router'
import React, { ReactNode } from 'react'
import { useBlogContext } from './blog-context'
import { BasicLayout } from './basic-layout'
import MDXTheme from './mdx-theme'
import Nav from './nav'
import { collectPostsAndNavs } from './utils/collect'
import getTags from './utils/get-tags'
Expand Down Expand Up @@ -48,10 +47,7 @@ export const PostsLayout = ({ children }: { children: ReactNode }) => {
</p>
)}
{date && (
<time
className="text-sm text-gray-300"
dateTime={date.toISOString()}
>
<time className="text-sm text-gray-300" dateTime={date.toISOString()}>
{date.toDateString()}
</time>
)}
Expand All @@ -61,7 +57,7 @@ export const PostsLayout = ({ children }: { children: ReactNode }) => {
return (
<BasicLayout>
<Nav />
<MDXTheme>{children}</MDXTheme>
{children}
{postList}
</BasicLayout>
)
Expand Down
5 changes: 1 addition & 4 deletions packages/nextra-theme-blog/src/utils/collect.ts
Expand Up @@ -9,10 +9,7 @@ const isNav = (page: PageMapItem) => {
const isPost = (page: PageMapItem) => {
if (page.children) return false
if (page.name.startsWith('_')) return false
return (
!page.frontMatter?.type ||
page.frontMatter.type === 'post'
)
return !page.frontMatter?.type || page.frontMatter.type === 'post'
}

export const collectPostsAndNavs = ({ opts }: LayoutProps) => {
Expand Down
3 changes: 2 additions & 1 deletion packages/nextra-theme-blog/src/utils/date.ts
Expand Up @@ -12,4 +12,5 @@ export const sortDate = (a: PageMapItem, b: PageMapItem): number => {
const DATE_REGEX = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2})?(:\d{2}\.\d{3}Z)?$/
const DATE_REGEX_WITH_SLASH = /^\d{4}\/\d{1,2}\/\d{1,2}( \d{1,2}:\d{1,2})?$/

export const isValidDate = (date: string): boolean => DATE_REGEX.test(date) || DATE_REGEX_WITH_SLASH.test(date)
export const isValidDate = (date: string): boolean =>
DATE_REGEX.test(date) || DATE_REGEX_WITH_SLASH.test(date)
2 changes: 1 addition & 1 deletion packages/nextra-theme-blog/tsup.config.ts
Expand Up @@ -7,7 +7,7 @@ function outExtension() {
}

export default defineConfig({
entry: ['src/index.tsx', "src/cusdis.tsx"],
entry: ['src/index.tsx', 'src/cusdis.tsx'],
format: 'esm',
dts: true,
name: 'nextra-theme-blog',
Expand Down
14 changes: 6 additions & 8 deletions packages/nextra-theme-docs/src/breadcrumb.tsx
Expand Up @@ -23,14 +23,12 @@ export default function Breadcrumb({
<ArrowRightIcon width={14} className="mx-1 select-none" />
) : null}
<div
className={cn(
'transition-colors whitespace-nowrap',
{
'active text-gray-600 dark:text-gray-400': isActive,
'min-w-[24px] overflow-hidden text-ellipsis': !isActive,
'hover:text-gray-900 dark:hover:text-gray-200': isLink && !isActive
}
)}
className={cn('transition-colors whitespace-nowrap', {
'active text-gray-600 dark:text-gray-400': isActive,
'min-w-[24px] overflow-hidden text-ellipsis': !isActive,
'hover:text-gray-900 dark:hover:text-gray-200':
isLink && !isActive
})}
title={item.title}
>
{isLink && !isActive ? (
Expand Down
57 changes: 20 additions & 37 deletions packages/nextra-theme-docs/src/index.tsx
@@ -1,18 +1,16 @@
import type { PageMapItem, PageOpts } from 'nextra'
import type { FC, ReactElement, ReactNode } from 'react'

import { getComponents } from './misc/theme'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import { useRouter } from 'next/router'
import 'focus-visible'
import scrollIntoView from 'scroll-into-view-if-needed'
import { SkipNavContent } from '@reach/skip-nav'
import { ThemeProvider } from 'next-themes'
import cn from 'classnames'

import Head from './head'
import Navbar from './navbar'
import Footer, { NavLinks } from './footer'
import { MDXTheme } from './misc/theme'
import Sidebar from './sidebar'
import ToC from './toc'
import { ThemeConfigContext, useConfig } from './config'
Expand Down Expand Up @@ -93,7 +91,7 @@ const Body = ({
<SkipNavContent />
{themeContext.layout === 'full' ? (
<article className="nextra-body full relative justify-center overflow-x-hidden pl-[max(env(safe-area-inset-left),1.5rem)] pr-[max(env(safe-area-inset-right),1.5rem)]">
<MDXTheme>{children}</MDXTheme>
{children}
{date && config.gitTimestamp ? (
<div className="pointer-default mt-12 mb-8 block text-right text-xs text-gray-500 dark:text-gray-400">
{typeof config.gitTimestamp === 'string'
Expand Down Expand Up @@ -132,7 +130,7 @@ const Body = ({
ref={mainElement}
>
{breadcrumb}
<MDXTheme>{children}</MDXTheme>
{children}
{date && config.gitTimestamp ? (
<div className="pointer-default mt-12 mb-8 block text-right text-xs text-gray-500 dark:text-gray-400">
{typeof config.gitTimestamp === 'string'
Expand Down Expand Up @@ -275,25 +273,18 @@ const InnerLayout = ({
)
}

const nextraPageContext: {
[key: string]: {
Content: FC
pageOpts: PageOpts
themeConfig: DocsThemeConfig
}
} = {}

function Layout(props: any) {
const { route } = useRouter()
const context = nextraPageContext[route]

if (!context) throw new Error(`No content found for ${route}.`)
interface Props {
children?: ReactNode
pageOpts: PageOpts
themeConfig: DocsThemeConfig
}

function Layout({ children, pageOpts, themeConfig }: Props) {
const extendedConfig = {
...defaultConfig,
...context.themeConfig,
unstable_flexsearch: context.pageOpts.unstable_flexsearch,
newNextLinkBehavior: context.pageOpts.newNextLinkBehavior
...themeConfig,
unstable_flexsearch: pageOpts.unstable_flexsearch,
newNextLinkBehavior: pageOpts.newNextLinkBehavior
}
const nextThemes = extendedConfig.nextThemes || {}

Expand All @@ -306,31 +297,23 @@ function Layout(props: any) {
storageKey={nextThemes.storageKey}
forcedTheme={nextThemes.forcedTheme}
>
<InnerLayout {...context.pageOpts}>
<context.Content {...props} />
</InnerLayout>
<InnerLayout {...pageOpts}>{children}</InnerLayout>
</ThemeProvider>
</ThemeConfigContext.Provider>
)
}

// Make sure the same component is always returned so Next.js will render the
// stable layout. We then put the actual content into a global store and use
// the route to identify it.
export default function withLayout(
route: string,
Content: FC,
MdxContent: FC<{ components: any }>,
pageOpts: PageOpts,
themeConfig: DocsThemeConfig
) {
nextraPageContext[route] = {
Content,
pageOpts,
themeConfig
}

return Layout
return (
<Layout pageOpts={pageOpts} themeConfig={themeConfig}>
<MdxContent components={getComponents()} />
</Layout>
)
}

export * from './types'
export { getComponents } from './misc/theme'
export { getComponents }