Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"lint": "next lint"
},
"dependencies": {
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-slot": "^1.2.3",
"clsx": "^2.1.1",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-separator": "^1.1.7",
"@starknet-react/chains": "^3.1.3",
Expand All @@ -32,6 +35,7 @@
"eslint": "^9",
"eslint-config-next": "15.3.1",
"tailwindcss": "^4",
"tw-animate-css": "^1.3.4",
"typescript": "^5"
}
}
3 changes: 3 additions & 0 deletions public/X.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
111 changes: 111 additions & 0 deletions src/app/playerselection/selection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"use client"

import { useState } from "react"
import { Button } from "../../components/ui/button"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Check } from "lucide-react"
import Image from "next/image"

const colors = [
{ id: "orange", value: "#FF8C42", name: "Orange" },
{ id: "cyan", value: "#00D4FF", name: "Cyan" },
{ id: "purple", value: "#A855F7", name: "Purple" },
{ id: "teal", value: "#0F766E", name: "Teal" },
{ id: "red", value: "#EF4444", name: "Red" },
{ id: "blue", value: "#3B82F6", name: "Blue" },
{ id: "yellow", value: "#EAB308", name: "Yellow" },
{ id: "pink", value: "#EC4899", name: "Pink" },
{ id: "lime", value: "#84CC16", name: "Lime" },
{ id: "white", value: "#FFFFFF", name: "White" },
{ id: "brown", value: "#92400E", name: "Brown" },
{ id: "maroon", value: "#B91C1C", name: "Maroon" },
]

export default function ColorSelection() {
const [selectedColor, setSelectedColor] = useState<string>("yellow")
const [takenColors] = useState<string[]>(["blue", "lime", "pink"]) // Simulated taken colors
const [showConflictDialog, setShowConflictDialog] = useState(false)
const [, setAttemptedColor] = useState<string>("")

const handleColorSelect = (colorId: string) => {
if (takenColors.includes(colorId)) {
setAttemptedColor(colorId)
setShowConflictDialog(true)
return
}
setSelectedColor(colorId)
}

const handleCloseDialog = () => {
setShowConflictDialog(false)
setAttemptedColor("")
}

const getColorStatus = (colorId: string) => {
if (selectedColor === colorId) return "selected"
if (takenColors.includes(colorId)) return "taken"
return "available"
}

return (
<div className="min-h-screen bg-slate-900 flex items-center justify-center p-4">
<div className="w-full max-w-md">
<h1 className="text-white text-xl font-medium text-center mb-8">Select your appearance</h1>

<div className="grid grid-cols-4 gap-4 mb-8">
{colors.map((color) => {
const status = getColorStatus(color.id)

return (
<button
key={color.id}
onClick={() => handleColorSelect(color.id)}
className="w-16 h-16 rounded-full relative transition-transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-cyan-400 focus:ring-offset-2 focus:ring-offset-slate-900"
style={{ backgroundColor: color.value }}
aria-label={`Select ${color.name} color`}
>
{status === "selected" && (
<div className="absolute inset-0 flex items-center justify-center">
<span className="text-black font-bold text-lg px-2 py-1 rounded-full">You</span>
</div>
)}
{status === "taken" && (
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-8 h-8 rounded-full flex items-center justify-center">
<Image src="/X.svg" alt="" width="40" height="40" />
</div>
</div>
)}
</button>
)
})}
</div>

<Button className="w-full bg-cyan-400 hover:bg-cyan-500 text-black font-bold py-4 text-lg rounded-xl" size="lg">
Let&apos;s Go!
</Button>
</div>

<Dialog open={showConflictDialog} onOpenChange={setShowConflictDialog}>
<DialogContent className="bg-slate-800 border-cyan-400 border-2 text-white w-[350px] mx-auto">
<DialogHeader>
<DialogTitle className="text-xl font-bold text-center">Oops!</DialogTitle>
<DialogDescription className="text-gray-300 text-center mt-4">
This appearance has been selected by another player. Choose another appearance.
</DialogDescription>
</DialogHeader>
<div className="flex justify-center mt-6">
<Button
onClick={handleCloseDialog}
className="bg-transparent hover:bg-slate-700 text-white border border-gray-600 px-6"
variant="outline"
>
<Check className="w-4 h-4 mr-2" />
Got it
</Button>
</div>
</DialogContent>
</Dialog>
</div>
)
}
59 changes: 59 additions & 0 deletions src/components/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)

function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
}) {
const Comp = asChild ? Slot : "button"

return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}

export { Button, buttonVariants }
143 changes: 143 additions & 0 deletions src/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
"use client"

import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react"

import { cn } from "@/lib/utils"

function Dialog({
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />
}

function DialogTrigger({
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
}

function DialogPortal({
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
}

function DialogClose({
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
}

function DialogOverlay({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
return (
<DialogPrimitive.Overlay
data-slot="dialog-overlay"
className={cn(
"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/50",
className
)}
{...props}
/>
)
}

function DialogContent({
className,
children,
showCloseButton = true,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean
}) {
return (
<DialogPortal data-slot="dialog-portal">
<DialogOverlay />
<DialogPrimitive.Content
data-slot="dialog-content"
className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
className
)}
{...props}
>
{children}
{showCloseButton && (
<DialogPrimitive.Close
data-slot="dialog-close"
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
>
<XIcon />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
)
}

function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-header"
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
{...props}
/>
)
}

function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className
)}
{...props}
/>
)
}

function DialogTitle({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
return (
<DialogPrimitive.Title
data-slot="dialog-title"
className={cn("text-lg leading-none font-semibold", className)}
{...props}
/>
)
}

function DialogDescription({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
return (
<DialogPrimitive.Description
data-slot="dialog-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
)
}

export {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
}
6 changes: 6 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}