import React, { useState, useEffect, useCallback } from 'react';
import {
Play, Pause, SkipForward, SkipBack, Volume2, Heart, Settings, X, Music, Maximize, MoreVertical
} from 'lucide-react';
// Компонент полностью автономен для работы в 21st
function PlayerModeView({
// Значения по умолчанию для демо
settings = {
primaryColor: '#b3b3b3',
backgroundColor: '#0a0a0a',
sidebarColor: '#111111',
cardColor: '#181818',
pmvBgMode: 'normal',
pmvBgBlur: 64,
pmvBgBrightness: 100,
enableVisualizer: true,
},
currentTrack = {
id: 1,
title: 'брудское',
artists: 'Kosnys',
cover: 'https://cdn.21st.dev/assets/mirror/98/987dd041c7c88ac71ad08593f5c234aff70ed0aedf7a208530d6739c83aba623.jpg'
},
isPlaying = false,
volume = 0.7,
setVolume = () => {},
onPlayPause = () => {},
onNext = () => {},
onPrev = () => {},
isFavorite = () => false,
onToggleFavorite = () => {},
onClose = () => {},
}) {
const [panelOpen, setPanelOpen] = useState(false);
const [entered, setEntered] = useState(false);
useEffect(() => {
const raf = requestAnimationFrame(() => setEntered(true));
return () => cancelAnimationFrame(raf);
}, []);
const handleClose = useCallback(() => {
onClose?.();
}, [onClose]);
const accent = settings.primaryColor;
const cover = currentTrack?.cover || null;
// Эмуляция фона
const bgMode = settings.pmvBgMode;
const bgBlur = settings.pmvBgBlur;
const bgBrightness = settings.pmvBgBrightness;
const bgImage = cover;
const bgFilter = `blur(${bgBlur}px) brightness(${bgBrightness}%)`;
return (
<>
<style>{`
.pmv-container {
position: relative;
width: 100%;
height: 100vh;
overflow: hidden;
background: ${settings.backgroundColor};
color: #fff;
font-family: system-ui, -apple-system, sans-serif;
perspective: 2000px;
}
.pmv-bg {
position: absolute;
inset: 0;
background-image: url(${bgImage});
background-size: cover;
background-position: center;
filter: ${bgFilter};
opacity: 0.25;
transition: all 0.5s ease;
}
.pmv-bg-overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.4);
}
/* Панель настроек */
.pmv-side {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 380px;
background: ${settings.sidebarColor};
border-right: 1px solid rgba(255,255,255,0.1);
z-index: 50;
display: flex;
flex-direction: column;
transform: translateX(-100%);
transition: transform 0.5s cubic-bezier(0.25, 1, 0.5, 1), opacity 0.5s;
opacity: 0;
}
.pmv-container.side-open .pmv-side {
transform: translateX(0);
opacity: 1;
box-shadow: 20px 0 50px rgba(0,0,0,0.8);
}
.pmv-side-head {
padding: 24px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.pmv-side-head h2 { margin: 0; font-size: 18px; font-weight: 600; }
.pmv-icon-btn {
background: transparent;
border: none;
color: #aaa;
cursor: pointer;
padding: 8px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.pmv-icon-btn:hover { background: rgba(255,255,255,0.1); color: #fff; }
.pmv-side-body { padding: 24px; flex: 1; overflow-y: auto; }
.pmv-side-title { font-size: 12px; text-transform: uppercase; color: #666; margin-bottom: 16px; margin-top: 24px; }
.pmv-side-title:first-child { margin-top: 0; }
/* Имитация карточек настроек */
.pmv-card-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.pmv-card { background: ${settings.cardColor}; border-radius: 8px; padding: 12px; border: 1px solid rgba(255,255,255,0.05); }
.pmv-card-img { width: 100%; height: 80px; background: #222; border-radius: 4px; margin-bottom: 12px; object-fit: cover; }
.pmv-card-title { font-size: 13px; font-weight: 500; margin-bottom: 4px; }
.pmv-card-sub { font-size: 11px; color: #666; }
/* Сцена */
.pmv-stage {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
transform-origin: left center;
transition: transform 0.5s cubic-bezier(0.25, 1, 0.5, 1), opacity 0.5s, filter 0.5s;
z-index: 10;
}
.pmv-container.side-open .pmv-stage {
transform: rotateY(-15deg) translateX(380px) scale(0.9);
opacity: 0.6;
filter: brightness(0.5);
pointer-events: none;
}
.pmv-topbar {
padding: 24px;
display: flex;
justify-content: space-between;
align-items: center;
}
.pmv-main {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 40px;
padding: 40px;
}
.pmv-cover-wrap {
position: relative;
width: 400px;
height: 400px;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
flex-shrink: 0;
}
.pmv-cover { width: 100%; height: 100%; object-fit: cover; }
.pmv-fav {
position: absolute;
bottom: 16px;
left: 16px;
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(0,0,0,0.6);
backdrop-filter: blur(10px);
border: none;
color: #fff;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.pmv-fav.active { color: #ff4d4d; }
.pmv-info {
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-end;
text-align: right;
}
.pmv-title { font-size: 64px; font-weight: 700; margin: 0 0 8px 0; letter-spacing: -0.02em; }
.pmv-artist { display: flex; align-items: center; gap: 8px; color: #888; font-size: 16px; margin-bottom: 32px; }
.pmv-artist-dot { width: 20px; height: 20px; border-radius: 50%; background: #333; }
.pmv-wave {
width: 100%;
height: 60px;
margin-bottom: 24px;
opacity: 0.6;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 2px;
}
.pmv-wave-bar {
width: 4px;
background: ${accent};
border-radius: 2px;
animation: pulse 1s infinite alternate;
}
@keyframes pulse { from { height: 10%; } to { height: 100%; } }
.pmv-progress {
width: 100%;
height: 4px;
background: rgba(255,255,255,0.1);
border-radius: 2px;
margin-bottom: 32px;
position: relative;
}
.pmv-progress-bar {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 33%;
background: ${accent};
border-radius: 2px;
}
.pmv-controls {
display: flex;
align-items: center;
justify-content: center;
gap: 24px;
width: 100%;
}
.pmv-control {
background: transparent;
border: none;
color: #888;
cursor: pointer;
transition: color 0.2s;
}
.pmv-control:hover { color: #fff; }
.pmv-play {
width: 64px;
height: 64px;
border-radius: 50%;
background: #fff;
color: #000;
border: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.2s;
}
.pmv-play:hover { transform: scale(1.05); }
.pmv-volume {
margin-left: auto;
display: flex;
align-items: center;
gap: 12px;
color: #888;
}
`}</style>
<div className={`pmv-container ${entered ? 'entered' : ''} ${panelOpen ? 'side-open' : ''}`}>
{/* Фон */}
{bgImage && <div className="pmv-bg" />}
<div className="pmv-bg-overlay" />
{/* Панель настроек */}
<aside className="pmv-side">
<div className="pmv-side-head">
<h2>Интерфейс</h2>
<button className="pmv-icon-btn" onClick={() => setPanelOpen(false)}>
<X size={20} />
</button>
</div>
<div className="pmv-side-body">
<div className="pmv-side-title">Темы</div>
<div className="pmv-card-grid">
<div className="pmv-card">
<img src="https://cdn.21st.dev/assets/mirror/35/35512adadb05376e1635de9c82f68ae607997a69a6b14967d30fb9b561d05d0a.jpg" className="pmv-card-img" alt="Theme" />
<div className="pmv-card-title">Я гуля в очке</div>
<div className="pmv-card-sub">made by skyline</div>
</div>
<div className="pmv-card">
<img src="https://cdn.21st.dev/assets/mirror/2b/2ba9de74b01421369a8b878f6bea01253474979a046ea120c7f73bdd4f6a4520.jpg" className="pmv-card-img" alt="Theme" />
<div className="pmv-card-title">Майнкрафт вайб</div>
<div className="pmv-card-sub">@Gena_BT06</div>
</div>
</div>
</div>
</aside>
{/* Сцена */}
<div className="pmv-stage">
<div className="pmv-topbar">
<button className="pmv-icon-btn" onClick={() => setPanelOpen(true)}>
<Settings size={24} />
</button>
<div style={{ display: 'flex', gap: '16px' }}>
<button className="pmv-icon-btn" onClick={handleClose}>
<Maximize size={20} />
</button>
<button className="pmv-icon-btn" onClick={handleClose}>
<X size={20} />
</button>
</div>
</div>
<div className="pmv-main">
<div className="pmv-cover-wrap">
{cover ? (
<img className="pmv-cover" src={cover} alt="Cover" />
) : (
<div className="pmv-cover" style={{ background: '#222', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Music size={72} color="#555" />
</div>
)}
<button className={`pmv-fav ${isFavorite(currentTrack?.id) ? 'active' : ''}`} onClick={() => onToggleFavorite(currentTrack)}>
<Heart size={20} fill={isFavorite(currentTrack?.id) ? 'currentColor' : 'none'} />
</button>
</div>
<div className="pmv-info">
<h1 className="pmv-title">{currentTrack?.title}</h1>
<div className="pmv-artist">
<span className="pmv-artist-dot" />
<span>{currentTrack?.artists}</span>
</div>
{settings.enableVisualizer && (
<div className="pmv-wave">
{[...Array(30)].map((_, i) => (
<div key={i} className="pmv-wave-bar" style={{ height: `${Math.random() * 100}%`, animationDelay: `${i * 0.05}s` }} />
))}
</div>
)}
<div className="pmv-progress">
<div className="pmv-progress-bar" />
</div>
<div className="pmv-controls">
<button className="pmv-control" onClick={onPrev}><SkipBack size={28} /></button>
<button className="pmv-play" onClick={onPlayPause}>
{isPlaying ? <Pause size={32} /> : <Play size={32} fill="currentColor" />}
</button>
<button className="pmv-control" onClick={onNext}><SkipForward size={28} /></button>
<div className="pmv-volume">
<Volume2 size={20} />
<div style={{ width: '80px', height: '4px', background: 'rgba(255,255,255,0.2)', borderRadius: '2px' }}>
<div style={{ width: `${volume * 100}%`, height: '100%', background: accent, borderRadius: '2px' }} />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
}
export default PlayerModeView;