mirror of
https://github.com/zen-browser/www.git
synced 2025-07-07 17:05:32 +02:00
feat: Add @radix-ui/react-tabs npm dependency and update header.tsx to include a download link
This commit is contained in:
parent
acebd412e7
commit
80fa3d1842
7 changed files with 230 additions and 3 deletions
60
package-lock.json
generated
60
package-lock.json
generated
|
@ -10,6 +10,7 @@
|
|||
"dependencies": {
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.0",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cobe": "^0.6.3",
|
||||
|
@ -2966,6 +2967,36 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-roving-focus": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz",
|
||||
"integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-collection": "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-id": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||
"@radix-ui/react-use-controllable-state": "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-slot": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||
|
@ -2983,6 +3014,35 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.0.tgz",
|
||||
"integrity": "sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-context": "1.1.0",
|
||||
"@radix-ui/react-direction": "1.1.0",
|
||||
"@radix-ui/react-id": "1.1.0",
|
||||
"@radix-ui/react-presence": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-roving-focus": "1.1.0",
|
||||
"@radix-ui/react-use-controllable-state": "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-use-callback-ref": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"dependencies": {
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.0",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cobe": "^0.6.3",
|
||||
|
|
14
src/app/download/page.tsx
Normal file
14
src/app/download/page.tsx
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
import DownloadPage from "@/components/download";
|
||||
import Footer from "@/components/footer";
|
||||
import { Navigation } from "@/components/navigation";
|
||||
|
||||
export default function Download() {
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-start">
|
||||
<DownloadPage />
|
||||
<Footer />
|
||||
<Navigation /> {/* At the bottom of the page */}
|
||||
</main>
|
||||
);
|
||||
}
|
95
src/components/download.tsx
Normal file
95
src/components/download.tsx
Normal file
|
@ -0,0 +1,95 @@
|
|||
"use client";
|
||||
|
||||
import { ny } from "@/lib/utils";
|
||||
import GridPattern from "./ui/grid-pattern";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs";
|
||||
import { DownloadCloudIcon, DownloadIcon } from "lucide-react";
|
||||
|
||||
const BASE_URL = "https://github.com/zen-browser/desktop/releases/download/latest";
|
||||
|
||||
const releases: any = {
|
||||
Windows: [
|
||||
"zen.win64.zip",
|
||||
],
|
||||
//MacOS: [],
|
||||
Linux: [
|
||||
"zen.linux.tar.bz2",
|
||||
],
|
||||
};
|
||||
|
||||
function getDefaultPlatformBasedOnUserAgent() {
|
||||
const userAgent = navigator.userAgent;
|
||||
if (userAgent.includes("Win")) {
|
||||
return "Windows";
|
||||
}
|
||||
if (userAgent.includes("Mac")) {
|
||||
return "MacOS";
|
||||
}
|
||||
if (userAgent.includes("Linux")) {
|
||||
return "Linux";
|
||||
}
|
||||
return "Windows";
|
||||
}
|
||||
|
||||
export default function DownloadPage() {
|
||||
return (
|
||||
<div className="w-full relative h-screen flex items-center justify-center">
|
||||
<div className="w-1/2 relative h-full px-64 flex items-cetner justify-center flex-col">
|
||||
<GridPattern
|
||||
numSquares={30}
|
||||
maxOpacity={0.5}
|
||||
height={50}
|
||||
width={50}
|
||||
duration={3}
|
||||
repeatDelay={1}
|
||||
x={-1}
|
||||
y={-1}
|
||||
strokeDasharray="4 2"
|
||||
className={ny(
|
||||
'[mask-image:radial-gradient(350px_circle_at_center,white,transparent)]',
|
||||
'w-full z-0',
|
||||
)}
|
||||
/>
|
||||
<h1 className="!text-4xl font-bold tracking-[-0.02em] text-black dark:text-white md:text-7xl md:leading-[5rem]">
|
||||
Download Zen Browser
|
||||
</h1>
|
||||
<p className="!text-md text-muted-foreground !font-medium">
|
||||
Get started with Zen Browser today. Get back to browsing the web with peace of mind.
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-1/2 relative flex items-cetner justify-start">
|
||||
<Tabs defaultValue={getDefaultPlatformBasedOnUserAgent()} className="mx-auto w-fit flex flex-col items-start justify-center">
|
||||
<TabsList>
|
||||
{Object.keys(releases).map((platform) => (
|
||||
<TabsTrigger key={platform} value={platform}>{platform}</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
{Object.keys(releases).map((platform) => (
|
||||
<TabsContent key={platform} value={platform} className="border rounded-md p-5 border-gray">
|
||||
<table>
|
||||
<thead className="">
|
||||
<tr>
|
||||
<th className="text-start pb-4 min-w-64">File</th>
|
||||
<th className="text-start pb-4">Download</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{releases[platform].map((release: string) => (
|
||||
<tr key={release} className="relative w-full">
|
||||
<td className="min-w-64">{release}</td>
|
||||
<td className="flex items-center w-full justify-center">
|
||||
<a href={`${BASE_URL}/${release}`} className="text-blue-500">
|
||||
<DownloadIcon size={24} />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</TabsContent>
|
||||
))}
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -26,7 +26,8 @@ export default function Header() {
|
|||
'w-full z-0',
|
||||
)}
|
||||
/>
|
||||
<div className="z-10 flex mb-10 items-center justify-center">
|
||||
<div className="z-10 flex mb-10 items-center justify-center">
|
||||
<a href="/download">
|
||||
<AnimatedGradientText>
|
||||
🎉
|
||||
{' '}
|
||||
|
@ -41,6 +42,7 @@ export default function Header() {
|
|||
</span>
|
||||
<ChevronRight className="ml-1 size-3 transition-transform duration-300 ease-in-out group-hover:translate-x-0.5" />
|
||||
</AnimatedGradientText>
|
||||
</a>
|
||||
</div>
|
||||
<WordPullUp
|
||||
className="text-3xl font-bold tracking-[-0.02em] text-black dark:text-white md:text-7xl md:leading-[5rem]"
|
||||
|
@ -58,7 +60,7 @@ export default function Header() {
|
|||
framerProps={{
|
||||
show: { transition: { delay: 0.2 } },
|
||||
}}
|
||||
text="Fade Up"
|
||||
text="Documentation"
|
||||
/>
|
||||
</a>
|
||||
<ShinyButton text="Download now" />
|
||||
|
|
|
@ -30,7 +30,7 @@ const components: { title: string; href: string; description: string }[] = [
|
|||
|
||||
export function Navigation() {
|
||||
return (
|
||||
<div className="absolute z-10 top-0 left-0 w-full flex fixed border-b border-grey p-2 items-center justify-center">
|
||||
<div className="backdrop-blur fixed z-10 top-0 left-0 w-full flex fixed border-b border-grey p-2 items-center justify-center">
|
||||
<NavigationMenu>
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
|
|
55
src/components/ui/tabs.tsx
Normal file
55
src/components/ui/tabs.tsx
Normal file
|
@ -0,0 +1,55 @@
|
|||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import * as TabsPrimitive from '@radix-ui/react-tabs'
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
|
||||
const Tabs = TabsPrimitive.Root
|
||||
|
||||
const TabsList = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsList.displayName = TabsPrimitive.List.displayName
|
||||
|
||||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
||||
|
||||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsContent.displayName = TabsPrimitive.Content.displayName
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
Loading…
Add table
Add a link
Reference in a new issue