<div id="root"></div>
body {
	background-color: #e6f4f9;
	display: flex;
	justify-content: center;
	align-items: center;
	margin: 50px 0;
	width: 100%;
	font-family: "Bitter", serif;
}

.container {
	height: max-content;
	width: 400px;
	border-radius: 30px;
	background-color: #63Add0;
	box-shadow: 2px 5px 5px #cccccc, -1px 7px 5px #cccccc;
}

.header{
	display:flex;
	justify-content: space-between;
	padding: 20px 15px 5px 15px;
}

.header button{
	cursor: pointer;
}

.icon{
	background: transparent;
	border: none;
	font-size: 20px;
	color: white;
}

.headerText{
	color: white;
	font-size: 20px;
}

.playerContaier{
	width: 100%;
	min-height: 200px;
	background-color: white;
	border-radius: 35px 35px 0 0;
	display: flex;
	justify-content: center;
	align-items: center;
	flex-direction: column;
	padding: 75px 0 30px 0;
	margin-top: 130px;
	border-radius: 30px;
}

.avatar, .avatar1{
	position: absolute;
	top:130px;
	left: auto;
	right: auto;
	width: 200px;
	height:200px;
	border-radius: 30px;
}

.name{
	margin-top: 27px;
	margin-bottom: 10px;
	width:100%;
	text-align:center;
}

.title{
	margin-top: 0;
	margin-bottom: 5px;
	width:100%;
	text-align:center;
}

.avatar1{
	top: 140px;
	width: 190px;
	filter: blur(8px);
}

.controls{
	width: 60%;
	display:flex;
	justify-content:space-between;
	align-items: center;
	margin: 0;
}

.controlButton{
	border: none;
	background-color: transparent;
	cursor: pointer;
	border-radius: 50%;
	width: 60px;
	height: 60px;
	display: flex;
	justify-content: center;
	align-items: center;
	font-size: 25px;
	transition: box-shadow 0.4s;
}

.controlButton:hover{
	box-shadow: 3px 3px 5px #cccccc, -3px 0px 5px #cccccc;
}

.centerButton{
	border: none;
	background-color: transparent;
	cursor: pointer;
	border-radius: 50%;
	width: 80px;
	height: 80px;
	display: flex;
	justify-content: center;
	align-items: center;
	font-size: 40px;
	border: 7px solid #63ADD0;
	color: #63ADD0;
	transition: box-shadow 0.5s;
}

.progress{
	margin: 5px 0;
	width: 100%;
	display: flex;
	justify-content: space-evenly;
	align-items: center;
}
.progressCenter{
	width: 70%;
	height: 7px;
	background-color: #d9d9d9;
	border-radius: 10px;
}

.progressCenter:hover{
	cursor: pointer;
}

.progressBar{
	width: 0px;
	height: 7px;
	border-radius: 10px;
	background-color: #63ADD0;
}

.options{
	display: flex;
	align-items:center;
	justify-content: space-evenly;
	width: 100%;
	margin-top: 50px;
}
.opt{
	border: none;
	background-color: transparent;
	font-size: 25px;	
	height:auto;
	width:auto;
	border-radius:50%;
	padding: 20px;
	display: flex;
	justify-content: center;
	align-items: center;
	transition: box-shadow 0.4s;
}
.opt:hover{
	cursor:pointer;
	box-shadow: 3px 3px 5px #cccccc, -3px 0px 5px #cccccc;
	color: #147CC0;
}
/*
Design Credits : https://dribbble.com/shots/6437042-Music-App-UI
Also I couldn't find any open music API
so credits to 
https://codepen.io/JavaScriptJunkie/details/qBWrRyg
to make his repo public and make the songs easily accesible
also I used his UI as inspiration too
*/

import * as React from "https://cdn.skypack.dev/react";
import * as ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";
import {Transition} from "https://cdn.skypack.dev/react-transition-group@4.4.1";
import { 
	ThreeDots, 
	ChevronLeft, 
	PauseFill, 
	PlayFill,
	SkipBackwardFill,
	SkipForwardFill,
	Shuffle,
	ArrowRepeat,
	Heart,
	BoxArrowUpRight,
	HeartFill
} from "https://cdn.skypack.dev/react-bootstrap-icons@1.5.0";


