Skip to content

Commit

Permalink
feat: header popover
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <tukon479@gmail.com>
  • Loading branch information
Innei committed Jun 15, 2023
1 parent 30d5e52 commit f3702a4
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 35 deletions.
98 changes: 74 additions & 24 deletions src/components/layout/header/HeaderContent.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
'use client'

import React from 'react'
import React, { useMemo } from 'react'
import { motion, useMotionValue } from 'framer-motion'
import Link from 'next/link'
import { usePathname } from 'next/navigation'

import { FloatPopover } from '~/components/ui/float-popover'
import { OnlyLg } from '~/components/ui/viewport'
import { usePageScrollDirection } from '~/providers/root/page-scroll-info-provider'
import { clsxm } from '~/utils/helper'
Expand Down Expand Up @@ -34,10 +35,12 @@ const AnimatedMenu: Component = ({ children }) => {
return (
<div
className="duration-[100ms]"
style={{
opacity,
visibility: opacity === 0 ? 'hidden' : 'visible',
}}
style={
{
// opacity,
// visibility: opacity === 0 ? 'hidden' : 'visible',
}
}
>
{children}
</div>
Expand All @@ -61,6 +64,8 @@ function ForDesktop({
[mouseX, mouseY, radius],
)

const pathname = usePathname()

return (
<nav
onMouseMove={handleMouseMove}
Expand All @@ -69,56 +74,101 @@ function ForDesktop({
'rounded-full bg-gradient-to-b from-zinc-50/70 to-white/90',
'shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur-md',
'dark:from-zinc-900/70 dark:to-zinc-800/90 dark:ring-zinc-100/10',
'[--spotlight-color:rgb(236_252_203_/_0.6)] dark:[--spotlight-color:rgb(217_249_157_/_0.07)]',

className,
)}
{...props}
>
<ul className="flex bg-transparent px-4 font-medium text-zinc-800 dark:text-zinc-200 ">
{headerMenuConfig.map((section) => {
const href = section.path
const isActive = pathname === href || pathname.startsWith(`${href}/`)
return (
<NavItem
key={section.path}
href={section.path}
className="[&:hover_.icon]:-translate-x-[calc(100%+6px)] [&:hover_.icon]:opacity-100"
>
<span className="relative">
<span
className={clsxm(
'pointer-events-none absolute bottom-0 left-0 top-0 flex items-center opacity-0 duration-200',
'icon',
)}
>
{section.icon}
<MenuPopover subMenu={section.subMenu} key={href}>
<NavItem
href={href}
isActive={isActive}
className={clsxm(
'[&:hover_.icon]:-translate-x-[calc(100%+6px)] [&:hover_.icon]:opacity-100',
'[&.active_.icon]:-translate-x-[calc(100%+6px)] [&.active_.icon]:opacity-80',
'[&.active]:pl-6',
)}
>
<span className="relative">
<span
className={clsxm(
'pointer-events-none absolute bottom-0 left-0 top-0 flex items-center opacity-0 duration-200',
'icon',
)}
>
{section.icon}
</span>
{section.title}
</span>
{section.title}
</span>
</NavItem>
</NavItem>
</MenuPopover>
)
})}
</ul>
</nav>
)
}

const MenuPopover: Component<{
subMenu: (typeof headerMenuConfig)[number]['subMenu']
}> = ({ children, subMenu }) => {
const TriggerComponent = useMemo(() => () => children, [children])
if (!subMenu) return children
return (
<FloatPopover
strategy="fixed"
headless
placement="bottom"
offset={10}
popoverWrapperClassNames="z-[19] relative"
popoverClassNames="rounded-xl"
TriggerComponent={TriggerComponent}
>
{!!subMenu.length && (
<div className="relative flex w-[100px] flex-col p-4">
{subMenu.map((m) => {
return (
<Link
key={m.title}
href={m.path}
className="flex w-full items-center justify-around space-x-2 py-3 duration-200 first:pt-0 last:pb-0 hover:text-accent"
role="button"
>
<span>{m.icon}</span>
<span>{m.title}</span>
</Link>
)
})}
</div>
)}
</FloatPopover>
)
}

function NavItem({
href,
children,
className,
isActive,
}: {
href: string
children: React.ReactNode
className?: string
isActive?: boolean
}) {
const isActive = usePathname() === href

return (
<li>
<Link
href={href}
className={clsxm(
'relative block whitespace-nowrap px-4 py-2 transition',
isActive ? 'text-accent' : 'hover:text-accent-focus',
isActive ? 'active' : '',
className,
)}
>
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/header/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const headerMenuConfig: IHeaderMenu[] = [
{
title: '记',
type: 'Note',
path: '/notes/latest',
path: '/notes',
icon: h(FaSolidFeatherAlt),
},
{
Expand Down
17 changes: 10 additions & 7 deletions src/components/ui/float-popover/FloatPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const FloatPopover: FC<
padding?: number
offset?: number
popoverWrapperClassNames?: string
popoverClassNames?: string

/**
* 不消失
Expand All @@ -41,6 +42,7 @@ export const FloatPopover: FC<
padding,
offset: offsetValue,
popoverWrapperClassNames,
popoverClassNames,
debug,
animate = true,
as: As = 'div',
Expand Down Expand Up @@ -181,8 +183,8 @@ export const FloatPopover: FC<
])

const TriggerWrapper = (
// @ts-ignore
<As
// @ts-ignore
role={trigger === 'both' || trigger === 'click' ? 'button' : 'note'}
className={clsx('inline-block', wrapperClassNames)}
ref={refs.setReference}
Expand All @@ -195,8 +197,8 @@ export const FloatPopover: FC<
)

useEffect(() => {
if (containerRef.current && open) {
containerRef.current.focus()
if (refs.floating.current && open) {
refs.floating.current.focus()
}
}, [open])

Expand All @@ -218,17 +220,18 @@ export const FloatPopover: FC<
)}
{...(trigger === 'hover' || trigger === 'both' ? listener : {})}
ref={containerRef}
tabIndex={-1}
role="dialog"
aria-modal="true"
>
<div ref={setContainerAnchorRef} />
{open && (
<div
tabIndex={-1}
role="dialog"
aria-modal="true"
className={clsxm(
'bg-base-100',
'bg-base-100 !shadow-out-sm focus:!shadow-out-sm focus-visible:!shadow-out-sm',
headless ? styles['headless'] : styles['popover-root'],
animate && styles['animate'],
popoverClassNames,
)}
ref={refs.setFloating}
style={{
Expand Down
4 changes: 2 additions & 2 deletions src/components/ui/theme-switcher/ThemeSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ export const ThemeSwitcher = () => {
}, [])
return (
<div className="relative inline-block" onClick={handleClient}>
<ButtonGroup />
<ThemeIndicator />
<ButtonGroup />
</div>
)
}
Expand All @@ -112,7 +112,7 @@ const ThemeIndicator = () => {
if (!theme) return null
return (
<div
className="absolute top-[4px] h-[32px] w-[32px] rounded-full bg-neutral/50 duration-200"
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"
style={{
left: { light: 4, system: 36, dark: 68 }[theme],
}}
Expand Down
2 changes: 1 addition & 1 deletion src/styles/tailwindcss.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f3702a4

Please sign in to comment.