120 lines
5.7 KiB
TypeScript
120 lines
5.7 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Play, Lock, Activity, Zap } from 'lucide-react';
|
|
import { MOCK_SCENARIOS } from '../constants';
|
|
import { Scenario } from '../types';
|
|
|
|
interface LibraryProps {
|
|
onPlay: (scenario: Scenario) => void;
|
|
}
|
|
|
|
const Library: React.FC<LibraryProps> = ({ onPlay }) => {
|
|
const categories = ['全部', '职场', '邻家', '科幻', 'ASMR'];
|
|
const [activeCategory, setActiveCategory] = useState('全部');
|
|
|
|
const filtered = activeCategory === '全部'
|
|
? MOCK_SCENARIOS
|
|
: MOCK_SCENARIOS.filter(s => s.category === activeCategory || s.tags.includes(activeCategory));
|
|
|
|
const getIntensityColor = (intensity: string) => {
|
|
switch(intensity) {
|
|
case 'Low': return 'text-[#34D399]';
|
|
case 'Medium': return 'text-[#60A5FA]';
|
|
case 'High': return 'text-[#FBBF24]';
|
|
case 'Extreme': return 'text-[#F472B6]';
|
|
default: return 'text-white/50';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="pb-24 px-4 min-h-full">
|
|
{/* Filter Bar */}
|
|
<div className="sticky top-0 z-20 pt-2 pb-2 -mx-4 mb-4">
|
|
<div className="relative">
|
|
<div className="flex items-center px-6 gap-3 overflow-x-auto no-scrollbar pr-12">
|
|
{categories.map(cat => {
|
|
const isActive = activeCategory === cat;
|
|
return (
|
|
<button
|
|
key={cat}
|
|
onClick={() => setActiveCategory(cat)}
|
|
className={`relative px-4 py-1.5 rounded-full border transition-all duration-300 shrink-0 ${
|
|
isActive
|
|
? 'bg-gradient-to-r from-[#C084FC] to-[#F472B6] text-white font-bold border-transparent shadow-[0_0_15px_rgba(192,132,252,0.4)]'
|
|
: 'bg-white/5 text-white/70 border-white/10 hover:bg-white/10 backdrop-blur-md'
|
|
}`}
|
|
>
|
|
<span className="text-sm">{cat}</span>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
<div className="absolute top-0 right-0 h-full w-12 bg-gradient-to-l from-[#4c1d95]/0 to-transparent pointer-events-none"></div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* List Layout */}
|
|
<div className="flex flex-col gap-3">
|
|
{filtered.map(scenario => (
|
|
<div
|
|
key={scenario.id}
|
|
onClick={() => onPlay(scenario)}
|
|
className="group relative flex items-center p-3 rounded-2xl bg-[#4c1d95]/20 border border-white/10 overflow-hidden active:scale-[0.98] transition-all duration-300 backdrop-blur-md hover:border-[#C084FC]/40 hover:bg-[#4c1d95]/40 hover:shadow-[0_4px_20px_rgba(0,0,0,0.2)]"
|
|
>
|
|
{/* Left: Thumbnail */}
|
|
<div className="relative w-20 h-20 rounded-xl overflow-hidden shrink-0 shadow-lg border border-white/10">
|
|
<img src={scenario.cover} alt={scenario.title} className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500" />
|
|
{scenario.isLocked && (
|
|
<div className="absolute inset-0 bg-[#2e1065]/60 flex items-center justify-center backdrop-blur-[2px]">
|
|
<Lock size={16} className="text-white/90" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Center: Info */}
|
|
<div className="flex-1 ml-4 flex flex-col justify-center min-w-0">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<span className="text-[10px] text-white/90 font-mono uppercase tracking-wider bg-white/10 px-1.5 rounded border border-white/10 backdrop-blur-md">
|
|
{scenario.category}
|
|
</span>
|
|
<div className="flex items-center gap-1">
|
|
<Zap size={10} className={getIntensityColor(scenario.intensity)} fill="currentColor" />
|
|
<span className={`text-[9px] font-bold ${getIntensityColor(scenario.intensity)}`}>{scenario.intensity}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 className="text-sm font-bold text-white mb-1.5 truncate pr-2 group-hover:text-[#F472B6] transition-colors drop-shadow-sm">{scenario.title}</h3>
|
|
|
|
{/* Tags */}
|
|
<div className="flex flex-wrap gap-1.5">
|
|
{scenario.tags.slice(0, 3).map((tag, i) => (
|
|
<span key={i} className="text-[10px] text-[#E2E8F0] bg-white/5 px-1.5 py-0.5 rounded-sm border border-white/5">#{tag}</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Right: Action Button */}
|
|
<div className="shrink-0 mr-1">
|
|
<div className={`w-10 h-10 rounded-full flex items-center justify-center transition-all duration-300 ${
|
|
scenario.isLocked
|
|
? 'bg-white/5 text-white/30'
|
|
: 'bg-[#C084FC]/20 text-[#C084FC] group-hover:bg-gradient-to-r group-hover:from-[#C084FC] group-hover:to-[#F472B6] group-hover:text-white group-hover:shadow-[0_0_15px_rgba(192,132,252,0.5)]'
|
|
}`}>
|
|
{scenario.isLocked ? <Lock size={16} /> : <Play size={18} fill="currentColor" className="ml-0.5" />}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Empty State */}
|
|
{filtered.length === 0 && (
|
|
<div className="flex flex-col items-center justify-center py-12 opacity-50">
|
|
<Activity size={32} className="text-white mb-2" />
|
|
<p className="text-xs text-white">暂无相关剧本</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Library; |