Added mobile support

This commit is contained in:
Mauro Balades
2024-07-12 00:22:11 +02:00
parent 68ff4a7308
commit d9ff1a48b4
9 changed files with 373 additions and 10 deletions

67
package-lock.json generated
View File

@@ -10,10 +10,12 @@
"dependencies": { "dependencies": {
"@hookform/resolvers": "^3.7.0", "@hookform/resolvers": "^3.7.0",
"@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-navigation-menu": "^1.2.0", "@radix-ui/react-navigation-menu": "^1.2.0",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-select": "^2.1.1", "@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0",
@@ -2938,6 +2940,41 @@
} }
} }
}, },
"node_modules/@radix-ui/react-dialog": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz",
"integrity": "sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==",
"dependencies": {
"@radix-ui/primitive": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-context": "1.1.0",
"@radix-ui/react-dismissable-layer": "1.1.0",
"@radix-ui/react-focus-guards": "1.1.0",
"@radix-ui/react-focus-scope": "1.1.0",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-portal": "1.1.1",
"@radix-ui/react-presence": "1.1.0",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-slot": "1.1.0",
"@radix-ui/react-use-controllable-state": "1.1.0",
"aria-hidden": "^1.1.1",
"react-remove-scroll": "2.5.7"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-direction": { "node_modules/@radix-ui/react-direction": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz",
@@ -3294,6 +3331,36 @@
} }
} }
}, },
"node_modules/@radix-ui/react-scroll-area": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.1.0.tgz",
"integrity": "sha512-9ArIZ9HWhsrfqS765h+GZuLoxaRHD/j0ZWOWilsCvYTpYJp8XwCqNG7Dt9Nu/TItKOdgLGkOPCodQvDc+UMwYg==",
"dependencies": {
"@radix-ui/number": "1.1.0",
"@radix-ui/primitive": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-context": "1.1.0",
"@radix-ui/react-direction": "1.1.0",
"@radix-ui/react-presence": "1.1.0",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-select": { "node_modules/@radix-ui/react-select": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.1.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.1.tgz",

View File

@@ -11,10 +11,12 @@
"dependencies": { "dependencies": {
"@hookform/resolvers": "^3.7.0", "@hookform/resolvers": "^3.7.0",
"@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-navigation-menu": "^1.2.0", "@radix-ui/react-navigation-menu": "^1.2.0",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-select": "^2.1.1", "@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0",

View File

@@ -19,7 +19,7 @@ function getDefaultPlatformBasedOnUserAgent() {
userAgent = window.navigator.userAgent; userAgent = window.navigator.userAgent;
} }
if (userAgent.includes("Win")) { if (userAgent.includes("Win")) {
return "WindowsStubInstaller"; return "WindowsInstaller";
} }
if (userAgent.includes("Mac")) { if (userAgent.includes("Mac")) {
return "MacOS"; return "MacOS";
@@ -56,8 +56,8 @@ export default function DownloadPage() {
} }
return ( return (
<div className="w-full relative h-screen flex items-center justify-center"> <div className="w-full relative h-screen flex items-center justify-center flex-col lg:flex-row">
<div className="w-1/2 relative h-full px-64 flex items-cetner justify-center flex-col"> <div className="w-full lg:w-1/2 relative h-full px-12 lg:px-24 xl:px-32 2xl:px-64 text-center flex items-cetner justify-center flex-col">
<GridPattern <GridPattern
numSquares={30} numSquares={30}
maxOpacity={0.5} maxOpacity={0.5}
@@ -80,8 +80,8 @@ export default function DownloadPage() {
Get started with Zen Browser today. Get back to browsing the web with peace of mind. Get started with Zen Browser today. Get back to browsing the web with peace of mind.
</p> </p>
</div> </div>
<div className="w-1/2 relative flex flex-col relative items-cetner justify-start"> <div className="w-full lg:w-1/2 relative flex flex-col relative items-cetner justify-start">
<div className="w-1/2 relative"> <div className="w-full lg:w-2/3 relative flex flex-col items-center mx-auto mt-10 lg:mt-0">
<Form {...form}> <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6"> <form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6">
<FormField <FormField

View File

@@ -3,10 +3,10 @@ import TextReveal from "./ui/text-reveal";
export default function Footer() { export default function Footer() {
return ( return (
<div className="font-medium border-t w-full border-grey py-10 mt-10 flex justify-center align-center"> <div className="font-medium flex-col md:flex-row px-10 md:px-0 border-t w-full border-grey py-10 mt-10 flex justify-center align-center">
Zen Browser © {new Date().getFullYear()} - Zen Browser © {new Date().getFullYear()} -
Made with by the Zen team. Made with by the Zen team.
<a className="ml-2 font-bold" href="https://github.com/zen-browser" target="_blank">Source Code</a> <a className="mt-5 md:mt-0 md:ml-2 font-bold" href="https://github.com/zen-browser" target="_blank">Source Code</a>
</div> </div>
); );
} }

View File

@@ -0,0 +1,100 @@
'use client'
import { SidebarOpen } from 'lucide-react'
import type { LinkProps } from 'next/link'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import * as React from 'react'
import { Sheet, SheetContent, SheetTrigger } from './ui/sheet'
import { Button } from './ui/button'
import { ScrollArea } from './ui/scroll-area'
import Logo from './logo'
import { ny } from '@/lib/utils'
import { components } from './navigation'
export function MobileNav() {
const [open, setOpen] = React.useState(false)
return (
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
<Button
variant="ghost"
className="mr-2 px-0 ml-auto text-base hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 sm:hidden"
>
<SidebarOpen className="size-6" />
<span className="sr-only">Toggle Menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="pr-0">
<MobileLink
href="/"
className="flex items-center"
onOpenChange={setOpen}
>
<Logo withText />
</MobileLink>
<ScrollArea className="my-4 h-[calc(100vh-8rem)] pb-10 pl-6">
<div className="flex flex-col space-y-3">
<MobileLink
href="/download"
onOpenChange={setOpen}
>
Download
</MobileLink>
<MobileLink
href="/release-notes"
onOpenChange={setOpen}
>
Release Notes
</MobileLink>
<MobileLink
href="https://github.com/zen-browser"
onOpenChange={setOpen}
>
Source Code
</MobileLink>
{components.map(({title, href, description}) => (
<MobileLink
href={href}
key={href}
onOpenChange={setOpen}
>
{title}
</MobileLink>
))}
</div>
</ScrollArea>
</SheetContent>
</Sheet>
)
}
interface MobileLinkProps extends LinkProps {
onOpenChange?: (open: boolean) => void
children: React.ReactNode
className?: string
}
function MobileLink({
href,
onOpenChange,
className,
children,
...props
}: MobileLinkProps) {
const router = useRouter()
return (
<Link
href={href}
onClick={() => {
router.push(href.toString())
onOpenChange?.(false)
}}
className={ny(className)}
{...props}
>
{children}
</Link>
)
}

View File

@@ -15,8 +15,9 @@ import {
} from "@/components/ui/navigation-menu" } from "@/components/ui/navigation-menu"
import Logo from "./logo" import Logo from "./logo"
import { ModeToggle } from "./mode-toggle" import { ModeToggle } from "./mode-toggle"
import { MobileNav } from "./mobile-nav"
const components: { title: string; href: string; description: string }[] = [ export const components: { title: string; href: string; description: string }[] = [
{ {
title: "Privacy Policy", title: "Privacy Policy",
href: "/privacy-policy", href: "/privacy-policy",
@@ -32,8 +33,9 @@ const components: { title: string; href: string; description: string }[] = [
export function Navigation() { export function Navigation() {
return ( return (
<div className="bg-background fixed z-10 top-0 left-0 w-full flex fixed border-b border-grey p-2 items-center justify-center"> <div className="bg-background fixed z-10 top-0 left-0 w-full flex fixed border-b border-grey p-2 items-center justify-center">
<MobileNav />
<NavigationMenu> <NavigationMenu>
<NavigationMenuList className="w-full"> <NavigationMenuList className="w-full hidden sm:flex">
<NavigationMenuItem className="cursor-pointer mr-20"> <NavigationMenuItem className="cursor-pointer mr-20">
<NavigationMenuLink href="/"> <NavigationMenuLink href="/">
<Logo withText /> <Logo withText />

View File

@@ -6,7 +6,7 @@ import { Button } from "./ui/button";
export default function ReleaseNoteElement({ data }: { data: ReleaseNote }) { export default function ReleaseNoteElement({ data }: { data: ReleaseNote }) {
return ( return (
<div className="flex flex-col mt-52 mb-24"> <div className="flex flex-col mt-52 mb-24">
<div className="mx-auto w-1/3"> <div className="mx-auto w-full px-10 md:px-0 md:w-1/2 lg:w-1/3">
<h1 className="text-4xl font-bold">Release notes for {data.version} 🎉</h1> <h1 className="text-4xl font-bold">Release notes for {data.version} 🎉</h1>
<p className="text-sm mt-1 font-bold text-muted-foreground">{data.date}</p> <p className="text-sm mt-1 font-bold text-muted-foreground">{data.date}</p>
{data.extra && ( {data.extra && (

View File

@@ -0,0 +1,48 @@
'use client'
import * as React from 'react'
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
import { ny } from '@/lib/utils'
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={ny('relative overflow-hidden', className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className="size-full rounded-[inherit]">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
))
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
const ScrollBar = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = 'vertical', ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={ny(
'flex touch-none select-none transition-colors',
orientation === 'vertical'
&& 'h-full w-2.5 border-l border-l-transparent p-px',
orientation === 'horizontal'
&& 'h-2.5 flex-col border-t border-t-transparent p-px',
className,
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="bg-border relative flex-1 rounded-full" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
))
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
export { ScrollArea, ScrollBar }

144
src/components/ui/sheet.tsx Normal file
View File

@@ -0,0 +1,144 @@
'use client'
import * as React from 'react'
import * as SheetPrimitive from '@radix-ui/react-dialog'
import { Cross2Icon } from '@radix-ui/react-icons'
import { type VariantProps, cva } from 'class-variance-authority'
import { ny } from '@/lib/utils'
const Sheet = SheetPrimitive.Root
const SheetTrigger = SheetPrimitive.Trigger
const SheetClose = SheetPrimitive.Close
const SheetPortal = SheetPrimitive.Portal
const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={ny(
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80',
className,
)}
{...props}
ref={ref}
/>
))
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
const sheetVariants = cva(
'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out',
{
variants: {
side: {
top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
bottom:
'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
right:
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
},
},
defaultVariants: {
side: 'right',
},
},
)
interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
>(({ side = 'right', className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={ny(sheetVariants({ side }), className)}
{...props}
>
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
<Cross2Icon className="size-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
{children}
</SheetPrimitive.Content>
</SheetPortal>
))
SheetContent.displayName = SheetPrimitive.Content.displayName
function SheetHeader({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={ny(
'flex flex-col space-y-2 text-center sm:text-left',
className,
)}
{...props}
/>
)
}
SheetHeader.displayName = 'SheetHeader'
function SheetFooter({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={ny(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className,
)}
{...props}
/>
)
}
SheetFooter.displayName = 'SheetFooter'
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={ny('text-foreground text-lg font-semibold', className)}
{...props}
/>
))
SheetTitle.displayName = SheetPrimitive.Title.displayName
const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={ny('text-muted-foreground text-sm', className)}
{...props}
/>
))
SheetDescription.displayName = SheetPrimitive.Description.displayName
export {
Sheet,
SheetPortal,
SheetOverlay,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
}