Reuben_OS / app /components /BackgroundSelector.tsx
Reubencf's picture
First Push
8af739b
'use client'
import React, { useState, useRef } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { X, Upload, Image, Check } from '@phosphor-icons/react'
interface BackgroundSelectorProps {
isOpen: boolean
onClose: () => void
onSelectBackground: (background: string | File) => void
currentBackground: string
}
const presetBackgrounds = [
{
id: 'gradient-purple',
name: 'Ubuntu Purple',
style: 'linear-gradient(135deg, #77216F 0%, #5E2750 50%, #2C001E 100%)'
},
{
id: 'gradient-blue',
name: 'Ocean Blue',
style: 'linear-gradient(135deg, #1e3c72 0%, #2a5298 50%, #7e8ba3 100%)'
},
{
id: 'gradient-green',
name: 'Forest Green',
style: 'linear-gradient(135deg, #134e5e 0%, #71b280 50%, #a8e063 100%)'
},
{
id: 'gradient-orange',
name: 'Sunset Orange',
style: 'linear-gradient(135deg, #ff512f 0%, #dd2476 50%, #f09819 100%)'
},
{
id: 'gradient-dark',
name: 'Dark Mode',
style: 'linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 50%, #2d2d2d 100%)'
},
{
id: 'gradient-cosmic',
name: 'Cosmic',
style: 'linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%)'
}
]
export function BackgroundSelector({
isOpen,
onClose,
onSelectBackground,
currentBackground
}: BackgroundSelectorProps) {
const [selectedTab, setSelectedTab] = useState<'presets' | 'upload'>('presets')
const [uploadedImage, setUploadedImage] = useState<string | null>(null)
const fileInputRef = useRef<HTMLInputElement>(null)
const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (file) {
const reader = new FileReader()
reader.onload = (e) => {
const result = e.target?.result as string
setUploadedImage(result)
onSelectBackground(file)
}
reader.readAsDataURL(file)
}
}
return (
<AnimatePresence>
{isOpen && (
<>
{/* Backdrop */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50"
/>
{/* Modal */}
<motion.div
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.9, opacity: 0 }}
transition={{ type: "spring", damping: 20, stiffness: 300 }}
className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[90%] max-w-2xl bg-[#2C2C2C]/95 backdrop-blur-md rounded-xl shadow-2xl z-50 border border-white/10"
>
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-white/10">
<h2 className="text-xl font-semibold text-white">Change Desktop Background</h2>
<button
onClick={onClose}
className="text-gray-400 hover:text-white transition-colors p-1 hover:bg-white/10 rounded-lg"
>
<X size={20} />
</button>
</div>
{/* Tabs */}
<div className="flex border-b border-white/10">
<button
onClick={() => setSelectedTab('presets')}
className={`flex-1 px-4 py-3 text-sm font-medium transition-colors ${
selectedTab === 'presets'
? 'text-white border-b-2 border-blue-500'
: 'text-gray-400 hover:text-white'
}`}
>
Gallery
</button>
<button
onClick={() => setSelectedTab('upload')}
className={`flex-1 px-4 py-3 text-sm font-medium transition-colors ${
selectedTab === 'upload'
? 'text-white border-b-2 border-blue-500'
: 'text-gray-400 hover:text-white'
}`}
>
Upload Image
</button>
</div>
{/* Content */}
<div className="p-6">
{selectedTab === 'presets' ? (
<div className="grid grid-cols-3 gap-4">
{presetBackgrounds.map((bg) => (
<button
key={bg.id}
onClick={() => onSelectBackground(bg.id)}
className={`relative aspect-video rounded-lg overflow-hidden border-2 transition-all ${
currentBackground === bg.id
? 'border-blue-500 scale-105'
: 'border-white/20 hover:border-white/40'
}`}
>
<div
className="w-full h-full"
style={{ background: bg.style }}
/>
{currentBackground === bg.id && (
<div className="absolute top-2 right-2 w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center">
<Check size={14} weight="bold" className="text-white" />
</div>
)}
<div className="absolute bottom-0 left-0 right-0 bg-black/50 backdrop-blur-sm px-2 py-1">
<span className="text-xs text-white">{bg.name}</span>
</div>
</button>
))}
</div>
) : (
<div className="flex flex-col items-center justify-center py-12">
<input
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleFileUpload}
className="hidden"
/>
{uploadedImage ? (
<div className="relative w-full max-w-md">
<img
src={uploadedImage}
alt="Uploaded background"
className="w-full h-48 object-cover rounded-lg"
/>
<button
onClick={() => fileInputRef.current?.click()}
className="mt-4 w-full px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg transition-colors"
>
Choose Different Image
</button>
</div>
) : (
<>
<div
onClick={() => fileInputRef.current?.click()}
className="w-32 h-32 border-2 border-dashed border-white/30 rounded-lg flex flex-col items-center justify-center cursor-pointer hover:border-white/50 transition-colors"
>
<Upload size={32} className="text-gray-400 mb-2" />
<span className="text-sm text-gray-400">Click to upload</span>
</div>
<p className="mt-4 text-sm text-gray-400">
Supported formats: JPG, PNG, WEBP, GIF
</p>
</>
)}
</div>
)}
</div>
{/* Footer */}
<div className="p-6 pt-0 flex gap-3 justify-end">
<button
onClick={onClose}
className="px-4 py-2 text-gray-300 hover:text-white transition-colors"
>
Cancel
</button>
<button
onClick={onClose}
className="px-4 py-2 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-lg font-medium hover:from-blue-600 hover:to-purple-600 transition-all"
>
Apply
</button>
</div>
</motion.div>
</>
)}
</AnimatePresence>
)
}