Skip to content

Commit 7a87b93

Browse files
committed
feat: header responsive
Signed-off-by: Innei <[email protected]>
1 parent 2f39e0a commit 7a87b93

17 files changed

+212
-71
lines changed

src/components/layout/header/Header.tsx

+34-16
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
import { memo } from 'react'
22

3-
import { BluredBackground } from './BluredBackground'
4-
import { HeaderContent } from './HeaderContent'
5-
import { HeaderDataConfigureProvider } from './HeaderDataConfigureProvider'
6-
import { Logo } from './Logo'
7-
import { SiteOwnerAvatar } from './SiteOwnerAvatar'
8-
import { UserAuth } from './UserAuth'
3+
import { clsxm } from '~/utils/helper'
4+
5+
import { BluredBackground } from './internal/BluredBackground'
6+
import { headerGrid } from './internal/grid.css'
7+
import {
8+
HeaderCenterArea,
9+
HeaderLeftButtonArea,
10+
HeaderLogoArea,
11+
} from './internal/HeaderArea'
12+
import { HeaderContent } from './internal/HeaderContent'
13+
import { HeaderDataConfigureProvider } from './internal/HeaderDataConfigureProvider'
14+
import { HeaderDrawerButton } from './internal/HeaderDrawerButton'
15+
import { Logo } from './internal/Logo'
16+
import { SiteOwnerAvatar } from './internal/SiteOwnerAvatar'
17+
import { UserAuth } from './internal/UserAuth'
918

