mirror of
https://github.com/yusufipk/distromatch.git
synced 2026-09-11 18:56:06 +00:00
Enhance the application's visual appeal with refined animations and effects
Update UI components including ActionButtons, CardStack, DynamicBackground, EndOfDeck, Header, and SwipeCard to incorporate more visually engaging animations, gradients, and effects, while also adjusting button positioning for improved user experience. Replit-Commit-Author: Agent Replit-Commit-Session-Id: e522d728-b28a-437f-b576-83935afe7ea0 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 0e0edafc-30e9-4e23-88d0-1ecbc1922137 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/55e426a8-5a2b-421b-8bd4-30481ae0063b/e522d728-b28a-437f-b576-83935afe7ea0/m8j6ju0 Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { Heart, X, RotateCcw } from 'lucide-react';
|
import { motion } from 'framer-motion';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Heart, X, RotateCcw, Zap } from 'lucide-react';
|
||||||
|
|
||||||
interface ActionButtonsProps {
|
interface ActionButtonsProps {
|
||||||
onLike: () => void;
|
onLike: () => void;
|
||||||
@@ -17,40 +17,68 @@ export default function ActionButtons({
|
|||||||
disabled = false,
|
disabled = false,
|
||||||
}: ActionButtonsProps) {
|
}: ActionButtonsProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center gap-6">
|
<div className="flex items-center justify-center gap-8">
|
||||||
<Button
|
<motion.button
|
||||||
size="icon"
|
className="group relative flex h-20 w-20 items-center justify-center rounded-full"
|
||||||
variant="outline"
|
|
||||||
className="h-16 w-16 rounded-full border-2 border-red-500/50 bg-transparent transition-all duration-200 hover:scale-110 hover:border-red-500 hover:bg-red-500/10"
|
|
||||||
onClick={onDislike}
|
onClick={onDislike}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
whileHover={{ scale: 1.1 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
data-testid="button-dislike"
|
data-testid="button-dislike"
|
||||||
>
|
>
|
||||||
<X className="h-8 w-8 text-red-500" />
|
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-red-500/20 to-rose-600/20 backdrop-blur-xl border border-red-500/30" />
|
||||||
</Button>
|
<div className="absolute inset-0 rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||||
|
style={{ boxShadow: '0 0 40px rgba(239, 68, 68, 0.4), inset 0 0 20px rgba(239, 68, 68, 0.1)' }}
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-1 rounded-full bg-gradient-to-br from-red-500 to-rose-600 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||||
|
<X className="relative h-10 w-10 text-red-400 group-hover:text-white transition-colors duration-300" strokeWidth={3} />
|
||||||
|
</motion.button>
|
||||||
|
|
||||||
{showReset && onReset && (
|
{showReset && onReset && (
|
||||||
<Button
|
<motion.button
|
||||||
size="icon"
|
className="group relative flex h-14 w-14 items-center justify-center rounded-full"
|
||||||
variant="outline"
|
|
||||||
className="h-12 w-12 rounded-full border-2 border-yellow-500/50 bg-transparent transition-all duration-200 hover:scale-110 hover:border-yellow-500 hover:bg-yellow-500/10"
|
|
||||||
onClick={onReset}
|
onClick={onReset}
|
||||||
|
whileHover={{ scale: 1.1, rotate: -180 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
|
transition={{ type: 'spring', stiffness: 300 }}
|
||||||
data-testid="button-reset"
|
data-testid="button-reset"
|
||||||
>
|
>
|
||||||
<RotateCcw className="h-5 w-5 text-yellow-500" />
|
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-amber-500/20 to-orange-600/20 backdrop-blur-xl border border-amber-500/30" />
|
||||||
</Button>
|
<div className="absolute inset-0 rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||||
|
style={{ boxShadow: '0 0 30px rgba(245, 158, 11, 0.4)' }}
|
||||||
|
/>
|
||||||
|
<RotateCcw className="relative h-6 w-6 text-amber-400 group-hover:text-amber-300 transition-colors duration-300" strokeWidth={2.5} />
|
||||||
|
</motion.button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<motion.button
|
||||||
size="icon"
|
className="group relative flex h-20 w-20 items-center justify-center rounded-full"
|
||||||
variant="outline"
|
|
||||||
className="h-16 w-16 rounded-full border-2 border-green-500/50 bg-transparent transition-all duration-200 hover:scale-110 hover:border-green-500 hover:bg-green-500/10"
|
|
||||||
onClick={onLike}
|
onClick={onLike}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
whileHover={{ scale: 1.1 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
data-testid="button-like"
|
data-testid="button-like"
|
||||||
>
|
>
|
||||||
<Heart className="h-8 w-8 text-green-500" fill="currentColor" />
|
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-emerald-500/20 to-green-600/20 backdrop-blur-xl border border-emerald-500/30" />
|
||||||
</Button>
|
<div className="absolute inset-0 rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||||
|
style={{ boxShadow: '0 0 40px rgba(34, 197, 94, 0.4), inset 0 0 20px rgba(34, 197, 94, 0.1)' }}
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-1 rounded-full bg-gradient-to-br from-emerald-500 to-green-600 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||||
|
<Heart className="relative h-10 w-10 text-emerald-400 group-hover:text-white transition-colors duration-300" fill="currentColor" strokeWidth={2} />
|
||||||
|
</motion.button>
|
||||||
|
|
||||||
|
<motion.button
|
||||||
|
className="group relative flex h-14 w-14 items-center justify-center rounded-full"
|
||||||
|
whileHover={{ scale: 1.1 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
|
data-testid="button-superlike"
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-cyan-500/20 to-blue-600/20 backdrop-blur-xl border border-cyan-500/30" />
|
||||||
|
<div className="absolute inset-0 rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||||
|
style={{ boxShadow: '0 0 30px rgba(6, 182, 212, 0.4)' }}
|
||||||
|
/>
|
||||||
|
<Zap className="relative h-6 w-6 text-cyan-400 group-hover:text-cyan-300 transition-colors duration-300" fill="currentColor" />
|
||||||
|
</motion.button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export default function CardStack({ cards, onBackgroundChange }: CardStackProps)
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
|
className="mt-4"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: 0.3 }}
|
transition={{ delay: 0.3 }}
|
||||||
@@ -94,8 +95,21 @@ export default function CardStack({ cards, onBackgroundChange }: CardStackProps)
|
|||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="text-center text-sm text-white/60">
|
<div className="mt-6 flex items-center gap-3 text-center">
|
||||||
{currentIndex + 1} / {cards.length}
|
<div className="flex gap-1.5">
|
||||||
|
{cards.map((_, idx) => (
|
||||||
|
<div
|
||||||
|
key={idx}
|
||||||
|
className={`h-1.5 w-8 rounded-full transition-all duration-300 ${
|
||||||
|
idx < currentIndex
|
||||||
|
? 'bg-gradient-to-r from-purple-500 to-pink-500'
|
||||||
|
: idx === currentIndex
|
||||||
|
? 'bg-white'
|
||||||
|
: 'bg-white/20'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,14 +10,62 @@ export default function DynamicBackground({ colors }: DynamicBackgroundProps) {
|
|||||||
: ['#6B46C1', '#4F46E5', '#2563EB'];
|
: ['#6B46C1', '#4F46E5', '#2563EB'];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<div className="fixed inset-0 -z-10 overflow-hidden">
|
||||||
className="fixed inset-0 -z-10"
|
<div className="absolute inset-0 bg-[#0a0a0f]" />
|
||||||
animate={{
|
|
||||||
background: `radial-gradient(ellipse at center, ${gradientColors[0]}30 0%, ${gradientColors[1]}20 50%, ${gradientColors[2] || gradientColors[0]}10 100%)`,
|
<motion.div
|
||||||
}}
|
className="absolute inset-0"
|
||||||
transition={{ duration: 0.5, ease: 'easeInOut' }}
|
animate={{
|
||||||
>
|
background: `radial-gradient(ellipse 80% 50% at 50% 50%, ${gradientColors[0]}25 0%, transparent 50%)`,
|
||||||
<div className="absolute inset-0 bg-background/80 backdrop-blur-3xl" />
|
}}
|
||||||
</motion.div>
|
transition={{ duration: 0.8, ease: 'easeInOut' }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
className="absolute -top-1/2 -left-1/2 h-full w-full"
|
||||||
|
animate={{
|
||||||
|
background: `radial-gradient(circle at center, ${gradientColors[1]}15 0%, transparent 40%)`,
|
||||||
|
}}
|
||||||
|
transition={{ duration: 0.8, ease: 'easeInOut' }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
className="absolute -bottom-1/2 -right-1/2 h-full w-full"
|
||||||
|
animate={{
|
||||||
|
background: `radial-gradient(circle at center, ${gradientColors[2] || gradientColors[0]}15 0%, transparent 40%)`,
|
||||||
|
}}
|
||||||
|
transition={{ duration: 0.8, ease: 'easeInOut' }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1IiBoZWlnaHQ9IjUiPgo8cmVjdCB3aWR0aD0iNSIgaGVpZ2h0PSI1IiBmaWxsPSIjMDAwIj48L3JlY3Q+CjxyZWN0IHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiMxMTEiPjwvcmVjdD4KPC9zdmc+')] opacity-30" />
|
||||||
|
|
||||||
|
<div className="absolute top-20 left-20 h-96 w-96 rounded-full bg-purple-500/10 blur-3xl" />
|
||||||
|
<div className="absolute bottom-20 right-20 h-96 w-96 rounded-full bg-cyan-500/10 blur-3xl" />
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
className="absolute top-1/4 right-1/4 h-2 w-2 rounded-full bg-white/30"
|
||||||
|
animate={{
|
||||||
|
y: [0, -20, 0],
|
||||||
|
opacity: [0.3, 0.8, 0.3],
|
||||||
|
}}
|
||||||
|
transition={{ duration: 3, repeat: Infinity }}
|
||||||
|
/>
|
||||||
|
<motion.div
|
||||||
|
className="absolute top-1/3 left-1/4 h-1 w-1 rounded-full bg-purple-400/50"
|
||||||
|
animate={{
|
||||||
|
y: [0, -15, 0],
|
||||||
|
opacity: [0.5, 1, 0.5],
|
||||||
|
}}
|
||||||
|
transition={{ duration: 2.5, repeat: Infinity, delay: 0.5 }}
|
||||||
|
/>
|
||||||
|
<motion.div
|
||||||
|
className="absolute bottom-1/3 right-1/3 h-1.5 w-1.5 rounded-full bg-cyan-400/40"
|
||||||
|
animate={{
|
||||||
|
y: [0, -25, 0],
|
||||||
|
opacity: [0.4, 0.9, 0.4],
|
||||||
|
}}
|
||||||
|
transition={{ duration: 4, repeat: Infinity, delay: 1 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { PartyPopper, RotateCcw, Download } from 'lucide-react';
|
import { PartyPopper, RotateCcw, Download, Trophy, Star, ExternalLink } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import type { DistroCard } from '@/data/distros';
|
import type { DistroCard } from '@/data/distros';
|
||||||
|
|
||||||
interface EndOfDeckProps {
|
interface EndOfDeckProps {
|
||||||
@@ -11,74 +10,125 @@ interface EndOfDeckProps {
|
|||||||
export default function EndOfDeck({ likedCards, onReset }: EndOfDeckProps) {
|
export default function EndOfDeck({ likedCards, onReset }: EndOfDeckProps) {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
className="flex flex-col items-center gap-8 text-center"
|
className="flex flex-col items-center gap-10 text-center px-4"
|
||||||
initial={{ opacity: 0, scale: 0.8 }}
|
initial={{ opacity: 0, scale: 0.8 }}
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
transition={{ type: 'spring', stiffness: 200, damping: 20 }}
|
transition={{ type: 'spring', stiffness: 200, damping: 20 }}
|
||||||
>
|
>
|
||||||
<motion.div
|
<div className="relative">
|
||||||
animate={{
|
<motion.div
|
||||||
rotate: [0, -10, 10, -10, 10, 0],
|
className="absolute inset-0 rounded-full blur-3xl"
|
||||||
scale: [1, 1.1, 1],
|
style={{ background: 'radial-gradient(circle, rgba(234, 179, 8, 0.3) 0%, transparent 70%)' }}
|
||||||
}}
|
animate={{ scale: [1, 1.2, 1] }}
|
||||||
transition={{ duration: 0.6, delay: 0.2 }}
|
transition={{ duration: 2, repeat: Infinity }}
|
||||||
>
|
/>
|
||||||
<PartyPopper className="h-24 w-24 text-yellow-400" />
|
<motion.div
|
||||||
</motion.div>
|
animate={{
|
||||||
|
rotate: [0, -10, 10, -10, 10, 0],
|
||||||
|
scale: [1, 1.1, 1],
|
||||||
|
}}
|
||||||
|
transition={{ duration: 0.6, delay: 0.2 }}
|
||||||
|
className="relative"
|
||||||
|
>
|
||||||
|
<div className="rounded-full bg-gradient-to-br from-yellow-400 to-orange-500 p-6">
|
||||||
|
<Trophy className="h-16 w-16 text-white" />
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
className="absolute -top-2 -right-2"
|
||||||
|
animate={{ rotate: [0, 360] }}
|
||||||
|
transition={{ duration: 4, repeat: Infinity, ease: 'linear' }}
|
||||||
|
>
|
||||||
|
<Star className="h-8 w-8 text-yellow-300" fill="currentColor" />
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2 className="font-mono text-3xl font-bold text-white" data-testid="text-end-title">
|
<motion.h2
|
||||||
Öneriler Hazır!
|
className="font-mono text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-yellow-200 via-white to-yellow-200"
|
||||||
</h2>
|
data-testid="text-end-title"
|
||||||
<p className="mt-2 text-lg text-white/70">
|
animate={{ backgroundPosition: ['0%', '100%', '0%'] }}
|
||||||
|
transition={{ duration: 3, repeat: Infinity }}
|
||||||
|
style={{ backgroundSize: '200%' }}
|
||||||
|
>
|
||||||
|
Eşleşme Tamamlandı!
|
||||||
|
</motion.h2>
|
||||||
|
<p className="mt-3 text-xl text-white/70">
|
||||||
{likedCards.length > 0
|
{likedCards.length > 0
|
||||||
? `${likedCards.length} dağıtım beğendin`
|
? `${likedCards.length} dağıtım ile eşleştin`
|
||||||
: 'Hiçbir dağıtım beğenmedin'}
|
: 'Henüz bir eşleşme yok'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{likedCards.length > 0 && (
|
{likedCards.length > 0 && (
|
||||||
<div className="flex flex-wrap justify-center gap-4">
|
<div className="flex flex-wrap justify-center gap-6 max-w-4xl">
|
||||||
{likedCards.map((card) => (
|
{likedCards.map((card, idx) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={card.id}
|
key={card.id}
|
||||||
className="overflow-hidden rounded-lg bg-white/10 backdrop-blur-sm"
|
className="group relative overflow-hidden rounded-2xl"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20, scale: 0.9 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||||
|
transition={{ delay: idx * 0.1 }}
|
||||||
|
whileHover={{ scale: 1.05, y: -5 }}
|
||||||
data-testid={`result-card-${card.id}`}
|
data-testid={`result-card-${card.id}`}
|
||||||
>
|
>
|
||||||
<img
|
<div className="absolute inset-0 bg-gradient-to-br from-purple-500/20 to-pink-500/20 backdrop-blur-xl" />
|
||||||
src={card.screenshot}
|
<div className="absolute inset-0 rounded-2xl border border-white/10" />
|
||||||
alt={card.distroName}
|
|
||||||
className="h-24 w-40 object-cover"
|
<div className="relative">
|
||||||
/>
|
<img
|
||||||
<div className="p-3">
|
src={card.screenshot}
|
||||||
<p className="font-semibold text-white">{card.distroName}</p>
|
alt={card.distroName}
|
||||||
<p className="text-xs text-white/60">{card.desktopEnvironment}</p>
|
className="h-32 w-56 object-cover"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent" />
|
||||||
|
|
||||||
|
<div className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
|
<div className="rounded-full bg-white/20 backdrop-blur-md p-2">
|
||||||
|
<ExternalLink className="h-4 w-4 text-white" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative p-4 bg-gradient-to-r from-gray-900/90 to-gray-800/90">
|
||||||
|
<p className="font-bold text-lg text-white">{card.distroName}</p>
|
||||||
|
<p className="text-sm text-white/60">{card.desktopEnvironment}</p>
|
||||||
|
<div className="mt-2 flex gap-1">
|
||||||
|
{[...Array(5)].map((_, i) => (
|
||||||
|
<Star key={i} className="h-3 w-3 text-yellow-400" fill={i < 4 ? 'currentColor' : 'none'} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-6">
|
||||||
<Button
|
<motion.button
|
||||||
variant="outline"
|
className="group relative flex items-center gap-3 rounded-full px-8 py-4 overflow-hidden"
|
||||||
className="gap-2 border-white/20 bg-white/10 text-white hover:bg-white/20"
|
|
||||||
onClick={onReset}
|
onClick={onReset}
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
data-testid="button-restart"
|
data-testid="button-restart"
|
||||||
>
|
>
|
||||||
<RotateCcw className="h-4 w-4" />
|
<div className="absolute inset-0 bg-white/10 backdrop-blur-xl border border-white/20 rounded-full" />
|
||||||
Tekrar Başla
|
<RotateCcw className="relative h-5 w-5 text-white group-hover:rotate-180 transition-transform duration-500" />
|
||||||
</Button>
|
<span className="relative font-medium text-white">Tekrar Başla</span>
|
||||||
|
</motion.button>
|
||||||
|
|
||||||
{likedCards.length > 0 && (
|
{likedCards.length > 0 && (
|
||||||
<Button
|
<motion.button
|
||||||
className="gap-2 bg-primary text-primary-foreground"
|
className="group relative flex items-center gap-3 rounded-full px-8 py-4 overflow-hidden"
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
data-testid="button-download"
|
data-testid="button-download"
|
||||||
>
|
>
|
||||||
<Download className="h-4 w-4" />
|
<div className="absolute inset-0 bg-gradient-to-r from-purple-600 to-pink-600 rounded-full" />
|
||||||
İndir
|
<div className="absolute inset-0 bg-gradient-to-r from-purple-500 to-pink-500 rounded-full opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||||
</Button>
|
<Download className="relative h-5 w-5 text-white" />
|
||||||
|
<span className="relative font-medium text-white">Karşılaştır</span>
|
||||||
|
</motion.button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -1,21 +1,40 @@
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Terminal } from 'lucide-react';
|
import { Terminal, Sparkles } from 'lucide-react';
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
return (
|
return (
|
||||||
<motion.header
|
<motion.header
|
||||||
className="fixed left-0 right-0 top-0 z-50 flex items-center justify-center p-6"
|
className="fixed left-0 right-0 top-0 z-50 flex items-center justify-between px-8 py-6"
|
||||||
initial={{ opacity: 0, y: -20 }}
|
initial={{ opacity: 0, y: -20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: 0.1 }}
|
transition={{ delay: 0.1 }}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-4">
|
||||||
<div className="rounded-lg bg-primary p-2">
|
<motion.div
|
||||||
<Terminal className="h-6 w-6 text-primary-foreground" />
|
className="relative rounded-xl bg-gradient-to-br from-purple-500 to-pink-600 p-3"
|
||||||
|
whileHover={{ scale: 1.05, rotate: 5 }}
|
||||||
|
style={{ boxShadow: '0 0 30px rgba(168, 85, 247, 0.4)' }}
|
||||||
|
>
|
||||||
|
<Terminal className="h-7 w-7 text-white" />
|
||||||
|
<motion.div
|
||||||
|
className="absolute -top-1 -right-1"
|
||||||
|
animate={{ scale: [1, 1.2, 1], rotate: [0, 10, -10, 0] }}
|
||||||
|
transition={{ duration: 2, repeat: Infinity }}
|
||||||
|
>
|
||||||
|
<Sparkles className="h-4 w-4 text-yellow-300" />
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
<div>
|
||||||
|
<h1 className="font-mono text-2xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-white via-purple-200 to-pink-200" data-testid="text-logo">
|
||||||
|
DistroMatch
|
||||||
|
</h1>
|
||||||
|
<p className="text-xs text-white/50">Sana uygun Linux'u bul</p>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="font-mono text-2xl font-bold text-white" data-testid="text-logo">
|
</div>
|
||||||
DistroMatch
|
|
||||||
</h1>
|
<div className="flex items-center gap-2 rounded-full bg-white/5 backdrop-blur-xl border border-white/10 px-4 py-2">
|
||||||
|
<div className="h-2 w-2 rounded-full bg-green-400 animate-pulse" />
|
||||||
|
<span className="text-sm text-white/70">6 Dağıtım</span>
|
||||||
</div>
|
</div>
|
||||||
</motion.header>
|
</motion.header>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { motion, useMotionValue, useTransform, PanInfo } from 'framer-motion';
|
import { motion, useMotionValue, useTransform, PanInfo } from 'framer-motion';
|
||||||
import { Heart, X } from 'lucide-react';
|
import { Heart, X, Sparkles } from 'lucide-react';
|
||||||
import type { DistroCard } from '@/data/distros';
|
import type { DistroCard } from '@/data/distros';
|
||||||
|
|
||||||
interface SwipeCardProps {
|
interface SwipeCardProps {
|
||||||
@@ -23,6 +23,7 @@ export default function SwipeCard({
|
|||||||
const rotate = useTransform(x, [-300, 0, 300], [-15, 0, 15]);
|
const rotate = useTransform(x, [-300, 0, 300], [-15, 0, 15]);
|
||||||
const likeOpacity = useTransform(x, [0, 150], [0, 1]);
|
const likeOpacity = useTransform(x, [0, 150], [0, 1]);
|
||||||
const nopeOpacity = useTransform(x, [-150, 0], [1, 0]);
|
const nopeOpacity = useTransform(x, [-150, 0], [1, 0]);
|
||||||
|
const glowIntensity = useTransform(x, [-200, 0, 200], [1, 0, 1]);
|
||||||
|
|
||||||
const handleDragEnd = (_: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {
|
const handleDragEnd = (_: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {
|
||||||
const threshold = 150;
|
const threshold = 150;
|
||||||
@@ -64,63 +65,111 @@ export default function SwipeCard({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="relative w-[900px] max-w-[90vw] overflow-hidden rounded-2xl bg-black/90 shadow-2xl"
|
className="group relative w-[900px] max-w-[90vw] overflow-hidden rounded-3xl shadow-2xl"
|
||||||
data-testid={`card-distro-${card.id}`}
|
data-testid={`card-distro-${card.id}`}
|
||||||
|
style={{
|
||||||
|
boxShadow: `0 25px 50px -12px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.1)`,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="relative aspect-video">
|
<div className="absolute inset-0 rounded-3xl bg-gradient-to-b from-white/10 via-transparent to-transparent pointer-events-none z-10" />
|
||||||
<img
|
|
||||||
src={card.screenshot}
|
|
||||||
alt={`${card.distroName} desktop`}
|
|
||||||
className="h-full w-full object-cover"
|
|
||||||
draggable={false}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{isTop && (
|
<div className="absolute -inset-1 rounded-3xl bg-gradient-to-r from-purple-500/20 via-pink-500/20 to-cyan-500/20 blur-xl opacity-50 group-hover:opacity-75 transition-opacity duration-500" />
|
||||||
<>
|
|
||||||
<motion.div
|
|
||||||
className="absolute inset-0 flex items-center justify-center bg-green-500/30"
|
|
||||||
style={{ opacity: likeOpacity }}
|
|
||||||
>
|
|
||||||
<div className="rounded-full border-4 border-white bg-green-500 p-6">
|
|
||||||
<Heart className="h-16 w-16 text-white" fill="white" />
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
|
|
||||||
<motion.div
|
<div className="relative bg-gradient-to-b from-gray-900 to-black rounded-3xl overflow-hidden">
|
||||||
className="absolute inset-0 flex items-center justify-center bg-red-500/30"
|
<div className="relative aspect-video overflow-hidden">
|
||||||
style={{ opacity: nopeOpacity }}
|
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-black/20 z-10 pointer-events-none" />
|
||||||
>
|
|
||||||
<div className="rounded-full border-4 border-white bg-red-500 p-6">
|
|
||||||
<X className="h-16 w-16 text-white" />
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-black/80 px-8 py-6 backdrop-blur-md">
|
<img
|
||||||
<h2
|
src={card.screenshot}
|
||||||
className="font-mono text-2xl font-bold text-white"
|
alt={`${card.distroName} desktop`}
|
||||||
data-testid={`text-title-${card.id}`}
|
className="h-full w-full object-cover transform group-hover:scale-105 transition-transform duration-700"
|
||||||
>
|
draggable={false}
|
||||||
{card.wittyTitle}
|
/>
|
||||||
</h2>
|
|
||||||
<div className="mt-2 flex flex-wrap items-center gap-3">
|
<div className="absolute top-4 right-4 z-20">
|
||||||
<span className="text-lg font-semibold text-white/90">
|
<div className="flex items-center gap-2 rounded-full bg-black/50 backdrop-blur-md px-4 py-2 border border-white/10">
|
||||||
{card.distroName}
|
<Sparkles className="h-4 w-4 text-yellow-400" />
|
||||||
</span>
|
<span className="text-sm font-medium text-white">{card.desktopEnvironment}</span>
|
||||||
<span className="text-sm text-white/60">
|
</div>
|
||||||
{card.desktopEnvironment}
|
</div>
|
||||||
</span>
|
|
||||||
<div className="flex gap-2">
|
{isTop && (
|
||||||
{card.tags.slice(0, 3).map((tag) => (
|
<>
|
||||||
<span
|
<motion.div
|
||||||
key={tag}
|
className="absolute inset-0 flex items-center justify-center z-30"
|
||||||
className="rounded-full bg-white/10 px-3 py-1 text-xs text-white/70"
|
style={{ opacity: likeOpacity }}
|
||||||
>
|
>
|
||||||
{tag}
|
<div className="absolute inset-0 bg-gradient-to-r from-green-500/40 to-emerald-500/40" />
|
||||||
</span>
|
<motion.div
|
||||||
))}
|
className="relative rounded-full border-4 border-white bg-gradient-to-br from-green-400 to-emerald-600 p-8 shadow-2xl"
|
||||||
|
style={{
|
||||||
|
boxShadow: '0 0 60px rgba(34, 197, 94, 0.6), 0 0 120px rgba(34, 197, 94, 0.3)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Heart className="h-20 w-20 text-white drop-shadow-lg" fill="white" />
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
className="absolute inset-0 flex items-center justify-center z-30"
|
||||||
|
style={{ opacity: nopeOpacity }}
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-red-500/40 to-rose-500/40" />
|
||||||
|
<motion.div
|
||||||
|
className="relative rounded-full border-4 border-white bg-gradient-to-br from-red-400 to-rose-600 p-8 shadow-2xl"
|
||||||
|
style={{
|
||||||
|
boxShadow: '0 0 60px rgba(239, 68, 68, 0.6), 0 0 120px rgba(239, 68, 68, 0.3)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<X className="h-20 w-20 text-white drop-shadow-lg" />
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
className="absolute inset-0 pointer-events-none z-20"
|
||||||
|
style={{
|
||||||
|
opacity: glowIntensity,
|
||||||
|
background: x.get() > 0
|
||||||
|
? 'radial-gradient(circle at center, rgba(34, 197, 94, 0.2) 0%, transparent 70%)'
|
||||||
|
: 'radial-gradient(circle at center, rgba(239, 68, 68, 0.2) 0%, transparent 70%)'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative bg-gradient-to-r from-gray-900 via-gray-800 to-gray-900 px-8 py-6 border-t border-white/5">
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-purple-500/5 via-transparent to-cyan-500/5" />
|
||||||
|
|
||||||
|
<div className="relative flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h2
|
||||||
|
className="font-mono text-2xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-white via-purple-200 to-white"
|
||||||
|
data-testid={`text-title-${card.id}`}
|
||||||
|
>
|
||||||
|
"{card.wittyTitle}"
|
||||||
|
</h2>
|
||||||
|
<div className="mt-2 flex items-center gap-3">
|
||||||
|
<span className="text-xl font-bold text-white">
|
||||||
|
{card.distroName}
|
||||||
|
</span>
|
||||||
|
<div className="h-1 w-1 rounded-full bg-white/40" />
|
||||||
|
<span className="text-sm text-white/50">
|
||||||
|
Linux Distribution
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{card.tags.slice(0, 3).map((tag) => (
|
||||||
|
<span
|
||||||
|
key={tag}
|
||||||
|
className="rounded-full bg-gradient-to-r from-purple-500/20 to-pink-500/20 border border-white/10 px-4 py-1.5 text-xs font-medium text-white/80 backdrop-blur-sm"
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user