const tracks = [
        {
          name: "Mekanın Sahibi",
          artist: "Norm Ender",
          cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/1.jpg",
          source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/1.mp3",
          url: "https://www.youtube.com/watch?v=z3wAjJXbYzA",
          favorited: false
        },
        {
          name: "Everybody Knows",
          artist: "Leonard Cohen",
          cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/2.jpg",
          source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/2.mp3",
          url: "https://www.youtube.com/watch?v=Lin-a2lTelg",
          favorited: true
        },
        {
          name: "Extreme Ways",
          artist: "Moby",
          cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/3.jpg",
          source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/3.mp3",
          url: "https://www.youtube.com/watch?v=ICjyAe9S54c",
          favorited: false
        },
        {
          name: "Butterflies",
          artist: "Sia",
          cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/4.jpg",
          source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/4.mp3",
          url: "https://www.youtube.com/watch?v=kYgGwWYOd9Y",
          favorited: false
        },
        {
          name: "The Final Victory",
          artist: "Haggard",
          cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/5.jpg",
          source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/5.mp3",
          url: "https://www.youtube.com/watch?v=0WlpALnQdN8",
          favorited: true
        },
        {
          name: "Genius ft. Sia, Diplo, Labrinth",
          artist: "LSD",
          cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/6.jpg",
          source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/6.mp3",
          url: "https://www.youtube.com/watch?v=HhoATZ1Imtw",
          favorited: false
        },
        {
          name: "The Comeback Kid",
          artist: "Lindi Ortega",
          cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/7.jpg",
          source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/7.mp3",
          url: "https://www.youtube.com/watch?v=me6aoX0wCV8",
          favorited: true
        },
        {
          name: "Overdose",
          artist: "Grandson",
          cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/8.jpg",
          source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/8.mp3",
          url: "https://www.youtube.com/watch?v=00-Rl3Jlx-o",
          favorited: false
        },
        {
          name: "Rag'n'Bone Man",
          artist: "Human",
          cover: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/img/9.jpg",
          source: "https://raw.githubusercontent.com/muhammederdem/mini-player/master/mp3/9.mp3",
          url: "https://www.youtube.com/watch?v=L3wKzyIN1yk",
          favorited: false
        }]
const player = new Audio(tracks[0].source)
player.setAttribute('preload', 'metadata')
const userOptions = React.createContext({
	shuffle: false,
	repeat: false,
})

function Options(props){
	let options = React.useContext(userOptions)
	let [shuffl, setShuffle] = React.useState(options.shuffle)
	let [repet, setRepeat] = React.useState(options.repeat)
	let [fav, setFav] = React.useState(tracks[props.idx].favorited)
	
	React.useEffect(() => setFav(tracks[props.idx].favorited))
	
	function shuffle(){
		options.shuffle = !options.shuffle
		options.repeat = false
		setShuffle(!shuffl)
		setRepeat(false)
	}
	
	function repeat(){
		options.repeat = !options.repeat
		options.shuffle = false
		setShuffle(false)
		setRepeat(!repet)
	}
	
	function favorite(){
		tracks[props.idx].favorited = !tracks[props.idx].favorited
		setFav(tracks[props.idx].favorited)
	}

	function openURL(){
		window.open(tracks[props.idx].url, "_blank")
	}
	
	return(
		<div className="options">
			{
				shuffl &&
				<button onClick={shuffle} className="opt" style={{color: '#147CC0'}}>
					<Shuffle/>
				</button>
				||
				<button onClick={shuffle} className="opt" >
					<Shuffle/>
				</button>
			}
			<button className="opt" onClick={openURL}>
				<BoxArrowUpRight/>
			</button>
			{
				fav &&
			<button onClick={favorite}  className="opt" style={{color: '#147CC0'}}>
				<HeartFill/>
			</button>
					||
				<button onClick={favorite}  className="opt" >
				<Heart/>
			</button>
						
				}
			{
				repet &&
			<button onClick={repeat} className="opt" style={{color: '#147CC0'}}>
				<ArrowRepeat/>
			</button>
					||
						<button onClick={repeat} className="opt">
				<ArrowRepeat/>
			</button>
				}
		</div>
	);
}

