diff --git a/src/app/themes/page.tsx b/src/app/themes/page.tsx new file mode 100644 index 0000000..4dd2eda --- /dev/null +++ b/src/app/themes/page.tsx @@ -0,0 +1,14 @@ + +import Footer from "@/components/footer"; +import MarketplacePage from "@/components/marketplace"; +import { Navigation } from "@/components/navigation"; + +export default function ThemesMarketplace() { + return ( +
+ +
+ ); +} diff --git a/src/components/marketplace.tsx b/src/components/marketplace.tsx new file mode 100644 index 0000000..0678b98 --- /dev/null +++ b/src/components/marketplace.tsx @@ -0,0 +1,28 @@ +"use client"; +import React from "react"; +import ThemesSearch from "./themes-search"; +import { getAllThemes, getThemesFromSearch, ZenTheme } from "@/lib/themes"; +import ThemeCard from "./theme-card"; + +export default function MarketplacePage() { + const [searchInput, setSearchInput] = React.useState(""); + const [themes, setThemes] = React.useState([]); + + React.useEffect(() => { + setThemes(getAllThemes()); + }, []); + + return ( +
+
+

Themes Marketplace

+ +
+
+ {getThemesFromSearch(themes, searchInput).map((theme) => ( + + ))} +
+
+ ); +} diff --git a/src/components/mobile-nav.tsx b/src/components/mobile-nav.tsx index 397d6ac..02aab0c 100644 --- a/src/components/mobile-nav.tsx +++ b/src/components/mobile-nav.tsx @@ -42,6 +42,12 @@ export function MobileNav() { > Download + + Themes + Start using Zen Browser today with just a few clicks. - - View the source code on GitHub and maybe leave a star! + + Customize your browser with a variety of themes! Stay up to date with the latest changes. diff --git a/src/components/theme-card.tsx b/src/components/theme-card.tsx new file mode 100644 index 0000000..84f2df8 --- /dev/null +++ b/src/components/theme-card.tsx @@ -0,0 +1,41 @@ +import { ZenTheme } from "@/lib/themes"; +import styled from "styled-components"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog"; +import { Button } from "./ui/button"; + +const ThemeCardWrapepr = styled.div` +`; + +export default function ThemeCard({ + theme +}: { + theme: ZenTheme; +}) { + return ( + + + + {theme.name} +

{theme.name}

+

{theme.description}

+
+
+ + + {theme.name} + {theme.name} + {theme.description} +
+
+ +

+ You need to have Zen Browser installed to use this theme. Download +

+
+
+
+
+ ); +} diff --git a/src/components/themes-search.tsx b/src/components/themes-search.tsx new file mode 100644 index 0000000..2a13020 --- /dev/null +++ b/src/components/themes-search.tsx @@ -0,0 +1,21 @@ +import { SearchIcon } from "lucide-react"; + +export default function ThemesSearch({ + input, setInput +}: { + input: string; + setInput: (input: string) => void; +}) { + return ( +
+ + setInput(e.target.value)} + placeholder="Search themes" + className="w-full bg-transparent border-none focus:outline-none focus:border-none focus:ring-0 text-white placeholder-muted" + /> +
+ ); +} diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx new file mode 100644 index 0000000..81886b1 --- /dev/null +++ b/src/components/ui/dialog.tsx @@ -0,0 +1,126 @@ +'use client' + +import * as React from 'react' +import * as DialogPrimitive from '@radix-ui/react-dialog' +import { Cross2Icon } from '@radix-ui/react-icons' + +import { ny } from '@/lib/utils' + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = DialogPrimitive.Portal + +const DialogClose = DialogPrimitive.Close + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +function DialogHeader({ + className, + ...props +}: React.HTMLAttributes) { + return ( +
+ ) +} +DialogHeader.displayName = 'DialogHeader' + +function DialogFooter({ + className, + ...props +}: React.HTMLAttributes) { + return ( +
+ ) +} +DialogFooter.displayName = 'DialogFooter' + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogTrigger, + DialogClose, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/src/lib/themes.ts b/src/lib/themes.ts new file mode 100644 index 0000000..3ff214c --- /dev/null +++ b/src/lib/themes.ts @@ -0,0 +1,25 @@ + +export interface ZenTheme { + name: string + description: string + image: string + downloadUrl: string + id: string +} + +export function getAllThemes(): ZenTheme[] { + // TODO: Fetch themes from the marketplace (database or JSON file) + return [ + { + name: "Zen", + description: "The default theme for Zen Browser", + downloadUrl: "https://zen-browser.app/download", // idrc + id: "zen", + image: "https://imgs.search.brave.com/qcDBMGuBLvJGLxWR3IkZyg35vROTSZ2omLn_0iLU2rs/rs:fit:860:0:0:0/g:ce/aHR0cHM6Ly9pLnBp/bmltZy5jb20vb3Jp/Z2luYWxzLzgxL2Mz/LzE0LzgxYzMxNDI5/MmViOGM3YzYxNmY5/ZjM3YTRmZDI5ODU4/LmpwZw", + }, + ]; +} + +export function getThemesFromSearch(themes: ZenTheme[], query: string): ZenTheme[] { + return themes.filter((theme) => theme.name.toLowerCase().includes(query.toLowerCase())); +}