1019
export const Header = () => {
1120
return (
@@ -19,17 +28,26 @@ const MemoedHeader = memo(() => {
1928
return (
2029
<header className="fixed left-0 right-0 top-0 z-[9] h-[4.5rem]">
2130
<BluredBackground />
22-
<div className="relative mx-auto grid h-full min-h-0 max-w-7xl grid-cols-[4.5rem_auto_4.5rem] lg:px-8">
23-
<div className="relative">
31+
<div
32+
className={clsxm(
33+
'relative mx-auto grid h-full min-h-0 max-w-7xl grid-cols-[4.5rem_auto_4.5rem] lg:px-8',
34+
headerGrid,
35+
)}
36+
>
37+
<HeaderLeftButtonArea>
38+
<HeaderDrawerButton />
39+
</HeaderLeftButtonArea>
40+
41+
<HeaderLogoArea>
2442
<Logo />
25-
<SiteOwnerAvatar />
26-
</div>
27-
<div className="flex min-w-0 flex-grow">
28-
<div className="flex flex-grow items-center justify-center">
29-
<HeaderContent />
30-
</div>
31-
</div>
32-
<div className="flex items-center">
43+
<SiteOwnerAvatar className="hidden lg:inline-block" />
44+
</HeaderLogoArea>
45+
46+
<HeaderCenterArea>
47+
<HeaderContent />
48+
</HeaderCenterArea>
49+
50+
<div className="flex h-full w-full items-center">
3351
<UserAuth />
3452
</div>
3553
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const HeaderActionButton: Component = ({ children }) => {
2+
return (
3+
<button
4+
type="button"
5+
className="group h-10 rounded-full bg-gradient-to-b from-zinc-50/50 to-white/90 px-3 text-sm shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur transition dark:from-zinc-900/50 dark:to-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
6+
>
7+
{children}
8+
</button>
9+
)
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use client'
2+
3+
import { OnlyDesktop } from '~/components/ui/viewport'
4+
import { clsxm } from '~/utils/helper'
5+
6+
import styles from './grid.module.css'
7+
8+
export const HeaderLogoArea: Component = ({ children }) => {
9+
return (
10+
<div className={clsxm('relative', styles['header--grid__logo'])}>
11+
<div className={clsxm('relative flex justify-center')}>{children}</div>
12+
</div>
13+
)
14+
}
15+
16+
export const HeaderLeftButtonArea: Component = ({ children }) => {
17+
return (
18+
<div
19+
className={clsxm(
20+
'relative flex h-full w-full items-center justify-center lg:hidden',
21+
styles['header--grid__left-button'],
22+
)}
23+
>
24+
{children}
25+
</div>
26+
)
27+
}
28+
29+
export const HeaderCenterArea: Component = ({ children }) => {
30+
return (
31+
<OnlyDesktop>
32+
<div className="flex min-w-0 flex-grow">
33+
<div className="flex flex-grow items-center justify-center">
34+
{children}
35+
</div>
36+
</div>
37+
</OnlyDesktop>
38+
)
39+
}

src/components/layout/header/HeaderContent.tsx renamed to src/components/layout/header/internal/HeaderContent.tsx

+8-10
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ import React, { memo, useMemo } from 'react'
44
import { motion, useMotionValue } from 'framer-motion'
55
import Link from 'next/link'
66
import { usePathname } from 'next/navigation'
7-
import type { IHeaderMenu } from './config'
7+
import type { IHeaderMenu } from '../config'
88

99
import { FloatPopover } from '~/components/ui/float-popover'
10-
import { OnlyLg } from '~/components/ui/viewport'
1110
import { usePageScrollDirection } from '~/providers/root/page-scroll-info-provider'
1211
import { clsxm } from '~/utils/helper'
1312

@@ -16,11 +15,9 @@ import { useHeaderConfig } from './HeaderDataConfigureProvider'
1615

1716
export const HeaderContent = () => {
1817
return (
19-
<OnlyLg>
20-
<AnimatedMenu>
21-
<ForDesktop />
22-
</AnimatedMenu>
23-
</OnlyLg>
18+
<AnimatedMenu>
19+
<ForDesktop />
20+
</AnimatedMenu>
2421
)
2522
}
2623

@@ -116,6 +113,7 @@ const HeaderMenuItem = memo<{
116113
</MenuPopover>
117114
)
118115
})
116+
119117
const MenuPopover: Component<{
120118
subMenu: IHeaderMenu['subMenu']
121119
}> = memo(({ children, subMenu }) => {
@@ -128,17 +126,17 @@ const MenuPopover: Component<{
128126
placement="bottom"
129127
offset={10}
130128
popoverWrapperClassNames="z-[19] relative"
131-
popoverClassNames="rounded-xl"
129+
popoverClassNames="rounded-xl !p-0"
132130
TriggerComponent={TriggerComponent}
133131
>
134132
{!!subMenu.length && (
135-
<div className="relative flex w-[130px] flex-col p-4">
133+
<div className="relative flex w-[130px] flex-col px-4">
136134
{subMenu.map((m) => {
137135
return (
138136
<Link
139137
key={m.title}
140138
href={m.path}
141-
className="flex w-full items-center justify-around space-x-2 py-3 duration-200 first:pt-0 last:pb-0 hover:text-accent"
139+
className="flex w-full items-center justify-around space-x-2 py-3 duration-200 hover:text-accent"
142140
role="button"
143141
>
144142
{!!m.icon && <span>{m.icon}</span>}

src/components/layout/header/HeaderDataConfigureProvider.tsx renamed to src/components/layout/header/internal/HeaderDataConfigureProvider.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { createContext, useContext, useEffect, useMemo, useState } from 'react'
66
import { useAggregationQuery } from '~/hooks/data/use-aggregation'
77
import { cloneDeep } from '~/lib/_'
88

9-
import { headerMenuConfig as baseHeaderMenuConfig } from './config'
9+
import { headerMenuConfig as baseHeaderMenuConfig } from '../config'
1010

1111
const HeaderMenuConfigContext = createContext({
1212
config: baseHeaderMenuConfig,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { SVGProps } from 'react'
2+
3+
import { HeaderActionButton } from './HeaderActionButton'
4+
5+
function IcBaselineMenuOpen(props: SVGProps<SVGSVGElement>) {
6+
return (
7+
<svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
8+
<path
9+
fill="currentColor"
10+
d="M3 18h13v-2H3v2zm0-5h10v-2H3v2zm0-7v2h13V6H3zm18 9.59L17.42 12L21 8.41L19.59 7l-5 5l5 5L21 15.59z"
11+
/>
12+
</svg>
13+
)
14+
}
15+
16+
export const HeaderDrawerButton = () => {
17+
return (
18+
<HeaderActionButton>
19+
<IcBaselineMenuOpen />
20+
</HeaderActionButton>
21+
)
22+
}

src/components/layout/header/SiteOwnerAvatar.tsx renamed to src/components/layout/header/internal/SiteOwnerAvatar.tsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,25 @@
33
import Image from 'next/image'
44

55
import { useAggregationSelector } from '~/providers/root/aggregation-data-provider'
6+
import { clsxm } from '~/utils/helper'
67

7-
export const SiteOwnerAvatar = () => {
8+
export const SiteOwnerAvatar: Component = ({ className }) => {
89
const avatar = useAggregationSelector((data) => data.user.avatar)
910

1011
if (!avatar) return
1112
return (
12-
<div className="absolute bottom-[8px] right-[-5px] overflow-hidden rounded-full border-[1.5px] border-accent/50">
13+
<div
14+
className={clsxm(
15+
'absolute bottom-[8px] right-[-5px] overflow-hidden rounded-full border-[1.5px] border-accent/50',
16+
className,
17+
)}
18+
>
1319
<Image
1420
src={avatar}
1521
alt=""
1622
width={25}
1723
height={25}
18-
className="rounded-full border-[1.5px] border-transparent"
24+
className="rounded-full border-[1.5px] border-base-100"
1925
/>
2026
</div>
2127
)

src/components/layout/header/UserAuth.tsx renamed to src/components/layout/header/internal/UserAuth.tsx

+10-14
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,19 @@ import React from 'react'
44
import { AnimatePresence } from 'framer-motion'
55
import { usePathname } from 'next/navigation'
66

7-
import {
8-
SignedIn,
9-
SignedOut,
10-
SignInButton,
11-
UserButton,
12-
useUser,
13-
} from '@clerk/nextjs'
7+
import { SignedIn, SignedOut, SignInButton, UserButton } from '@clerk/nextjs'
148

159
import { appConfig } from '~/app.config'
1610
import { UserArrowLeftIcon } from '~/components/icons/user-arrow-left'
1711
import { FloatPopover } from '~/components/ui/float-popover'
1812

13+
import { HeaderActionButton } from './HeaderActionButton'
14+
1915
function url(path = '') {
2016
return new URL(path, appConfig.site.url)
2117
}
2218

2319
export function UserAuth() {
24-
console.log(useUser())
2520
const pathname = usePathname()
2621

2722
return (
@@ -39,7 +34,11 @@ export function UserAuth() {
3934
</div>
4035
</SignedIn>
4136
<SignedOut key="sign-in">
42-
<FloatPopover TriggerComponent={TriggerComponent} type="tooltip">
37+
<FloatPopover
38+
TriggerComponent={TriggerComponent}
39+
wrapperClassNames="h-full w-full flex items-center justify-center"
40+
type="tooltip"
41+
>
4342
登陆
4443
</FloatPopover>
4544
</SignedOut>
@@ -51,12 +50,9 @@ const TriggerComponent = () => {
5150
const pathname = usePathname()
5251
return (
5352
<SignInButton mode="modal" redirectUrl={url(pathname).href}>
54-
<button
55-
type="button"
56-
className="group h-10 rounded-full bg-gradient-to-b from-zinc-50/50 to-white/90 px-3 text-sm shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur transition dark:from-zinc-900/50 dark:to-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
57-
>
53+
<HeaderActionButton>
5854
<UserArrowLeftIcon className="h-4 w-4" />
59-
</button>
55+
</HeaderActionButton>
6056
</SignInButton>
6157
)
6258
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { style } from '@vanilla-extract/css'
2+
3+
export const headerGrid = style({
4+
gridTemplateAreas: "'left center right'",
5+
})
6+
7+
export const headerLogoGrid = style({
8+
gridArea: 'left',
9+
})
10+
11+
export const headerCenterGrid = style({
12+
gridArea: 'center',
13+
})
14+
15+
export const headerRightGrid = style({
16+
gridArea: 'right',
17+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.header--grid {
2+
grid-area: 'left center right';
3+
}
4+
5+
.header--grid__logo {
6+
grid-area: center;
7+
}
8+
9+
.header--grid__left-button {
10+
@apply hidden;
11+
}
12+
13+
@screen lg {
14+
.header--grid__logo {
15+
grid-area: left;
16+
}
17+
}

src/components/ui/viewport/OnlyLg.tsx renamed to src/components/ui/viewport/OnlyDesktop.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { viewportAtom } from '~/atoms/viewport'
88
import { useIsClient } from '~/hooks/common/use-is-client'
99

1010
const selector = (v: ExtractAtomValue<typeof viewportAtom>) => v.lg
11-
export const OnlyLg: Component = ({ children }) => {
11+
export const OnlyDesktop: Component = ({ children }) => {
1212
const isClient = useIsClient()
1313

1414
const isLg = useAtomValue(selectAtom(viewportAtom, selector))
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use client'
2+
3+
import { useAtomValue } from 'jotai'
4+
import { selectAtom } from 'jotai/utils'
5+
import type { ExtractAtomValue } from 'jotai/vanilla'
6+
7+
import { viewportAtom } from '~/atoms/viewport'
8+
import { useIsClient } from '~/hooks/common/use-is-client'
9+
10+
const selector = (v: ExtractAtomValue<typeof viewportAtom>) =>
11+
(v.sm || v.md || !v.sm) && !v.lg
12+
export const OnlyMobile: Component = ({ children }) => {
13+
const isClient = useIsClient()
14+
15+
const isMobile = useAtomValue(selectAtom(viewportAtom, selector))
16+
17+
if (!isClient) return null
18+
19+
if (!isMobile) return null
20+
21+
return <>{children}</>
22+
}

src/components/ui/viewport/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export * from './OnlyLg'
1+
export * from './OnlyDesktop'
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
import { OnlyLg } from '~/components/ui/viewport'
1+
import { OnlyDesktop } from '~/components/ui/viewport'
22

33
import { NoteTimeline } from './NoteTimeline'
44
import { NoteTopicInfo } from './NoteTopicInfo'
55

66
export const NoteLeftSidebar: Component = ({ className }) => {
77
return (
88
<div className={className}>
9-
<OnlyLg>
9+
<OnlyDesktop>
1010
<div className="sticky top-20 mt-20">
1111
<NoteTimeline />
1212

1313
<NoteTopicInfo />
1414
</div>
15-
</OnlyLg>
15+
</OnlyDesktop>
1616
</div>
1717
)
1818
}

0 commit comments

Comments
 (0)