function Control(props){
	
	return(
		<div className="controls">
			<button 
				className="controlButton"
				onClick={
					x => props.setIdx(props.idx-1 < 0 ? 8 : props.idx-1)
				}>
				<SkipBackwardFill />
			</button>
			{
				props.playState === true ? 
					<button 
						className="centerButton"
						onClick={x => props.setPlayState(false)}>
						<PauseFill />
					</button> : 
					<button
						className="centerButton"
						onClick={x => props.setPlayState(true)}>
						<PlayFill />
					</button>
			}
			<button
				className="controlButton"
				onClick={x => props.setIdx((props.idx+1)%9)}>
				<SkipForwardFill />
			</button>
		</div>
	);
}

function Progress(props){
	let [currLength, setCurrLength] = React.useState(0)
	let [length, setLength] = React.useState(0)
	let options = React.useContext(userOptions)
	const progressBar = document.querySelector('.progressBar')
	
	function updateProgress(e){
		let offset = e.target.getBoundingClientRect().left
		let newOffSet = e.clientX
		let newWidth = newOffSet - offset
		progressBar.style.width = newWidth+"px"
		let secPerPx = length / 280
		player.currentTime = secPerPx * newWidth
	}
	
	setInterval(() => {
		setLength(Math.ceil(player.duration))
		setCurrLength(Math.ceil(player.currentTime))
		let secPerPx = Math.ceil(player.duration) / 280
		let newWidth = player.currentTime / secPerPx
		document.querySelector('.progressBar').style.width = newWidth+"px"
		if(player.currentTime === player.duration){
			if(options.shuffle === true){
				props.setIdx((parseInt(Math.random()*1000))%9)
			}
			else if(options.repeat === true){
				player.play()
			}
			else{
				props.setIdx((props.idx+1)%9)
			}
		}
	}, 1000);
	
	function formatTime(s){
		return Number.isNaN(s) ? '0:00' : (s-(s%=60))/60+(9<s?':':':0')+s
	}
	
	return(
		<div className="progress">
			<div className="currentTime">
				<p>{formatTime(currLength)}</p>
			</div>
			<div 
			className="progressCenter" 
			onClick={(e) => updateProgress(e)}>
				<div className="progressBar">
				</div>
			</div>
			<div className="songLength">
				<p>{formatTime(length)}</p>
			</div>
		</div>
	);
}

function Avatar(props){	
	return(
		<>
			<img src={tracks[props.idx].cover} className="avatar1" />
			<img src={tracks[props.idx].cover} className="avatar"/>
			<h4 className="name">{tracks[props.idx].artist}</h4>
			<h1 className="title">{tracks[props.idx].name}</h1>
		</>
	);
}

function Container(){
	let [idx, setIdx] = React.useState(0);
	let [playState, setPlayState] = React.useState(false);
	let oldIdx = React.useRef(idx)
	React.useEffect(() => {
		
		if(playState === true)
			player.play()
		else
			player.pause()
		if(idx !== oldIdx.current){
			player.pause()
			player.src = tracks[idx].source
			player.load()
			player.play()
			setPlayState(true)
			oldIdx.current = idx
		}
			
	})
	
	return(
		<div className="playerContaier">
			<Avatar idx={idx}/>
			<Progress 
				setIdx={setIdx} 
				idx={idx} 
			/>
			<Control 
				setIdx={setIdx} 
				idx={idx}  
				playState={playState} 
				setPlayState={setPlayState}/>
			<Options 
				setIdx={setIdx} 
				idx={idx}
			/>
		</div>
	);
}

function Header(){
	return(
		<div className="header">
			<button className="icon">
				<ChevronLeft/>
			</button>
			<h1 className="headerText">Song</h1>
			<button className="icon">
				<ThreeDots/>
			</button>
		</div>
	);
}

// root method
function Index() {
	return (
		<div className="container" >
			<Header/>
			<Container/>
		</div>
	);
}

ReactDOM.render(<Index />, document.getElementById("root"));
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.