mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
Merge pull request #271 from premiare/feat/mobileNavigation
feat: Mobile Navigation
This commit is contained in:
@ -37,4 +37,4 @@
|
|||||||
"@types/react": "18.2.18",
|
"@types/react": "18.2.18",
|
||||||
"@types/react-dom": "18.2.7"
|
"@types/react-dom": "18.2.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
apps/marketing/public/logo_icon.png
Normal file
BIN
apps/marketing/public/logo_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
@ -3,7 +3,7 @@ import { HTMLAttributes } from 'react';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { Github, Slack, Twitter } from 'lucide-react';
|
import { Github, MessagesSquare, Twitter } from 'lucide-react';
|
||||||
|
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
|
|
||||||
@ -36,11 +36,11 @@ export const Footer = ({ className, ...props }: FooterProps) => {
|
|||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href="https://documenso.slack.com"
|
href="https://documen.so/discord"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="hover:text-[#6D6D6D]"
|
className="hover:text-[#6D6D6D]"
|
||||||
>
|
>
|
||||||
<Slack className="h-6 w-6" />
|
<MessagesSquare className="h-6 w-6" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,20 +1,27 @@
|
|||||||
import { HTMLAttributes } from 'react';
|
'use client';
|
||||||
|
|
||||||
|
import { HTMLAttributes, useState } from 'react';
|
||||||
|
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
|
|
||||||
|
import { HamburgerMenu } from './mobile-hamburger';
|
||||||
|
import { MobileNavigation } from './mobile-navigation';
|
||||||
|
|
||||||
export type HeaderProps = HTMLAttributes<HTMLElement>;
|
export type HeaderProps = HTMLAttributes<HTMLElement>;
|
||||||
|
|
||||||
export const Header = ({ className, ...props }: HeaderProps) => {
|
export const Header = ({ className, ...props }: HeaderProps) => {
|
||||||
|
const [isHamburgerMenuOpen, setIsHamburgerMenuOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className={cn('flex items-center justify-between', className)} {...props}>
|
<header className={cn('flex items-center justify-between', className)} {...props}>
|
||||||
<Link href="/">
|
<Link href="/" className="z-10" onClick={() => setIsHamburgerMenuOpen(false)}>
|
||||||
<Image src="/logo.png" alt="Documenso Logo" width={170} height={0}></Image>
|
<Image src="/logo.png" alt="Documenso Logo" width={170} height={25} />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="flex items-center gap-x-6">
|
<div className="hidden items-center gap-x-6 md:flex">
|
||||||
<Link href="/pricing" className="text-sm font-semibold text-[#8D8D8D] hover:text-[#6D6D6D]">
|
<Link href="/pricing" className="text-sm font-semibold text-[#8D8D8D] hover:text-[#6D6D6D]">
|
||||||
Pricing
|
Pricing
|
||||||
</Link>
|
</Link>
|
||||||
@ -35,6 +42,15 @@ export const Header = ({ className, ...props }: HeaderProps) => {
|
|||||||
Sign in
|
Sign in
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<HamburgerMenu
|
||||||
|
onToggleMenuOpen={() => setIsHamburgerMenuOpen((v) => !v)}
|
||||||
|
isMenuOpen={isHamburgerMenuOpen}
|
||||||
|
/>
|
||||||
|
<MobileNavigation
|
||||||
|
isMenuOpen={isHamburgerMenuOpen}
|
||||||
|
onMenuOpenChange={setIsHamburgerMenuOpen}
|
||||||
|
/>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Menu, X } from 'lucide-react';
|
||||||
|
|
||||||
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
|
|
||||||
|
export interface HamburgerMenuProps {
|
||||||
|
isMenuOpen: boolean;
|
||||||
|
onToggleMenuOpen?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HamburgerMenu = ({ isMenuOpen, onToggleMenuOpen }: HamburgerMenuProps) => {
|
||||||
|
return (
|
||||||
|
<div className="flex md:hidden">
|
||||||
|
<Button variant="outline" className="z-20 w-10 p-0" onClick={onToggleMenuOpen}>
|
||||||
|
{isMenuOpen ? <X /> : <Menu />}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
121
apps/marketing/src/components/(marketing)/mobile-navigation.tsx
Normal file
121
apps/marketing/src/components/(marketing)/mobile-navigation.tsx
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Image from 'next/image';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
import { motion, useReducedMotion } from 'framer-motion';
|
||||||
|
import { Github, MessagesSquare, Twitter } from 'lucide-react';
|
||||||
|
|
||||||
|
import { Sheet, SheetContent } from '@documenso/ui/primitives/sheet';
|
||||||
|
|
||||||
|
export type MobileNavigationProps = {
|
||||||
|
isMenuOpen: boolean;
|
||||||
|
onMenuOpenChange?: (_value: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MENU_NAVIGATION_LINKS = [
|
||||||
|
{
|
||||||
|
href: '/blog',
|
||||||
|
text: 'Blog',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/pricing',
|
||||||
|
text: 'Pricing',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: 'https://status.documenso.com',
|
||||||
|
text: 'Status',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: 'mailto:support@documenso.com',
|
||||||
|
text: 'Support',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/privacy',
|
||||||
|
text: 'Privacy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: 'https://app.documenso.com/login',
|
||||||
|
text: 'Sign in',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const MobileNavigation = ({ isMenuOpen, onMenuOpenChange }: MobileNavigationProps) => {
|
||||||
|
const shouldReduceMotion = useReducedMotion();
|
||||||
|
|
||||||
|
const handleMenuItemClick = () => {
|
||||||
|
onMenuOpenChange?.(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Sheet open={isMenuOpen} onOpenChange={onMenuOpenChange}>
|
||||||
|
<SheetContent className="w-full max-w-[400px]">
|
||||||
|
<Link href="/" className="z-10" onClick={handleMenuItemClick}>
|
||||||
|
<Image src="/logo.png" alt="Documenso Logo" width={170} height={25} />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
className="mt-12 flex w-full flex-col items-start gap-y-4"
|
||||||
|
initial="initial"
|
||||||
|
animate="animate"
|
||||||
|
transition={{
|
||||||
|
staggerChildren: 0.2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{MENU_NAVIGATION_LINKS.map(({ href, text }) => (
|
||||||
|
<motion.div
|
||||||
|
key={href}
|
||||||
|
variants={{
|
||||||
|
initial: {
|
||||||
|
opacity: 0,
|
||||||
|
x: shouldReduceMotion ? 0 : 100,
|
||||||
|
},
|
||||||
|
animate: {
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
transition: {
|
||||||
|
duration: 0.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
className="text-2xl font-semibold text-[#8D8D8D] hover:text-[#6D6D6D]"
|
||||||
|
href={href}
|
||||||
|
onClick={() => handleMenuItemClick()}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<div className="mx-auto mt-8 flex w-full flex-wrap items-center gap-x-4 gap-y-4 ">
|
||||||
|
<Link
|
||||||
|
href="https://twitter.com/documenso"
|
||||||
|
target="_blank"
|
||||||
|
className="text-[#8D8D8D] hover:text-[#6D6D6D]"
|
||||||
|
>
|
||||||
|
<Twitter className="h-6 w-6" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href="https://github.com/documenso/documenso"
|
||||||
|
target="_blank"
|
||||||
|
className="text-[#8D8D8D] hover:text-[#6D6D6D]"
|
||||||
|
>
|
||||||
|
<Github className="h-6 w-6" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href="https://documen.so/discord"
|
||||||
|
target="_blank"
|
||||||
|
className="text-[#8D8D8D] hover:text-[#6D6D6D]"
|
||||||
|
>
|
||||||
|
<MessagesSquare className="h-6 w-6" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
);
|
||||||
|
};
|
||||||
27
apps/marketing/src/hooks/use-window-size.ts
Normal file
27
apps/marketing/src/hooks/use-window-size.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export function useWindowSize() {
|
||||||
|
const [size, setSize] = useState({
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onResize = () => {
|
||||||
|
setSize({
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onResize();
|
||||||
|
|
||||||
|
window.addEventListener('resize', onResize);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', onResize);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user