Skip to content

Commit 5636fb6

Browse files
committed
Merge branch 'dev' into colors-1
2 parents 508be8f + 1d4e2cc commit 5636fb6

24 files changed

+441
-380
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"remark-gfm": "^3.0.1",
8585
"swiper": "^11.1.10",
8686
"tailwind-merge": "^2.3.0",
87+
"tailwind-variants": "^0.2.1",
8788
"tailwindcss-animate": "^1.0.7",
8889
"usehooks-ts": "^3.1.0",
8990
"yaml-loader": "^0.8.0"

src/components/Nav/Desktop/index.tsx

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ import { useRouter } from "next/router"
33
import { useTranslation } from "next-i18next"
44
import { BsTranslate } from "react-icons/bs"
55
import { MdBrightness2, MdWbSunny } from "react-icons/md"
6-
import { HStack, useColorModeValue, useEventListener } from "@chakra-ui/react"
76

87
import LanguagePicker from "@/components/LanguagePicker"
98
import { Button } from "@/components/ui/buttons/Button"
10-
11-
import { cn } from "@/lib/utils/cn"
9+
import { HStack } from "@/components/ui/flex"
1210

1311
import { DESKTOP_LANGUAGE_BUTTON_NAME } from "@/lib/constants"
1412

13+
import useColorModeValue from "@/hooks/useColorModeValue"
14+
import { useEventListener } from "@/hooks/useEventListener"
15+
1516
type DesktopNavMenuProps = {
1617
toggleColorMode: () => void
1718
}
@@ -21,6 +22,7 @@ const DesktopNavMenu = ({ toggleColorMode }: DesktopNavMenuProps) => {
2122
const { locale } = useRouter()
2223
const languagePickerRef = useRef<HTMLButtonElement>(null)
2324

25+
const ThemeIcon = useColorModeValue(MdBrightness2, MdWbSunny)
2426
const themeIconAriaLabel = useColorModeValue(
2527
// TODO: Add i18n support
2628
"Switch to Dark Theme",
@@ -45,26 +47,15 @@ const DesktopNavMenu = ({ toggleColorMode }: DesktopNavMenuProps) => {
4547
})
4648

4749
return (
48-
<HStack hideBelow="md" gap="0">
50+
<HStack className="hidden gap-0 md:flex">
4951
<Button
52+
aria-label={themeIconAriaLabel}
5053
variant="ghost"
5154
isSecondary
52-
className="group p-2 hover:!text-primary-hover xl:p-3"
55+
className="group px-2 xl:px-3 [&>svg]:transition-all [&>svg]:duration-500 [&>svg]:hover:rotate-12 [&>svg]:hover:text-primary-hover"
5356
onClick={toggleColorMode}
54-
aria-label={themeIconAriaLabel}
5557
>
56-
<MdBrightness2
57-
className={cn(
58-
"dark:hidden",
59-
"transform-transform duration-500 group-hover:rotate-12 group-hover:transition-transform group-hover:duration-500"
60-
)}
61-
/>
62-
<MdWbSunny
63-
className={cn(
64-
"hidden dark:block",
65-
"transform-transform duration-500 group-hover:rotate-12 group-hover:transition-transform group-hover:duration-500"
66-
)}
67-
/>
58+
<ThemeIcon className="transform-transform duration-500 group-hover:rotate-12 group-hover:transition-transform group-hover:duration-500" />
6859
</Button>
6960

7061
{/* Locale-picker menu */}

src/components/Nav/Menu/MenuContent.tsx

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,47 @@
11
import { motion } from "framer-motion"
2-
import { Box } from "@chakra-ui/react"
2+
import { tv } from "tailwind-variants"
33
import { Content } from "@radix-ui/react-navigation-menu"
44

5+
import { cn } from "@/lib/utils/cn"
6+
57
import { NavItem, NavSections } from "../types"
68

79
import SubMenu from "./SubMenu"
810
import { useNavMenu } from "./useNavMenu"
911

12+
export const navMenuVariants = tv({
13+
slots: {
14+
base: "text-body",
15+
item: "has-[button[data-state=open]]:rounded-s-md has-[button[data-state=open]]:rounded-e-none has-[button[data-state=open]]:-me-2 has-[button[data-state=open]]:pe-2",
16+
link: "w-full relative py-4 hover:text-menu-active [&:hover_p]:text-menu-active focus-visible:text-menu-active [&:focus-visible_p]:text-menu-active hover:outline-0 rounded-md hover:shadow-none focus-visible:outline-0 focus-visible:rounded-md focus-visible:shadow-none",
17+
submenu: "grid h-full w-full grid-cols-1",
18+
},
19+
variants: {
20+
level: {
21+
1: {
22+
submenu: "grid-cols-3 bg-menu-1-background",
23+
item: "has-[button[data-state=open]]:bg-menu-1-active-background",
24+
link: "data-[active=true]:bg-menu-1-active-background hover:bg-menu-1-active-background focus-visible:bg-menu-1-active-background",
25+
},
26+
2: {
27+
submenu: "grid-cols-2 bg-menu-2-background",
28+
item: "has-[button[data-state=open]]:bg-menu-2-active-background",
29+
link: "hover:bg-menu-2-active-background focus-visible:bg-menu-2-active-background data-[active=true]:bg-menu-2-active-background",
30+
},
31+
3: {
32+
submenu: "grid-cols-1 bg-menu-3-background",
33+
item: "has-[button[data-state=open]]:bg-menu-3-active-background",
34+
link: "data-[active=true]:bg-menu-3-active-background hover:bg-menu-3-active-background",
35+
},
36+
4: {
37+
submenu: "grid-cols-1 bg-menu-4-background",
38+
item: "has-[button[data-state=open]]:bg-menu-4-active-background",
39+
link: "data-[active=true]:bg-menu-4-active-background hover:bg-menu-4-active-background",
40+
},
41+
},
42+
},
43+
})
44+
1045
type MenuContentProps = {
1146
items: NavItem[]
1247
isOpen: boolean
@@ -15,31 +50,27 @@ type MenuContentProps = {
1550

1651
// Desktop Menu content
1752
const MenuContent = ({ items, isOpen, sections }: MenuContentProps) => {
18-
const { activeSection, containerVariants, menuColors, onClose } =
19-
useNavMenu(sections)
53+
const { activeSection, containerVariants, onClose } = useNavMenu(sections)
54+
const { base } = navMenuVariants()
2055

2156
return (
2257
<Content asChild>
23-
<Box
24-
as={motion.div}
58+
<motion.div
59+
className={cn(
60+
"absolute inset-x-0 top-19 border border-body-light bg-menu-1-background shadow-md",
61+
base()
62+
)}
2563
variants={containerVariants}
2664
initial={false}
2765
animate={isOpen ? "open" : "closed"}
28-
position="absolute"
29-
top="19"
30-
insetInline="0"
31-
shadow="md"
32-
border="1px"
33-
borderColor={menuColors.stroke}
34-
bg={menuColors.lvl[1].background}
3566
>
3667
<SubMenu
3768
lvl={1}
3869
items={items}
3970
activeSection={activeSection}
4071
onClose={onClose}
4172
/>
42-
</Box>
73+
</motion.div>
4374
</Content>
4475
)
4576
}

src/components/Nav/Menu/SubMenu.tsx

Lines changed: 35 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
import { AnimatePresence, motion } from "framer-motion"
22
import NextLink from "next/link"
3-
import {
4-
Box,
5-
Button,
6-
Grid,
7-
Icon,
8-
ListItem,
9-
UnorderedList,
10-
} from "@chakra-ui/react"
113
import {
124
Content,
135
Item,
@@ -18,16 +10,19 @@ import {
1810
Viewport,
1911
} from "@radix-ui/react-navigation-menu"
2012

21-
import { ButtonProps } from "@/components/Buttons"
2213
import { ChevronNext } from "@/components/Chevron"
23-
import Link from "@/components/Link"
14+
import { Button } from "@/components/ui/buttons/Button"
15+
import { BaseLink } from "@/components/ui/Link"
16+
import { ListItem, UnorderedList } from "@/components/ui/list"
2417

18+
import { cn } from "@/lib/utils/cn"
2519
import { trackCustomEvent } from "@/lib/utils/matomo"
2620
import { cleanPath } from "@/lib/utils/url"
2721

2822
import type { Level, NavItem, NavSectionKey } from "../types"
2923

3024
import ItemContent from "./ItemContent"
25+
import { navMenuVariants } from "./MenuContent"
3126
import { useSubMenu } from "./useSubMenu"
3227

3328
type LvlContentProps = {
@@ -47,77 +42,41 @@ type LvlContentProps = {
4742
* @returns The JSX element representing the menu content.
4843
*/
4944
const SubMenu = ({ lvl, items, activeSection, onClose }: LvlContentProps) => {
50-
const { asPath, locale, menuColors, menuVariants, PADDING } = useSubMenu()
45+
const { asPath, locale, menuVariants } = useSubMenu()
46+
const { submenu, item: itemClasses, link } = navMenuVariants({ level: lvl })
5147

5248
if (lvl > 3) return null
5349

54-
const templateColumns = `repeat(${4 - lvl}, 1fr)`
55-
5650
return (
5751
<Sub orientation="vertical" asChild>
5852
<AnimatePresence>
59-
<Grid
60-
as={motion.div}
53+
<motion.div
54+
className={submenu()}
6155
variants={menuVariants}
6256
initial="closed"
6357
animate="open"
6458
exit="closed"
65-
w="full"
66-
h="full"
67-
gridTemplateColumns={templateColumns}
6859
>
6960
<List asChild>
70-
<UnorderedList listStyleType="none" p={PADDING / 2} m="0">
61+
<UnorderedList className="m-0 list-none p-2">
7162
{items.map((item) => {
72-
const { label, icon, ...action } = item
63+
const { label, icon: Icon, ...action } = item
7364
const subItems = action.items || []
7465
const isLink = "href" in action
7566
const isActivePage = isLink && cleanPath(asPath) === action.href
76-
const activeStyles = {
77-
outline: "none",
78-
rounded: "md",
79-
"p, svg": { color: menuColors.highlight },
80-
bg: menuColors.lvl[lvl].activeBackground,
81-
boxShadow: "none",
82-
}
83-
const buttonProps: ButtonProps = {
84-
color: menuColors.body,
85-
leftIcon: lvl === 1 && icon ? <Icon as={icon} /> : undefined,
86-
rightIcon: isLink ? undefined : <ChevronNext />,
87-
position: "relative",
88-
w: "full",
89-
me: -PADDING,
90-
sx: {
91-
"span:first-of-type": { m: 0, me: 4 }, // Spacing for icon
92-
},
93-
py: PADDING,
94-
bg: isActivePage
95-
? menuColors.lvl[lvl].activeBackground
96-
: "none",
97-
_hover: activeStyles,
98-
_focus: activeStyles,
99-
variant: "ghost",
100-
}
67+
68+
const buttonClasses = cn("no-underline text-body", link())
69+
10170
return (
10271
<Item key={label} asChild>
103-
<ListItem
104-
mb={PADDING / 2}
105-
_last={{ mb: 0 }}
106-
sx={{
107-
'&:has(button[data-state="open"])': {
108-
roundedStart: "md",
109-
roundedEnd: "none",
110-
bg: menuColors.lvl[lvl].activeBackground,
111-
me: -PADDING,
112-
pe: PADDING,
113-
},
114-
}}
115-
>
72+
<ListItem className={cn("mb-2 last:mb-0", itemClasses())}>
11673
{isLink ? (
11774
<NextLink href={action.href!} passHref legacyBehavior>
11875
<NavigationMenuLink asChild>
11976
<Button
120-
as={Link}
77+
variant="ghost"
78+
className={buttonClasses}
79+
data-active={isActivePage}
12180
onClick={() => {
12281
onClose()
12382
trackCustomEvent({
@@ -126,31 +85,39 @@ const SubMenu = ({ lvl, items, activeSection, onClose }: LvlContentProps) => {
12685
eventName: action.href!,
12786
})
12887
}}
129-
{...buttonProps}
88+
asChild
13089
>
131-
<ItemContent item={item} lvl={lvl} />
90+
<BaseLink>
91+
{lvl === 1 && Icon ? (
92+
<Icon className="me-4 h-6 w-6" />
93+
) : null}
94+
95+
<ItemContent item={item} lvl={lvl} />
96+
</BaseLink>
13297
</Button>
13398
</NavigationMenuLink>
13499
</NextLink>
135100
) : (
136101
<>
137102
<Trigger asChild>
138-
<Button {...buttonProps}>
103+
<Button variant="ghost" className={buttonClasses}>
104+
{lvl === 1 && Icon ? (
105+
<Icon className="me-4 h-6 w-6" />
106+
) : null}
107+
139108
<ItemContent item={item} lvl={lvl} />
109+
<ChevronNext />
140110
</Button>
141111
</Trigger>
142112
<Content asChild>
143-
<Box
144-
bg={menuColors.lvl[lvl + 1].background}
145-
h="full"
146-
>
113+
<div className="h-full">
147114
<SubMenu
148115
lvl={(lvl + 1) as Level}
149116
items={subItems}
150117
activeSection={activeSection}
151118
onClose={onClose}
152119
/>
153-
</Box>
120+
</div>
154121
</Content>
155122
</>
156123
)}
@@ -161,7 +128,7 @@ const SubMenu = ({ lvl, items, activeSection, onClose }: LvlContentProps) => {
161128
</UnorderedList>
162129
</List>
163130
<Viewport style={{ gridColumn: "2/4" }} />
164-
</Grid>
131+
</motion.div>
165132
</AnimatePresence>
166133
</Sub>
167134
)

src/components/Nav/Menu/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import type { NavSections } from "../types"
1818

1919
import { useNavMenu } from "./useNavMenu"
2020

21+
const MenuContent = dynamic(() => import("./MenuContent"))
22+
2123
type NavMenuProps = BaseHTMLAttributes<HTMLDivElement> & {
2224
sections: NavSections
2325
}
@@ -26,8 +28,6 @@ const Menu = ({ sections, ...props }: NavMenuProps) => {
2628
const { activeSection, direction, handleSectionChange, isOpen } =
2729
useNavMenu(sections)
2830

29-
const MenuContent = dynamic(() => import("./MenuContent"))
30-
3131
return (
3232
<div {...props}>
3333
<Root

src/components/Nav/Menu/useNavMenu.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
import { useState } from "react"
22
import type { MotionProps } from "framer-motion"
3-
import { useEventListener } from "@chakra-ui/react"
43

54
import { isModified } from "@/lib/utils/keyboard"
65

76
import { MAIN_NAV_ID, SECTION_LABELS } from "@/lib/constants"
87

98
import type { NavSectionKey, NavSections } from "../types"
109

11-
import { useNavMenuColors } from "@/hooks/useNavMenuColors"
10+
import { useEventListener } from "@/hooks/useEventListener"
1211
import { useRtlFlip } from "@/hooks/useRtlFlip"
1312

1413
export const useNavMenu = (sections: NavSections) => {
1514
const { direction } = useRtlFlip()
16-
const menuColors = useNavMenuColors()
1715
const [activeSection, setActiveSection] = useState<NavSectionKey | null>(null)
1816

1917
// Focus corresponding nav section when number keys pressed
@@ -72,7 +70,6 @@ export const useNavMenu = (sections: NavSections) => {
7270
direction,
7371
handleSectionChange,
7472
isOpen,
75-
menuColors,
7673
onClose,
7774
}
7875
}

0 commit comments

Comments
 (0)