Skip to content

Commit 94f6d65

Browse files
committed
Merge branch 'nav-menu-radix-ui' of https://github.com/ethereum/ethereum-org-website into nav-menu-radix-ui
2 parents 77dcf7e + ddfaf99 commit 94f6d65

File tree

5 files changed

+247
-168
lines changed

5 files changed

+247
-168
lines changed

src/components/Nav/Menu/LvlContent.tsx

Lines changed: 0 additions & 152 deletions
This file was deleted.

src/components/Nav/Menu/SubMenu.tsx

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import { AnimatePresence, motion } from "framer-motion"
2+
import NextLink from "next/link"
3+
import {
4+
Box,
5+
Button,
6+
Grid,
7+
Icon,
8+
ListItem,
9+
UnorderedList,
10+
} from "@chakra-ui/react"
11+
import * as NavigationMenu from "@radix-ui/react-navigation-menu"
12+
13+
import { ButtonProps } from "@/components/Buttons"
14+
import Link from "@/components/Link"
15+
16+
import { trackCustomEvent } from "@/lib/utils/matomo"
17+
import { cleanPath } from "@/lib/utils/url"
18+
19+
import type { Level, NavItem, NavSectionKey } from "../types"
20+
21+
import ItemContent from "./ItemContent"
22+
import NextChevron from "./NextChevron"
23+
import { useSubMenu } from "./useSubMenu"
24+
25+
type LvlContentProps = {
26+
lvl: Level
27+
items: NavItem[]
28+
activeSection: NavSectionKey | null
29+
onClose: () => void
30+
}
31+
32+
/**
33+
* Content for each sub-menu below top-level navigation
34+
* Content renders inside sibling Viewport
35+
* @param lvl - The level of the menu
36+
* @param items - The items to be displayed in the menu
37+
* @param activeSection - English label of the active section for event tracking
38+
* @param onClose - Function to close the menu
39+
* @returns The JSX element representing the menu content.
40+
*/
41+
const SubMenu = ({ lvl, items, activeSection, onClose }: LvlContentProps) => {
42+
const { asPath, locale, menuColors, menuVariants, PADDING } = useSubMenu()
43+
44+
if (lvl > 3) return null
45+
46+
const templateColumns = `repeat(${4 - lvl}, 1fr)`
47+
48+
return (
49+
<NavigationMenu.Sub orientation="vertical" asChild>
50+
<AnimatePresence>
51+
<Grid
52+
as={motion.div}
53+
variants={menuVariants}
54+
initial="closed"
55+
animate="open"
56+
exit="closed"
57+
w="full"
58+
h="full"
59+
gridTemplateColumns={templateColumns}
60+
>
61+
<NavigationMenu.List asChild>
62+
<UnorderedList listStyleType="none" p={PADDING / 2} m="0">
63+
{items.map((item) => {
64+
const { label, description, icon, ...action } = item
65+
const subItems = action.items || []
66+
const isLink = "href" in action
67+
const isActivePage = isLink && cleanPath(asPath) === action.href
68+
const activeStyles = {
69+
outline: "none",
70+
rounded: "md",
71+
"p, svg": { color: menuColors.highlight },
72+
bg: menuColors.lvl[lvl].activeBackground,
73+
boxShadow: "none",
74+
}
75+
const buttonProps: ButtonProps = {
76+
color: menuColors.body,
77+
leftIcon: lvl === 1 && icon ? <Icon as={icon} /> : undefined,
78+
rightIcon: isLink ? undefined : <NextChevron />,
79+
position: "relative",
80+
w: "full",
81+
me: -PADDING,
82+
sx: {
83+
"span:first-of-type": { m: 0, me: 4 }, // Spacing for icon
84+
},
85+
py: PADDING,
86+
bg: isActivePage
87+
? menuColors.lvl[lvl].activeBackground
88+
: "none",
89+
_hover: activeStyles,
90+
_focus: activeStyles,
91+
variant: "ghost",
92+
}
93+
return (
94+
<NavigationMenu.Item key={label} asChild>
95+
<ListItem
96+
mb={PADDING / 2}
97+
_last={{ mb: 0 }}
98+
sx={{
99+
'&:has(button[data-state="open"])': {
100+
roundedStart: "md",
101+
roundedEnd: "none",
102+
bg: menuColors.lvl[lvl].activeBackground,
103+
me: -PADDING,
104+
pe: PADDING,
105+
},
106+
}}
107+
>
108+
{isLink ? (
109+
<NextLink href={action.href!} passHref legacyBehavior>
110+
<NavigationMenu.Link asChild>
111+
<Button
112+
as={Link}
113+
onClick={() => {
114+
onClose()
115+
trackCustomEvent({
116+
eventCategory: "Desktop navigation menu",
117+
eventAction: `Menu - ${activeSection} - ${locale}`,
118+
eventName: action.href!,
119+
})
120+
}}
121+
{...buttonProps}
122+
>
123+
<ItemContent item={item} lvl={lvl} />
124+
</Button>
125+
</NavigationMenu.Link>
126+
</NextLink>
127+
) : (
128+
<>
129+
<NavigationMenu.Trigger asChild>
130+
<Button {...buttonProps}>
131+
<ItemContent item={item} lvl={lvl} />
132+
</Button>
133+
</NavigationMenu.Trigger>
134+
<NavigationMenu.Content asChild>
135+
<Box
136+
bg={menuColors.lvl[lvl + 1].background}
137+
h="full"
138+
>
139+
<SubMenu
140+
lvl={(lvl + 1) as Level}
141+
items={subItems}
142+
activeSection={activeSection}
143+
onClose={onClose}
144+
/>
145+
</Box>
146+
</NavigationMenu.Content>
147+
</>
148+
)}
149+
</ListItem>
150+
</NavigationMenu.Item>
151+
)
152+
})}
153+
</UnorderedList>
154+
</NavigationMenu.List>
155+
<NavigationMenu.Viewport style={{ gridColumn: "2/4" }} />
156+
</Grid>
157+
</AnimatePresence>
158+
</NavigationMenu.Sub>
159+
)
160+
}
161+
162+
export default SubMenu

0 commit comments

Comments
 (0)