'init'
This commit is contained in:
83
app/components/CityCard.tsx
Normal file
83
app/components/CityCard.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
"use client";
|
||||
|
||||
import { memo, useState } from "react";
|
||||
import { City } from "../data/cities";
|
||||
|
||||
interface CityCardProps {
|
||||
city: City;
|
||||
rank: number;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
function CityCardComponent({ city, rank, onClick }: CityCardProps) {
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={onClick}
|
||||
onKeyDown={(e) => e.key === "Enter" && onClick?.()}
|
||||
className="city-card cursor-pointer relative overflow-hidden rounded-xl min-h-[200px]"
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${city.gradientFrom}, ${city.gradientTo})`,
|
||||
}}
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
>
|
||||
<span className="absolute bottom-8 right-2 text-[70px] sm:text-[90px] opacity-[0.12] select-none pointer-events-none leading-none">
|
||||
{city.icon}
|
||||
</span>
|
||||
|
||||
<div
|
||||
className={`absolute inset-0 z-10 flex flex-col justify-between p-3 sm:p-4 transition-opacity duration-300 ${hovered ? "opacity-0" : "opacity-100"}`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<span className="text-white/70 text-base sm:text-xl font-bold">{rank}</span>
|
||||
<span className="text-[10px] sm:text-xs bg-white/20 backdrop-blur-sm rounded-full px-1.5 sm:px-2 py-0.5 sm:py-1 text-white">
|
||||
📡 {city.internetSpeed}Mbps
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="text-center -mt-2">
|
||||
<h3 className="text-xl sm:text-[28px] font-bold text-white drop-shadow-md tracking-wide">
|
||||
{city.name}
|
||||
</h3>
|
||||
<p className="text-xs sm:text-sm text-white/60 mt-0.5">
|
||||
{city.countryEmoji} {city.country}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-1 mb-1.5">
|
||||
<span className="text-[9px] sm:text-[11px] bg-white/15 backdrop-blur-sm rounded-full px-1.5 py-0.5 text-white/80">
|
||||
👥 {city.nomadsNow} 人在此
|
||||
</span>
|
||||
<span className="text-[9px] sm:text-[11px] bg-white/15 backdrop-blur-sm rounded-full px-1.5 py-0.5 text-white/80">
|
||||
🌡 {city.temperature}°C
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-end justify-between text-white text-xs sm:text-sm">
|
||||
<span className="text-white/80 text-[10px] sm:text-xs line-clamp-1 max-w-[60%]">
|
||||
{city.description}
|
||||
</span>
|
||||
<span className="font-semibold shrink-0">
|
||||
¥{city.costPerMonth.toLocaleString()}/月
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`absolute inset-0 z-20 bg-[#2a2a2a]/95 hidden sm:flex flex-col justify-center p-4 sm:p-5 transition-opacity duration-300 ${hovered ? "opacity-100" : "opacity-0 pointer-events-none"}`}
|
||||
>
|
||||
<div className="text-center text-white">
|
||||
<p className="text-lg font-bold">{city.name}</p>
|
||||
<p className="text-sm text-white/70 mt-1">{city.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(CityCardComponent);
|
||||
Reference in New Issue
Block a user