From 0a0ca5afd5fe95e6602caf64d5aaaf5c7bebdd07 Mon Sep 17 00:00:00 2001 From: Boostvolt <51777660+boostvolt@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:13:42 +0200 Subject: [PATCH] feat: add acknowledgments section and appearance switch [NOTICKET] --- app/layout.tsx | 7 +- app/page.tsx | 54 ++------- components/settings-menu.tsx | 97 +++++++++++++++ components/theme-toggle.tsx | 19 --- components/ui/alert-dialog.tsx | 107 +++++++++++++++++ components/ui/menubar.tsx | 209 +++++++++++++++++++++++++++++++++ package.json | 10 +- pnpm-lock.yaml | 96 +++++++++++++++ 8 files changed, 527 insertions(+), 72 deletions(-) create mode 100644 components/settings-menu.tsx delete mode 100644 components/theme-toggle.tsx create mode 100644 components/ui/alert-dialog.tsx create mode 100644 components/ui/menubar.tsx diff --git a/app/layout.tsx b/app/layout.tsx index e39076a..5c80e96 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -75,12 +75,7 @@ interface RootLayoutProps { export default function RootLayout({ children }: RootLayoutProps) { return ( - - - + {children} diff --git a/app/page.tsx b/app/page.tsx index 8369f56..544823e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -2,24 +2,16 @@ import { zodResolver } from "@hookform/resolvers/zod" import html2canvas from "html2canvas" -import { Cog, Info } from "lucide-react" +import { Info } from "lucide-react" import Image from "next/image" import { useEffect, useRef, useState } from "react" import { useForm } from "react-hook-form" import * as z from "zod" import AppleMusic from "@/components/icons/apple-music" +import { SettingsMenu } from "@/components/settings-menu" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" import { Avatar } from "@/components/ui/avatar" -import { Button } from "@/components/ui/button" import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuLabel, - DropdownMenuRadioGroup, - DropdownMenuRadioItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" import { Form, FormControl, FormField, FormItem } from "@/components/ui/form" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" @@ -31,7 +23,6 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/comp import { SfProDisplay } from "@/lib/fonts/sf-pro-display" const formSchema = z.object({ - imageFormat: z.enum(["png", "jpeg"]), appleMusicLogo: z.boolean(), bigTitle: z.string(), subTitle: z.string(), @@ -44,7 +35,6 @@ export default function Home() { const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: formSchema.parse({ - imageFormat: "png", appleMusicLogo: true, bigTitle: "", subTitle: "", @@ -61,7 +51,7 @@ export default function Home() { if (element) { html2canvas(element, { useCORS: true, allowTaint: true }).then((canvas) => { const link = document.createElement("a") - link.download = `playlist-cover.${form.watch("imageFormat")}` + link.download = "playlist-cover.png" link.href = canvas.toDataURL() link.click() }) @@ -122,33 +112,7 @@ export default function Home() { iCover -
- - - - - - - Image Format - ( - - - - PNG - JPEG - - - - )} - /> - - -
+ @@ -175,7 +139,7 @@ export default function Home() {

{isEdited ? form.watch("bigTitle") : "Big Title"} @@ -183,7 +147,11 @@ export default function Home() {

{/* Sub Title */}
-

+

{isEdited ? form.watch("subTitle") : "Sub Title"}

@@ -191,7 +159,7 @@ export default function Home() {

{isEdited ? form.watch("footer") : "Footer"} diff --git a/components/settings-menu.tsx b/components/settings-menu.tsx new file mode 100644 index 0000000..8cf317f --- /dev/null +++ b/components/settings-menu.tsx @@ -0,0 +1,97 @@ +"use client" + +import { Cog } from "lucide-react" +import { useTheme } from "next-themes" +import { FC, useState } from "react" +import { + AlertDialog, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "./ui/alert-dialog" +import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarTrigger } from "./ui/menubar" + +interface SettingsMenuProps { + downloadOnClick: () => void +} + +export const SettingsMenu: FC = ({ downloadOnClick }) => { + const { setTheme, theme } = useTheme() + const [isAckknowledgmentsOpen, setAckknowledgmentsOpen] = useState(false) + + return ( + <> + + + Download + + + + + + + setTheme(theme === "light" ? "dark" : "light")}>Switch Appearance + + setAckknowledgmentsOpen(true)}>Acknowledgments + v1.0.1 + + + + { + if (isAckknowledgmentsOpen === true) return + setAckknowledgmentsOpen(false) + }} + > + + + Acknowledgments + + We are deeply inspired by the creativity and innovation of countless individuals and projects, which have + fueled our own endeavors and shaped the direction of this work. + + + + + Close + + + + + ) +} diff --git a/components/theme-toggle.tsx b/components/theme-toggle.tsx deleted file mode 100644 index e868ff1..0000000 --- a/components/theme-toggle.tsx +++ /dev/null @@ -1,19 +0,0 @@ -"use client" - -import { Moon, Sun } from "lucide-react" -import { useTheme } from "next-themes" -import * as React from "react" - -import { Button } from "@/components/ui/button" - -export function ThemeToggle() { - const { setTheme, theme } = useTheme() - - return ( - - ) -} diff --git a/components/ui/alert-dialog.tsx b/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..30c0604 --- /dev/null +++ b/components/ui/alert-dialog.tsx @@ -0,0 +1,107 @@ +"use client" + +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" +import * as React from "react" + +import { buttonVariants } from "@/components/ui/button" +import { cn } from "@/lib/utils" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = ({ className, ...props }: AlertDialogPrimitive.AlertDialogPortalProps) => ( + +) +AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/components/ui/menubar.tsx b/components/ui/menubar.tsx new file mode 100644 index 0000000..4457a25 --- /dev/null +++ b/components/ui/menubar.tsx @@ -0,0 +1,209 @@ +"use client" + +import * as MenubarPrimitive from "@radix-ui/react-menubar" +import { Check, ChevronRight, Circle } from "lucide-react" +import * as React from "react" + +import { cn } from "@/lib/utils" + +const MenubarMenu = MenubarPrimitive.Menu + +const MenubarGroup = MenubarPrimitive.Group + +const MenubarPortal = MenubarPrimitive.Portal + +const MenubarSub = MenubarPrimitive.Sub + +const MenubarRadioGroup = MenubarPrimitive.RadioGroup + +const Menubar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Menubar.displayName = MenubarPrimitive.Root.displayName + +const MenubarTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName + +const MenubarSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName + +const MenubarSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName + +const MenubarContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "start", alignOffset = -4, sideOffset = 8, ...props }, ref) => ( + + + +)) +MenubarContent.displayName = MenubarPrimitive.Content.displayName + +const MenubarItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +MenubarItem.displayName = MenubarPrimitive.Item.displayName + +const MenubarCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName + +const MenubarRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName + +const MenubarLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +MenubarLabel.displayName = MenubarPrimitive.Label.displayName + +const MenubarSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName + +const MenubarShortcut = ({ className, ...props }: React.HTMLAttributes) => { + return +} +MenubarShortcut.displayname = "MenubarShortcut" + +export { + Menubar, + MenubarMenu, + MenubarTrigger, + MenubarContent, + MenubarItem, + MenubarSeparator, + MenubarLabel, + MenubarCheckboxItem, + MenubarRadioGroup, + MenubarRadioItem, + MenubarPortal, + MenubarSubContent, + MenubarSubTrigger, + MenubarGroup, + MenubarSub, + MenubarShortcut, +} diff --git a/package.json b/package.json index 4b340dc..a3cabf4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "i-cover", - "version": "1.0.0", + "version": "1.0.1", "description": "Design a captivating and visually appealing cover artwork for your Apple Music playlist to engage listeners and convey the theme or mood of the curated songs.", "repository": "https://github.com/boostvolt/icover", "author": "Boostvolt", @@ -16,11 +16,13 @@ "format": "prettier --write \"**/*.{ts,tsx,md}\"" }, "dependencies": { - "@next/bundle-analyzer": "^13.4.12", "@hookform/resolvers": "^3.2.0", + "@next/bundle-analyzer": "^13.4.12", + "@radix-ui/react-alert-dialog": "^1.0.4", "@radix-ui/react-avatar": "^1.0.3", "@radix-ui/react-dropdown-menu": "^2.0.5", "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-menubar": "^1.0.3", "@radix-ui/react-radio-group": "^1.1.3", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", @@ -47,16 +49,16 @@ "@types/react-dom": "18.2.7", "@typescript-eslint/eslint-plugin": "^6.2.1", "@typescript-eslint/parser": "^6.2.1", + "autoprefixer": "10.4.14", "eslint": "8.46.0", "eslint-config-next": "13.4.12", "eslint-config-prettier": "^9.0.0", "eslint-plugin-import": "^2.28.0", "eslint-plugin-react": "7.33.1", "eslint-plugin-tailwindcss": "^3.13.0", + "postcss": "8.4.27", "prettier": "^3.0.1", "prettier-plugin-tailwindcss": "^0.4.1", - "autoprefixer": "10.4.14", - "postcss": "8.4.27", "typescript": "5.1.6" }, "engines": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b59190d..20d3a7c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: '@next/bundle-analyzer': specifier: ^13.4.12 version: 13.4.12 + '@radix-ui/react-alert-dialog': + specifier: ^1.0.4 + version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-avatar': specifier: ^1.0.3 version: 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) @@ -20,6 +23,9 @@ dependencies: '@radix-ui/react-label': specifier: ^2.0.2 version: 2.0.2(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-menubar': + specifier: ^1.0.3 + version: 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-radio-group': specifier: ^1.1.3 version: 1.1.3(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) @@ -406,6 +412,32 @@ packages: '@babel/runtime': 7.22.6 dev: false + /@radix-ui/react-alert-dialog@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-jbfBCRlKYlhbitueOAv7z74PXYeIQmWpKwm3jllsdkw7fGWNkxqP3v0nY9WmOzcPqpQuoorNtvViBgL46n5gVg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.22.6 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@radix-ui/react-dialog': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.18)(react@18.2.0) + '@types/react': 18.2.18 + '@types/react-dom': 18.2.7 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: @@ -503,6 +535,40 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-dialog@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-hJtRy/jPULGQZceSAP2Re6/4NpKo8im6V8P2hUqZsdFiSL8l35kYsw3qbRI6Ay5mQd2+wlLqje770eq+RJ3yZg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.22.6 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.18)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@types/react': 18.2.18 + '@types/react-dom': 18.2.7 + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.18)(react@18.2.0) + dev: false + /@radix-ui/react-direction@1.0.1(@types/react@18.2.18)(react@18.2.0): resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} peerDependencies: @@ -680,6 +746,36 @@ packages: react-remove-scroll: 2.5.5(@types/react@18.2.18)(react@18.2.0) dev: false + /@radix-ui/react-menubar@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-GqjdxzYCjjKhcgEODDP8SrYfbWNh/Hm3lyuFkP5Q5IbX0QfXklLF1o1AqA3oTV2kulUgN/kOZVS92hIIShEgpA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.22.6 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@radix-ui/react-menu': 2.0.5(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.18)(react@18.2.0) + '@types/react': 18.2.18 + '@types/react-dom': 18.2.7 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-popper@1.1.2(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==} peerDependencies: