Skip to content

Commit 3c6c5e1

Browse files
committed
Replace Nextra MDX Wrapper and restyle TOC
1 parent 9fb0acf commit 3c6c5e1

File tree

7 files changed

+378
-21
lines changed

7 files changed

+378
-21
lines changed

patches/nextra-theme-docs.patch

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
diff --git a/dist/index.d.mts b/dist/index.d.mts
2-
index 71f87bcd1dde49d7c19ad49fc098e715a76c5c10..6671e29326be99861058895916185910452ced17 100644
2+
index 71f87bcd1dde49d7c19ad49fc098e715a76c5c10..c26af21e9bf3e8b1a2641b9d91c597575c30bfd9 100644
33
--- a/dist/index.d.mts
44
+++ b/dist/index.d.mts
5-
@@ -1421,3 +1421,19 @@ declare function ThemeSwitch({ lite, className }: ThemeSwitchProps): ReactElemen
5+
@@ -1421,3 +1421,25 @@ declare function ThemeSwitch({ lite, className }: ThemeSwitchProps): ReactElemen
66
declare function Layout({ children, themeConfig, pageOpts }: NextraThemeLayoutProps): ReactElement;
77

88
export { Bleed, Collapse, type PartialDocsThemeConfig as DocsThemeConfig, Link, LocaleSwitch, Navbar, NotFoundPage, SkipNavContent, SkipNavLink, ThemeSwitch, Layout as default, getComponents, useConfig, useMenu, useThemeConfig };
@@ -22,8 +22,14 @@ index 71f87bcd1dde49d7c19ad49fc098e715a76c5c10..6671e29326be99861058895916185910
2222
+export declare const useIntersectionObserver: () => IntersectionObserver | null
2323
+export declare const useSlugs: () => WeakMap<any, any>
2424
+
25+
+export declare const Breadcrumb: (props: { activeElement: Item[] }) => ReactElement | null
26+
+export declare const NavLinks: (props: NavLinkProps) => ReactElement | null
27+
+export interface NavLinkProps {
28+
+ currentIndex: number
29+
+ flatDocsDirectories: Item[]
30+
+}
2531
diff --git a/dist/index.js b/dist/index.js
26-
index 56201641fd965dcc5ab7c5df53e444c41293c00e..b0344053f3e0682eda3dd23e142abc5458f9a683 100644
32+
index 56201641fd965dcc5ab7c5df53e444c41293c00e..07147c688ae75c4c7daf082833acc71de16b36ee 100644
2733
--- a/dist/index.js
2834
+++ b/dist/index.js
2935
@@ -100,10 +100,10 @@ IntersectionObserverContext.displayName = "IntersectionObserver";
@@ -41,12 +47,21 @@ index 56201641fd965dcc5ab7c5df53e444c41293c00e..b0344053f3e0682eda3dd23e142abc54
4147
var ActiveAnchorProvider = ({
4248
children
4349
}) => {
44-
@@ -2502,7 +2502,7 @@ var DEFAULT_COMPONENTS = {
45-
),
46-
children: [
47-
/* @__PURE__ */ jsx26(
48-
- Sidebar,
49-
+ themeConfig.sidebar.component,
50-
{
51-
docsDirectories,
52-
fullDirectories: directories,
50+
@@ -526,7 +526,7 @@ import NextLink2 from "next/link";
51+
import { ArrowRightIcon } from "nextra/icons";
52+
import { Fragment as Fragment3 } from "react";
53+
import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
54+
-function Breadcrumb({
55+
+export function Breadcrumb({
56+
activePath
57+
}) {
58+
return /* @__PURE__ */ jsx9("div", { className: "nextra-breadcrumb _mt-1.5 _flex _items-center _gap-1 _overflow-hidden _text-sm _text-gray-500 dark:_text-gray-400 contrast-more:_text-current", children: activePath.map((item, index, arr) => {
59+
@@ -1255,7 +1255,7 @@ var classes = {
60+
),
61+
icon: cn10("_inline _h-5 _shrink-0")
62+
};
63+
-function NavLinks({
64+
+export function NavLinks({
65+
flatDocsDirectories,
66+
currentIndex
67+
}) {

pnpm-lock.yaml

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/back-to-top.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* @file BackToTop component extracted from Nextra
3+
*/
4+
5+
import { clsx } from "clsx"
6+
import { Button } from "nextra/components"
7+
import { ArrowRightIcon } from "nextra/icons"
8+
import type { ComponentProps, ReactElement, ReactNode } from "react"
9+
10+
const SCROLL_TO_OPTIONS = { top: 0, behavior: "smooth" } as const
11+
12+
const scrollToTop: ComponentProps<"button">["onClick"] = event => {
13+
const buttonElement = event.currentTarget
14+
const tocElement = buttonElement.parentElement!.parentElement!
15+
16+
window.scrollTo(SCROLL_TO_OPTIONS)
17+
tocElement.scrollTo(SCROLL_TO_OPTIONS)
18+
19+
// Fixes https://github.com/facebook/react/issues/20770
20+
// Fixes https://github.com/shuding/nextra/issues/2917
21+
buttonElement.disabled = true
22+
}
23+
24+
export function BackToTop({
25+
children,
26+
className,
27+
hidden,
28+
}: {
29+
children: ReactNode
30+
className?: string
31+
hidden: boolean
32+
}): ReactElement {
33+
return (
34+
<Button
35+
// elements with `aria-hidden: true` must not be focusable or contain focusable elements
36+
aria-hidden={hidden ? "true" : undefined}
37+
onClick={scrollToTop}
38+
disabled={hidden}
39+
className={({ disabled }) =>
40+
clsx(
41+
"flex items-center gap-1.5",
42+
disabled ? "opacity-0" : "opacity-100",
43+
className,
44+
)
45+
}
46+
>
47+
{children}
48+
<ArrowRightIcon
49+
height="16"
50+
className="-rotate-90 rounded-full border border-current"
51+
/>
52+
</Button>
53+
)
54+
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import type { ReactElement, ReactNode } from "react"
2+
import { useMounted } from "nextra/hooks"
3+
import { Heading } from "nextra"
4+
import {
5+
useConfig,
6+
useThemeConfig,
7+
SkipNavContent,
8+
Breadcrumb,
9+
NavLinks,
10+
} from "nextra-theme-docs"
11+
import { clsx } from "clsx"
12+
13+
import { Sidebar } from "./sidebar"
14+
import { renderComponent } from "./utils"
15+
import { TableOfContents } from "./table-of-contents"
16+
17+
const classes = {
18+
toc: clsx("nextra-toc order-last max-xl:hidden w-64 shrink-0 print:hidden"),
19+
main: clsx("w-full break-words"),
20+
}
21+
22+
export interface NextraMdxWrapperProps {
23+
toc?: Heading[]
24+
children?: React.ReactNode
25+
}
26+
27+
export function NextraMdxWrapper({
28+
toc = [],
29+
children,
30+
}: NextraMdxWrapperProps) {
31+
const config = useConfig()
32+
const {
33+
activeType,
34+
activeThemeContext: themeContext,
35+
docsDirectories,
36+
directories,
37+
} = config.normalizePagesResult
38+
39+
const tocEl =
40+
activeType === "page" ||
41+
!themeContext.toc ||
42+
themeContext.layout !== "default" ? (
43+
themeContext.layout !== "full" &&
44+
themeContext.layout !== "raw" && (
45+
<nav className={classes.toc} aria-label="table of contents" />
46+
)
47+
) : (
48+
<nav className={classes.toc} aria-label="table of contents">
49+
<TableOfContents toc={toc} filePath={config.filePath} />
50+
</nav>
51+
)
52+
return (
53+
<div
54+
className={clsx(
55+
"mx-auto flex",
56+
themeContext.layout !== "raw" && "max-w-[90rem]",
57+
)}
58+
>
59+
<Sidebar
60+
docsDirectories={docsDirectories}
61+
fullDirectories={directories}
62+
toc={toc}
63+
asPopover={config.hideSidebar}
64+
includePlaceholder={themeContext.layout === "default"}
65+
/>
66+
{tocEl}
67+
<SkipNavContent />
68+
<Body>{children}</Body>
69+
</div>
70+
)
71+
}
72+
73+
function Body({ children }: { children: ReactNode }): ReactElement {
74+
const config = useConfig()
75+
const themeConfig = useThemeConfig()
76+
const mounted = useMounted()
77+
const {
78+
activeThemeContext: themeContext,
79+
activeType,
80+
activeIndex,
81+
flatDocsDirectories,
82+
activePath,
83+
} = config.normalizePagesResult
84+
85+
if (themeContext.layout === "raw") {
86+
return <div className={classes.main}>{children}</div>
87+
}
88+
89+
const date =
90+
themeContext.timestamp && themeConfig.gitTimestamp && config.timestamp
91+
? new Date(config.timestamp)
92+
: null
93+
94+
const gitTimestampEl =
95+
// Because a user's time zone may be different from the server page
96+
mounted && date ? (
97+
<div className="mb-8 mt-12 block text-xs text-neu-500 ltr:text-right rtl:text-left">
98+
{renderComponent(themeConfig.gitTimestamp, { timestamp: date })}
99+
</div>
100+
) : (
101+
<div className="mt-16" />
102+
)
103+
104+
const content = (
105+
<>
106+
{renderComponent(themeContext.topContent)}
107+
{children}
108+
{gitTimestampEl}
109+
{renderComponent(themeContext.bottomContent)}
110+
{activeType !== "page" && themeContext.pagination && (
111+
<NavLinks
112+
flatDocsDirectories={flatDocsDirectories}
113+
currentIndex={activeIndex}
114+
/>
115+
)}
116+
</>
117+
)
118+
119+
const body = themeConfig.main ? (
120+
<themeConfig.main>{content}</themeConfig.main>
121+
) : (
122+
content
123+
)
124+
125+
if (themeContext.layout === "full") {
126+
return (
127+
<article
128+
className={clsx(
129+
classes.main,
130+
"nextra-content min-h-[calc(100vh-var(--nextra-navbar-height))] pl-[max(env(safe-area-inset-left),1.5rem)] pr-[max(env(safe-area-inset-right),1.5rem)]",
131+
)}
132+
>
133+
{body}
134+
</article>
135+
)
136+
}
137+
138+
return (
139+
<article
140+
className={clsx(
141+
classes.main,
142+
"nextra-content flex min-h-[calc(100vh-var(--nextra-navbar-height))] min-w-0 justify-center pb-8 pr-[calc(env(safe-area-inset-right)-1.5rem)]",
143+
themeContext.typesetting === "article" &&
144+
"nextra-body-typesetting-article",
145+
)}
146+
>
147+
<main className="w-full min-w-0 max-w-6xl px-6 pt-4 md:px-12">
148+
{activeType !== "page" && themeContext.breadcrumb && (
149+
<Breadcrumb activePath={activePath} />
150+
)}
151+
{body}
152+
</main>
153+
</article>
154+
)
155+
}

src/components/sidebar/index.tsx renamed to src/components/sidebar.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/**
22
* @file sidebar module extracted from Nextra 3.3.1
3-
* @description
43
*/
54

65
import cn from "clsx"
@@ -21,7 +20,7 @@ import {
2120
useState,
2221
} from "react"
2322
import scrollIntoView from "scroll-into-view-if-needed"
24-
import { renderComponent } from "../utils"
23+
import { renderComponent } from "./utils"
2524
import {
2625
useActiveAnchor,
2726
useMenu,
@@ -30,7 +29,7 @@ import {
3029
LocaleSwitch,
3130
} from "nextra-theme-docs"
3231

33-
import { Anchor } from "../../app/conf/_design-system/anchor"
32+
import { Anchor } from "../app/conf/_design-system/anchor"
3433

3534
const TreeState: Record<string, boolean> = Object.create(null)
3635

0 commit comments

Comments
 (0)