Skip to content

Commit f3702a4

Browse files
committed
feat: header popover
Signed-off-by: Innei <[email protected]>
1 parent 30d5e52 commit f3702a4

File tree

5 files changed

+88
-35
lines changed

5 files changed

+88
-35
lines changed

src/components/layout/header/HeaderContent.tsx

+74-24
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
'use client'
22

3-
import React from 'react'
3+
import React, { useMemo } from 'react'
44
import { motion, useMotionValue } from 'framer-motion'
55
import Link from 'next/link'
66
import { usePathname } from 'next/navigation'
77

8+
import { FloatPopover } from '~/components/ui/float-popover'
89
import { OnlyLg } from '~/components/ui/viewport'
910
import { usePageScrollDirection } from '~/providers/root/page-scroll-info-provider'
1011
import { clsxm } from '~/utils/helper'
@@ -34,10 +35,12 @@ const AnimatedMenu: Component = ({ children }) => {
3435
return (
3536
<div
3637
className="duration-[100ms]"
37-
style={{
38-
opacity,
39-
visibility: opacity === 0 ? 'hidden' : 'visible',
40-
}}
38+
style={
39+
{
40+
// opacity,
41+
// visibility: opacity === 0 ? 'hidden' : 'visible',
42+
}
43+
}
4144
>
4245
{children}
4346
</div>
@@ -61,6 +64,8 @@ function ForDesktop({
6164
[mouseX, mouseY, radius],
6265
)
6366

67+
const pathname = usePathname()
68+
6469
return (
6570
<nav
6671
onMouseMove={handleMouseMove}
@@ -69,56 +74,101 @@ function ForDesktop({
6974
'rounded-full bg-gradient-to-b from-zinc-50/70 to-white/90',
7075
'shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur-md',
7176
'dark:from-zinc-900/70 dark:to-zinc-800/90 dark:ring-zinc-100/10',
72-
'[--spotlight-color:rgb(236_252_203_/_0.6)] dark:[--spotlight-color:rgb(217_249_157_/_0.07)]',
77+
7378
className,
7479
)}
7580
{...props}
7681
>
7782
<ul className="flex bg-transparent px-4 font-medium text-zinc-800 dark:text-zinc-200 ">
7883
{headerMenuConfig.map((section) => {
84+
const href = section.path
85+
const isActive = pathname === href || pathname.startsWith(`${href}/`)
7986
return (
80-
<NavItem
81-
key={section.path}
82-
href={section.path}
83-
className="[&:hover_.icon]:-translate-x-[calc(100%+6px)] [&:hover_.icon]:opacity-100"
84-
>
85-
<span className="relative">
86-
<span
87-
className={clsxm(
88-
'pointer-events-none absolute bottom-0 left-0 top-0 flex items-center opacity-0 duration-200',
89-
'icon',
90-
)}
91-
>
92-
{section.icon}
87+
<MenuPopover subMenu={section.subMenu} key={href}>
88+
<NavItem
89+
href={href}
90+
isActive={isActive}
91+
className={clsxm(
92+
'[&:hover_.icon]:-translate-x-[calc(100%+6px)] [&:hover_.icon]:opacity-100',
93+
'[&.active_.icon]:-translate-x-[calc(100%+6px)] [&.active_.icon]:opacity-80',
94+
'[&.active]:pl-6',
95+
)}
96+
>
97+
<span className="relative">
98+
<span
99+
className={clsxm(
100+
'pointer-events-none absolute bottom-0 left-0 top-0 flex items-center opacity-0 duration-200',
101+
'icon',
102+
)}
103+
>
104+
{section.icon}
105+
</span>
106+
{section.title}
93107
</span>
94-
{section.title}
95-
</span>
96-
</NavItem>
108+
</NavItem>
109+
</MenuPopover>
97110
)
98111
})}
99112
</ul>
100113
</nav>
101114
)
102115
}
103116

117+
const MenuPopover: Component<{
118+
subMenu: (typeof headerMenuConfig)[number]['subMenu']
119+
}> = ({ children, subMenu }) => {
120+
const TriggerComponent = useMemo(() => () => children, [children])
121+
if (!subMenu) return children
122+
return (
123+
<FloatPopover
124+
strategy="fixed"
125+
headless
126+
placement="bottom"
127+
offset={10}
128+
popoverWrapperClassNames="z-[19] relative"
129+
popoverClassNames="rounded-xl"
130+
TriggerComponent={TriggerComponent}
131+
>
132+
{!!subMenu.length && (
133+
<div className="relative flex w-[100px] flex-col p-4">
134+
{subMenu.map((m) => {
135+
return (
136+
<Link
137+
key={m.title}
138+
href={m.path}
139+
className="flex w-full items-center justify-around space-x-2 py-3 duration-200 first:pt-0 last:pb-0 hover:text-accent"
140+
role="button"
141+
>
142+
<span>{m.icon}</span>
143+
<span>{m.title}</span>
144+
</Link>
145+
)
146+
})}
147+
</div>
148+
)}
149+
</FloatPopover>
150+
)
151+
}
152+
104153
function NavItem({
105154
href,
106155
children,
107156
className,
157+
isActive,
108158
}: {
109159
href: string
110160
children: React.ReactNode
111161
className?: string
162+
isActive?: boolean
112163
}) {
113-
const isActive = usePathname() === href
114-
115164
return (
116165
<li>
117166
<Link
118167
href={href}
119168
className={clsxm(
120169
'relative block whitespace-nowrap px-4 py-2 transition',
121170
isActive ? 'text-accent' : 'hover:text-accent-focus',
171+
isActive ? 'active' : '',
122172
className,
123173
)}
124174
>

src/components/layout/header/config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const headerMenuConfig: IHeaderMenu[] = [
4343
{
4444
title: '记',
4545
type: 'Note',
46-
path: '/notes/latest',
46+
path: '/notes',
4747
icon: h(FaSolidFeatherAlt),
4848
},
4949
{

src/components/ui/float-popover/FloatPopover.tsx

+10-7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const FloatPopover: FC<
2121
padding?: number
2222
offset?: number
2323
popoverWrapperClassNames?: string
24+
popoverClassNames?: string
2425

2526
/**
2627
* 不消失
@@ -41,6 +42,7 @@ export const FloatPopover: FC<
4142
padding,
4243
offset: offsetValue,
4344
popoverWrapperClassNames,
45+
popoverClassNames,
4446
debug,
4547
animate = true,
4648
as: As = 'div',
@@ -181,8 +183,8 @@ export const FloatPopover: FC<
181183
])
182184

183185
const TriggerWrapper = (
184-
// @ts-ignore
185186
<As
187+
// @ts-ignore
186188
role={trigger === 'both' || trigger === 'click' ? 'button' : 'note'}
187189
className={clsx('inline-block', wrapperClassNames)}
188190
ref={refs.setReference}
@@ -195,8 +197,8 @@ export const FloatPopover: FC<
195197
)
196198

197199
useEffect(() => {
198-
if (containerRef.current && open) {
199-
containerRef.current.focus()
200+
if (refs.floating.current && open) {
201+
refs.floating.current.focus()
200202
}
201203
}, [open])
202204

@@ -218,17 +220,18 @@ export const FloatPopover: FC<
218220
)}
219221
{...(trigger === 'hover' || trigger === 'both' ? listener : {})}
220222
ref={containerRef}
221-
tabIndex={-1}
222-
role="dialog"
223-
aria-modal="true"
224223
>
225224
<div ref={setContainerAnchorRef} />
226225
{open && (
227226
<div
227+
tabIndex={-1}
228+
role="dialog"
229+
aria-modal="true"
228230
className={clsxm(
229-
'bg-base-100',
231+
'bg-base-100 !shadow-out-sm focus:!shadow-out-sm focus-visible:!shadow-out-sm',
230232
headless ? styles['headless'] : styles['popover-root'],
231233
animate && styles['animate'],
234+
popoverClassNames,
232235
)}
233236
ref={refs.setFloating}
234237
style={{

src/components/ui/theme-switcher/ThemeSwitcher.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ export const ThemeSwitcher = () => {
9797
}, [])
9898
return (
9999
<div className="relative inline-block" onClick={handleClient}>
100-
<ButtonGroup />
101100
<ThemeIndicator />
101+
<ButtonGroup />
102102
</div>
103103
)
104104
}
@@ -112,7 +112,7 @@ const ThemeIndicator = () => {
112112
if (!theme) return null
113113
return (
114114
<div
115-
className="absolute top-[4px] h-[32px] w-[32px] rounded-full bg-neutral/50 duration-200"
115+
className="absolute top-[4px] z-[-1] h-[32px] w-[32px] rounded-full bg-base-100 shadow-[0_1px_2px_0_rgba(122,122,122,.2),_0_1px_3px_0_rgba(122,122,122,.1)] duration-200"
116116
style={{
117117
left: { light: 4, system: 36, dark: 68 }[theme],
118118
}}

src/styles/tailwindcss.css

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)