Files
shadcn-ui/apps/www/components/apple-music-demo.tsx
2023-01-24 19:51:29 +04:00

746 lines
28 KiB
TypeScript

import * as React from "react"
import Image from "next/image"
import {
Album,
CreditCard,
Globe,
Keyboard,
LayoutGrid,
Library,
ListMusic,
LogOut,
Mail,
MessageSquare,
Mic,
Mic2,
Music,
Music2,
PlayCircle,
Plus,
PlusCircle,
Podcast,
Radio,
Settings,
User,
UserPlus,
Users,
} from "lucide-react"
import { cn } from "@/lib/utils"
import { AspectRatio } from "@/components/ui/aspect-ratio"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
Menubar,
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarLabel,
MenubarMenu,
MenubarRadioGroup,
MenubarRadioItem,
MenubarSeparator,
MenubarShortcut,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from "@/components/ui/menubar"
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
import { Separator } from "@/components/ui/separator"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
const playlists = [
"Recently Added",
"Recently Played",
"Top Songs",
"Top Albums",
"Top Artists",
"Logic Discography",
"Bedtime Beats",
"Feeling Happy",
"I miss Y2K Pop",
"Runtober",
"Mellow Days",
"Eminem Essentials",
]
interface Album {
name: string
artist: string
cover: string
}
const listenNowAlbums: Album[] = [
{
name: "Async Awakenings",
artist: "Nina Netcode",
cover:
"https://images.unsplash.com/photo-1547355253-ff0740f6e8c1?w=300&dpr=2&q=80",
},
{
name: "The Art of Reusability",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1576075796033-848c2a5f3696?w=300&dpr=2&q=80",
},
{
name: "Stateful Symphony",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1606542758304-820b04394ac2?w=300&dpr=2&q=80",
},
{
name: "React Rendezvous",
artist: "Ethan Byte",
cover:
"https://images.unsplash.com/photo-1598295893369-1918ffaf89a2?w=300&dpr=2&q=80",
},
]
const madeForYouAlbums: Album[] = [
{
name: "Async Awakenings",
artist: "Nina Netcode",
cover:
"https://images.unsplash.com/photo-1580428180098-24b353d7e9d9?w=300&dpr=2&q=80",
},
{
name: "Stateful Symphony",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1606542758304-820b04394ac2?w=300&dpr=2&q=80",
},
{
name: "Stateful Symphony",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1598062548091-a6fb6a052562?w=300&dpr=2&q=80",
},
{
name: "The Art of Reusability",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1626759486966-c067e3f79982?w=300&dpr=2&q=80",
},
{
name: "Thinking Components",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1576075796033-848c2a5f3696?w=300&dpr=2&q=80",
},
{
name: "Functional Fury",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1606542758304-820b04394ac2?w=300&dpr=2&q=80",
},
{
name: "React Rendezvous",
artist: "Ethan Byte",
cover:
"https://images.unsplash.com/photo-1598295893369-1918ffaf89a2?w=300&dpr=2&q=80",
},
]
export function AppleMusicDemo() {
return (
<div className="overflow-hidden rounded-md border border-slate-200 bg-gradient-to-b from-rose-500 to-indigo-700 shadow-2xl dark:border-slate-800">
<Menubar className="rounded-none border-b border-none dark:bg-slate-900">
<MenubarMenu>
<MenubarTrigger className="font-bold">Music</MenubarTrigger>
<MenubarContent>
<MenubarItem>About Music</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Preferences... <MenubarShortcut>,</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Hide Music... <MenubarShortcut>H</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Hide Others... <MenubarShortcut>H</MenubarShortcut>
</MenubarItem>
<MenubarShortcut />
<MenubarItem>
Quit Music <MenubarShortcut>Q</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger className="relative">
File
<DemoIndicator />
</MenubarTrigger>
<MenubarContent>
<MenubarSub>
<MenubarSubTrigger>New</MenubarSubTrigger>
<MenubarSubContent className="w-[230px]">
<MenubarItem>
Playlist <MenubarShortcut>N</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Playlist from Selection <MenubarShortcut>N</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Smart Playlist... <MenubarShortcut>N</MenubarShortcut>
</MenubarItem>
<MenubarItem>Playlist Folder</MenubarItem>
<MenubarItem disabled>Genius Playlist</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarItem>
Open Stream URL... <MenubarShortcut>U</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Close Window <MenubarShortcut>W</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>Library</MenubarSubTrigger>
<MenubarSubContent>
<MenubarItem>Update Cloud Library</MenubarItem>
<MenubarItem>Update Genius</MenubarItem>
<MenubarSeparator />
<MenubarItem>Organize Library...</MenubarItem>
<MenubarItem>Export Library...</MenubarItem>
<MenubarSeparator />
<MenubarItem>Import Playlist...</MenubarItem>
<MenubarItem disabled>Export Playlist...</MenubarItem>
<MenubarItem>Show Duplicate Items</MenubarItem>
<MenubarSeparator />
<MenubarItem>Get Album Artwork</MenubarItem>
<MenubarItem disabled>Get Track Names</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarItem>
Import... <MenubarShortcut>O</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>Burn Playlist to Disc...</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Show in Finder <MenubarShortcut>R</MenubarShortcut>{" "}
</MenubarItem>
<MenubarItem>Convert</MenubarItem>
<MenubarSeparator />
<MenubarItem>Page Setup...</MenubarItem>
<MenubarItem disabled>
Print... <MenubarShortcut>P</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Edit</MenubarTrigger>
<MenubarContent>
<MenubarItem disabled>
Undo <MenubarShortcut>Z</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Redo <MenubarShortcut>Z</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem disabled>
Cut <MenubarShortcut>X</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Copy <MenubarShortcut>C</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Paste <MenubarShortcut>V</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Select All <MenubarShortcut>A</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Deselect All <MenubarShortcut>A</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Smart Dictation...{" "}
<MenubarShortcut>
<Mic className="h-4 w-4" />
</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Emoji & Symbols{" "}
<MenubarShortcut>
<Globe className="h-4 w-4" />
</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>View</MenubarTrigger>
<MenubarContent>
<MenubarCheckboxItem>Show Playing Next</MenubarCheckboxItem>
<MenubarCheckboxItem checked>Show Lyrics</MenubarCheckboxItem>
<MenubarSeparator />
<MenubarItem inset disabled>
Show Status Bar
</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Hide Sidebar</MenubarItem>
<MenubarItem disabled inset>
Enter Full Screen
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Account</MenubarTrigger>
<MenubarContent forceMount>
<MenubarLabel inset>Switch Account</MenubarLabel>
<MenubarSeparator />
<MenubarRadioGroup value="benoit">
<MenubarRadioItem value="andy">Andy</MenubarRadioItem>
<MenubarRadioItem value="benoit">Benoit</MenubarRadioItem>
<MenubarRadioItem value="Luis">Luis</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSeparator />
<MenubarItem inset>Manage Famliy...</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Add Account...</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
<div className="p-8">
<div className="rounded-md bg-white shadow-2xl transition-all dark:bg-slate-900">
<div className="grid grid-cols-4 xl:grid-cols-5">
<aside className="pb-12">
<div className="px-8 py-6">
<p className="flex items-center text-2xl font-semibold tracking-tight">
<Music className="mr-2" />
Music
</p>
</div>
<div className="space-y-4">
<div className="px-6 py-2">
<h2 className="mb-2 px-2 text-lg font-semibold tracking-tight">
Discover
</h2>
<div className="space-y-1">
<Button
variant="subtle"
size="sm"
className="w-full justify-start"
>
<PlayCircle className="mr-2 h-4 w-4" />
Listen Now
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<LayoutGrid className="mr-2 h-4 w-4" />
Browse
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<Radio className="mr-2 h-4 w-4" />
Radio
</Button>
</div>
</div>
<div className="px-6 py-2">
<h2 className="mb-2 px-2 text-lg font-semibold tracking-tight">
Library
</h2>
<div className="space-y-1">
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<ListMusic className="mr-2 h-4 w-4" />
Playlists
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<Music2 className="mr-2 h-4 w-4" />
Songs
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<User className="mr-2 h-4 w-4" />
Made for You
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<Mic2 className="mr-2 h-4 w-4" />
Artists
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<Library className="mr-2 h-4 w-4" />
Albums
</Button>
</div>
</div>
<div className="py-2">
<h2 className="relative px-8 text-lg font-semibold tracking-tight">
Playlists <DemoIndicator className="right-28" />
</h2>
<ScrollArea className="h-[230px] px-4">
<div className="space-y-1 p-2">
{playlists.map((playlist) => (
<Button
variant="ghost"
size="sm"
className="w-full justify-start font-normal"
>
<ListMusic className="mr-2 h-4 w-4" />
{playlist}
</Button>
))}
</div>
</ScrollArea>
</div>
</div>
</aside>
<div className="col-span-3 border-l border-l-slate-200 dark:border-l-slate-700 xl:col-span-4">
<div className="h-full px-8 py-6">
<Tabs defaultValue="music" className="h-full space-y-6">
<div className="space-between flex items-center">
<TabsList>
<TabsTrigger value="music" className="relative">
Music <DemoIndicator className="right-2" />
</TabsTrigger>
<TabsTrigger value="podcasts">Podcasts</TabsTrigger>
<TabsTrigger value="live" disabled>
Live
</TabsTrigger>
</TabsList>
<div className="ml-auto mr-4">
<h3 className="text-sm font-semibold">Welcome back</h3>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="relative h-10 w-10 rounded-full"
>
<Avatar>
<AvatarImage
src="https://github.com/shadcn.png"
alt="@shadcn"
/>
<AvatarFallback>SC</AvatarFallback>
</Avatar>
<DemoIndicator className="right-0 top-0" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-56"
align="end"
forceMount
>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<User className="mr-2 h-4 w-4" />
<span>Profile</span>
<DropdownMenuShortcut>P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCard className="mr-2 h-4 w-4" />
<span>Billing</span>
<DropdownMenuShortcut>B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<Settings className="mr-2 h-4 w-4" />
<span>Settings</span>
<DropdownMenuShortcut>S</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<Keyboard className="mr-2 h-4 w-4" />
<span>Keyboard shortcuts</span>
<DropdownMenuShortcut>K</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Users className="mr-2 h-4 w-4" />
<span>Team</span>
</DropdownMenuItem>
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<UserPlus className="mr-2 h-4 w-4" />
<span>Invite users</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent forceMount>
<DropdownMenuItem>
<Mail className="mr-2 h-4 w-4" />
<span>Email</span>
</DropdownMenuItem>
<DropdownMenuItem>
<MessageSquare className="mr-2 h-4 w-4" />
<span>Message</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<PlusCircle className="mr-2 h-4 w-4" />
<span>More...</span>
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut className="mr-2 h-4 w-4" />
<span>Log out</span>
<DropdownMenuShortcut>Q</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<TabsContent value="music" className="border-none p-0">
<div className="flex items-center justify-between">
<div className="space-y-1">
<h2 className="text-2xl font-semibold tracking-tight">
Listen Now
</h2>
<p className="text-sm text-slate-500 dark:text-slate-400">
Top picks for you. Updated daily.
</p>
</div>
</div>
<Separator className="my-4" />
<div className="relative">
<DemoIndicator className="right-auto left-24 top-32 z-30" />
<div className="relative flex space-x-4">
{listenNowAlbums.map((album) => (
<AlbumArtwork
key={album.name}
album={album}
className="w-[250px]"
/>
))}
</div>
</div>
<div className="mt-6 space-y-1">
<h2 className="text-2xl font-semibold tracking-tight">
Made for You
</h2>
<p className="text-sm text-slate-500 dark:text-slate-400">
Your personal playlists. Updated daily.
</p>
</div>
<Separator className="my-4" />
<div className="relative">
<DemoIndicator className="top-32 right-auto left-16 z-30" />
<ScrollArea>
<div className="flex space-x-4 pb-4">
{madeForYouAlbums.map((album) => (
<AlbumArtwork
key={album.name}
album={album}
className="w-[150px]"
aspectRatio={1 / 1}
/>
))}
</div>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</div>
</TabsContent>
<TabsContent
value="podcasts"
className="h-full flex-col border-none p-0 data-[state=active]:flex"
>
<div className="flex items-center justify-between">
<div className="space-y-1">
<h2 className="text-2xl font-semibold tracking-tight">
New Episodes
</h2>
<p className="text-sm text-slate-500 dark:text-slate-400">
Your favorite podcasts. Updated daily.
</p>
</div>
</div>
<Separator className="my-4" />
<div className="flex h-[450px] shrink-0 items-center justify-center rounded-md border border-dashed border-slate-200 dark:border-slate-700">
<div className="mx-auto flex max-w-[420px] flex-col items-center justify-center text-center">
<Podcast className="h-10 w-10 text-slate-400" />
<h3 className="mt-4 text-lg font-semibold text-slate-900 dark:text-slate-50">
No episodes added
</h3>
<p className="mt-2 mb-4 text-sm text-slate-500 dark:text-slate-400">
You have not added any podcasts. Add one below.
</p>
<Dialog>
<DialogTrigger>
<Button size="sm" className="relative">
<Plus className="mr-2 h-4 w-4" />
Add Podcast
<DemoIndicator className="-top-1 -right-1 z-30" />
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Add Podcast</DialogTitle>
<DialogDescription>
Copy and paste the podcast feed URL to import.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="url">Podcast URL</Label>
<Input
id="url"
placeholder="https://example.com/feed.xml"
/>
</div>
</div>
<DialogFooter>
<Button>Import Podcast</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
</TabsContent>
</Tabs>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
interface AlbumArtworkProps extends React.HTMLAttributes<HTMLDivElement> {
album: Album
aspectRatio?: number
}
function AlbumArtwork({
album,
aspectRatio = 3 / 4,
className,
...props
}: AlbumArtworkProps) {
return (
<div className={cn("space-y-3", className)} {...props}>
<ContextMenu>
<ContextMenuTrigger>
<AspectRatio
ratio={aspectRatio}
className="overflow-hidden rounded-md"
>
<Image
src={album.cover}
alt={album.name}
fill
className="object-cover transition-all hover:scale-105"
/>
</AspectRatio>
</ContextMenuTrigger>
<ContextMenuContent className="w-40">
<ContextMenuItem>Add to Library</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger>Add to Playlist</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuItem>
<PlusCircle className="mr-2 h-4 w-4" />
New Playlist
</ContextMenuItem>
<ContextMenuSeparator />
{playlists.map((playlist) => (
<ContextMenuItem key={playlist}>
<ListMusic className="mr-2 h-4 w-4" /> {playlist}
</ContextMenuItem>
))}
</ContextMenuSubContent>
</ContextMenuSub>
<ContextMenuSeparator />
<ContextMenuItem>Play Next</ContextMenuItem>
<ContextMenuItem>Play Later</ContextMenuItem>
<ContextMenuItem>Create Station</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>Like</ContextMenuItem>
<ContextMenuItem>Share</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
<div className="space-y-1 text-sm">
<h3 className="font-medium leading-none">{album.name}</h3>
<p className="text-xs text-slate-500 dark:text-slate-400">
{album.artist}
</p>
</div>
</div>
)
}
interface DemoIndicatorProps extends React.HTMLAttributes<HTMLSpanElement> {}
export function DemoIndicator({ className }: DemoIndicatorProps) {
return (
<span
className={cn(
"absolute top-1 right-0 flex h-5 w-5 animate-bounce items-center justify-center",
className
)}
>
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-400 opacity-75" />
<span className="relative inline-flex h-3 w-3 rounded-full bg-sky-500" />
</span>
)
}