Skip to content

Commit 90b8c66

Browse files
authoredJan 29, 2025··
allow configuring theme.toc with layout: 'default' in _meta files (#4105)
* a * a * allow configuring `theme.toc` with `layout: 'default'` in `_meta` files * more
1 parent 1ae976b commit 90b8c66

File tree

19 files changed

+54
-53
lines changed

19 files changed

+54
-53
lines changed
 

‎.changeset/fluffy-mails-laugh.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"nextra-theme-docs": minor
3+
---
4+
5+
allow configuring `theme.toc` with `layout: 'default'` in `_meta` files

‎docs/app/about/page.mdx

+1-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { cloneElement } from 'react'
1111
export default function MdxLayout(props) {
1212
return cloneElement(props.children, {
1313
components: {
14-
img: Image
14+
img: props => <Image {...props} className="inline" />
1515
}
1616
})
1717
}
@@ -65,16 +65,12 @@ Feel free to use the Nextra logo and other assets in your project. But please
6565
don't modify the logo, and don't use the logo to represent your project or
6666
product.
6767

68-
<div className="[&_img]:inline mt-6">
69-
7068
| Name | Description | Preview |
7169
| :---------: | :----------------------------------------------: | :-----------------------------------: |
7270
| Icon | Useful for favicons, app icons, link icons, etc. | ![Nextra icon](../icon.svg) |
7371
| Logo | Full Nextra logo | ![Nextra logo](/logo.svg) |
7472
| Social Card | The Nextra social card | ![Nextra card](/opengraph-image.jpeg) |
7573

76-
</div>
77-
7874
## License
7975

8076
The Nextra project and themes are licensed under

‎docs/app/docs/docs-theme/built-ins/layout/page.mdx

-2
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,6 @@ The above is also equivalent to `navigation: true{:js}`.
154154

155155
## Page Map [!TODO]
156156

157-
## Search [!TODO]
158-
159157
## Sidebar
160158

161159
| | | | |

‎docs/app/showcase/page.mdx

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { cloneElement } from 'react'
1313
Projects powered by Nextra
1414

1515
{/* prettier-ignore */}
16-
<Cards className="[&_img]:object-cover [&_img]:[aspect-ratio:12/6.3]">
16+
<Cards>
1717
<>[![GraphQL Hive preview](./_logos/graphql-hive.png)](https://the-guild.dev/graphql/hive)</>
1818
<>[![Speakeasy preview](./_logos/speakeasy.png)](https://speakeasyapi.dev/docs)</>
1919
<>[![React Flow preview](./_logos/react-flow.jpg)](https://reactflow.dev)</>
@@ -64,7 +64,9 @@ Projects powered by Nextra
6464
export default function MdxLayout(props) {
6565
return cloneElement(props.children, {
6666
components: {
67-
img: Image,
67+
img: props => (
68+
<Image {...props} className="[aspect-ratio:12/6.3] object-cover" />
69+
),
6870
a({ children, href }) {
6971
const { alt } = children.props
7072
return (

‎docs/app/sponsors/page.mdx

+4-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { cloneElement } from 'react'
2323
</div>
2424

2525
{/* prettier-ignore */}
26-
<Cards num={3} className="[&_img]:object-cover [&_img]:[aspect-ratio:12/6.3]">
26+
<Cards num={3}>
2727
<>[![GraphQL Hive preview](../showcase/_logos/graphql-hive.png)](https://the-guild.dev/graphql/hive?utm_source=nextra.site&utm_campaign=nextra&utm_content=logolink)</>
2828
<>[![Speakeasy preview](../showcase/_logos/speakeasy.png)](https://speakeasyapi.dev/docs?utm_source=nextra.site&utm_campaign=nextra&utm_content=logolink)</>
2929
<>[![xyflow preview](../showcase/_logos/xyflow.jpg)](https://xyflow.com?utm_source=nextra.site&utm_campaign=nextra&utm_content=logolink)</>
@@ -32,7 +32,9 @@ import { cloneElement } from 'react'
3232
export default function MdxLayout(props) {
3333
return cloneElement(props.children, {
3434
components: {
35-
img: Image,
35+
img: props => (
36+
<Image {...props} className="[aspect-ratio:12/6.3] object-cover" />
37+
),
3638
a({ children, href }) {
3739
const { alt } = children.props
3840
return (

‎packages/nextra-theme-blog/src/components/go-back.tsx

+1-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ export const GoBack: FC = () => {
1212
const isNestedPage = segments.length > 2
1313
if (!isNestedPage) return null
1414
return (
15-
<Button
16-
onClick={router.back}
17-
className="x:print:hidden x:underline x:cursor-pointer"
18-
>
15+
<Button onClick={router.back} className="x:print:hidden x:underline">
1916
Back
2017
</Button>
2118
)

‎packages/nextra-theme-blog/src/components/theme-switch.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export function ThemeSwitch() {
2020
return (
2121
<Button
2222
aria-label="Toggle Dark Mode"
23-
className="x:p-2 x:cursor-pointer"
23+
className="x:p-2"
2424
onClick={toggleTheme}
2525
>
2626
<IconToUse height="14" />

‎packages/nextra-theme-docs/src/components/back-to-top.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export const BackToTop: FC<{
3030
disabled={hidden}
3131
className={({ disabled }) =>
3232
cn(
33-
'x:flex x:items-center x:gap-1.5 x:cursor-pointer',
33+
'x:flex x:items-center x:gap-1.5',
3434
'x:whitespace-nowrap', // Safari
3535
disabled ? 'x:opacity-0' : 'x:opacity-100',
3636
className
@@ -39,7 +39,7 @@ export const BackToTop: FC<{
3939
>
4040
{children}
4141
<ArrowRightIcon
42-
height="16"
42+
height="1.1em"
4343
className="x:-rotate-90 x:border x:rounded-full x:border-current"
4444
/>
4545
</Button>

‎packages/nextra-theme-docs/src/components/locale-switch.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ interface LocaleSwitchProps {
1818
export const LocaleSwitch: FC<LocaleSwitchProps> = ({ lite, className }) => {
1919
const { i18n } = useThemeConfig()
2020
const pathname = usePathname()
21-
if (!i18n.length) return
21+
if (!i18n.length) return null
2222

2323
const [, locale] = pathname.split('/', 2)
2424
return (

‎packages/nextra-theme-docs/src/components/sidebar.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ export const Sidebar: FC<{ toc: Heading[] }> = ({ toc }) => {
456456
title={isExpanded ? 'Collapse sidebar' : 'Expand sidebar'}
457457
className={({ hover }) =>
458458
cn(
459-
'x:rounded-md x:p-2 x:cursor-pointer',
459+
'x:rounded-md x:p-2',
460460
hover
461461
? 'x:bg-gray-100 x:text-gray-900 x:dark:bg-primary-100/5 x:dark:text-gray-50'
462462
: 'x:text-gray-600 x:dark:text-gray-400'

‎packages/nextra-theme-docs/src/components/theme-switch.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const ThemeSwitch: FC<ThemeSwitchProps> = ({ lite, className }) => {
1818
const mounted = useMounted()
1919
const { darkMode, themeSwitch } = useThemeConfig()
2020
if (!darkMode) {
21-
return
21+
return null
2222
}
2323
const IconToUse = mounted && resolvedTheme === 'dark' ? MoonIcon : SunIcon
2424
const id = mounted ? (theme as keyof typeof themeSwitch) : 'light'

‎packages/nextra-theme-docs/src/mdx-components/wrapper.client.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const ClientWrapper: MDXWrapper = ({
2828

2929
return (
3030
<>
31-
{themeContext.layout !== 'full' && (
31+
{(themeContext.layout === 'default' || themeContext.toc) && (
3232
<nav
3333
className="nextra-toc x:order-last x:max-xl:hidden x:w-64 x:shrink-0 x:print:hidden"
3434
aria-label="table of contents"

‎packages/nextra/src/client/components/banner/index.client.tsx

+16-14
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,23 @@ import { useEffect, useRef } from 'react'
55

66
export const ClientBanner: FC<ComponentPropsWithoutRef<'div'>> = props => {
77
const banner = useRef<HTMLDivElement>(null!)
8-
// Set height on mount because text can be wrapped on next line and height can be different
8+
99
useEffect(() => {
10-
const height = banner.current.clientHeight
11-
document.documentElement.style.setProperty(
12-
'--nextra-banner-height',
13-
`${height}px`
14-
)
10+
const resizeObserver = new ResizeObserver(entries => {
11+
for (const entry of entries) {
12+
const { height } = entry.contentRect
13+
// Set height because banner text can be wrapped on next line and his height can be different
14+
document.documentElement.style.setProperty(
15+
'--nextra-banner-height',
16+
`${height}px`
17+
)
18+
}
19+
})
20+
resizeObserver.observe(banner.current)
21+
return () => {
22+
resizeObserver.disconnect()
23+
}
1524
}, [])
1625

17-
return (
18-
<div
19-
ref={banner}
20-
// Because we update class in `<script>`
21-
suppressHydrationWarning
22-
{...props}
23-
/>
24-
)
26+
return <div ref={banner} {...props} />
2527
}

‎packages/nextra/src/client/components/banner/index.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ export const Banner: FC<{
1010
children: ReactNode
1111
dismissible?: boolean
1212
storageKey?: string
13-
}> = ({ children, dismissible = true, storageKey = 'nextra-banner' }) => {
13+
}> = ({ children, dismissible = true, storageKey = BANNER_CLASS_NAME }) => {
1414
if (!children) {
15-
return
15+
return null
1616
}
1717
const hideBannerScript = `try{document.querySelector('.${BANNER_CLASS_NAME}').classList.toggle('x:hidden',localStorage.getItem(${JSON.stringify(storageKey)}))}catch(e){}`
1818

@@ -24,6 +24,8 @@ export const Banner: FC<{
2424
'x:text-slate-50 x:dark:text-white x:bg-neutral-900 x:dark:bg-[linear-gradient(1deg,#383838,#212121)]',
2525
'x:print:[display:none]' // to not match `x:[.nextra-banner:not([class$=hidden])~&]` class
2626
)}
27+
// Because we update class in `<script>`
28+
suppressHydrationWarning
2729
>
2830
<div className="x:w-full x:text-center x:font-medium x:text-sm x:py-2.5">
2931
{children}

‎packages/nextra/src/client/components/button.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const Button: FC<ButtonProps> = ({
2020
<HeadlessButton
2121
className={args =>
2222
cn(
23-
'x:transition',
23+
'x:transition x:cursor-pointer',
2424
args.focus && 'x:nextra-focus',
2525
variant === 'outline' && [classes.border, 'x:rounded-md x:p-1.5'],
2626
typeof className === 'function' ? className(args) : className

‎packages/nextra/src/client/components/select.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export const Select: FC<MenuProps> = ({
7979
)}
8080
>
8181
{option.name}
82-
{selected && <CheckIcon height="16" />}
82+
{selected && <CheckIcon height="1em" />}
8383
</li>
8484
)}
8585
</ListboxOption>

‎packages/nextra/src/client/mdx-components/pre/copy-to-clipboard.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const CopyToClipboard: FC<ComponentProps<'button'>> = props => {
4040
variant="outline"
4141
{...props}
4242
>
43-
<IconToUse height="16" className="nextra-copy-icon" />
43+
<IconToUse height="1em" className="nextra-copy-icon" />
4444
</Button>
4545
)
4646
}

‎packages/nextra/src/client/mdx-components/pre/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const Pre: FC<PreProps> = ({
3232
...props
3333
}) => {
3434
const copyButton = copy === '' && (
35-
<CopyToClipboard className={filename ? 'x:ms-auto' : ''} />
35+
<CopyToClipboard className={filename ? 'x:ms-auto x:text-sm' : ''} />
3636
)
3737

3838
return (
@@ -81,7 +81,7 @@ export const Pre: FC<PreProps> = ({
8181
>
8282
{hasWordWrap === '' && (
8383
<ToggleWordWrapButton>
84-
<WordWrapIcon height="16" />
84+
<WordWrapIcon height="1em" />
8585
</ToggleWordWrapButton>
8686
)}
8787
{!filename && copyButton}

‎packages/nextra/src/client/remove-links.ts

+7-10
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
// should be used on server
22
'use no memo'
33

4-
import type { ReactElement, ReactNode, ReactPortal } from 'react'
4+
import type { ReactElement, ReactNode } from 'react'
55
import { Children, cloneElement } from 'react'
66

77
type TOCElement = ReactElement | string
88

9-
type WithProps = ReactElement | ReactPortal
10-
11-
function isLink(node: ReactNode): node is WithProps {
12-
// @ts-expect-error -- fixme
13-
return hasProps(node) && !!node.props.href
14-
}
15-
16-
function hasProps(node: ReactNode): node is WithProps {
9+
function hasProps(node: ReactNode) {
1710
return !!node && typeof node === 'object'
1811
}
1912

@@ -23,7 +16,11 @@ export function removeLinks(node: ReactNode): TOCElement[] | string {
2316
}
2417
// @ts-expect-error fixme
2518
return Children.map(node, child => {
26-
if (isLink(child)) {
19+
if (
20+
hasProps(child) &&
21+
// @ts-expect-error -- fixme
22+
child.props.href
23+
) {
2724
// Skip footnotes links
2825
// @ts-expect-error -- fixme
2926
if (child.props['data-footnote-ref']) {

0 commit comments

Comments
 (0)
Please sign in to comment.