Skip to content

Commit

Permalink
Add theme toggle and action button to header
Browse files Browse the repository at this point in the history
  • Loading branch information
balintking committed Dec 13, 2024
1 parent a9f84f7 commit f3ae67a
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 34 deletions.
1 change: 1 addition & 0 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@radix-ui/react-slot": "^1.1.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^11.14.4",
"geist": "^1.3.1",
"lucide-react": "^0.468.0",
"next": "14.2.13",
Expand Down
3 changes: 2 additions & 1 deletion apps/frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@tailwind components;
@tailwind utilities;


@layer base {
:root {
--background: 0 0% 100%;
Expand All @@ -23,7 +24,7 @@
--border: 20 5.9% 90%;
--input: 20 5.9% 90%;
--ring: 24.6 95% 53.1%;
--radius: 0.5rem;
--radius: .75rem;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function RootLayout({
return (
<html lang='hu'>
<body className={`${GeistSans.className}`}>
<ThemeProvider attribute='class' defaultTheme='dark' enableSystem disableTransitionOnChange>
<ThemeProvider attribute='class' defaultTheme='dark' enableSystem>
<div className='h-screen flex flex-col'>
<Header />
<div className='flex-1 flex overflow-hidden'>
Expand Down
78 changes: 52 additions & 26 deletions apps/frontend/src/components/layout/header.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
'use client';

import { motion } from 'framer-motion';
import { GeistSans } from 'geist/font/sans';
import { ChevronRight } from 'lucide-react';
import Link from 'next/link';

import { ThemeToggle } from '@/components/layout/theme-toggle';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
import {
Expand All @@ -22,36 +25,59 @@ export function Header() {
};

return (
<header className='px-4 py-3 flex items-center justify-between'>
<header className='px-4 py-4 flex items-center justify-between'>
<Link href='/apps/frontend/public' className='flex items-center space-x-2'>
<span className={`${GeistSans.className} text-2xl font-bold tracking-tighter`}>MMMK</span>
</Link>

{isLoggedIn ? (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant='ghost' className='relative h-8 w-8 rounded-full'>
<Avatar className='h-8 w-8'>
<AvatarImage src={user.image} alt={user.name} />
<AvatarFallback>{user.name.charAt(0)}</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className='w-56' align='end' forceMount>
<DropdownMenuItem className='flex flex-col items-start'>
<div className='font-medium'>{user.name}</div>
<div className='text-sm text-zinc-500'>{user.email}</div>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href='/apps/frontend/public'>Profile</Link>
</DropdownMenuItem>
<DropdownMenuItem>Log out</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
) : (
<Button variant='outline'>Log in</Button>
)}
<div className='w-80 pl-8 flex items-center justify-between'>
<motion.div className='group transition-colors' whileHover='hover'>
<Button
variant='outline'
className='relative h-10 text-primary rounded-full font-light hover:text-primary hover:bg-transparent'
>
My Bands
<motion.div
variants={{
hover: { x: 4 },
}}
transition={{ duration: 0.2 }}
>
<ChevronRight />
</motion.div>
</Button>
</motion.div>

<div className='flex gap-3 items-center'>
<ThemeToggle />

{isLoggedIn ? (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant='outline' className='relative h-10 w-10 rounded-full'>
<Avatar className='h-8 w-8'>
<AvatarImage src={user.image} alt={user.name} />
<AvatarFallback>{user.name.charAt(0)}</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className='w-56' align='end' forceMount>
<DropdownMenuItem className='flex flex-col items-start'>
<div className='font-medium'>{user.name}</div>
<div className='text-sm text-zinc-500'>{user.email}</div>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href='/apps/frontend/public'>Profile</Link>
</DropdownMenuItem>
<DropdownMenuItem>Log out</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
) : (
<Button variant='outline'>Log in</Button>
)}
</div>
</div>
</header>
);
}
34 changes: 34 additions & 0 deletions apps/frontend/src/components/layout/theme-toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use client';

import { Moon, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import * as React from 'react';

import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';

export function ThemeToggle() {
const { setTheme } = useTheme();

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant='ghost' size='icon' className='rounded-full'>
<Sun className='h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0' />
<Moon className='absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100' />
<span className='sr-only'>Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align='end'>
<DropdownMenuItem onClick={() => setTheme('light')}>Light</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('dark')}>Dark</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('system')}>System</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
2 changes: 1 addition & 1 deletion apps/frontend/src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const buttonVariants = cva(
variant: {
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
outline: 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
outline: 'border border-primary bg-background shadow-sm hover:bg-orange-500/10 hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
Expand Down
66 changes: 61 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1330,6 +1330,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"

ansi-styles@^6.1.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==

any-promise@^1.0.0:
version "1.3.0"
resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz"
Expand Down Expand Up @@ -2071,6 +2076,11 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"

eastasianwidth@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==

[email protected]:
version "1.1.1"
resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
Expand Down Expand Up @@ -2732,6 +2742,15 @@ [email protected]:
resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==

framer-motion@^11.14.4:
version "11.14.4"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-11.14.4.tgz#cb7197107d75ff4592444a83918e31827fcec3b3"
integrity sha512-NQuzr9JbeJDMQmy0FFLhLzk9h1kAjVC1tGE/HY4ubF02B95EBm2lpA21LE3Od/OpXqXgp0zl5Hdqu25hliBRsA==
dependencies:
motion-dom "^11.14.3"
motion-utils "^11.14.3"
tslib "^2.4.0"

[email protected]:
version "0.5.2"
resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz"
Expand Down Expand Up @@ -3715,6 +3734,16 @@ mkdirp@^0.5.4:
dependencies:
minimist "^1.2.6"

motion-dom@^11.14.3:
version "11.14.3"
resolved "https://registry.yarnpkg.com/motion-dom/-/motion-dom-11.14.3.tgz#725c72c0f1d0b632e42fdd8d13b69ecf9fe202c0"
integrity sha512-lW+D2wBy5vxLJi6aCP0xyxTxlTfiu+b+zcpVbGVFUxotwThqhdpPRSmX8xztAgtZMPMeU0WGVn/k1w4I+TbPqA==

motion-utils@^11.14.3:
version "11.14.3"
resolved "https://registry.yarnpkg.com/motion-utils/-/motion-utils-11.14.3.tgz#cd4a413463739498411f82abb67b3dd58768b0f8"
integrity sha512-Xg+8xnqIJTpr0L/cidfTTBFkvRw26ZtGGuIhA94J9PQ2p4mEa06Xx7QVYZH0BP+EpMSaDlu+q0I0mmvwADPsaQ==

[email protected]:
version "2.0.0"
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
Expand Down Expand Up @@ -4764,7 +4793,7 @@ streamsearch@^1.1.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@4.1.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.1.2:
string-width@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.1.0.tgz#ba846d1daa97c3c596155308063e075ed1c99aff"
integrity sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==
Expand All @@ -4773,6 +4802,24 @@ [email protected], string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.
is-fullwidth-code-point "^3.0.0"
strip-ansi "^5.2.0"

string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
dependencies:
eastasianwidth "^0.2.0"
emoji-regex "^9.2.2"
strip-ansi "^7.0.1"

string.prototype.matchall@^4.0.10:
version "4.0.11"
resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz"
Expand Down Expand Up @@ -5450,15 +5497,24 @@ which@^2.0.1:
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@7.0.0, wrap-ansi@^6.0.1, wrap-ansi@^6.2.0, wrap-ansi@^8.1.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
wrap-ansi@^6.0.1, wrap-ansi@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
dependencies:
ansi-styles "^6.1.0"
string-width "^5.0.1"
strip-ansi "^7.0.1"

wrappy@1:
version "1.0.2"
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
Expand Down

0 comments on commit f3ae67a

Please sign in to comment.