Fix headers on mobile with cool dropdown menu.
This commit is contained in:
parent
86c2ff4605
commit
393a4962c9
|
@ -6,40 +6,83 @@ import { useUser } from '../hooks/use-user'
|
||||||
import { formatMoney } from '../lib/util/format'
|
import { formatMoney } from '../lib/util/format'
|
||||||
import { Row } from './layout/row'
|
import { Row } from './layout/row'
|
||||||
import { firebaseLogin, User } from '../lib/firebase/users'
|
import { firebaseLogin, User } from '../lib/firebase/users'
|
||||||
|
import { MenuButton } from './menu'
|
||||||
|
|
||||||
const hoverClasses =
|
const hoverClasses =
|
||||||
'hover:underline hover:decoration-indigo-400 hover:decoration-2'
|
'hover:underline hover:decoration-indigo-400 hover:decoration-2'
|
||||||
|
|
||||||
|
const mobileNavigation = [
|
||||||
|
{
|
||||||
|
name: 'Home',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Account',
|
||||||
|
href: '/account',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Your bets',
|
||||||
|
href: '/bets',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Create a market',
|
||||||
|
href: '/create',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function ProfileSummary(props: { user: User }) {
|
||||||
|
const { user } = props
|
||||||
|
return (
|
||||||
|
<Row className="avatar items-center">
|
||||||
|
<div className="rounded-full w-10 h-10 mr-4">
|
||||||
|
<Image src={user.avatarUrl} width={40} height={40} />
|
||||||
|
</div>
|
||||||
|
<div className="truncate" style={{ maxWidth: 175 }}>
|
||||||
|
{user.name}
|
||||||
|
<div className="text-gray-700 text-sm">{formatMoney(user.balance)}</div>
|
||||||
|
</div>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function SignedInHeaders(props: { user: User; themeClasses?: string }) {
|
function SignedInHeaders(props: { user: User; themeClasses?: string }) {
|
||||||
const { user, themeClasses } = props
|
const { user, themeClasses } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Link href="/create">
|
<Link href="/create">
|
||||||
<a className={clsx('text-base font-medium', themeClasses)}>
|
<a
|
||||||
|
className={clsx(
|
||||||
|
'text-base font-medium hidden md:block',
|
||||||
|
themeClasses
|
||||||
|
)}
|
||||||
|
>
|
||||||
Create a market
|
Create a market
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link href="/bets">
|
<Link href="/bets">
|
||||||
<a className={clsx('text-base font-medium', themeClasses)}>Your bets</a>
|
<a
|
||||||
|
className={clsx(
|
||||||
|
'text-base font-medium hidden md:block',
|
||||||
|
themeClasses
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Your bets
|
||||||
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link href="/account">
|
<Link href="/account">
|
||||||
<a className={clsx('text-base font-medium', themeClasses)}>
|
<a className={clsx('text-base font-medium hidden md:block')}>
|
||||||
<Row className="avatar items-center">
|
<ProfileSummary user={user} />
|
||||||
<div className="rounded-full w-10 h-10 mr-4">
|
|
||||||
<Image src={user.avatarUrl} width={40} height={40} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{user.name}
|
|
||||||
<div className="text-gray-700 text-sm">
|
|
||||||
{formatMoney(user.balance)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Row>
|
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<MenuButton
|
||||||
|
className="md:hidden"
|
||||||
|
menuItems={mobileNavigation}
|
||||||
|
buttonContent={<ProfileSummary user={user} />}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -68,12 +111,12 @@ export function Header(props: { darkBackground?: boolean; children?: any }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
className="max-w-7xl w-full flex flex-row mx-auto pt-5 px-4 sm:px-6"
|
className="max-w-7xl w-full flex flex-row justify-between md:justify-start mx-auto pt-5 px-4 sm:px-6"
|
||||||
aria-label="Global"
|
aria-label="Global"
|
||||||
>
|
>
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<a className="flex flex-row gap-3">
|
<a className="flex flex-row gap-3">
|
||||||
<Image
|
<img
|
||||||
className="sm:h-10 sm:w-10 hover:rotate-12 transition-all"
|
className="sm:h-10 sm:w-10 hover:rotate-12 transition-all"
|
||||||
src="/logo-icon.svg"
|
src="/logo-icon.svg"
|
||||||
width={40}
|
width={40}
|
||||||
|
@ -81,7 +124,7 @@ export function Header(props: { darkBackground?: boolean; children?: any }) {
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'font-major-mono lowercase mt-1 hidden sm:block sm:text-2xl',
|
'font-major-mono lowercase mt-1 sm:text-2xl',
|
||||||
darkBackground && 'text-white'
|
darkBackground && 'text-white'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -90,7 +133,7 @@ export function Header(props: { darkBackground?: boolean; children?: any }) {
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Row className="gap-8 mt-1 md:ml-16 mr-8">
|
<Row className="gap-8 mt-1 md:ml-16">
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
{user ? (
|
{user ? (
|
||||||
|
|
51
web/components/menu.tsx
Normal file
51
web/components/menu.tsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import { Fragment } from 'react'
|
||||||
|
import { Menu, Transition } from '@headlessui/react'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
|
||||||
|
export function MenuButton(props: {
|
||||||
|
buttonContent: any
|
||||||
|
menuItems: { name: string; href: string }[]
|
||||||
|
className?: string
|
||||||
|
}) {
|
||||||
|
const { buttonContent, menuItems, className } = props
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
as="div"
|
||||||
|
className={clsx('flex-shrink-0 relative ml-4 z-10', className)}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Menu.Button className="rounded-full flex">
|
||||||
|
<span className="sr-only">Open user menu</span>
|
||||||
|
{buttonContent}
|
||||||
|
</Menu.Button>
|
||||||
|
</div>
|
||||||
|
<Transition
|
||||||
|
as={Fragment}
|
||||||
|
enter="transition ease-out duration-100"
|
||||||
|
enterFrom="transform opacity-0 scale-95"
|
||||||
|
enterTo="transform opacity-100 scale-100"
|
||||||
|
leave="transition ease-in duration-75"
|
||||||
|
leaveFrom="transform opacity-100 scale-100"
|
||||||
|
leaveTo="transform opacity-0 scale-95"
|
||||||
|
>
|
||||||
|
<Menu.Items className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 py-1 focus:outline-none">
|
||||||
|
{menuItems.map((item) => (
|
||||||
|
<Menu.Item key={item.name}>
|
||||||
|
{({ active }) => (
|
||||||
|
<a
|
||||||
|
href={item.href}
|
||||||
|
className={clsx(
|
||||||
|
active ? 'bg-gray-100' : '',
|
||||||
|
'block py-2 px-4 text-sm text-gray-700'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu.Items>
|
||||||
|
</Transition>
|
||||||
|
</Menu>
|
||||||
|
)
|
||||||
|
}
|
19
web/package-lock.json
generated
19
web/package-lock.json
generated
|
@ -6,6 +6,7 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "mantic",
|
"name": "mantic",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@headlessui/react": "^1.4.2",
|
||||||
"@heroicons/react": "^1.0.5",
|
"@heroicons/react": "^1.0.5",
|
||||||
"@nivo/core": "0.74.0",
|
"@nivo/core": "0.74.0",
|
||||||
"@nivo/line": "0.74.0",
|
"@nivo/line": "0.74.0",
|
||||||
|
@ -1586,6 +1587,18 @@
|
||||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz",
|
||||||
"integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw=="
|
"integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@headlessui/react": {
|
||||||
|
"version": "1.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.4.2.tgz",
|
||||||
|
"integrity": "sha512-N8tv7kLhg9qGKBkVdtg572BvKvWhmiudmeEpOCyNwzOsZHCXBtl8AazGikIfUS+vBoub20Fse3BjawXDVPPdug==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16 || ^17 || ^18",
|
||||||
|
"react-dom": "^16 || ^17 || ^18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@heroicons/react": {
|
"node_modules/@heroicons/react": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-1.0.5.tgz",
|
||||||
|
@ -9448,6 +9461,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz",
|
||||||
"integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw=="
|
"integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw=="
|
||||||
},
|
},
|
||||||
|
"@headlessui/react": {
|
||||||
|
"version": "1.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.4.2.tgz",
|
||||||
|
"integrity": "sha512-N8tv7kLhg9qGKBkVdtg572BvKvWhmiudmeEpOCyNwzOsZHCXBtl8AazGikIfUS+vBoub20Fse3BjawXDVPPdug==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"@heroicons/react": {
|
"@heroicons/react": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-1.0.5.tgz",
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"prepare": "cd .. && husky install web/.husky"
|
"prepare": "cd .. && husky install web/.husky"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@headlessui/react": "^1.4.2",
|
||||||
"@heroicons/react": "^1.0.5",
|
"@heroicons/react": "^1.0.5",
|
||||||
"@nivo/core": "0.74.0",
|
"@nivo/core": "0.74.0",
|
||||||
"@nivo/line": "0.74.0",
|
"@nivo/line": "0.74.0",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user