Make sidebar nav groups with landing pages collapsible and fix mobile drawer closing on expand (#786)

* Add collapsible nav groups and delete old /docs dir

* coderabbit fixes: auto-open effect, stable key, push guard
This commit is contained in:
Brandon Hopkins
2026-06-12 09:24:47 -07:00
committed by GitHub
parent b70301ee4d
commit 9cf8498a33
3 changed files with 50 additions and 339 deletions

View File

@@ -8,12 +8,13 @@ import {
} from '@/components/NavigationAPI'
import { AnimatePresence, motion } from 'framer-motion'
import { Button } from '@/components/mdx'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import {
NavigationStateProvider,
useNavigationState,
} from '@/components/NavigationState'
import ChevronDownIcon from '@/components/icons/ChevronDownIcon'
import { useIsInsideMobileNavigation } from '@/components/MobileNavigation'
export const docsNavigation = [
{
@@ -51,7 +52,7 @@ export const docsNavigation = [
{ title: 'Quickstart Guide', href: '/get-started' },
{
title: 'Install NetBird',
isOpen: true,
isOpen: false,
href: '/get-started/install',
links: [
{ title: 'Linux', href: '/get-started/install/linux' },
@@ -951,11 +952,17 @@ const findActiveGroupIndex = (group, pathname) => {
function NavigationGroup({ group, className, hasChildren }) {
let router = useRouter()
let isActiveGroup = findActiveGroupIndex(group, router.pathname) !== -1
let isActiveGroup =
group.href === router.pathname ||
findActiveGroupIndex(group, router.pathname) !== -1
const [isOpen, setIsOpen] = useState(
group.isOpen ? group.isOpen : !hasChildren
(group.isOpen ?? !hasChildren) || isActiveGroup
)
useEffect(() => {
if (isActiveGroup) setIsOpen(true)
}, [router.pathname, isActiveGroup])
const [, setActiveHighlight] = useNavigationState()
const isInsideMobileNavigation = useIsInsideMobileNavigation()
return (
<li className={clsx('relative', className, hasChildren ? '' : 'mt-6')}>
@@ -965,12 +972,19 @@ function NavigationGroup({ group, className, hasChildren }) {
'group flex items-center justify-between gap-2',
hasChildren
? 'cursor-pointer select-none py-1 pr-3 text-sm font-medium text-zinc-700 hover:text-zinc-900 dark:text-zinc-300 dark:hover:text-white'
: 'text-xs font-semibold text-zinc-900 dark:text-white'
: 'text-xs font-semibold text-zinc-900 dark:text-white',
group.href === router.pathname && 'text-zinc-900 dark:text-white'
)}
onClick={() => {
if (group.href) {
if (!isOpen) setIsOpen(true)
if (group.href !== router.pathname) router.push(group.href)
setActiveHighlight()
return
}
setIsOpen(!isOpen)
if (!isOpen) {
if (!isActiveGroup && group.links[0]?.href)
if (!isActiveGroup && !isInsideMobileNavigation && group.links[0]?.href)
router.push(group.links[0].href)
setActiveHighlight()
} else {
@@ -982,14 +996,28 @@ function NavigationGroup({ group, className, hasChildren }) {
>
{group.title}
{hasChildren && (
<ChevronDownIcon
className={clsx(
'fill-zinc-700 group-hover:fill-zinc-900 dark:fill-zinc-300 dark:group-hover:fill-white',
'transition',
isOpen ? 'rotate-180 transform' : ''
)}
size={10}
/>
<span
className="-m-1 flex items-center justify-center p-1"
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
setIsOpen(!isOpen)
if (isOpen) {
setActiveHighlight(group.title)
} else {
setActiveHighlight()
}
}}
>
<ChevronDownIcon
className={clsx(
'fill-zinc-700 group-hover:fill-zinc-900 dark:fill-zinc-300 dark:group-hover:fill-white',
'transition',
isOpen ? 'rotate-180 transform' : ''
)}
size={10}
/>
</span>
)}
</motion.h2>
<div className={clsx('relative', hasChildren ? '' : 'mt-3 pl-2')}>
@@ -1031,23 +1059,22 @@ function NavigationGroup({ group, className, hasChildren }) {
className="border-l border-transparent"
>
{group.links.map((link) => {
return link.href ? (
return link.links ? (
<NavigationGroup
className={'ml-4'}
key={link.title}
group={link}
hasChildren={true}
/>
) : (
<motion.li key={link.href} className="relative">
<NavLink
href={link.href}
active={link.href === router.pathname}
links={link.links}
>
{link.title}
</NavLink>
</motion.li>
) : (
<NavigationGroup
className={'ml-4'}
key={link.title + isOpen}
group={link}
hasChildren={true}
/>
)
})}
</motion.ul>