first commit
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useRef } from 'react'
|
||||
import { Upload, Link as LinkIcon, Sparkles, X, Image as ImageIcon } from 'lucide-react'
|
||||
import { AppIcon } from './AppIcon'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface IconPickerProps {
|
||||
value: string
|
||||
onChange: (val: string) => void
|
||||
appName: string
|
||||
}
|
||||
|
||||
const PRESET_EMOJIS = [
|
||||
'📱', '🚀', '🛍️', '💳', '⚡', '🛡️', '🤖', '📦',
|
||||
'🎮', '📊', '🎧', '💬', '🌐', '🔒', '🍔', '🚗',
|
||||
'✈️', '📈', '🎨', '🎵', '🏥', '🏢', '⚙️', '🔑'
|
||||
]
|
||||
|
||||
export function IconPicker({ value, onChange, appName }: IconPickerProps) {
|
||||
const [activeTab, setActiveTab] = useState<'presets' | 'url' | 'upload'>('presets')
|
||||
const [urlInput, setUrlInput] = useState(value?.startsWith('http') ? value : '')
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
function handleEmojiSelect(emoji: string) {
|
||||
onChange(emoji)
|
||||
}
|
||||
|
||||
function handleUrlChange(url: string) {
|
||||
setUrlInput(url)
|
||||
onChange(url)
|
||||
}
|
||||
|
||||
function handleFileUpload(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
const file = e.target.files?.[0]
|
||||
if (!file) return
|
||||
|
||||
// Limit size to 1.5MB
|
||||
if (file.size > 1.5 * 1024 * 1024) {
|
||||
alert('İkon boyutu 1.5MB\'dan küçük olmalıdır.')
|
||||
return
|
||||
}
|
||||
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => {
|
||||
if (typeof reader.result === 'string') {
|
||||
onChange(reader.result)
|
||||
}
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
|
||||
function handleClear() {
|
||||
onChange('')
|
||||
setUrlInput('')
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block text-xs font-semibold text-slate-300 uppercase tracking-wider">
|
||||
Uygulama İkonu <span className="text-slate-500 font-normal lowercase">(isteğe bağlı)</span>
|
||||
</label>
|
||||
{value && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClear}
|
||||
className="text-[11px] text-rose-400 hover:text-rose-300 flex items-center gap-1 transition-colors"
|
||||
>
|
||||
<X className="w-3 h-3" />
|
||||
<span>İkonu Kaldır</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-2xl bg-slate-900/70 border border-slate-800 space-y-4">
|
||||
{/* Live Preview & Tabs Header */}
|
||||
<div className="flex items-center gap-4 pb-3 border-b border-slate-800/80">
|
||||
<div className="flex flex-col items-center gap-1.5">
|
||||
<AppIcon name={appName || 'A'} iconUrl={value} size="lg" className="ring-2 ring-emerald-500/20" />
|
||||
<span className="text-[10px] text-slate-500 font-medium">Önizleme</span>
|
||||
</div>
|
||||
|
||||
{/* Mode Switcher */}
|
||||
<div className="flex-1 flex gap-1 p-1 bg-slate-950/80 rounded-xl border border-slate-800">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab('presets')}
|
||||
className={cn(
|
||||
'flex-1 py-1.5 px-2 rounded-lg text-xs font-medium transition-all flex items-center justify-center gap-1.5',
|
||||
activeTab === 'presets' ? 'bg-slate-800 text-emerald-400 shadow-sm' : 'text-slate-400 hover:text-white'
|
||||
)}
|
||||
>
|
||||
<Sparkles className="w-3 h-3" />
|
||||
<span>Hazır İkonlar</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab('upload')}
|
||||
className={cn(
|
||||
'flex-1 py-1.5 px-2 rounded-lg text-xs font-medium transition-all flex items-center justify-center gap-1.5',
|
||||
activeTab === 'upload' ? 'bg-slate-800 text-emerald-400 shadow-sm' : 'text-slate-400 hover:text-white'
|
||||
)}
|
||||
>
|
||||
<Upload className="w-3 h-3" />
|
||||
<span>Görsel Yükle</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab('url')}
|
||||
className={cn(
|
||||
'flex-1 py-1.5 px-2 rounded-lg text-xs font-medium transition-all flex items-center justify-center gap-1.5',
|
||||
activeTab === 'url' ? 'bg-slate-800 text-emerald-400 shadow-sm' : 'text-slate-400 hover:text-white'
|
||||
)}
|
||||
>
|
||||
<LinkIcon className="w-3 h-3" />
|
||||
<span>URL</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tab 1: Presets */}
|
||||
{activeTab === 'presets' && (
|
||||
<div className="grid grid-cols-8 sm:grid-cols-12 gap-1.5 animate-fade-in">
|
||||
{PRESET_EMOJIS.map((emoji) => (
|
||||
<button
|
||||
key={emoji}
|
||||
type="button"
|
||||
onClick={() => handleEmojiSelect(emoji)}
|
||||
className={cn(
|
||||
'h-9 rounded-xl flex items-center justify-center text-lg transition-all hover:scale-110 hover:bg-slate-800',
|
||||
value === emoji
|
||||
? 'bg-emerald-500/20 ring-2 ring-emerald-400'
|
||||
: 'bg-slate-900/60 border border-slate-800/60'
|
||||
)}
|
||||
>
|
||||
{emoji}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tab 2: Upload */}
|
||||
{activeTab === 'upload' && (
|
||||
<div className="space-y-2 animate-fade-in">
|
||||
<input
|
||||
type="file"
|
||||
ref={fileInputRef}
|
||||
onChange={handleFileUpload}
|
||||
accept="image/png, image/jpeg, image/webp, image/svg+xml"
|
||||
className="hidden"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
className="w-full py-4 border-2 border-dashed border-slate-700/80 hover:border-emerald-500/50 rounded-xl bg-slate-950/40 hover:bg-slate-900/40 flex flex-col items-center justify-center gap-1.5 transition-colors cursor-pointer"
|
||||
>
|
||||
<Upload className="w-5 h-5 text-emerald-400" />
|
||||
<span className="text-xs font-semibold text-slate-300">
|
||||
Görsel Seçin veya Sürükleyin
|
||||
</span>
|
||||
<span className="text-[10px] text-slate-500">
|
||||
PNG, JPG, WebP veya SVG (Önerilen: 256x256)
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tab 3: URL */}
|
||||
{activeTab === 'url' && (
|
||||
<div className="space-y-2 animate-fade-in">
|
||||
<div className="flex items-center glass-input rounded-xl overflow-hidden">
|
||||
<span className="px-3 text-slate-500 text-xs border-r border-slate-800 py-2.5">
|
||||
https://
|
||||
</span>
|
||||
<input
|
||||
type="url"
|
||||
value={urlInput}
|
||||
onChange={(e) => handleUrlChange(e.target.value)}
|
||||
placeholder="ornek.com/logo.png"
|
||||
className="flex-1 bg-transparent px-3 py-2 text-white text-xs font-mono placeholder-slate-500 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-[11px] text-slate-500">
|
||||
CDN veya harici doğrudan görsel linki yapıştırın.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user