mirror of https://github.com/MaxLeiter/Drift
client: refactor header component for improved SSR
parent
e7cec9b827
commit
a1fef656bb
@ -1,43 +1,62 @@
|
||||
.tabs {
|
||||
flex: 1 1;
|
||||
padding: 0 var(--gap);
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
margin-top: var(--gap);
|
||||
}
|
||||
|
||||
.tabs .buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tabs .buttons > button,
|
||||
.tabs .buttons > a > button {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tabs .active {
|
||||
border-bottom: 1px solid var(--darker-gray) !important;
|
||||
}
|
||||
|
||||
.mobile {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: none !important;
|
||||
margin-top: var(--gap);
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 650px) {
|
||||
.tabs {
|
||||
display: none;
|
||||
}
|
||||
.tabs {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: block !important;
|
||||
}
|
||||
.controls {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.controls button:active,
|
||||
.controls button:focus,
|
||||
.controls button:hover {
|
||||
outline: 1px solid rgba(0, 0, 0, 0.2);
|
||||
outline: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: min-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: min-content;
|
||||
}
|
||||
|
||||
.selectContent {
|
||||
width: auto;
|
||||
height: 18px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: auto;
|
||||
height: 18px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
@ -1,197 +0,0 @@
|
||||
|
||||
import { ButtonGroup, Page, Spacer, Tabs, useBodyScroll, useMediaQuery, } from "@geist-ui/core";
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import styles from './header.module.css';
|
||||
import { useRouter } from "next/router";
|
||||
import useSignedIn from "../../lib/hooks/use-signed-in";
|
||||
|
||||
import HomeIcon from '@geist-ui/icons/home';
|
||||
import MenuIcon from '@geist-ui/icons/menu';
|
||||
import GitHubIcon from '@geist-ui/icons/github';
|
||||
import SignOutIcon from '@geist-ui/icons/userX';
|
||||
import SignInIcon from '@geist-ui/icons/user';
|
||||
import SignUpIcon from '@geist-ui/icons/userPlus';
|
||||
import NewIcon from '@geist-ui/icons/plusCircle';
|
||||
import YourIcon from '@geist-ui/icons/list'
|
||||
import MoonIcon from '@geist-ui/icons/moon';
|
||||
import SettingsIcon from '@geist-ui/icons/settings';
|
||||
import SunIcon from '@geist-ui/icons/sun';
|
||||
import { useTheme } from "next-themes"
|
||||
import { Button } from "@geist-ui/core";
|
||||
import useUserData from "@lib/hooks/use-user-data";
|
||||
|
||||
type Tab = {
|
||||
name: string
|
||||
icon: JSX.Element
|
||||
value: string
|
||||
onClick?: () => void
|
||||
href?: string
|
||||
}
|
||||
|
||||
|
||||
const Header = () => {
|
||||
const router = useRouter();
|
||||
const [selectedTab, setSelectedTab] = useState<string>(router.pathname === '/' ? 'home' : router.pathname.split('/')[1]);
|
||||
const [expanded, setExpanded] = useState<boolean>(false)
|
||||
const [, setBodyHidden] = useBodyScroll(null, { scrollLayer: true })
|
||||
const isMobile = useMediaQuery('xs', { match: 'down' })
|
||||
const { signedIn: isSignedIn, signout } = useSignedIn()
|
||||
const userData = useUserData();
|
||||
const [pages, setPages] = useState<Tab[]>([])
|
||||
const { setTheme, resolvedTheme } = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
setBodyHidden(expanded)
|
||||
}, [expanded, setBodyHidden])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMobile) {
|
||||
setExpanded(false)
|
||||
}
|
||||
}, [isMobile])
|
||||
|
||||
useEffect(() => {
|
||||
const defaultPages: Tab[] = [
|
||||
{
|
||||
name: isMobile ? "GitHub" : "",
|
||||
href: "https://github.com/maxleiter/drift",
|
||||
icon: <GitHubIcon />,
|
||||
value: "github"
|
||||
},
|
||||
{
|
||||
name: isMobile ? "Change theme" : "",
|
||||
onClick: function () {
|
||||
if (typeof window !== 'undefined')
|
||||
setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
|
||||
},
|
||||
icon: resolvedTheme === 'light' ? <MoonIcon /> : <SunIcon />,
|
||||
value: "theme",
|
||||
}
|
||||
]
|
||||
|
||||
if (isSignedIn)
|
||||
setPages([
|
||||
{
|
||||
name: 'new',
|
||||
icon: <NewIcon />,
|
||||
value: 'new',
|
||||
href: '/new'
|
||||
},
|
||||
{
|
||||
name: 'yours',
|
||||
icon: <YourIcon />,
|
||||
value: 'yours',
|
||||
href: '/mine'
|
||||
},
|
||||
// {
|
||||
// name: 'settings',
|
||||
// icon: <SettingsIcon />,
|
||||
// value: 'settings',
|
||||
// href: '/settings'
|
||||
// },
|
||||
{
|
||||
name: 'sign out',
|
||||
icon: <SignOutIcon />,
|
||||
value: 'signout',
|
||||
onClick: signout
|
||||
},
|
||||
...defaultPages
|
||||
])
|
||||
else
|
||||
setPages([
|
||||
{
|
||||
name: 'home',
|
||||
icon: <HomeIcon />,
|
||||
value: 'home',
|
||||
href: '/'
|
||||
},
|
||||
{
|
||||
name: 'Sign in',
|
||||
icon: <SignInIcon />,
|
||||
value: 'signin',
|
||||
href: '/signin'
|
||||
},
|
||||
{
|
||||
name: 'Sign up',
|
||||
icon: <SignUpIcon />,
|
||||
value: 'signup',
|
||||
href: '/signup'
|
||||
},
|
||||
...defaultPages
|
||||
])
|
||||
if (userData?.role === "admin") {
|
||||
setPages((pages) => [
|
||||
...pages,
|
||||
{
|
||||
name: 'admin',
|
||||
icon: <SettingsIcon />,
|
||||
value: 'admin',
|
||||
href: '/admin'
|
||||
}
|
||||
])
|
||||
}
|
||||
// TODO: investigate deps causing infinite loop
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isMobile, isSignedIn, resolvedTheme, userData])
|
||||
|
||||
const onTabChange = useCallback((tab: string) => {
|
||||
if (typeof window === 'undefined') return
|
||||
const match = pages.find(page => page.value === tab)
|
||||
if (match?.onClick) {
|
||||
match.onClick()
|
||||
} else {
|
||||
router.push(match?.href || '/')
|
||||
}
|
||||
}, [pages, router])
|
||||
|
||||
|
||||
return (
|
||||
<Page.Header height={'var(--page-nav-height)'} marginBottom={2}>
|
||||
<div className={styles.tabs}>
|
||||
<Tabs
|
||||
value={selectedTab}
|
||||
leftSpace={0}
|
||||
align="center"
|
||||
hideDivider
|
||||
hideBorder
|
||||
onChange={onTabChange}>
|
||||
{pages.map((tab) => {
|
||||
return <Tabs.Item
|
||||
font="14px"
|
||||
label={<>{tab.icon} {tab.name}</>}
|
||||
value={tab.value}
|
||||
key={`${tab.value}`}
|
||||
/>
|
||||
})}
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className={styles.controls}>
|
||||
<Button
|
||||
auto
|
||||
type="abort"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
aria-label="Menu"
|
||||
>
|
||||
<Spacer height={5 / 6} width={0} />
|
||||
<MenuIcon />
|
||||
</Button>
|
||||
</div>
|
||||
{isMobile && expanded && (<div className={styles.mobile}>
|
||||
<ButtonGroup vertical>
|
||||
{pages.map((tab, index) => {
|
||||
return <Button
|
||||
key={`${tab.name}-${index}`}
|
||||
onClick={() => onTabChange(tab.value)}
|
||||
icon={tab.icon}
|
||||
>
|
||||
{tab.name}
|
||||
</Button>
|
||||
})}
|
||||
</ButtonGroup>
|
||||
</div>)}
|
||||
</Page.Header >
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
@ -1,10 +1,207 @@
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const Header = dynamic(import('./header'), {
|
||||
// ssr: false,
|
||||
// loading: () => <MenuSkeleton />,
|
||||
})
|
||||
import { ButtonGroup, Button, Page, Spacer, useBodyScroll, useMediaQuery, } from "@geist-ui/core";
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import styles from './header.module.css';
|
||||
import useSignedIn from "../../lib/hooks/use-signed-in";
|
||||
|
||||
import HomeIcon from '@geist-ui/icons/home';
|
||||
import MenuIcon from '@geist-ui/icons/menu';
|
||||
import GitHubIcon from '@geist-ui/icons/github';
|
||||
import SignOutIcon from '@geist-ui/icons/userX';
|
||||
import SignInIcon from '@geist-ui/icons/user';
|
||||
import SignUpIcon from '@geist-ui/icons/userPlus';
|
||||
import NewIcon from '@geist-ui/icons/plusCircle';
|
||||
import YourIcon from '@geist-ui/icons/list'
|
||||
import MoonIcon from '@geist-ui/icons/moon';
|
||||
import SettingsIcon from '@geist-ui/icons/settings';
|
||||
import SunIcon from '@geist-ui/icons/sun';
|
||||
import { useTheme } from "next-themes"
|
||||
import useUserData from "@lib/hooks/use-user-data";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
type Tab = {
|
||||
name: string
|
||||
icon: JSX.Element
|
||||
value: string
|
||||
onClick?: () => void
|
||||
href?: string
|
||||
}
|
||||
|
||||
|
||||
const Header = () => {
|
||||
const router = useRouter()
|
||||
const [expanded, setExpanded] = useState<boolean>(false)
|
||||
const [, setBodyHidden] = useBodyScroll(null, { scrollLayer: true })
|
||||
const isMobile = useMediaQuery('xs', { match: 'down' })
|
||||
const { signedIn: isSignedIn, signout } = useSignedIn()
|
||||
const userData = useUserData();
|
||||
const [pages, setPages] = useState<Tab[]>([])
|
||||
const { setTheme, resolvedTheme } = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
setBodyHidden(expanded)
|
||||
}, [expanded, setBodyHidden])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMobile) {
|
||||
setExpanded(false)
|
||||
}
|
||||
}, [isMobile])
|
||||
|
||||
useEffect(() => {
|
||||
const defaultPages: Tab[] = [
|
||||
{
|
||||
name: isMobile ? "GitHub" : "",
|
||||
href: "https://github.com/maxleiter/drift",
|
||||
icon: <GitHubIcon />,
|
||||
value: "github"
|
||||
},
|
||||
{
|
||||
name: isMobile ? "Change theme" : "",
|
||||
onClick: function () {
|
||||
if (typeof window !== 'undefined')
|
||||
setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
|
||||
},
|
||||
icon: resolvedTheme === 'light' ? <MoonIcon /> : <SunIcon />,
|
||||
value: "theme",
|
||||
}
|
||||
]
|
||||
|
||||
if (isSignedIn)
|
||||
setPages([
|
||||
{
|
||||
name: 'new',
|
||||
icon: <NewIcon />,
|
||||
value: 'new',
|
||||
href: '/new'
|
||||
},
|
||||
{
|
||||
name: 'yours',
|
||||
icon: <YourIcon />,
|
||||
value: 'yours',
|
||||
href: '/mine'
|
||||
},
|
||||
// {
|
||||
// name: 'settings',
|
||||
// icon: <SettingsIcon />,
|
||||
// value: 'settings',
|
||||
// href: '/settings'
|
||||
// },
|
||||
{
|
||||
name: 'sign out',
|
||||
icon: <SignOutIcon />,
|
||||
value: 'signout',
|
||||
// onClick: signout,
|
||||
href: '/signout'
|
||||
},
|
||||
...defaultPages
|
||||
])
|
||||
else
|
||||
setPages([
|
||||
{
|
||||
name: 'home',
|
||||
icon: <HomeIcon />,
|
||||
value: 'home',
|
||||
href: '/'
|
||||
},
|
||||
{
|
||||
name: 'Sign in',
|
||||
icon: <SignInIcon />,
|
||||
value: 'signin',
|
||||
href: '/signin'
|
||||
},
|
||||
{
|
||||
name: 'Sign up',
|
||||
icon: <SignUpIcon />,
|
||||
value: 'signup',
|
||||
href: '/signup'
|
||||
},
|
||||
...defaultPages
|
||||
])
|
||||
if (userData?.role === "admin") {
|
||||
setPages((pages) => [
|
||||
...pages,
|
||||
{
|
||||
name: 'admin',
|
||||
icon: <SettingsIcon />,
|
||||
value: 'admin',
|
||||
href: '/admin'
|
||||
}
|
||||
])
|
||||
}
|
||||
// TODO: investigate deps causing infinite loop
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isMobile, isSignedIn, resolvedTheme, userData])
|
||||
|
||||
const onTabChange = useCallback((tab: string) => {
|
||||
if (typeof window === 'undefined') return
|
||||
const match = pages.find(page => page.value === tab)
|
||||
if (match?.onClick) {
|
||||
match.onClick()
|
||||
}
|
||||
}, [pages])
|
||||
|
||||
const getButton = useCallback((tab: Tab) => {
|
||||
const activeStyle = router.pathname === tab.href ? styles.active : ""
|
||||
if (tab.onClick) {
|
||||
return <Button
|
||||
auto={isMobile ? false : true}
|
||||
key={tab.value}
|
||||
icon={tab.icon}
|
||||
onClick={() => onTabChange(tab.value)}
|
||||
className={`${styles.tab} ${activeStyle}`}
|
||||
shadow={false}
|
||||
>
|
||||
{tab.name ? tab.name : undefined}
|
||||
</Button>
|
||||
} else if (tab.href) {
|
||||
return <Link key={tab.value} href={tab.href}>
|
||||
<a className={styles.tab}>
|
||||
<Button
|
||||
className={activeStyle}
|
||||
auto={isMobile ? false : true}
|
||||
icon={tab.icon}
|
||||
shadow={false}
|
||||
>
|
||||
{tab.name ? tab.name : undefined}
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
}
|
||||
}, [isMobile, onTabChange, router.pathname])
|
||||
|
||||
const buttons = useMemo(() => pages.map(getButton), [pages, getButton])
|
||||
|
||||
return (
|
||||
<Page.Header height={'var(--page-nav-height)'} marginBottom={2}>
|
||||
<div className={styles.tabs}>
|
||||
<div className={styles.buttons}>
|
||||
{buttons}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.controls}>
|
||||
<Button
|
||||
effect={false}
|
||||
auto
|
||||
type="abort"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
aria-label="Menu"
|
||||
>
|
||||
<Spacer height={5 / 6} width={0} />
|
||||
<MenuIcon />
|
||||
</Button>
|
||||
</div>
|
||||
{isMobile && expanded && (<div className={styles.mobile}>
|
||||
<ButtonGroup vertical style={{
|
||||
background: "var(--bg)",
|
||||
}}>
|
||||
{buttons}
|
||||
</ButtonGroup>
|
||||
</div>)}
|
||||
</Page.Header >
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
|
@ -1,19 +1,14 @@
|
||||
import { Page } from '@geist-ui/core';
|
||||
import PageSeo from "@components/page-seo";
|
||||
import Auth from "@components/auth";
|
||||
import Header from "@components/header/header";
|
||||
import styles from '@styles/Home.module.css'
|
||||
const SignIn = () => (
|
||||
<Page width={"100%"}>
|
||||
<PageSeo title="Drift - Sign In" />
|
||||
|
||||
<Page.Header>
|
||||
<Header />
|
||||
</Page.Header>
|
||||
<Page.Content className={styles.main}>
|
||||
<Auth page="signin" />
|
||||
</Page.Content>
|
||||
</Page>
|
||||
)
|
||||
|
||||
export default SignIn
|
||||
export default SignIn
|
||||
|
Loading…
Reference in New Issue