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

v4: Add defaultOpen option for sidebar visibility control #4042

Merged
merged 14 commits into from
Jan 28, 2025
Merged
5 changes: 5 additions & 0 deletions .changeset/famous-ligers-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"nextra-theme-docs": patch
---

add `Layout#sidebar.defaultOpen` prop to configure sidebar default open state
1 change: 1 addition & 0 deletions docs/app/docs/docs-theme/built-ins/layout/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ The above is also equivalent to `navigation: true{:js}`.
| `sidebar.autoCollapse` | `boolean{:ts}` | | If true, automatically collapse inactive folders above `defaultMenuCollapseLevel`. |
| `sidebar.defaultMenuCollapseLevel` | `number{:ts}` | `2{:ts}` | Specifies the folder level at which the menu on the left is collapsed by default. |
| `sidebar.toggleButton` | `boolean{:ts}` | `true{:ts}` | Hide/show sidebar toggle button. |
| `sidebar.defaultOpen` | `boolean{:ts}` | `true{:ts}` | Hide/show sidebar by default. |

### Menu Collapse Level

Expand Down
18 changes: 10 additions & 8 deletions docs/app/docs/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ project. If you have a question that isn't answered here, please

<details>
{/* prettier-ignore */}
<summary className="mb-2">Can I use Nextra with Next.js `pages` router?</summary>
Nextra 4 only works with the `app` router. Only Nextra 1/2/3 supports the
`pages` router.
<summary>Can I use Nextra with Next.js `pages` router?</summary>
Nextra 4 only works with the `app` router. Only Nextra 1/2/3 supports the
`pages` router.

</details>

<details>
<summary className="mb-2">Can I use X with Nextra?</summary>
<summary>Can I use X with Nextra?</summary>
The answer is "yes" for most things. Since Nextra is just a Next.js plugin, almost all the things
that can be done with React can be done with Nextra. Here are some examples and guides:

Expand All @@ -64,8 +65,9 @@ that can be done with React can be done with Nextra. Here are some examples and

<details>
{/* prettier-ignore */}
<summary className="mb-2">How can I add a live coding component in Nextra?</summary>
There are libraries like [Sandpack](https://sandpack.codesandbox.io) and
[react-live](https://github.com/FormidableLabs/react-live) that can help you
add live coding components to your MDX.
<summary>How can I add a live coding component in Nextra?</summary>
There are libraries like [Sandpack](https://sandpack.codesandbox.io) and
[react-live](https://github.com/FormidableLabs/react-live) that can help you
add live coding components to your MDX.

</details>
6 changes: 3 additions & 3 deletions packages/nextra-theme-docs/src/components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ export const MobileNav: FC = () => {

export const Sidebar: FC<{ toc: Heading[] }> = ({ toc }) => {
const { normalizePagesResult, hideSidebar } = useConfig()
const [isExpanded, setIsExpanded] = useState(true)
const themeConfig = useThemeConfig()
const [isExpanded, setIsExpanded] = useState(themeConfig.sidebar.defaultOpen)
const [showToggleAnimation, setToggleAnimation] = useState(false)
const sidebarRef = useRef<HTMLDivElement>(null)
const sidebarControlsId = useId()
Expand All @@ -380,7 +381,6 @@ export const Sidebar: FC<{ toc: Heading[] }> = ({ toc }) => {
}
}, [])

const themeConfig = useThemeConfig()
const anchors =
// When the viewport size is larger than `md`, hide the anchors in
// the sidebar when `floatTOC` is enabled.
Expand Down Expand Up @@ -456,7 +456,7 @@ export const Sidebar: FC<{ toc: Heading[] }> = ({ toc }) => {
title={isExpanded ? 'Collapse sidebar' : 'Expand sidebar'}
className={({ hover }) =>
cn(
'x:rounded-md x:p-2',
'x:rounded-md x:p-2 x:cursor-pointer',
hover
? 'x:bg-gray-100 x:text-gray-900 x:dark:bg-primary-100/5 x:dark:text-gray-50'
: 'x:text-gray-600 x:dark:text-gray-400'
Expand Down
1 change: 1 addition & 0 deletions packages/nextra-theme-docs/src/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const theme = z.strictObject({
.strictObject({
autoCollapse: z.boolean().optional(),
defaultMenuCollapseLevel: z.number().min(1).int().default(2),
defaultOpen: z.boolean().default(true),
toggleButton: z.boolean().default(true)
})
.default({}),
Expand Down
14 changes: 10 additions & 4 deletions packages/nextra/src/client/components/collapse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import type { FC, ReactNode } from 'react'
import { Children, useEffect, useRef, useState } from 'react'

export const Collapse: FC<{
className?: string
children: ReactNode
isOpen: boolean
horizontal?: boolean
openDuration?: number
closeDuration?: number
}> = ({
className,
children,
isOpen,
horizontal = false,
Expand Down Expand Up @@ -47,7 +49,7 @@ export const Collapse: FC<{
container.style.removeProperty('height')
}, openDuration)
} else {
setTimeout(() => {
requestAnimationFrame(() => {
if (horizontal) {
container.style.width = '0'
} else {
Expand All @@ -58,8 +60,11 @@ export const Collapse: FC<{
}, [horizontal, isOpen, openDuration])

useEffect(() => {
initialRender.current = false
}, [])
if (isOpen || !horizontal) {
initialRender.current = false
}
}, [horizontal, isOpen])

// Add inner <div> only if children.length != 1
const newChildren =
Children.count(children) === 1 &&
Expand All @@ -76,7 +81,8 @@ export const Collapse: FC<{
ref={containerRef}
className={cn(
'x:transform-gpu x:transition-all x:ease-in-out x:motion-reduce:transition-none',
isOpen ? 'x:opacity-100' : ['x:opacity-0', 'x:overflow-hidden']
isOpen ? 'x:opacity-100' : 'x:opacity-0 x:overflow-hidden',
className
)}
style={{
...(initialOpen || horizontal ? undefined : { height: 0 }),
Expand Down
1 change: 1 addition & 0 deletions packages/nextra/src/client/components/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const Select: FC<MenuProps> = ({
title={title}
className={({ hover, open, focus }) =>
cn(
'x:cursor-pointer',
'x:h-7 x:rounded-md x:px-2 x:text-xs x:font-medium x:transition-colors',
open
? 'x:bg-gray-200 x:text-gray-900 x:dark:bg-primary-100/10 x:dark:text-gray-50'
Expand Down
4 changes: 3 additions & 1 deletion packages/nextra/src/client/mdx-components/details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ export const Details: FC<ComponentProps<'details'>> = ({
data-expanded={isOpen ? '' : undefined}
>
{summaryElement}
<Collapse isOpen={isOpen}>{restChildren}</Collapse>
<Collapse isOpen={isOpen} className="x:*:pt-2">
{restChildren}
</Collapse>
</details>
)
}
Expand Down
Loading