diff --git a/apps/docs/components/Navigation/NavSection.tsx b/apps/docs/components/Navigation/NavSection.tsx
new file mode 100644
index 00000000000..7ff5c7bbd12
--- /dev/null
+++ b/apps/docs/components/Navigation/NavSection.tsx
@@ -0,0 +1,25 @@
+import { ChevronDown } from 'lucide-react'
+import type { HTMLAttributes } from 'react'
+import { cn } from 'ui'
+
+export const NavSectionCaret = ({ className }: { className?: string }) => (
+
+)
+
+export const NavSectionList = ({ className, ...props }: HTMLAttributes) => (
+
+)
+
+export const NavSectionContent = ({ className, ...props }: HTMLAttributes) => (
+
+)
diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx
index 89b64e78b9d..c859d3a31ee 100644
--- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx
+++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx
@@ -1,4 +1,8 @@
-import { ChevronDown } from 'lucide-react'
+import {
+ NavSectionCaret,
+ NavSectionContent,
+ NavSectionList,
+} from '~/components/Navigation/NavSection'
import { useTheme } from 'next-themes'
import Image from 'next/legacy/image'
import Link from 'next/link'
@@ -10,14 +14,23 @@ import MenuIconPicker from './MenuIconPicker'
type NavAccordionItem = {
url?: string
+ enabled?: boolean
items?: NavAccordionItem[]
}
function hasActiveDescendant(item: NavAccordionItem, pathname: string): boolean {
+ if (item.enabled === false) return false
if (item.url === pathname) return true
return item.items?.some((child) => hasActiveDescendant(child, pathname)) ?? false
}
+function isRenderable(item: NavAccordionItem): boolean {
+ if (item.enabled === false) return false
+ if (item.url) return true
+
+ return item.items?.some(isRenderable) ?? false
+}
+
const HeaderLink = React.memo(function HeaderLink(props: {
title: string
id: string
@@ -41,13 +54,19 @@ const HeaderLink = React.memo(function HeaderLink(props: {
const ContentAccordionLink = React.memo(function ContentAccordionLink(props: any) {
const pathname = usePathname()
const { resolvedTheme } = useTheme()
- const activeItem = props.subItem.url === pathname
+ const childItems = props.subItem.items ?? []
+ const activeItem =
+ props.subItem.url === pathname &&
+ !childItems.some((child: NavAccordionItem) => child.url === pathname)
const activeItemRef = useRef(null)
- const hasChildren = props.subItem.items && props.subItem.items.length > 0
+ const enabledChildren = childItems.filter(isRenderable)
+ const hasChildren = enabledChildren.length > 0
- const isChildActive =
- hasChildren &&
- props.subItem.items.some((child: NavAccordionItem) => hasActiveDescendant(child, pathname))
+ const isChildActive = enabledChildren.some((child: NavAccordionItem) =>
+ hasActiveDescendant(child, pathname)
+ )
+
+ const accordionValue = props.subItem.url || props.subItem.name
const LinkContainer = (props) => {
const isExternal = props.url.startsWith('https://')
@@ -73,6 +92,9 @@ const ContentAccordionLink = React.memo(function ContentAccordionLink(props: any
}, 0)
}
})
+
+ if (!hasChildren && !props.subItem.url) return null
+
return (
{hasChildren ? (
@@ -80,13 +102,14 @@ const ContentAccordionLink = React.memo(function ContentAccordionLink(props: any
collapsible
type="single"
className="space-y-0.5"
- defaultValue={isChildActive ? props.subItem.url : undefined}
+ defaultValue={isChildActive ? accordionValue : undefined}
>
-
+
-
+
-
-
- {props.subItem.items
- .filter((subItem) => subItem.enabled !== false)
- .map((subSubItem) => {
- if (subSubItem.items && subSubItem.items.length > 0) {
- return
+
+
+
+ {enabledChildren.map((child) => {
+ if (child.items && child.items.length > 0) {
+ return
}
return (
- -
+
-
- {subSubItem.name}
+ {child.url === pathname && (
+
+ )}
+ {child.name}
)
})}
-
+
+
@@ -142,6 +172,7 @@ const ContentAccordionLink = React.memo(function ContentAccordionLink(props: any
className={[
'flex items-center gap-2',
'cursor-pointer transition text-sm',
+ 'focus-inset rounded-md',
activeItem
? 'text-brand-link font-medium'
: 'hover:text-foreground text-foreground-lighter',
@@ -174,6 +205,7 @@ const ContentLink = React.memo(function ContentLink(props: any) {
href={props.url}
className={[
'cursor-pointer transition text-sm',
+ 'focus-inset rounded-md',
props.url === pathname
? 'text-brand-link'
: 'hover:text-foreground text-foreground-lighter',
@@ -205,31 +237,33 @@ const Content = (props) => {
- {menu.items.map((x) => {
- if (x.enabled === false) return null
+ {menu.items.map((entry) => {
+ if (entry.enabled === false) return null
- if (x.items && x.items.length > 0) {
- const enabledItems = x.items.filter((item) => item.enabled !== false)
+ if (entry.items && entry.items.length > 0) {
+ const enabledItems = entry.items.filter(isRenderable)
if (enabledItems.length === 0) return null
return (
- -
+
-
- {x.name}
+ {entry.name}
- {enabledItems.map((subItem) => {
- return
- })}
+ {enabledItems.map((subItem) => (
+
+ ))}
)
}
- return x.url ? : null
+ return entry.url ? (
+
+ ) : null
})}
diff --git a/apps/docs/features/docs/Reference.navigation.client.tsx b/apps/docs/features/docs/Reference.navigation.client.tsx
index b676573baaf..1716b2c27ba 100644
--- a/apps/docs/features/docs/Reference.navigation.client.tsx
+++ b/apps/docs/features/docs/Reference.navigation.client.tsx
@@ -1,10 +1,14 @@
'use client'
+import {
+ NavSectionCaret,
+ NavSectionContent,
+ NavSectionList,
+} from '~/components/Navigation/NavSection'
import type { AbbrevApiReferenceSection } from '~/features/docs/Reference.utils'
import { isElementInViewport } from '~/features/ui/helpers.dom'
import { BASE_PATH } from '~/lib/constants'
import { debounce } from 'lodash-es'
-import { ChevronUp } from 'lucide-react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { Collapsible } from 'radix-ui'
@@ -350,28 +354,26 @@ function CompoundRefLink({
)}
>
{section.title}
-
+
-
-
- {(section.items || []).map((item, idx) => {
- return (
- -
-
-
- )
- })}
-
+
+
+
+ {(section.items || []).map((item, idx) => {
+ return (
+
+
+
+ )
+ })}
+
+
)
diff --git a/packages/config/css/animations.css b/packages/config/css/animations.css
index 3e1362a5d4a..77e0d959e16 100644
--- a/packages/config/css/animations.css
+++ b/packages/config/css/animations.css
@@ -149,14 +149,14 @@
opacity: 0;
}
100% {
- height: var(--radix-accordion-content-height);
+ height: var(--radix-accordion-content-height, var(--radix-collapsible-content-height));
opacity: 1;
}
}
@keyframes slideUp {
0% {
- height: var(--radix-accordion-content-height);
+ height: var(--radix-accordion-content-height, var(--radix-collapsible-content-height));
opacity: 1;
}
100% {