Parallax setup
This commit is contained in:
|
Before Width: | Height: | Size: 735 B After Width: | Height: | Size: 735 B |
@ -10,7 +10,9 @@ export default class ListItem extends React.Component{
|
||||
}
|
||||
|
||||
handleClick(e){
|
||||
browserHistory.push( this.props.link );
|
||||
if( e.target.tagName.toLowerCase() !== 'a' ){
|
||||
browserHistory.push( this.props.link );
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
|
||||
33
src/js/components/Parallax.js
Executable file
33
src/js/components/Parallax.js
Executable file
@ -0,0 +1,33 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import * as helpers from '../helpers'
|
||||
|
||||
export default class Parallax extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
image: false
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
this.setState({
|
||||
image: helpers.SizedImages( this.props.images )
|
||||
})
|
||||
this.update();
|
||||
}
|
||||
|
||||
update(){
|
||||
// TODO: copy canvas rules in here from Spotmop
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<div className="parallax">
|
||||
{ this.state.image ? <img src={this.state.image.huge} /> : null }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
29
src/js/components/RelatedArtistList.js
Executable file
29
src/js/components/RelatedArtistList.js
Executable file
@ -0,0 +1,29 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import { Link } from 'react-router'
|
||||
import Thumbnail from './Thumbnail'
|
||||
|
||||
export default class RelatedArtistList extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<span className="related-artist-list">
|
||||
{
|
||||
this.props.artists.map( (artist, index) => {
|
||||
var link = '/artist/' + artist.uri;
|
||||
return (
|
||||
<Link to={ link } key={artist.uri} className="artist">
|
||||
<Thumbnail circle={true} size="small" images={artist.images} />
|
||||
{ artist.name }
|
||||
</Link>
|
||||
);
|
||||
})
|
||||
}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import * as helpers from '../helpers'
|
||||
|
||||
export default class Thumbnail extends React.Component{
|
||||
|
||||
@ -24,22 +25,33 @@ export default class Thumbnail extends React.Component{
|
||||
|
||||
mapImageSizes(){
|
||||
var images = this.props.images;
|
||||
for( var i = 0; i < images.length; i++ ){
|
||||
if( images[i].height > 800 ){
|
||||
this.setState({ huge: images[i].url });
|
||||
}else if( images[i].height > 600 ){
|
||||
this.setState({ large: images[i].url });
|
||||
}else if( images[i].height > 280 ){
|
||||
this.setState({ medium: images[i].url });
|
||||
}else{
|
||||
this.setState({ small: images[i].url });
|
||||
var state = this.state;
|
||||
|
||||
if( images.length <= 0 ){
|
||||
state = {
|
||||
small: require('../../images/no-image.svg'),
|
||||
medium: require('../../images/no-image.svg'),
|
||||
large: require('../../images/no-image.svg'),
|
||||
huge: require('../../images/no-image.svg')
|
||||
}
|
||||
}else{
|
||||
state = helpers.SizedImages( images );
|
||||
}
|
||||
|
||||
this.setState( state );
|
||||
}
|
||||
|
||||
render(){
|
||||
var style = {
|
||||
backgroundImage: 'url("'+this.state[this.props.size]+'")'
|
||||
}
|
||||
var className = 'thumbnail '+this.props.size;
|
||||
if( this.props.circle ) className += ' circle';
|
||||
|
||||
return (
|
||||
<img className={ 'thumbnail '+ this.props.size } src={ this.state[this.props.size] } />
|
||||
<div className={className}>
|
||||
<div className="image" style={style}></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
32
src/js/helpers.js
Executable file
32
src/js/helpers.js
Executable file
@ -0,0 +1,32 @@
|
||||
|
||||
|
||||
export let SizedImages = function( images ){
|
||||
|
||||
var sizes = {
|
||||
small: false,
|
||||
medium: false,
|
||||
large: false,
|
||||
huge: false
|
||||
}
|
||||
|
||||
if( images.length > 0 ){
|
||||
|
||||
for( var i = 0; i < images.length; i++ ){
|
||||
if( images[i].height > 800 ){
|
||||
sizes.huge = images[i].url;
|
||||
}else if( images[i].height > 600 ){
|
||||
sizes.large = images[i].url;
|
||||
}else if( images[i].height > 280 ){
|
||||
sizes.medium = images[i].url;
|
||||
}else{
|
||||
sizes.small = images[i].url;
|
||||
}
|
||||
}
|
||||
|
||||
if( !sizes.medium ) sizes.medium = sizes.small;
|
||||
if( !sizes.large ) sizes.large = sizes.medium;
|
||||
if( !sizes.huge ) sizes.huge = sizes.large;
|
||||
}
|
||||
|
||||
return sizes;
|
||||
}
|
||||
@ -199,9 +199,14 @@ export function getArtist( uri ){
|
||||
Object.assign(artist, response);
|
||||
}),
|
||||
|
||||
sendRequest( dispatch, getState, 'artists/'+ getFromUri('artistid', uri) +'/top-tracks?country='+getState().spotify.country )
|
||||
sendRequest( dispatch, getState, 'artists/'+ getFromUri('artistid', uri) +'/top-tracks?country='+getState().spotify.country )
|
||||
.then( response => {
|
||||
Object.assign(artist, response);
|
||||
}),
|
||||
|
||||
sendRequest( dispatch, getState, 'artists/'+ getFromUri('artistid', uri) +'/related-artists' )
|
||||
.then( response => {
|
||||
Object.assign(artist, response);
|
||||
Object.assign(artist, { related_artists: response.artists });
|
||||
})
|
||||
|
||||
).then( () => {
|
||||
|
||||
@ -6,6 +6,9 @@ import { bindActionCreators } from 'redux'
|
||||
import Header from '../components/Header'
|
||||
import TrackList from '../components/TrackList'
|
||||
import AlbumGrid from '../components/AlbumGrid'
|
||||
import Thumbnail from '../components/Thumbnail'
|
||||
import Parallax from '../components/Parallax'
|
||||
import RelatedArtistList from '../components/RelatedArtistList'
|
||||
|
||||
import * as spotifyActions from '../services/spotify/actions'
|
||||
|
||||
@ -32,7 +35,10 @@ class Artist extends React.Component{
|
||||
return (
|
||||
<div>
|
||||
<Header icon="mic" title={ this.props.spotify.artist.name } />
|
||||
{ this.props.spotify.artist.images ? <Parallax images={ this.props.spotify.artist.images } /> : null }
|
||||
{ this.props.spotify.artist.images ? <Thumbnail size="huge" images={ this.props.spotify.artist.images } /> : null }
|
||||
<p>{ this.props.spotify.artist.followers.total.toLocaleString() } followers</p>
|
||||
{ this.props.spotify.artist.related_artists ? <RelatedArtistList artists={ this.props.spotify.artist.related_artists } /> : null }
|
||||
{ this.props.spotify.artist.tracks ? <TrackList tracks={ this.props.spotify.artist.tracks } /> : null }
|
||||
{ this.props.spotify.artist_albums ? <AlbumGrid items={ this.props.spotify.artist_albums } /> : null }
|
||||
</div>
|
||||
|
||||
@ -67,7 +67,6 @@ class Queue extends React.Component{
|
||||
title="Now playing"
|
||||
/>
|
||||
{ this.renderTrackInFocus() }
|
||||
<h4>Other tracks</h4>
|
||||
{ this.renderTrackList() }
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -48,7 +48,7 @@ class Settings extends React.Component{
|
||||
|
||||
<section>
|
||||
|
||||
<h3>Mopidy</h3>
|
||||
<h3 className="underline">Mopidy</h3>
|
||||
<form onSubmit={() => this.setMopidyConfig()}>
|
||||
<label>
|
||||
<span className="label">Host</span>
|
||||
@ -61,7 +61,7 @@ class Settings extends React.Component{
|
||||
<button type="submit">Apply</button>
|
||||
</form>
|
||||
|
||||
<h3>Spotify</h3>
|
||||
<h3 className="underline">Spotify</h3>
|
||||
<form onSubmit={() => this.setSpotifyConfig()}>
|
||||
<label>
|
||||
<span className="label">Country</span>
|
||||
|
||||
@ -7,9 +7,12 @@
|
||||
@import 'global/reset';
|
||||
@import 'global/forms';
|
||||
|
||||
@import 'components/images';
|
||||
@import 'components/icon';
|
||||
@import 'components/sidebar';
|
||||
@import 'components/lists';
|
||||
@import 'components/grid';
|
||||
@import 'components/header';
|
||||
|
||||
@import 'views/artist';
|
||||
|
||||
|
||||
29
src/scss/components/_images.scss
Executable file
29
src/scss/components/_images.scss
Executable file
@ -0,0 +1,29 @@
|
||||
|
||||
|
||||
.thumbnail {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
padding-bottom: 100%;
|
||||
position: relative;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #DDDDDD;
|
||||
background-position: 50% 50%;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
&.circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.parallax {
|
||||
height: 300px;
|
||||
overflow: hidden;
|
||||
.thumbnail {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
@ -13,9 +13,19 @@ aside{
|
||||
.thumbnail {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: auto;
|
||||
width: auto;
|
||||
max-width: none;
|
||||
z-index: -1;
|
||||
opacity: 0.1;
|
||||
opacity: 0.2;
|
||||
|
||||
.image {
|
||||
padding-bottom: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
nav {
|
||||
|
||||
@ -34,8 +34,11 @@ main {
|
||||
h3 {
|
||||
font-size: 14px;
|
||||
margin-bottom: 14px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid #000000;
|
||||
text-transform: uppercase;
|
||||
|
||||
&.underline {
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid #000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/scss/views/_artist.scss
Executable file
10
src/scss/views/_artist.scss
Executable file
@ -0,0 +1,10 @@
|
||||
|
||||
.tracks {
|
||||
width: 65%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.related-artist-list {
|
||||
width: 30%;
|
||||
float: right;
|
||||
}
|
||||
Reference in New Issue
Block a user