Merge branch 'white'
This commit is contained in:
@ -12,7 +12,7 @@ from handlers import WebsocketHandler, HttpHandler
|
||||
from core import IrisCore
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
__version__ = '2.14.5'
|
||||
__version__ = '3.0.0'
|
||||
|
||||
##
|
||||
# Core extension class
|
||||
|
||||
@ -45,8 +45,8 @@
|
||||
"react-ga": "2.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "NODE_ENV=development webpack && ./build.sh",
|
||||
"prod": "NODE_ENV=production webpack && ./build.sh",
|
||||
"dev": "./build.sh && NODE_ENV=development webpack --watch",
|
||||
"prod": "./build.sh && NODE_ENV=development webpack && NODE_ENV=production webpack",
|
||||
"release": "NODE_ENV=production webpack && NODE_ENV=development webpack && ./build.sh && python setup.py sdist upload -r pypi"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
@ -14,7 +14,7 @@
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/iris/assets/favicon.ico" />
|
||||
<link rel="shortcut-icon" href="/iris/assets/favicon.ico" />
|
||||
|
||||
<link href="//fonts.googleapis.com/css?family=Archivo+Narrow:300,300i,400,400i,700" rel="stylesheet">
|
||||
<link href="//fonts.googleapis.com/css?family=Archivo+Narrow:400,700|Lato:400,400i,700|Overpass:400,400i,600,700,800|Montserrat:400|500|600|News+Cycle:400,700|PT+Sans+Narrow:400,700" rel="stylesheet">
|
||||
<link href="/iris/app.css" rel="stylesheet" />
|
||||
|
||||
<link rel="manifest" href="/iris/assets/manifest.json">
|
||||
|
||||
@ -6,7 +6,7 @@ import { hashHistory, Link } from 'react-router'
|
||||
import { connect } from 'react-redux'
|
||||
|
||||
import Sidebar from './components/Sidebar'
|
||||
import MiniPlayer from './components/MiniPlayer'
|
||||
import PlaybackControls from './components/PlaybackControls'
|
||||
import SidebarToggleButton from './components/SidebarToggleButton'
|
||||
import ContextMenu from './components/ContextMenu'
|
||||
import Dragger from './components/Dragger'
|
||||
@ -190,7 +190,7 @@ class App extends React.Component{
|
||||
return (
|
||||
<div className={className}>
|
||||
<Sidebar />
|
||||
<MiniPlayer />
|
||||
<PlaybackControls />
|
||||
<main>
|
||||
{this.props.children}
|
||||
</main>
|
||||
|
||||
@ -453,9 +453,9 @@ class ContextMenu extends React.Component{
|
||||
{play_uris}
|
||||
{play_uris_next}
|
||||
{add_to_queue}
|
||||
{this.canBeInLibrary() ? toggle_in_library : null}
|
||||
{go_to_artist}
|
||||
{copy_uris}
|
||||
{this.canBeInLibrary() ? toggle_in_library : null}
|
||||
</div>
|
||||
)
|
||||
break
|
||||
@ -464,9 +464,9 @@ class ContextMenu extends React.Component{
|
||||
return (
|
||||
<div>
|
||||
{start_radio}
|
||||
{this.canBeInLibrary() ? toggle_in_library : null}
|
||||
{context.source == 'spotify' ? go_to_recommendations : null}
|
||||
{copy_uris}
|
||||
{this.canBeInLibrary() ? toggle_in_library : null}
|
||||
</div>
|
||||
)
|
||||
break
|
||||
@ -475,9 +475,9 @@ class ContextMenu extends React.Component{
|
||||
return (
|
||||
<div>
|
||||
{play_playlist}
|
||||
{this.canBeInLibrary() ? toggle_in_library : null}
|
||||
{context.source == 'spotify' ? go_to_user : null}
|
||||
{copy_uris}
|
||||
{this.canBeInLibrary() ? toggle_in_library : null}
|
||||
</div>
|
||||
)
|
||||
break
|
||||
@ -487,6 +487,7 @@ class ContextMenu extends React.Component{
|
||||
<div>
|
||||
{play_queue_item}
|
||||
{add_to_playlist}
|
||||
{context.source == 'spotify' ? go_to_recommendations : null}
|
||||
{copy_uris}
|
||||
{remove_from_queue}
|
||||
</div>
|
||||
|
||||
@ -14,11 +14,11 @@ export default class ContextMenuTrigger extends React.Component{
|
||||
className += ' '+this.props.className
|
||||
}
|
||||
return (
|
||||
<button
|
||||
className={className}
|
||||
onClick={e => this.props.onTrigger(e)}>
|
||||
<FontAwesome name="ellipsis-v" />
|
||||
</button>
|
||||
<span className={className} onClick={e => this.props.onTrigger(e)}>
|
||||
<span className="dot"></span>
|
||||
<span className="dot"></span>
|
||||
<span className="dot"></span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,11 +45,11 @@ export default class Header extends React.Component{
|
||||
render(){
|
||||
return (
|
||||
<header className={(this.props.className ? this.props.className : null)}>
|
||||
<Icon name={ this.props.icon } />
|
||||
{this.props.icon ? <Icon name={this.props.icon} /> : null}
|
||||
<SidebarToggleButton />
|
||||
<h1>{ this.props.title }</h1>
|
||||
{this.props.title ? <h1>{ this.props.title }</h1> : null}
|
||||
{this.renderOptions()}
|
||||
</header>
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -27,8 +27,15 @@ class MiniPlayer extends React.Component{
|
||||
}
|
||||
|
||||
render(){
|
||||
var images = []
|
||||
if (this.props.current_track && this.props.current_track.album && this.props.current_track.album.images){
|
||||
images = this.props.current_track.album.images
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="player mini-player">
|
||||
|
||||
<Thumbnail size="small" images={images} />
|
||||
|
||||
<div className="current-track">
|
||||
<div className="title">{ this.props.current_track ? this.props.current_track.name : <span>-</span> }</div>
|
||||
|
||||
@ -26,7 +26,7 @@ export default class AddToPlaylistModal extends React.Component{
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4>Add to playlist</h4>
|
||||
<h1>Add to playlist</h1>
|
||||
<div className="list small playlists">
|
||||
{
|
||||
playlists.map( playlist => {
|
||||
|
||||
@ -25,7 +25,7 @@ export default class AddToQueueModal extends React.Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<h4>Add URI(s) to queue</h4>
|
||||
<h1>Add URI(s) to queue</h1>
|
||||
|
||||
<form onSubmit={e => this.handleSubmit(e)}>
|
||||
<div className="field text">
|
||||
|
||||
@ -30,8 +30,8 @@ export default class extends React.Component{
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4 className="no-bottom-padding">Spotify authorization received</h4>
|
||||
<h3 className="grey-text">{user.display_name ? user.display_name : user.id} has shared their Spotify authorization with you. Importing this will overwrite your existing Spotify Authorization.</h3>
|
||||
<h1>Spotify authorization received</h1>
|
||||
<h2 className="grey-text">{user.display_name ? user.display_name : user.id} has shared their Spotify authorization with you. Importing this will overwrite your existing Spotify Authorization.</h2>
|
||||
|
||||
<div className="user-view">
|
||||
<div className="intro">
|
||||
|
||||
@ -32,8 +32,8 @@ class AuthorizationModal_Send extends React.Component{
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4 className="no-bottom-padding">Share Spotify authentication</h4>
|
||||
<h3 className="grey-text">Send your authentication tokens to another client. When the recipient client imports this, their Iris will have full access to your Spotify account ({this.props.me.id}).</h3>
|
||||
<h1>Share Spotify authentication</h1>
|
||||
<h2 className="grey-text">Send your authentication tokens to another client. When the recipient client imports this, their Iris will have full access to your Spotify account ({this.props.me.id}).</h2>
|
||||
<div className="list small pusher-connection-list">
|
||||
{
|
||||
connections.map( (connection, index) => {
|
||||
|
||||
@ -35,7 +35,7 @@ export default class CreatePlaylistModal extends React.Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<h4>Create playlist</h4>
|
||||
<h1>Create playlist</h1>
|
||||
<form onSubmit={(e) => this.createPlaylist(e)}>
|
||||
<div className="field">
|
||||
<input
|
||||
|
||||
@ -34,7 +34,7 @@ export default class EditPlaylistModal extends React.Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<h4>Edit playlist</h4>
|
||||
<h1>Edit playlist</h1>
|
||||
<form onSubmit={(e) => this.savePlaylist(e)}>
|
||||
<div className="field">
|
||||
<input
|
||||
|
||||
@ -146,7 +146,7 @@ export default class EditRadioModal extends React.Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<h4>Manage radio</h4>
|
||||
<h1>Manage radio</h1>
|
||||
|
||||
<form>
|
||||
{this.renderSeeds()}
|
||||
|
||||
@ -39,7 +39,7 @@ export default class KioskModeModal extends React.Component{
|
||||
<div className="player">
|
||||
<div className="current-track">
|
||||
<div className="title">{ this.props.current_track ? this.props.current_track.name : <span>-</span> }</div>
|
||||
{ this.props.current_track ? <ArtistSentence artists={ this.props.current_track.artists } /> : <ArtistSentence /> }
|
||||
{ this.props.current_track ? <ArtistSentence nolinks artists={ this.props.current_track.artists } /> : <ArtistSentence /> }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -49,8 +49,8 @@ export default class SearchSettingsModal extends React.Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<h4 className="no-bottom-padding">Search sourced</h4>
|
||||
<h3 className="grey-text bottom-padding">Customise the providers used when searching. Reduce the number of providers to speed up your searches. Please note that not all backends support searching by artist, album or playlist.</h3>
|
||||
<h1 className="no-bottom-padding">Search sources</h1>
|
||||
<h2 className="grey-text bottom-padding">Customise the providers used when searching. Reduce the number of providers to speed up your searches. Please note that not all backends support searching by artist, album or playlist.</h2>
|
||||
|
||||
<form onSubmit={e => this.handleSubmit(e)}>
|
||||
<div className="list small">
|
||||
|
||||
151
src/js/components/PlaybackControls.js
Executable file
151
src/js/components/PlaybackControls.js
Executable file
@ -0,0 +1,151 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { Link } from 'react-router'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
|
||||
import ProgressSlider from './ProgressSlider'
|
||||
import VolumeControl from './VolumeControl'
|
||||
import Dater from './Dater'
|
||||
import ArtistSentence from './ArtistSentence'
|
||||
import Thumbnail from './Thumbnail'
|
||||
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
import * as mopidyActions from '../services/mopidy/actions'
|
||||
|
||||
class PlaybackControls extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
expanded: false
|
||||
}
|
||||
}
|
||||
|
||||
renderPlayButton(){
|
||||
var button = <a className="control play" onClick={() => this.props.mopidyActions.play()}><FontAwesome name="play" /> </a>
|
||||
if( this.props.play_state == 'playing' ){
|
||||
button = <a className="control play" onClick={() => this.props.mopidyActions.pause()}><FontAwesome name="pause" /> </a>
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
renderConsumeButton(){
|
||||
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setConsume', [true])}><FontAwesome name="fire" /><span className="tooltip">Consume</span></a>
|
||||
if( this.props.consume ){
|
||||
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setConsume', [false])}><FontAwesome name="fire" /><span className="tooltip">Consume</span></a>
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
renderRandomButton(){
|
||||
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRandom', [true])}><FontAwesome name="random" /><span className="tooltip">Shuffle</span></a>
|
||||
if( this.props.random ){
|
||||
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRandom', [false])}><FontAwesome name="random" /><span className="tooltip">Shuffle</span></a>
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
renderRepeatButton(){
|
||||
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRepeat', [true])}><FontAwesome name="repeat" /><span className="tooltip">Repeat</span></a>
|
||||
if( this.props.repeat ){
|
||||
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRepeat', [false])}><FontAwesome name="repeat" /><span className="tooltip">Repeat</span></a>
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
handleThumbnailClick(e){
|
||||
e.preventDefault();
|
||||
this.props.uiActions.openModal('kiosk_mode')
|
||||
}
|
||||
|
||||
render(){
|
||||
var images = []
|
||||
if (this.props.current_track && this.props.current_track.album && this.props.current_track.album.images){
|
||||
images = this.props.current_track.album.images
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={(this.state.expanded ? "expanded playback-controls" : "playback-controls")}>
|
||||
|
||||
<Link className="current-track" to={(this.props.current_track && this.props.current_track.album ? global.baseURL+'album/'+this.props.current_track.album.uri : null)}>
|
||||
<div className="thumbnail-wrapper" onClick={e => this.handleThumbnailClick(e)}>
|
||||
<Thumbnail size="small" images={images} />
|
||||
</div>
|
||||
<div className="title">
|
||||
{ this.props.current_track ? this.props.current_track.name : <span>-</span> }
|
||||
</div>
|
||||
<div className="artist">
|
||||
{ this.props.current_track ? <ArtistSentence nolinks artists={ this.props.current_track.artists } /> : <ArtistSentence /> }
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<section className="playback">
|
||||
<a className="control previous" onClick={() => this.props.mopidyActions.previous()}>
|
||||
<FontAwesome name="step-backward" />
|
||||
</a>
|
||||
{ this.renderPlayButton() }
|
||||
<a className="control stop" onClick={() => this.props.mopidyActions.stop()}>
|
||||
<FontAwesome name="stop" />
|
||||
</a>
|
||||
<a className="control next" onClick={() => this.props.mopidyActions.next()}>
|
||||
<FontAwesome name="step-forward" />
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section className="settings">
|
||||
{ this.renderConsumeButton() }
|
||||
{ this.renderRandomButton() }
|
||||
{ this.renderRepeatButton() }
|
||||
</section>
|
||||
|
||||
<section className="progress">
|
||||
<ProgressSlider />
|
||||
<span className="current">{ this.props.time_position ? <Dater type="length" data={this.props.time_position} /> : '-' }</span>
|
||||
<span className="total">{ this.props.current_track ? <Dater type="length" data={this.props.current_track.length} /> : '-' }</span>
|
||||
</section>
|
||||
|
||||
<section className="volume">
|
||||
<VolumeControl />
|
||||
</section>
|
||||
|
||||
<section className="triggers">
|
||||
<a className="control next" onClick={() => this.setState({expanded: !this.state.expanded})}>
|
||||
{this.state.expanded ? <FontAwesome name="chevron-down" /> : <FontAwesome name="chevron-up" />}
|
||||
</a>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Export our component
|
||||
*
|
||||
* We also integrate our global store, using connect()
|
||||
**/
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {
|
||||
current_track: (typeof(state.ui.current_track) !== 'undefined' && typeof(state.ui.tracks) !== 'undefined' && typeof(state.ui.tracks[state.ui.current_track.uri]) !== 'undefined' ? state.ui.tracks[state.ui.current_track.uri] : null),
|
||||
radio_enabled: (state.ui.radio && state.ui.radio.enabled ? true : false),
|
||||
play_state: state.mopidy.play_state,
|
||||
time_position: state.mopidy.time_position,
|
||||
consume: state.mopidy.consume,
|
||||
repeat: state.mopidy.repeat,
|
||||
random: state.mopidy.random
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
uiActions: bindActionCreators(uiActions, dispatch),
|
||||
mopidyActions: bindActionCreators(mopidyActions, dispatch)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(PlaybackControls)
|
||||
@ -14,7 +14,21 @@ class SearchForm extends React.Component{
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
query: ''
|
||||
query: '',
|
||||
in_focus: false
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
if (this.props.query){
|
||||
this.setState({query: this.props.query})
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps){
|
||||
if (newProps.query && newProps.query != this.state.query && newProps.query != this.props.query && !this.state.in_focus){
|
||||
console.log('changing',newProps.query,this.props.query)
|
||||
this.setState({query: newProps.query})
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,12 +60,16 @@ class SearchForm extends React.Component{
|
||||
|
||||
render(){
|
||||
return (
|
||||
<form className={this.props.context+' search-form'} onSubmit={ e => this.handleSubmit(e) }>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
onChange={ e => this.setState({ query: e.target.value }) }
|
||||
value={ this.state.query } />
|
||||
<form className="search-form" onSubmit={e => this.handleSubmit(e)}>
|
||||
<label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
onChange={ e => this.setState({ query: e.target.value }) }
|
||||
onFocus={e => this.setState({in_focus: true})}
|
||||
onBlur={e => this.setState({in_focus: false})}
|
||||
value={ this.state.query } />
|
||||
</label>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
@ -2,12 +2,11 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import { Link } from 'react-router'
|
||||
import { Link, withRouter } from 'react-router'
|
||||
|
||||
import Icon from './Icon'
|
||||
import Thumbnail from './Thumbnail'
|
||||
import SearchForm from './SearchForm'
|
||||
import Dropzones from './Dropzones'
|
||||
import Thumbnail from './Thumbnail'
|
||||
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
import * as mopidyActions from '../services/mopidy/actions'
|
||||
@ -18,79 +17,84 @@ class Sidebar extends React.Component{
|
||||
super(props)
|
||||
}
|
||||
|
||||
linkClassName(link){
|
||||
if (this.props.location.pathname.startsWith('/'+link)){
|
||||
return 'active'
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<aside>
|
||||
|
||||
{ this.props.current_track && this.props.current_track.album && this.props.current_track.album.images ? <Thumbnail size="large" images={this.props.current_track.album.images} /> : null }
|
||||
|
||||
<div className="liner">
|
||||
|
||||
<SearchForm context="sidebar" />
|
||||
|
||||
<nav>
|
||||
|
||||
<section>
|
||||
<Link activeClassName="active" to={global.baseURL+"queue"}>
|
||||
<Icon name="play" className="white" />
|
||||
<Link className={this.linkClassName('queue')} to={global.baseURL+"queue"}>
|
||||
<Icon name="play" />
|
||||
Now playing
|
||||
</Link>
|
||||
<Link className={this.linkClassName('search')} to={global.baseURL+"search"}>
|
||||
<Icon name="search" />
|
||||
Search
|
||||
</Link>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discover</title>
|
||||
<Link activeClassName="active" to={global.baseURL+"discover/recommendations"}>
|
||||
<Icon name="compass" className="white" />
|
||||
<Link className={this.linkClassName('discover/recommendations')} to={global.baseURL+"discover/recommendations"}>
|
||||
<Icon name="compass" />
|
||||
Discover
|
||||
</Link>
|
||||
<Link activeClassName="active" to={global.baseURL+"discover/categories"}>
|
||||
<Icon name="grid" className="white" />
|
||||
<Link className={this.linkClassName('discover/categories')} to={global.baseURL+"discover/categories"}>
|
||||
<Icon name="grid" />
|
||||
Genre / Mood
|
||||
</Link>
|
||||
<Link activeClassName="active" to={global.baseURL+"discover/featured"}>
|
||||
<Icon name="star" className="white" />
|
||||
<Link className={this.linkClassName('discover/featured')} to={global.baseURL+"discover/featured"}>
|
||||
<Icon name="star" />
|
||||
Featured playlists
|
||||
</Link>
|
||||
<Link activeClassName="active" to={global.baseURL+"discover/new-releases"}>
|
||||
<Icon name="leaf" className="white" />
|
||||
<Link className={this.linkClassName('discover/new-releases')} to={global.baseURL+"discover/new-releases"}>
|
||||
<Icon name="leaf" />
|
||||
New releases
|
||||
</Link>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>My Music</title>
|
||||
<Link activeClassName="active" to={global.baseURL+"library/playlists"}>
|
||||
<Icon name="playlist" className="white" />
|
||||
<Link className={this.linkClassName('library/playlists')} to={global.baseURL+"library/playlists"}>
|
||||
<Icon name="playlist" />
|
||||
Playlists
|
||||
</Link>
|
||||
<Link activeClassName="active" disabled={!this.props.spotify_authorized} to={this.props.spotify_authorized ? global.baseURL+"library/artists" : null}>
|
||||
<Icon name="mic" className="white" />
|
||||
<Link className={this.linkClassName('library/artists')} disabled={!this.props.spotify_authorized} to={this.props.spotify_authorized ? global.baseURL+"library/artists" : null}>
|
||||
<Icon name="mic" />
|
||||
Artists
|
||||
</Link>
|
||||
<Link activeClassName="active" disabled={!this.props.spotify_authorized} to={this.props.spotify_authorized ? global.baseURL+"library/albums" : null}>
|
||||
<Icon name="cd" className="white" />
|
||||
<Link className={this.linkClassName('library/albums')} disabled={!this.props.spotify_authorized} to={this.props.spotify_authorized ? global.baseURL+"library/albums" : null}>
|
||||
<Icon name="cd" />
|
||||
Albums
|
||||
</Link>
|
||||
<Link activeClassName="active" disabled={!this.props.spotify_authorized} to={this.props.spotify_authorized ? global.baseURL+"library/tracks" : null}>
|
||||
<Icon name="music" className="white" />
|
||||
<Link className={this.linkClassName('library/tracks')} disabled={!this.props.spotify_authorized} to={this.props.spotify_authorized ? global.baseURL+"library/tracks" : null}>
|
||||
<Icon name="music" />
|
||||
Tracks
|
||||
</Link>
|
||||
<Link activeClassName="active" to={global.baseURL+"library/local"}>
|
||||
<Icon name="folder" className="white" />
|
||||
<Link className={this.linkClassName('library/local')} to={global.baseURL+"library/local"}>
|
||||
<Icon name="folder" />
|
||||
Local
|
||||
</Link>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<Link activeClassName="active" to={global.baseURL+"settings"}>
|
||||
<Icon name="cog" className="white" />
|
||||
<Link className={this.linkClassName('settings')} to={global.baseURL+"settings"}>
|
||||
<Icon name="cog" />
|
||||
Settings
|
||||
{ !this.props.mopidy_connected || !this.props.spotify_connected || !this.props.pusher_connected ? <FontAwesome name="exclamation-triangle" className="red-text pull-right" /> : null }
|
||||
</Link>
|
||||
</section>
|
||||
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
<Dropzones />
|
||||
@ -113,7 +117,6 @@ const mapStateToProps = (state, ownProps) => {
|
||||
pusher_connected: state.pusher.connected,
|
||||
spotify_connected: state.spotify.connected,
|
||||
spotify_authorized: state.spotify.authorized,
|
||||
current_track: (typeof(state.ui.current_track) !== 'undefined' && typeof(state.ui.tracks) !== 'undefined' && typeof(state.ui.tracks[state.ui.current_track.uri]) !== 'undefined' ? state.ui.tracks[state.ui.current_track.uri] : null),
|
||||
dragger: state.ui.dragger
|
||||
}
|
||||
}
|
||||
@ -124,4 +127,5 @@ const mapDispatchToProps = (dispatch) => {
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Sidebar)
|
||||
// We wrap our Sidebar with the Router, and then to the redux store
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Sidebar))
|
||||
@ -55,13 +55,13 @@ export default class Track extends React.Component{
|
||||
var track_columns = (
|
||||
<span>
|
||||
<span className="col name">
|
||||
{ track.name ? track.name : <span className="grey-text">{track.uri}</span> }
|
||||
{track.name ? track.name : <span className="grey-text">{track.uri}</span>}
|
||||
</span>
|
||||
<span className="col source">
|
||||
{helpers.uriSource(track.uri)}
|
||||
</span>
|
||||
<span className="col played_at">
|
||||
{ track.played_at ? <span><Dater type="ago" data={track.played_at} /> ago</span> : null }
|
||||
{track.played_at ? <span><Dater type="ago" data={track.played_at} /> ago</span> : null}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
@ -87,11 +87,12 @@ export default class Track extends React.Component{
|
||||
var track_columns = (
|
||||
<span>
|
||||
<span className="col name">
|
||||
{ track.name ? track.name : <span className="grey-text">{track.uri}</span> }
|
||||
{track.name ? track.name : <span className="grey-text">{track.uri}</span>}
|
||||
{track.explicit ? <span className="flag dark">EXPLICIT</span> : null}
|
||||
</span>
|
||||
{this.props.show_source_icon ? <FontAwesome name={helpers.sourceIcon(track.uri)} className="source" fixedWidth /> : null}
|
||||
<span className="col artists">
|
||||
{ track.artists ? <ArtistSentence artists={track.artists} /> : '-' }
|
||||
{track.artists ? <ArtistSentence artists={track.artists} /> : '-'}
|
||||
</span>
|
||||
<span className="col album">
|
||||
{album}
|
||||
@ -100,8 +101,8 @@ export default class Track extends React.Component{
|
||||
{added}
|
||||
</span>
|
||||
<span className="col duration">
|
||||
{ track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null }
|
||||
{ track.length ? <Dater type="length" data={track.length} /> : null }
|
||||
{track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null}
|
||||
{track.length ? <Dater type="length" data={track.length} /> : null}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
@ -111,18 +112,19 @@ export default class Track extends React.Component{
|
||||
var track_columns = (
|
||||
<span>
|
||||
<span className="col name">
|
||||
{ track.name ? track.name : <span className="grey-text">{track.uri}</span> }
|
||||
{track.name ? track.name : <span className="grey-text">{track.uri}</span>}
|
||||
{track.explicit ? <span className="flag dark">EXPLICIT</span> : null}
|
||||
</span>
|
||||
{this.props.show_source_icon ? <FontAwesome name={helpers.sourceIcon(track.uri)} className="source" fixedWidth /> : null}
|
||||
<span className="col artists">
|
||||
{ track.artists ? <ArtistSentence artists={track.artists} /> : '-' }
|
||||
{track.artists ? <ArtistSentence artists={track.artists} /> : '-'}
|
||||
</span>
|
||||
<span className="col album">
|
||||
{album}
|
||||
</span>
|
||||
<span className="col duration">
|
||||
{ track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null }
|
||||
{ track.length ? <Dater type="length" data={track.length} /> : null }
|
||||
{track.duration_ms ? <Dater type="length" data={track.duration_ms} /> : null}
|
||||
{track.length ? <Dater type="length" data={track.length} /> : null}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
@ -138,8 +140,7 @@ export default class Track extends React.Component{
|
||||
onDoubleClick={ e => this.props.handleDoubleClick(e) }
|
||||
onContextMenu={ e => this.handleContextMenu(e) }>
|
||||
<span className="state-icon">
|
||||
{ this.props.track.selected ? <FontAwesome name="check" className="selected" fixedWidth /> : null }
|
||||
{ this.props.track.playing && !this.props.track.selected ? <FontAwesome name="play" className="playing" fixedWidth /> : null }
|
||||
{this.props.track.selected ? <FontAwesome name="check" className="selected" fixedWidth /> : null}
|
||||
</span>
|
||||
{ track_columns }
|
||||
</div>
|
||||
|
||||
@ -16,36 +16,54 @@ class VolumeControl extends React.Component{
|
||||
|
||||
handleClick(e){
|
||||
var slider = e.target;
|
||||
if( slider.className != 'slider' ) slider = slider.parentElement;
|
||||
|
||||
var sliderY = e.clientY - slider.getBoundingClientRect().top;
|
||||
var sliderHeight = slider.getBoundingClientRect().height;
|
||||
var percent = 100 - parseInt( sliderY / sliderHeight * 100 );
|
||||
var sliderX = e.clientX - slider.getBoundingClientRect().left;
|
||||
var sliderWidth = slider.getBoundingClientRect().width;
|
||||
var percent = Math.round(( sliderX / sliderWidth ) * 100);
|
||||
|
||||
if (percent > 100){
|
||||
percent = 100
|
||||
} else if (percent < 0 ){
|
||||
percent = 0
|
||||
}
|
||||
|
||||
this.props.mopidyActions.setVolume( percent )
|
||||
}
|
||||
|
||||
renderMuteButton(){
|
||||
if (this.props.mute){
|
||||
return (
|
||||
<a className="control has-tooltip" onClick={() => this.props.mopidyActions.setMute(false)}>
|
||||
<FontAwesome className="red-text" name="volume-down" />
|
||||
<span className="tooltip">Unmute</span>
|
||||
</a>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<a className="control has-tooltip" onClick={() => this.props.mopidyActions.setMute(true)}>
|
||||
<FontAwesome className="muted" name="volume-off" />
|
||||
<span className="tooltip">Mute</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
var className = "volume-control"
|
||||
if (this.props.mute){
|
||||
className += " muted"
|
||||
}
|
||||
return (
|
||||
<span className={className}>
|
||||
|
||||
<a className="modal-trigger" onClick={() => this.props.uiActions.openModal('volume')}>
|
||||
{this.props.mute ? <FontAwesome className="muted" name="volume-off" /> : <FontAwesome name="volume-down" />}
|
||||
</a>
|
||||
|
||||
<span className="hover-trigger">
|
||||
<a onClick={() => this.props.mopidyActions.setMute(!this.props.mute)}>
|
||||
{this.props.mute ? <FontAwesome className="muted" name="volume-off" /> : <FontAwesome name="volume-down" />}
|
||||
</a>
|
||||
<div className="slider-wrapper">
|
||||
<div className={this.props.mute ? "slider vertical disabled" : "slider vertical"} onClick={ (e) => this.handleClick(e) } >
|
||||
<div className="track">
|
||||
<div className="progress" style={{ height: this.props.volume+'%' }}></div>
|
||||
</div>
|
||||
{this.renderMuteButton()}
|
||||
<div className="slider-wrapper">
|
||||
<div className="slider horizontal" onClick={e => this.handleClick(e)}>
|
||||
<div className="track">
|
||||
<div className="progress" style={{ width: this.props.volume+'%' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ ReactDOM.render(
|
||||
<Route path="settings" component={Settings} />
|
||||
<Route path="settings/debug" component={Debug} />
|
||||
|
||||
<Route path="search/iris::search::type::query" component={Search} />
|
||||
<Route path="search(/iris::search::type::query)" component={Search} />
|
||||
<Route path="album/:uri" component={Album} />
|
||||
<Route path="artist/:uri(/:sub_view)" component={Artist} />
|
||||
<Route path="playlist/:uri" component={Playlist} />
|
||||
|
||||
@ -302,7 +302,7 @@ const MopidyMiddleware = (function(){
|
||||
var batch_size = 5
|
||||
while (uris.length > 0){
|
||||
batches.push({
|
||||
uris: uris.splice(0,batch_size),
|
||||
uris: uris.splice(0,batch_size-1),
|
||||
at_position: action.at_position,
|
||||
next: action.next,
|
||||
offset: action.offset + (batch_size * batches.length),
|
||||
|
||||
@ -119,21 +119,25 @@ class Album extends React.Component{
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="view album-view">
|
||||
<div className="view album-view content-wrapper">
|
||||
|
||||
<SidebarToggleButton />
|
||||
|
||||
<Thumbnail size="large" canZoom images={ this.props.album.images } />
|
||||
<div className="thumbnail-wrapper">
|
||||
<Thumbnail size="large" canZoom images={ this.props.album.images } />
|
||||
</div>
|
||||
|
||||
<div className="title">
|
||||
<div className="source grey-text">
|
||||
<FontAwesome name={helpers.sourceIcon( this.props.params.uri )} />
|
||||
{helpers.uriSource( this.props.params.uri )}
|
||||
{this.props.album.album_type ? this.props.album.album_type : 'album'}
|
||||
</div>
|
||||
|
||||
<h1>{ this.props.album.name }</h1>
|
||||
|
||||
<ul className="details">
|
||||
<li className="has-tooltip">
|
||||
<FontAwesome name={helpers.sourceIcon( this.props.params.uri )} />
|
||||
<span className="tooltip">
|
||||
{helpers.uriSource( this.props.params.uri )} {this.props.album.album_type ? this.props.album.album_type : 'album'}
|
||||
</span>
|
||||
</li>
|
||||
{ artists.length > 0 ? <li><ArtistSentence artists={artists} /></li> : null }
|
||||
{ this.props.album.release_date ? <li><Dater type="date" data={ this.props.album.release_date } /></li> : null }
|
||||
<li>
|
||||
|
||||
@ -131,7 +131,6 @@ class Artist extends React.Component{
|
||||
case 'related-artists':
|
||||
return (
|
||||
<div className="body related-artists">
|
||||
<h4 className="left-padding">Related artists</h4>
|
||||
<section className="grid-wrapper no-top-padding">
|
||||
<ArtistGrid artists={related_artists} />
|
||||
</section>
|
||||
@ -150,7 +149,6 @@ class Artist extends React.Component{
|
||||
</div>
|
||||
|
||||
<div className="col w60 biography">
|
||||
<h4>Biography</h4>
|
||||
<section>
|
||||
{ this.props.artist.bio ? <div className="biography-text"><p>{this.props.artist.bio.content}</p><br />
|
||||
<div className="grey-text">Published: { this.props.artist.bio.published }</div>
|
||||
@ -164,7 +162,7 @@ class Artist extends React.Component{
|
||||
return (
|
||||
<div className="body overview">
|
||||
<div className={related_artists.length > 0 ? "col w70" : "col w100"}>
|
||||
<h4 className="left-padding">Top tracks</h4>
|
||||
<h4>Top tracks</h4>
|
||||
<div className="list-wrapper">
|
||||
{ this.props.artist.tracks ? <TrackList className="artist-track-list" uri={this.props.params.uri} tracks={this.props.artist.tracks} /> : null }
|
||||
</div>
|
||||
@ -172,11 +170,11 @@ class Artist extends React.Component{
|
||||
|
||||
<div className="col w5"></div>
|
||||
|
||||
{related_artists.length > 0 ? <div className="col w25 related-artists"><h4>Related artists</h4><div className="list-wrapper"><RelatedArtists artists={related_artists.slice(0,6)} /></div></div> : null}
|
||||
{related_artists.length > 0 ? <div className="col w25 related-artists"><h4>Related artists</h4><div className="list-wrapper"><RelatedArtists artists={related_artists.slice(0,6)} /></div><Link to={global.baseURL+'artist/'+this.props.params.uri+'/related-artists'} className="button">All related artists</Link></div> : null}
|
||||
|
||||
<div className="cf"></div>
|
||||
|
||||
<h4 className="left-padding">Albums</h4>
|
||||
<h4>Albums</h4>
|
||||
<section className="grid-wrapper no-top-padding">
|
||||
<AlbumGrid albums={albums} />
|
||||
<LazyLoadListener enabled={this.props.artist.albums_more} loadMore={ () => this.loadMore() }/>
|
||||
@ -220,7 +218,9 @@ class Artist extends React.Component{
|
||||
{ this.renderSubViewMenu() }
|
||||
</div>
|
||||
</div>
|
||||
{this.renderBody()}
|
||||
<div className="content-wrapper">
|
||||
{this.renderBody()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -242,7 +242,9 @@ class Artist extends React.Component{
|
||||
{ this.renderSubViewMenu() }
|
||||
</div>
|
||||
</div>
|
||||
{this.renderBody()}
|
||||
<div className="content-wrapper">
|
||||
{this.renderBody()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ class Debug extends React.Component{
|
||||
<div className="view debugger-view">
|
||||
<Header icon="cog" title="Debugger" options={options} uiActions={this.props.uiActions} />
|
||||
|
||||
<section>
|
||||
<div className="content-wrapper">
|
||||
|
||||
<h4 className="underline">User interface</h4>
|
||||
<form>
|
||||
@ -190,7 +190,7 @@ class Debug extends React.Component{
|
||||
{ this.props.debug_response ? JSON.stringify(this.props.debug_response, null, 2) : null }
|
||||
</pre>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -159,26 +159,28 @@ class Playlist extends React.Component{
|
||||
if (this.props.playlist.can_edit) context = 'editable-playlist'
|
||||
|
||||
return (
|
||||
<div className="view playlist-view">
|
||||
<div className="view playlist-view content-wrapper">
|
||||
|
||||
<SidebarToggleButton />
|
||||
|
||||
<Thumbnail size="large" canZoom images={ this.props.playlist.images } />
|
||||
<div className="thumbnail-wrapper">
|
||||
<Thumbnail size="large" canZoom images={ this.props.playlist.images } />
|
||||
</div>
|
||||
|
||||
<div className="title">
|
||||
<div className="source grey-text">
|
||||
<FontAwesome name={helpers.sourceIcon( this.props.params.uri )} /> {helpers.uriSource( this.props.params.uri )} playlist
|
||||
</div>
|
||||
<h1>{ this.props.playlist.name }</h1>
|
||||
{ this.props.playlist.description ? <div className="description grey-text" dangerouslySetInnerHTML={{__html: this.props.playlist.description}}></div> : null }
|
||||
{ this.props.playlist.description ? <h2 className="description grey-text" dangerouslySetInnerHTML={{__html: this.props.playlist.description}}></h2> : null }
|
||||
|
||||
<ul className="details">
|
||||
<li className="has-tooltip">
|
||||
<FontAwesome name={helpers.sourceIcon( this.props.params.uri )} />
|
||||
<span className="tooltip">
|
||||
{helpers.uriSource( this.props.params.uri )} playlist
|
||||
</span>
|
||||
</li>
|
||||
{ this.props.playlist.owner ? <li><Link to={'/user/'+this.props.playlist.owner.uri}>{this.props.playlist.owner.id}</Link></li> : null }
|
||||
|
||||
{ this.props.playlist.followers ? <li>{this.props.playlist.followers.total.toLocaleString()} followers</li> : null }
|
||||
|
||||
{ this.props.playlist.last_modified ? <li><Dater type="ago" data={this.props.playlist.last_modified} /></li> : null }
|
||||
|
||||
<li>
|
||||
{ this.props.playlist.tracks_total ? this.props.playlist.tracks_total : '0'} tracks,
|
||||
{ this.props.playlist.tracks ? <Dater type="total-time" data={this.props.playlist.tracks} /> : '0 mins' }
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { hashHistory } from 'react-router'
|
||||
import { hashHistory, Link } from 'react-router'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
|
||||
@ -11,6 +11,7 @@ import Dater from '../components/Dater'
|
||||
import SidebarToggleButton from '../components/SidebarToggleButton'
|
||||
import FullPlayer from '../components/FullPlayer'
|
||||
import ArtistSentence from '../components/ArtistSentence'
|
||||
import Thumbnail from '../components/Thumbnail'
|
||||
import Header from '../components/Header'
|
||||
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
@ -56,6 +57,34 @@ class Queue extends React.Component{
|
||||
)
|
||||
}
|
||||
|
||||
renderArtwork(){
|
||||
if(
|
||||
!this.props.current_track ||
|
||||
!this.props.current_track.album ||
|
||||
!this.props.current_track.album.images ){
|
||||
return (
|
||||
<span className={this.props.radio_enabled ? 'artwork radio-enabled' : 'artwork'}>
|
||||
{this.props.radio_enabled ? <img className="radio-overlay" src="assets/radio-overlay.png" /> : null}
|
||||
<Thumbnail size="huge" />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
var images = []
|
||||
if (this.props.current_track && this.props.current_track.album && this.props.current_track.album.images){
|
||||
images = this.props.current_track.album.images
|
||||
}
|
||||
|
||||
var link = null
|
||||
if( this.props.current_track.album.uri ) link = '/album/'+this.props.current_track.album.uri
|
||||
return (
|
||||
<Link className={this.props.radio_enabled ? 'artwork radio-enabled' : 'artwork'} to={link} onContextMenu={e => this.handleContextMenu(e,this.props.current_track.album)}>
|
||||
{this.props.radio_enabled ? <img className="radio-overlay" src="assets/radio-overlay.png" /> : null}
|
||||
<Thumbnail size="huge" images={images} canZoom />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
render(){
|
||||
var options = (
|
||||
<span>
|
||||
@ -82,20 +111,30 @@ class Queue extends React.Component{
|
||||
return (
|
||||
<div className="view queue-view">
|
||||
<Header icon="play" title="Now playing" options={options} uiActions={this.props.uiActions} />
|
||||
<FullPlayer />
|
||||
|
||||
<section className="list-wrapper">
|
||||
<TrackList
|
||||
show_source_icon={true}
|
||||
context="queue"
|
||||
className="queue-track-list"
|
||||
tracks={this.props.current_tracklist}
|
||||
removeTracks={ tracks => this.removeTracks( tracks ) }
|
||||
playTracks={ tracks => this.playTracks( tracks ) }
|
||||
playTrack={ track => this.playTrack( track ) }
|
||||
reorderTracks={ (indexes, index) => this.reorderTracks(indexes, index) } />
|
||||
</section>
|
||||
<div className="content-wrapper">
|
||||
|
||||
<div className="current-track">
|
||||
{ this.renderArtwork() }
|
||||
<div className="title">
|
||||
{this.props.current_track ? this.props.current_track.name : <span>-</span>}
|
||||
</div>
|
||||
{this.props.current_track ? <ArtistSentence artists={ this.props.current_track.artists } /> : <ArtistSentence />}
|
||||
</div>
|
||||
|
||||
<section className="list-wrapper">
|
||||
<TrackList
|
||||
show_source_icon={true}
|
||||
context="queue"
|
||||
className="queue-track-list"
|
||||
tracks={this.props.current_tracklist}
|
||||
removeTracks={ tracks => this.removeTracks( tracks ) }
|
||||
playTracks={ tracks => this.playTracks( tracks ) }
|
||||
playTrack={ track => this.playTrack( track ) }
|
||||
reorderTracks={ (indexes, index) => this.reorderTracks(indexes, index) } />
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -111,7 +150,9 @@ class Queue extends React.Component{
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {
|
||||
radio: state.ui.radio,
|
||||
current_tracklist: state.ui.current_tracklist
|
||||
radio_enabled: (state.ui.radio && state.ui.radio.enabled ? true : false),
|
||||
current_tracklist: state.ui.current_tracklist,
|
||||
current_track: (typeof(state.ui.current_track) !== 'undefined' && typeof(state.ui.tracks) !== 'undefined' && typeof(state.ui.tracks[state.ui.current_track.uri]) !== 'undefined' ? state.ui.tracks[state.ui.current_track.uri] : null),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -50,7 +50,7 @@ class QueueHistory extends React.Component{
|
||||
<div className="view queue-history-view">
|
||||
<Header icon="play" title="Playback history" options={options} uiActions={this.props.uiActions} />
|
||||
|
||||
<section className="list-wrapper">
|
||||
<section className="content-wrapper list-wrapper">
|
||||
<TrackList
|
||||
className="queue-history-track-list"
|
||||
show_source_icon={true}
|
||||
|
||||
@ -13,6 +13,7 @@ import ArtistGrid from '../components/ArtistGrid'
|
||||
import AlbumGrid from '../components/AlbumGrid'
|
||||
import PlaylistGrid from '../components/PlaylistGrid'
|
||||
import LazyLoadListener from '../components/LazyLoadListener'
|
||||
import SearchForm from '../components/SearchForm'
|
||||
|
||||
import * as helpers from '../helpers'
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
@ -22,22 +23,33 @@ import * as spotifyActions from '../services/spotify/actions'
|
||||
class Search extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
super(props)
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
this.props.uiActions.startSearch(this.props.params.type, this.props.params.query)
|
||||
|
||||
// Make sure we have search parameters to start with
|
||||
if (this.props.params && this.props.params.type && this.props.params.query){
|
||||
this.props.uiActions.startSearch(this.props.params.type, this.props.params.query)
|
||||
}
|
||||
|
||||
// Auto-focus on the input field
|
||||
$(document).find('.search-form input').focus();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps){
|
||||
|
||||
// Make sure we have some search parameters
|
||||
if (newProps.params && newProps.params.type && newProps.params.query){
|
||||
|
||||
if (this.props.params.query != newProps.params.query || this.props.params.type != newProps.params.type){
|
||||
this.props.uiActions.startSearch(newProps.params.type, newProps.params.query)
|
||||
}
|
||||
if (this.props.params.query != newProps.params.query || this.props.params.type != newProps.params.type){
|
||||
this.props.uiActions.startSearch(newProps.params.type, newProps.params.query)
|
||||
}
|
||||
|
||||
// mopidy comes online
|
||||
if (!this.props.mopidy_connected && newProps.mopidy_connected){
|
||||
this.props.uiActions.startSearch(newProps.params.type, newProps.params.query, true)
|
||||
// mopidy comes online
|
||||
if (!this.props.mopidy_connected && newProps.mopidy_connected){
|
||||
this.props.uiActions.startSearch(newProps.params.type, newProps.params.query, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,7 +195,7 @@ class Search extends React.Component{
|
||||
</div>
|
||||
|
||||
<section className="list-wrapper">
|
||||
<h4 className="left-padding"><Link to={global.baseURL+'search/'+this.props.params.query+'/tracks'}>Tracks</Link></h4>
|
||||
<h4><Link to={global.baseURL+'search/'+this.props.params.query+'/tracks'}>Tracks</Link></h4>
|
||||
<TrackList tracks={tracks} uri={'iris:search:'+this.props.params.type+':'+this.props.params.query} show_source_icon />
|
||||
<LazyLoadListener enabled={this.props['tracks_more'] && spotify_search_enabled} loadMore={ () => this.loadMore('tracks') }/>
|
||||
</section>
|
||||
@ -234,8 +246,11 @@ class Search extends React.Component{
|
||||
|
||||
return (
|
||||
<div className="view search-view">
|
||||
<Header icon="search" title="Search results" options={options} uiActions={this.props.uiActions} />
|
||||
{ this.renderResults() }
|
||||
<Header icon="search" options={options} uiActions={this.props.uiActions} />
|
||||
<SearchForm query={this.props.params.query} />
|
||||
<div className="content-wrapper">
|
||||
{ this.renderResults() }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -180,7 +180,7 @@ class Settings extends React.Component{
|
||||
<div className="view settings-view">
|
||||
<Header icon="cog" title="Settings" options={options} uiActions={this.props.uiActions} />
|
||||
|
||||
<section>
|
||||
<section className="content-wrapper">
|
||||
|
||||
<div className="status">
|
||||
|
||||
|
||||
@ -8,7 +8,8 @@ import Thumbnail from '../components/Thumbnail'
|
||||
import PlaylistGrid from '../components/PlaylistGrid'
|
||||
import FollowButton from '../components/FollowButton'
|
||||
import LazyLoadListener from '../components/LazyLoadListener'
|
||||
import Header from '../components/Header'
|
||||
import SidebarToggleButton from '../components/SidebarToggleButton'
|
||||
import Parallax from '../components/Parallax'
|
||||
import ContextMenuTrigger from '../components/ContextMenuTrigger'
|
||||
|
||||
import * as helpers from '../helpers'
|
||||
@ -54,41 +55,48 @@ class User extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.user && this.props.user.images ){
|
||||
var image = helpers.sizedImages(this.props.user.images).huge
|
||||
} else {
|
||||
var image = null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="view user-view">
|
||||
|
||||
<Header icon="play" title="User" />
|
||||
|
||||
<SidebarToggleButton />
|
||||
|
||||
<div className="intro">
|
||||
<Thumbnail circle={true} size="medium" images={ this.props.user.images } />
|
||||
|
||||
<h1>{ this.props.user.display_name ? this.props.user.display_name : this.props.user.id }</h1>
|
||||
|
||||
<ul className="details">
|
||||
{this.isMe() ? <li>You</li> : null}
|
||||
<li>{this.props.user.playlists_total ? this.props.user.playlists_total.toLocaleString() : 0} playlists</li>
|
||||
<li>{this.props.user.followers.total.toLocaleString()} followers</li>
|
||||
</ul>
|
||||
|
||||
<div className="actions">
|
||||
<FollowButton className="secondary" uri={this.props.params.uri} addText="Follow" removeText="Unfollow" />
|
||||
<Parallax image={image} />
|
||||
<div className="liner">
|
||||
<Thumbnail image={image} canZoom circle />
|
||||
<h1>{ this.props.user.display_name ? this.props.user.display_name : this.props.user.id }</h1>
|
||||
<h2>
|
||||
<ul className="details">
|
||||
<li>{this.props.user.playlists_total ? this.props.user.playlists_total.toLocaleString() : 0} playlists</li>
|
||||
<li>{this.props.user.followers.total.toLocaleString()} followers</li>
|
||||
{this.isMe() ? <li>You</li> : null}
|
||||
</ul>
|
||||
</h2>
|
||||
<div className="actions">
|
||||
<FollowButton className="secondary" uri={this.props.params.uri} addText="Follow" removeText="Unfollow" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="main">
|
||||
|
||||
|
||||
<div className="content-wrapper">
|
||||
<section className="grid-wrapper">
|
||||
<h4>Playlists</h4>
|
||||
<PlaylistGrid playlists={playlists} />
|
||||
<LazyLoadListener enabled={this.props.user.playlists_more} loadMore={ () => this.loadMore() }/>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
|
||||
return (
|
||||
<div className="view user-view">
|
||||
<Header icon="play" title="User" />
|
||||
<div className="view user-view">
|
||||
<div className="intro">
|
||||
<Thumbnail circle size="medium" images={[]} />
|
||||
<h1><span className="placeholder"></span></h1>
|
||||
|
||||
@ -43,7 +43,7 @@ class DiscoverCategories extends React.Component{
|
||||
return (
|
||||
<div className="view discover-categories-view">
|
||||
<Header icon="grid" title="Genre / Mood" />
|
||||
<section className="grid-wrapper">
|
||||
<section className="content-wrapper grid-wrapper">
|
||||
<CategoryGrid categories={categories} />
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@ -62,10 +62,12 @@ class DiscoverCategory extends React.Component{
|
||||
return (
|
||||
<div className="view discover-categories-view">
|
||||
<Header icon="grid" title={this.props.category.name} />
|
||||
<section className="grid-wrapper">
|
||||
<PlaylistGrid playlists={playlists} />
|
||||
</section>
|
||||
<LazyLoadListener enabled={this.props.category.playlists_more} loadMore={ () => this.loadMore() }/>
|
||||
<div className="content-wrapper">
|
||||
<section className="grid-wrapper">
|
||||
<PlaylistGrid playlists={playlists} />
|
||||
</section>
|
||||
<LazyLoadListener enabled={this.props.category.playlists_more} loadMore={ () => this.loadMore() }/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ class DiscoverFeatured extends React.Component{
|
||||
<Thumbnail images={playlist.images} />
|
||||
</Link>
|
||||
<Link to={global.baseURL+'playlist/'+playlist.uri}>
|
||||
<h1>{playlist.name}</h1>
|
||||
<h2>{playlist.name}</h2>
|
||||
</Link>
|
||||
{playlist.description ? <h3 dangerouslySetInnerHTML={{__html: playlist.description}}></h3> : null}
|
||||
<div className="actions">
|
||||
@ -101,7 +101,7 @@ class DiscoverFeatured extends React.Component{
|
||||
<div className="view discover-featured-view">
|
||||
<Header className="overlay" icon="star" title="Featured playlists" />
|
||||
{this.renderIntro(first_playlist)}
|
||||
<section className="grid-wrapper">
|
||||
<section className="content-wrapper grid-wrapper">
|
||||
{playlists ? <PlaylistGrid playlists={playlists} /> : null }
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@ -57,7 +57,7 @@ class DiscoverNewReleases extends React.Component{
|
||||
<Thumbnail images={album.images} />
|
||||
</Link>
|
||||
<Link to={global.baseURL+'album/'+album.uri}>
|
||||
<h1>{album.name}</h1>
|
||||
<h2>{album.name}</h2>
|
||||
</Link>
|
||||
<h3>
|
||||
<ArtistSentence artists={album.artists} />
|
||||
@ -109,7 +109,7 @@ class DiscoverNewReleases extends React.Component{
|
||||
<div className="view discover-new-releases-view">
|
||||
<Header className="overlay" icon="leaf" title="New Releases" />
|
||||
{this.renderIntro(first_album)}
|
||||
<section className="grid-wrapper">
|
||||
<section className="content-wrapper grid-wrapper">
|
||||
<AlbumGrid albums={albums} />
|
||||
</section>
|
||||
<LazyLoadListener enabled={this.props.new_releases_more} loadMore={ () => this.loadMore() }/>
|
||||
|
||||
@ -209,7 +209,7 @@ class Discover extends React.Component{
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="recommendations-results">
|
||||
<div className="content-wrapper recommendations-results">
|
||||
<section className="grid-wrapper">
|
||||
<h4>Artists</h4>
|
||||
<ArtistGrid artists={artists} />
|
||||
@ -219,7 +219,7 @@ class Discover extends React.Component{
|
||||
<AlbumGrid albums={albums} />
|
||||
</section>
|
||||
<section className="list-wrapper">
|
||||
<h4 className="left-padding">Tracks</h4>
|
||||
<h4>Tracks</h4>
|
||||
{this.props.recommendations.tracks ? <TrackList className="discover-track-list" uri={uri} tracks={this.props.recommendations.tracks} /> : null}
|
||||
</section>
|
||||
</div>
|
||||
@ -229,15 +229,14 @@ class Discover extends React.Component{
|
||||
render(){
|
||||
return (
|
||||
<div className="view discover-view">
|
||||
<Header icon="compass" title="Discover" className="overlay" />
|
||||
<div className="intro">
|
||||
<Parallax image="/iris/assets/backgrounds/discover.jpg" />
|
||||
<div className="liner">
|
||||
<h1>Explore new music</h1>
|
||||
<h3>
|
||||
<h2 className="grey-text">
|
||||
Add seeds below to build your sound. Let's start with
|
||||
{this.props.authorized ? " two of your favorite artists" : " the chill genre"}.
|
||||
</h3>
|
||||
</h2>
|
||||
{this.renderSeeds()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -82,7 +82,7 @@ class LibraryAlbums extends React.Component{
|
||||
}
|
||||
]
|
||||
return (
|
||||
<section className="list-wrapper">
|
||||
<section className="content-wrapper">
|
||||
<List
|
||||
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
||||
rows={albums}
|
||||
@ -93,7 +93,7 @@ class LibraryAlbums extends React.Component{
|
||||
)
|
||||
}else if( this.props.view == 'thumbnails' ){
|
||||
return (
|
||||
<section className="grid-wrapper">
|
||||
<section className="content-wrapper">
|
||||
<AlbumGrid
|
||||
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
||||
albums={albums} />
|
||||
@ -101,7 +101,7 @@ class LibraryAlbums extends React.Component{
|
||||
)
|
||||
}else{
|
||||
return (
|
||||
<section className="grid-wrapper albums-detail-subview">
|
||||
<section className="content-wrapper albums-detail-subview">
|
||||
{
|
||||
albums.map( album => {
|
||||
return (
|
||||
@ -111,9 +111,9 @@ class LibraryAlbums extends React.Component{
|
||||
</Link>
|
||||
<div className="detail">
|
||||
<Link to={global.baseURL+'album/'+album.uri}>
|
||||
<h3>{ album.name }</h3>
|
||||
<h2>{ album.name }</h2>
|
||||
</Link>
|
||||
<h4><ArtistSentence className="grey-text" artists={album.artists} /></h4>
|
||||
<h3><ArtistSentence className="grey-text" artists={album.artists} /></h3>
|
||||
</div>
|
||||
<div className="list-wrapper">
|
||||
<TrackList tracks={album.tracks} />
|
||||
|
||||
@ -69,7 +69,7 @@ class LibraryArtists extends React.Component{
|
||||
}
|
||||
]
|
||||
return (
|
||||
<section className="list-wrapper">
|
||||
<section className="content-wrapper">
|
||||
<List
|
||||
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
||||
rows={artists}
|
||||
@ -80,7 +80,7 @@ class LibraryArtists extends React.Component{
|
||||
)
|
||||
}else{
|
||||
return (
|
||||
<section className="grid-wrapper">
|
||||
<section className="content-wrapper">
|
||||
<ArtistGrid
|
||||
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
||||
artists={artists} />
|
||||
|
||||
@ -36,7 +36,7 @@ export default class LibraryLocal extends React.Component{
|
||||
return (
|
||||
<div className="view library-local-view">
|
||||
<Header icon="folder" title="Local" />
|
||||
<section className="grid-wrapper">
|
||||
<section className="content-wrapper">
|
||||
<div className="grid category-grid">
|
||||
{
|
||||
grid_items.map(
|
||||
|
||||
@ -91,7 +91,7 @@ class LibraryLocalAlbums extends React.Component{
|
||||
]
|
||||
|
||||
return (
|
||||
<section className="list-wrapper">
|
||||
<section className="content-wrapper">
|
||||
<List
|
||||
columns={columns}
|
||||
rows={albums}
|
||||
@ -101,7 +101,7 @@ class LibraryLocalAlbums extends React.Component{
|
||||
)
|
||||
}else{
|
||||
return (
|
||||
<section className="grid-wrapper">
|
||||
<section className="content-wrapper">
|
||||
<AlbumGrid albums={albums} />
|
||||
</section>
|
||||
)
|
||||
|
||||
@ -69,7 +69,7 @@ class LibraryLocalArtists extends React.Component{
|
||||
return (
|
||||
<div className="view library-local-view">
|
||||
<Header icon="music" title="Local artists" />
|
||||
<section className="list-wrapper">
|
||||
<section className="content-wrapper">
|
||||
<List
|
||||
columns={[{ name: 'name', width: '100'}]}
|
||||
rows={artists}
|
||||
|
||||
@ -94,7 +94,7 @@ class LibraryLocalDirectory extends React.Component{
|
||||
return (
|
||||
<div className="view library-local-view">
|
||||
<Header icon="music" title="Local files" options={options} uiActions={this.props.uiActions} />
|
||||
<section className="list-wrapper">
|
||||
<section className="content-wrapper">
|
||||
<List
|
||||
columns={[{ name: 'name', width: '100'}]}
|
||||
rows={items.folders}
|
||||
|
||||
@ -91,7 +91,7 @@ class LibraryPlaylists extends React.Component{
|
||||
}
|
||||
]
|
||||
return (
|
||||
<section className="list-wrapper">
|
||||
<section className="content-wrapper">
|
||||
<List
|
||||
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
||||
rows={playlists}
|
||||
@ -102,7 +102,7 @@ class LibraryPlaylists extends React.Component{
|
||||
)
|
||||
}else{
|
||||
return (
|
||||
<section className="grid-wrapper">
|
||||
<section className="content-wrapper">
|
||||
<PlaylistGrid
|
||||
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
|
||||
playlists={playlists} />
|
||||
|
||||
@ -42,7 +42,7 @@ class LibraryTracks extends React.Component{
|
||||
return (
|
||||
<div className="view library-tracks-view">
|
||||
<Header icon="music" title="My tracks" />
|
||||
<section className="list-wrapper">
|
||||
<section className="content-wrapper">
|
||||
{ this.props.tracks ? <TrackList tracks={this.props.tracks} /> : null }
|
||||
<LazyLoadListener enabled={this.props.tracks_more} loadMore={ () => this.loadMore() }/>
|
||||
</section>
|
||||
|
||||
@ -12,9 +12,8 @@
|
||||
@import 'components/dragger';
|
||||
@import 'components/images';
|
||||
@import 'components/icon';
|
||||
@import 'components/search-form';
|
||||
@import 'components/slider';
|
||||
@import 'components/player';
|
||||
@import 'components/playback-controls';
|
||||
@import 'components/sidebar';
|
||||
@import 'components/sidebar-toggle-button';
|
||||
@import 'components/lists';
|
||||
|
||||
@ -4,9 +4,13 @@
|
||||
z-index: 99;
|
||||
|
||||
.liner {
|
||||
background: $dark_grey;
|
||||
color: #FFFFFF;
|
||||
width: 160px;
|
||||
box-shadow: 2px 3px 6px rgba(0,0,0,0.2);
|
||||
border-radius: 3px;
|
||||
background: $dark_grey;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
font-family: "Overpass";
|
||||
|
||||
.title {
|
||||
display: block;
|
||||
@ -58,12 +62,6 @@
|
||||
font-weight: 200;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.notouch &:hover{
|
||||
.background {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item-wrapper {
|
||||
@ -73,11 +71,38 @@
|
||||
.menu-item {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding: 12px 14px;
|
||||
padding: 9px 12px 8px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
color: $white;
|
||||
border-radius: 3px;
|
||||
|
||||
&:before {
|
||||
@include animate();
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
z-index: 9;
|
||||
background: -webkit-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: -moz-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: -o-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
}
|
||||
|
||||
.notouch &:hover {
|
||||
background: lighten($dark_grey, 5%);
|
||||
&:before {
|
||||
opacity: 0.15;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $black;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +129,7 @@
|
||||
width: 0;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
background: lighten($dark_grey, 15%);
|
||||
background: $faint_grey;
|
||||
z-index: 3;
|
||||
|
||||
&.expanded {
|
||||
@ -116,20 +141,8 @@
|
||||
width: 100%;
|
||||
|
||||
&.close-submenu {
|
||||
background: $dark_grey;
|
||||
border-top: 0;
|
||||
color: $mid_grey;
|
||||
}
|
||||
|
||||
.notouch &:hover {
|
||||
background: lighten($dark_grey, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-child){
|
||||
.menu-item {
|
||||
border-top: 1px solid lighten($dark_grey, 8%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -232,10 +245,25 @@
|
||||
max-width: 90vw;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
border-radius: 3px;
|
||||
|
||||
.title {
|
||||
padding: 16px 20px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
& > span {
|
||||
|
||||
button,
|
||||
.button {
|
||||
color: $white;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.dropdown-field {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item-wrapper {
|
||||
@ -248,6 +276,59 @@
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu-trigger {
|
||||
@include animate();
|
||||
display: inline-block;
|
||||
padding: 14px 12px;
|
||||
cursor: pointer;
|
||||
vertical-align: top;
|
||||
margin-top: -1px;
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
|
||||
.dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 5px;
|
||||
background: $black;
|
||||
display: inline-block;
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
&.white {
|
||||
.dot {
|
||||
background: $white;
|
||||
}
|
||||
}
|
||||
|
||||
&:before {
|
||||
@include animate();
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
z-index: 9;
|
||||
background: -webkit-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: -moz-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: -o-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
}
|
||||
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
box-shadow: 2px 3px 6px rgba(0,0,0,0.2);
|
||||
|
||||
&:before {
|
||||
opacity: 0.15;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
&.track-list {
|
||||
|
||||
@ -258,17 +339,21 @@
|
||||
@include responsive( $bp_medium ){
|
||||
|
||||
.context-menu-trigger {
|
||||
@include gradient_overlay(50%, 0.15);
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 50px;
|
||||
bottom: 55px;
|
||||
right: 5px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
width: 48px;
|
||||
height: 8px;
|
||||
padding: 20px 0;
|
||||
overflow: hidden;
|
||||
z-index: 97;
|
||||
border-radius: 50%;
|
||||
background: $blue;
|
||||
color: #FFFFFF;
|
||||
|
||||
.dot {
|
||||
background: $white;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
|
||||
@ -1,20 +1,23 @@
|
||||
|
||||
.dropdown-field {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
font-family: "Overpass";
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
.label {
|
||||
padding: 14px 10px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
.label {
|
||||
background: $dark_grey;
|
||||
color: #FFFFFF;
|
||||
color: $blue;
|
||||
}
|
||||
.options {
|
||||
display: block;
|
||||
@ -22,29 +25,66 @@
|
||||
}
|
||||
|
||||
.options {
|
||||
color: #FFFFFF;
|
||||
color: $black;
|
||||
position: absolute;
|
||||
top: 52px;
|
||||
right: 0px;
|
||||
top: 46px;
|
||||
right: 10px;
|
||||
z-index: 97;
|
||||
background: $dark_grey;
|
||||
background: $white;
|
||||
display: none;
|
||||
box-shadow: 2px 3px 6px rgba(0,0,0,0.2);
|
||||
border-radius: 3px;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: 10px;
|
||||
background: $white;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-bottom: 6px solid $mid_grey;
|
||||
}
|
||||
|
||||
.option {
|
||||
@include animate();
|
||||
position: relative;
|
||||
padding: 6px 14px 6px 30px;
|
||||
border-top: 1px solid lighten($dark_grey, 10%);
|
||||
padding: 9px 12px 8px 24px;
|
||||
cursor: pointer;
|
||||
|
||||
&:before {
|
||||
@include animate();
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
z-index: 9;
|
||||
background: -webkit-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: -moz-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: -o-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
}
|
||||
|
||||
.fa {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 10px;
|
||||
font-size: 12px;
|
||||
top: 9px;
|
||||
left: 8px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: lighten($dark_grey, 10%);
|
||||
color: $black;
|
||||
|
||||
&:before {
|
||||
opacity: 0.15;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,27 +1,26 @@
|
||||
|
||||
main {
|
||||
header {
|
||||
padding: 14px 20px;
|
||||
background: #EEEEEE;
|
||||
line-height: 24px;
|
||||
box-sizing: border-box;
|
||||
padding: 40px;
|
||||
margin-bottom: -40px;
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
font-weight: 100;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
padding-right: 14px;
|
||||
height: 16px;
|
||||
height: 2.6rem;
|
||||
vertical-align: bottom;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.options {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 10px;
|
||||
top: 2.6rem;
|
||||
right: 40px;
|
||||
|
||||
.context-menu-trigger {
|
||||
display: none;
|
||||
@ -31,19 +30,23 @@ main {
|
||||
|
||||
.button,
|
||||
button {
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
font-family: "Overpass";
|
||||
background: transparent;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
text-transform: none;
|
||||
padding: 13px 10px;
|
||||
margin: 0;
|
||||
line-height: 24px;
|
||||
text-transform: uppercase;
|
||||
min-width: 0;
|
||||
border: 0;
|
||||
color: #000000;
|
||||
color: $black;
|
||||
|
||||
&:active {
|
||||
background: rgba(0,0,0,0.15);
|
||||
&:hover {
|
||||
color: $blue;
|
||||
box-shadow: none;
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,7 +56,6 @@ main {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: rgba(0,0,0,0.1);
|
||||
color: $white;
|
||||
z-index: 95;
|
||||
|
||||
@ -65,11 +67,20 @@ main {
|
||||
}
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
padding: 10px 10px 10px 0;
|
||||
margin-bottom: -20px;
|
||||
|
||||
.icon {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
h1 {
|
||||
vertical-align: top;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.options {
|
||||
top: 8px;
|
||||
right: 0;
|
||||
|
||||
.context-menu-trigger {
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
background-color: $light_grey;
|
||||
background-position: 50% 50%;
|
||||
background-size: cover;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.zoom {
|
||||
|
||||
@ -12,14 +12,16 @@
|
||||
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 12px 10px 12px 20px;
|
||||
border-top: 1px solid $faint_grey;
|
||||
border-bottom: 1px solid $faint_grey;
|
||||
margin-bottom: -1px;
|
||||
padding: 12px 10px;
|
||||
margin: 0 -10px -1px -10px;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
border-bottom: 1px solid #FAFAFA;
|
||||
border-top: 1px solid #FAFAFA;
|
||||
|
||||
&.selected {
|
||||
background: $yellow !important;
|
||||
border-color: darken($yellow,10%);
|
||||
}
|
||||
|
||||
&.playing {
|
||||
@ -27,20 +29,11 @@
|
||||
}
|
||||
|
||||
.state-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
font-size: 10px;
|
||||
|
||||
.fa {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
}
|
||||
display: none;
|
||||
}
|
||||
|
||||
:root .notouch:not(.dragging) &:not(.header):not(.no-click):hover {
|
||||
background: rgba(150,150,150,0.1);
|
||||
background: lighten($yellow, 10%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -73,8 +66,6 @@
|
||||
|
||||
&.track-list {
|
||||
.list-item {
|
||||
padding-left: 40px;
|
||||
|
||||
.col {
|
||||
&.name {
|
||||
width: 40%;
|
||||
@ -179,6 +170,8 @@
|
||||
}
|
||||
|
||||
&.related-artist-list {
|
||||
padding-bottom: 20px;
|
||||
|
||||
.artist {
|
||||
display: block;
|
||||
border: 0;
|
||||
@ -209,8 +202,9 @@
|
||||
}
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
|
||||
.list-item {
|
||||
padding: 7px 30px 7px 48px !important;
|
||||
padding: 12px !important;
|
||||
|
||||
.source {
|
||||
position: static;
|
||||
@ -218,12 +212,16 @@
|
||||
}
|
||||
|
||||
.state-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 48px;
|
||||
font-size: 10px;
|
||||
display: block;
|
||||
|
||||
.fa {
|
||||
position: absolute;
|
||||
top: 19px;
|
||||
left: 17px;
|
||||
}
|
||||
@ -238,10 +236,6 @@
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
.playing {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.col {
|
||||
@ -310,5 +304,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.track-list {
|
||||
.list-item {
|
||||
padding: 7px 30px 7px 48px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
z-index: 999;
|
||||
background: $overlay_dark;
|
||||
overflow-y: scroll;
|
||||
|
||||
@ -75,19 +75,19 @@
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
padding: 16px 18px;
|
||||
border-bottom: 1px solid $mid_grey;
|
||||
border-top: 0;
|
||||
position: relative;
|
||||
|
||||
&:nth-child(1){
|
||||
border-top: 1px solid $mid_grey;
|
||||
}
|
||||
border: 0;
|
||||
|
||||
.source {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: transparent !important;
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
|
||||
&.playlists {
|
||||
@ -102,10 +102,6 @@
|
||||
width: 49%;
|
||||
float: left;
|
||||
|
||||
&:nth-child(2){
|
||||
border-top: 1px solid $mid_grey;
|
||||
}
|
||||
|
||||
&:nth-child(2n){
|
||||
margin-left: 2%;
|
||||
}
|
||||
@ -113,6 +109,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.add_to_queue,
|
||||
&.create_playlist {
|
||||
h1 {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
&.image_zoom {
|
||||
.content {
|
||||
@ -188,6 +190,10 @@
|
||||
.current-track {
|
||||
padding-top: 50px;
|
||||
font-size: 30px;
|
||||
|
||||
.artist-sentence {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
.notifications {
|
||||
position: fixed;
|
||||
bottom: 15px;
|
||||
right: 30px;
|
||||
bottom: 65px;
|
||||
right: 15px;
|
||||
z-index: 97;
|
||||
pointer-events: none;
|
||||
width: 300px;
|
||||
@ -17,6 +17,7 @@
|
||||
margin: 10px 0 0 0;
|
||||
color: $white;
|
||||
pointer-events: all;
|
||||
border-radius: 3px;
|
||||
|
||||
h4 {
|
||||
padding-top: 0;
|
||||
@ -64,17 +65,19 @@
|
||||
}
|
||||
|
||||
&:after {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: '';
|
||||
background: $blue;
|
||||
opacity: 1;
|
||||
background: $white;
|
||||
opacity: 0.1;
|
||||
animation: slideloader 1s infinite;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
z-index: 1;
|
||||
border-radius: 0 0 3px 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,9 +124,9 @@
|
||||
}
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
bottom: 48px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 60px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
width: auto;
|
||||
|
||||
.loading {
|
||||
|
||||
282
src/scss/components/_playback-controls.scss
Executable file
282
src/scss/components/_playback-controls.scss
Executable file
@ -0,0 +1,282 @@
|
||||
|
||||
.playback-controls {
|
||||
position: fixed;
|
||||
z-index: 99;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 50px;
|
||||
background: $light_grey;
|
||||
color: $black;
|
||||
|
||||
background: -webkit-linear-gradient(to bottom left, $light_grey, darken($light_grey,5%));
|
||||
background: -moz-linear-gradient(to bottom left, $light_grey, darken($light_grey,5%));
|
||||
background: -o-linear-gradient(to bottom left, $light_grey, darken($light_grey,5%));
|
||||
background: linear-gradient(to bottom left, $light_grey, darken($light_grey,5%));
|
||||
|
||||
.current-track {
|
||||
@include animate();
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
width: 220px;
|
||||
font-size: 14px;
|
||||
color: $black;
|
||||
text-decoration: none;
|
||||
|
||||
.thumbnail {
|
||||
float: left;
|
||||
margin: 12px;
|
||||
|
||||
&:before {
|
||||
@include animate();
|
||||
display: block;
|
||||
content: "\f065";
|
||||
font-family: "FontAwesome";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin-top: -9px;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
color: $white;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:after {
|
||||
@include animate();
|
||||
content: '';
|
||||
display: block;
|
||||
background: rgba(0,0,0,0.75);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-radius: 3px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover{
|
||||
&:after,
|
||||
&:before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-top: 20px;
|
||||
@include one_line_text;
|
||||
}
|
||||
|
||||
.artist-sentence {
|
||||
opacity: 0.5;
|
||||
display: block;
|
||||
@include one_line_text;
|
||||
|
||||
.artist {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.control {
|
||||
@include animate();
|
||||
font-size: 12px;
|
||||
color: $dark_grey;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
|
||||
&.play {
|
||||
font-size: 24px;
|
||||
margin-bottom: -6px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $turquoise !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
position: absolute;
|
||||
bottom: 11px;
|
||||
|
||||
&.playback {
|
||||
left: 20px;
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.progress {
|
||||
bottom: 12px;
|
||||
left: 140px;
|
||||
right: 280px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
color: $mid_grey;
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50px;
|
||||
right: 50px;
|
||||
}
|
||||
|
||||
.current {
|
||||
font-size: 11px;
|
||||
position: absolute;
|
||||
bottom: 6px;
|
||||
left: 0;
|
||||
width: 45px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.total {
|
||||
font-size: 11px;
|
||||
position: absolute;
|
||||
bottom: 6px;
|
||||
right: 0;
|
||||
width: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
&.settings {
|
||||
right: 170px;
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.volume {
|
||||
bottom: 10px;
|
||||
right: 20px;
|
||||
width: 150px;
|
||||
|
||||
.modal-trigger {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.control {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.slider-wrapper {
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 0;
|
||||
left: 30px;
|
||||
}
|
||||
|
||||
.muted {
|
||||
.slider .progress {
|
||||
background: $mid_grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.triggers {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive($bp_medium){
|
||||
|
||||
.current-track {
|
||||
width: auto;
|
||||
bottom: auto;
|
||||
bottom: 8px;
|
||||
left: 60px;
|
||||
right: 60px;
|
||||
text-align: center;
|
||||
|
||||
.title {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
|
||||
&.playback {
|
||||
left: 0;
|
||||
width: 60px;
|
||||
|
||||
.control {
|
||||
&.play {
|
||||
font-size: 12px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&.previous {
|
||||
display: none;
|
||||
}
|
||||
&.stop {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.settings {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.volume {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.progress {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.triggers {
|
||||
display: block;
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
height: 150px;
|
||||
|
||||
section {
|
||||
|
||||
&.settings {
|
||||
display: block;
|
||||
right: 20px;
|
||||
bottom: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&.volume {
|
||||
width: auto;
|
||||
display: block;
|
||||
right: 130px;
|
||||
left: 22px;
|
||||
bottom: 60px;
|
||||
}
|
||||
|
||||
&.progress {
|
||||
display: block;
|
||||
bottom: 110px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,16 +111,18 @@
|
||||
&.mini-player {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
width: 220px;
|
||||
height: 50px;
|
||||
background: $white;
|
||||
border-top: 1px solid $light_grey;
|
||||
box-sizing: border-box;
|
||||
z-index: 97;
|
||||
padding: 14px;
|
||||
color: #FFFFFF;
|
||||
color: $black;
|
||||
|
||||
.artist-sentence {
|
||||
.artist:hover {
|
||||
border-color: #FFFFFF !important;
|
||||
border-color: $black !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,3 +196,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.playback-controls {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
|
||||
.search-form {
|
||||
z-index: 3;
|
||||
position: relative;
|
||||
|
||||
input {
|
||||
background: transparent;
|
||||
border-bottom: 1px solid #FFFFFF;
|
||||
padding: 16px 0 8px;
|
||||
width: 100%;
|
||||
color: #FFFFFF;
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
border-color: $turquoise;
|
||||
}
|
||||
}
|
||||
|
||||
&.sidebar {
|
||||
padding: 10px 15px 0 15px;
|
||||
|
||||
.autocomplete-field {
|
||||
.results {
|
||||
top: 42px;
|
||||
}
|
||||
|
||||
&.loading {
|
||||
.input:after {
|
||||
background: $turquoise;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,10 +5,10 @@ aside{
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 96;
|
||||
background: $darkest_grey;
|
||||
width: 220px;
|
||||
overflow: hidden;
|
||||
box-shadow: inset -20px 0px 30px -20px rgba(0, 0, 0, 0.5);
|
||||
background: $off_white;
|
||||
color: $darkest_grey;
|
||||
|
||||
.liner {
|
||||
overflow-y: auto;
|
||||
@ -16,16 +16,83 @@ aside{
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
bottom: 120px;
|
||||
box-sizing: border-box;
|
||||
z-index: 2;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
nav {
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
padding-top: 20px;
|
||||
line-height: 1em;
|
||||
|
||||
a {
|
||||
@include feature_font();
|
||||
@include gradient_overlay(3px);
|
||||
font-weight: 800;
|
||||
font-size: 15px;
|
||||
color: $darkest_grey;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
padding: 9px 10px 5px;
|
||||
margin: 0 20px;
|
||||
border-radius: 3px;
|
||||
line-height: 18px;
|
||||
|
||||
.icon {
|
||||
height: 14px;
|
||||
padding-right: 8px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: $turquoise;
|
||||
color: $white;
|
||||
opacity: 1 !important;
|
||||
|
||||
&:before {
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.icon {
|
||||
@include invert();
|
||||
}
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&:hover:not([disabled]){
|
||||
&:before {
|
||||
opacity: 0.15;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
padding-bottom: 25px;
|
||||
|
||||
title {
|
||||
display: block;
|
||||
padding: 0 26px 5px 26px;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
opacity: 0.5;
|
||||
color: $mid_grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
width: 220px;
|
||||
left: -220px;
|
||||
padding-bottom: 0;
|
||||
position: fixed;
|
||||
z-index: 98;
|
||||
z-index: 100;
|
||||
|
||||
.sidebar-open & {
|
||||
left: 0px;
|
||||
@ -39,82 +106,4 @@ aside{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive( null, null, $bp_shallow ){
|
||||
.liner {
|
||||
bottom: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
right: -30px;
|
||||
bottom: -30px;
|
||||
left: -30px;
|
||||
height: auto;
|
||||
width: auto;
|
||||
max-width: none;
|
||||
z-index: 1;
|
||||
opacity: 0.2;
|
||||
|
||||
.image {
|
||||
@include blur();
|
||||
padding-bottom: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
nav {
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
padding-top: 8px;
|
||||
|
||||
a {
|
||||
font-size: 16px;
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
padding: 6px 18px 6px 14px;
|
||||
border-left: 4px solid transparent;
|
||||
|
||||
.icon {
|
||||
height: 14px;
|
||||
padding-right: 8px;
|
||||
vertical-align: top;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: $turquoise;
|
||||
background: rgba( 0, 0, 0, 0.5 );
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&:hover:not([disabled]) {
|
||||
background: rgba( 255, 255, 255, 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 10px 0;
|
||||
|
||||
title {
|
||||
display: block;
|
||||
padding: 0 18px 2px;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
opacity: 0.5;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
}
|
||||
}
|
||||
@ -5,13 +5,15 @@
|
||||
|
||||
.track {
|
||||
position: absolute;
|
||||
background: rgba(255,255,255,0.3);
|
||||
background: darken($light_grey, 6%);
|
||||
border-radius: 2px;
|
||||
|
||||
.progress {
|
||||
position: absolute;
|
||||
background: $turquoise;
|
||||
pointer-events: none;
|
||||
left: 0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,14 +24,14 @@
|
||||
}
|
||||
|
||||
&.horizontal {
|
||||
margin: 10px 0;
|
||||
height: 20px;
|
||||
|
||||
.track {
|
||||
height: 2px;
|
||||
top: 9px;
|
||||
height: 5px;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
left: 0;
|
||||
margin-top: -3px;
|
||||
|
||||
.progress {
|
||||
top: 0;
|
||||
@ -43,10 +45,10 @@
|
||||
height: 100%;
|
||||
|
||||
.track {
|
||||
width: 2px;
|
||||
width: 5px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 9px;
|
||||
left: 12px;
|
||||
|
||||
.progress {
|
||||
bottom: 0;
|
||||
|
||||
@ -2,11 +2,12 @@
|
||||
|
||||
body,
|
||||
html {
|
||||
background: $off_white;
|
||||
color: #000000;
|
||||
background: $white;
|
||||
color: $darkest_grey;
|
||||
font-family: "Archivo Narrow", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
touch-action: manipulation;
|
||||
text-shadow: 1px 1px 1px rgba(0,0,0,0.004);
|
||||
}
|
||||
|
||||
body {
|
||||
@ -41,6 +42,10 @@ main {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
|
||||
.content-wrapper {
|
||||
padding: 40px 40px 80px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
@ -58,20 +63,10 @@ main {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
section.grid-wrapper,
|
||||
section.text-wrapper {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
section.list-wrapper {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
|
||||
section.grid-wrapper,
|
||||
section.text-wrapper {
|
||||
padding: 20px;
|
||||
.content-wrapper {
|
||||
padding: 20px 10px 40px;
|
||||
}
|
||||
|
||||
section.list-wrapper {
|
||||
@ -88,13 +83,17 @@ main {
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
font-size: 40px;
|
||||
@include feature_font();
|
||||
font-weight: 800;
|
||||
font-size: 3rem;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 500;
|
||||
font-size: 30px;
|
||||
@include feature_font();
|
||||
font-size: 1.8rem;
|
||||
font-weight: 400;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@ -108,9 +107,10 @@ h3 {
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
@include feature_font();
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 14px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 800;
|
||||
|
||||
&.underline {
|
||||
padding-bottom: 4px;
|
||||
@ -268,15 +268,24 @@ footer {
|
||||
}
|
||||
|
||||
.flag {
|
||||
font-size: 12px;
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
padding: 2px 4px;
|
||||
padding: 2px 5px;
|
||||
background: $light_grey;
|
||||
color: $dark_grey;
|
||||
border-radius: 3px;
|
||||
vertical-align: bottom;
|
||||
margin: 0 8px 1px;
|
||||
|
||||
&.blue {
|
||||
background: $blue;
|
||||
color: #FFFFFF;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.dark {
|
||||
background: $dark_grey;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,8 +304,8 @@ footer {
|
||||
color: $white;
|
||||
font-size: 11px;
|
||||
line-height: 11px;
|
||||
top: -16px;
|
||||
left: 4px;
|
||||
top: -22px;
|
||||
left: 1px;
|
||||
z-index: 97;
|
||||
|
||||
&::before {
|
||||
@ -325,6 +334,20 @@ footer {
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
|
||||
h1 {
|
||||
font-size: 1.8rem;
|
||||
line-height: 1.8rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.3rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.right-padding {
|
||||
padding-right: 20px;
|
||||
}
|
||||
@ -332,17 +355,4 @@ footer {
|
||||
.left-padding {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
}
|
||||
@ -16,8 +16,8 @@ input[type="email"],
|
||||
input[type="date"],
|
||||
select {
|
||||
padding: 10px 14px;
|
||||
background: #FFFFFF;
|
||||
color: #000000;
|
||||
background: $faint_grey;
|
||||
color: $darkest_grey;
|
||||
font-family: "Archivo Narrow", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
@ -44,91 +44,59 @@ button,
|
||||
input[type="button"],
|
||||
input[type="submit"] {
|
||||
@include animate();
|
||||
@include feature_font();
|
||||
@include gradient_overlay();
|
||||
display: inline-block;
|
||||
padding: 6px 20px;
|
||||
min-width: 160px;
|
||||
border: 0;
|
||||
padding: 9px 30px 7px;
|
||||
margin: 0 20px 10px 0;
|
||||
text-align: center;
|
||||
border: 2px solid $mid_grey;
|
||||
color: $mid_grey;
|
||||
background: transparent;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
color: $white;
|
||||
background: $dark_grey;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.wide {
|
||||
min-width: 200px;
|
||||
}
|
||||
border-radius: 3px;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
border: 0 !important;
|
||||
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
border-color: darken( $mid_grey, 10%);
|
||||
box-shadow: 1px 2px 6px rgba(0,0,0,0.2);
|
||||
|
||||
&:before {
|
||||
opacity: 0.15;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
@include noanimate();
|
||||
background: $mid_grey;
|
||||
color: #FFFFFF;
|
||||
border-color: $mid_grey;
|
||||
background: $black;
|
||||
color: $white;
|
||||
margin-top: 1px;
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
|
||||
&.primary {
|
||||
border-color: $turquoise;
|
||||
color: $turquoise;
|
||||
&:hover {
|
||||
color: darken( $turquoise, 10% );
|
||||
border-color: darken( $turquoise, 10% );
|
||||
}
|
||||
background: $turquoise;
|
||||
color: $white;
|
||||
&:active {
|
||||
color: #FFFFFF;
|
||||
background: $turquoise;
|
||||
border-color: $turquoise;
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
color: $black;
|
||||
border-color: $black;
|
||||
&:hover {
|
||||
color: lighten( $black, 40% );
|
||||
border-color: lighten( $black, 40% );
|
||||
}
|
||||
&:active {
|
||||
color: $white;
|
||||
background: $black;
|
||||
border-color: $black;
|
||||
}
|
||||
}
|
||||
|
||||
&.alternative {
|
||||
color: $blue;
|
||||
border-color: $blue;
|
||||
&:hover {
|
||||
color: darken($blue, 10%);
|
||||
border-color: darken($blue, 10%);
|
||||
}
|
||||
&:active {
|
||||
color: $white;
|
||||
background: $blue;
|
||||
border-color: $blue;
|
||||
}
|
||||
&.white {
|
||||
color: $black;
|
||||
background: $white;
|
||||
}
|
||||
|
||||
&.confirming,
|
||||
&.destructive {
|
||||
border-color: $red;
|
||||
color: $red;
|
||||
&:hover {
|
||||
color: darken( $red, 6% );
|
||||
border-color: darken( $red, 6% );
|
||||
}
|
||||
&:active {
|
||||
color: #FFFFFF;
|
||||
background: $red;
|
||||
border-color: $red;
|
||||
}
|
||||
background: $red;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.alternative {
|
||||
background: $blue;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.timing-out {
|
||||
@ -137,25 +105,12 @@ input[type="submit"] {
|
||||
@include animate_timeout(2s);
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
height: 3px;
|
||||
background: $red;
|
||||
}
|
||||
}
|
||||
|
||||
&.white {
|
||||
color: #FFFFFF;
|
||||
border-color: #FFFFFF;
|
||||
&:hover {
|
||||
color: rgba(255,255,255,0.8);
|
||||
border-color: rgba(255,255,255,0.8);
|
||||
}
|
||||
&:active {
|
||||
color: #000000;
|
||||
background: #FFFFFF;
|
||||
border-color: #FFFFFF;
|
||||
background: $black;
|
||||
opacity: 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,81 +127,19 @@ input[type="submit"] {
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: '';
|
||||
background: $mid_grey;
|
||||
opacity: 1;
|
||||
animation: slideloader 1s infinite;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: $mid_grey;
|
||||
opacity: 0.5;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 3px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&.primary {
|
||||
&:after {
|
||||
background: $turquoise;
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
&:after {
|
||||
background: $black;
|
||||
}
|
||||
}
|
||||
|
||||
&.destructive {
|
||||
&:after {
|
||||
background: $red;
|
||||
}
|
||||
}
|
||||
|
||||
&.white {
|
||||
&:after {
|
||||
background: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.context-menu-trigger {
|
||||
border-color: transparent;
|
||||
padding: 6px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 20px;
|
||||
min-width: 0;
|
||||
line-height: 20px;
|
||||
color: #000000;
|
||||
vertical-align: bottom;
|
||||
|
||||
.fa {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: transparent;
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #FFFFFF;
|
||||
background: #000000;
|
||||
}
|
||||
|
||||
&.white {
|
||||
color: #FFFFFF;
|
||||
|
||||
&:hover {
|
||||
border-color: transparent;
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #000000;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&[disabled],
|
||||
@ -273,20 +166,12 @@ input[type="submit"] {
|
||||
}
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
min-width: 120px;
|
||||
margin-right: 10px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
@include responsive( $bp_narrow ){
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
|
||||
.button:last-of-type,
|
||||
button:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
|
||||
$turquoise: #08d58f;
|
||||
$off_white: #F5F5F5;
|
||||
$faint_grey: #EEEEEE;
|
||||
$light_grey: #DDDDDD;
|
||||
$mid_grey: #AAAAAA;
|
||||
$dark_grey: #333333;
|
||||
$darkest_grey: #111111;
|
||||
$faint_grey: #f4f2f0;
|
||||
$light_grey: #edeae7;
|
||||
$mid_grey: #a3a19f;
|
||||
$grey: #888888;
|
||||
$dark_grey: #232221;
|
||||
$darkest_grey: #1b1a19;
|
||||
$secondary_grey: #888888;
|
||||
$white: #FFFFFF;
|
||||
$black: #000000;
|
||||
@ -18,6 +19,7 @@ $overlay_dark: rgba(10, 10, 10, 0.95);
|
||||
|
||||
$bp_wide: 1000px;
|
||||
$bp_medium: 800px;
|
||||
$bp_small: 500px;
|
||||
$bp_narrow: 350px;
|
||||
$bp_shallow: 650px;
|
||||
|
||||
@ -112,11 +114,6 @@ $bp_shallow: 650px;
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
@mixin blur( $size: 10px ) {
|
||||
-webkit-filter: blur( $size );
|
||||
filter: blur( $size );
|
||||
}
|
||||
|
||||
@keyframes slideloader {
|
||||
0% { left: 0%; right: 100%; }
|
||||
50% { left: 0%; right: 0%; }
|
||||
@ -177,3 +174,41 @@ $bp_shallow: 650px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin blur( $size: 10px ) {
|
||||
-webkit-filter: blur( $size );
|
||||
filter: blur( $size );
|
||||
}
|
||||
|
||||
@mixin feature_font(){
|
||||
font-family: "Overpass";
|
||||
letter-spacing: -0.04em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@mixin invert(){
|
||||
-webkit-filter: invert(1);
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
@mixin gradient_overlay($radius: 0, $opacity: 0){
|
||||
position: relative;
|
||||
border-radius: $radius;
|
||||
|
||||
&:before {
|
||||
@include animate();
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: $opacity;
|
||||
z-index: 9;
|
||||
background: -webkit-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: -moz-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: -o-linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
background: linear-gradient(to bottom right, rgba(255,255,255,1), rgba(0,0,0,0.5));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,53 +1,77 @@
|
||||
|
||||
.album-view {
|
||||
main .album-view {
|
||||
padding-left: 30%;
|
||||
position: relative;
|
||||
|
||||
.title {
|
||||
padding: 30px 40px;
|
||||
|
||||
h3 {
|
||||
color: $mid_grey;
|
||||
h2 {
|
||||
a:hover {
|
||||
color: darken($mid_grey, 20%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.source {
|
||||
text-transform: uppercase;
|
||||
padding-bottom: 15px;
|
||||
.details {
|
||||
padding: 25px 0 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
@include clearfix();
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.description {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.thumbnail-wrapper {
|
||||
position: absolute;
|
||||
padding: 40px;
|
||||
width: 30%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
.thumbnail {
|
||||
float: left;
|
||||
max-width: 230px;
|
||||
margin: 0 30px 30px 0;
|
||||
.thumbnail {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
.title {
|
||||
padding: 20px 20px;
|
||||
padding-top: 0;
|
||||
padding-left: 10px;
|
||||
|
||||
.thumbnail-wrapper {
|
||||
width: 40%;
|
||||
padding: 10px 20px 10px 0;
|
||||
float: left;
|
||||
position: static;
|
||||
|
||||
.thumbnail {
|
||||
float: none;
|
||||
max-width: none;
|
||||
margin: 0 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
float: none;
|
||||
max-width: none;
|
||||
margin: 0 0 0 0;
|
||||
.title {
|
||||
padding: 30px 0 20px 40%;
|
||||
|
||||
.image {
|
||||
padding-bottom: 70%;
|
||||
.details {
|
||||
padding: 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
text-align: center;
|
||||
padding-bottom: 20px;
|
||||
@include clearfix();
|
||||
padding: 10px 0 20px 40%;
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive( $bp_small ){
|
||||
.title {
|
||||
@include clearfix();
|
||||
padding: 20px 20px 10px 40%;
|
||||
min-height: 20vw;
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
.intro {
|
||||
position: relative;
|
||||
@include clearfix();
|
||||
margin-bottom: 40px;
|
||||
|
||||
.parallax {
|
||||
position: absolute;
|
||||
@ -18,7 +17,7 @@
|
||||
.liner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 20vh 40px 0 260px;
|
||||
padding: 30vh 40px 0 260px;
|
||||
|
||||
.thumbnail {
|
||||
position: absolute;
|
||||
@ -33,12 +32,16 @@
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #FFFFFF;
|
||||
color: $white;
|
||||
padding-bottom: 25px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding-bottom: 15px;
|
||||
|
||||
.context-menu-trigger {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.sub-views {
|
||||
@ -68,7 +71,6 @@
|
||||
|
||||
.body.about {
|
||||
@include clearfix();
|
||||
padding: 0 40px 40px 40px;
|
||||
|
||||
.biography {
|
||||
padding-left: 40px;
|
||||
@ -82,7 +84,7 @@
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
.intro {
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 0;
|
||||
|
||||
.parallax {
|
||||
canvas {
|
||||
@ -91,20 +93,28 @@
|
||||
}
|
||||
|
||||
.liner {
|
||||
text-align: center;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
padding-top: 10vh;
|
||||
|
||||
.thumbnail {
|
||||
position: static;
|
||||
float: none;
|
||||
margin: 0 auto 20px;
|
||||
max-width: 65%;
|
||||
float: left;
|
||||
margin: 0 20px 0 20px;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@include clearfix();
|
||||
padding: 12vw 10px 0 0;
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding: 30px 10px 10px;
|
||||
}
|
||||
|
||||
.sub-views {
|
||||
margin-left: 0;
|
||||
margin-left: 5px;
|
||||
|
||||
.option {
|
||||
margin: 0 8px;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
.intro {
|
||||
position: relative;
|
||||
padding: 100px 40px 60px 40px;
|
||||
padding: 90px 40px 70px;
|
||||
z-index: 1;
|
||||
|
||||
.parallax {
|
||||
@ -20,7 +20,7 @@
|
||||
position: relative;
|
||||
color: $white;
|
||||
|
||||
h1 {
|
||||
h2 {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
}
|
||||
|
||||
.seeds {
|
||||
padding-top: 40px;
|
||||
|
||||
.seed {
|
||||
display: inline-block;
|
||||
@ -50,6 +51,7 @@
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
margin: 0 10px 10px 0;
|
||||
border-radius: 3px;
|
||||
|
||||
.type {
|
||||
display: inline-block;
|
||||
@ -109,7 +111,11 @@
|
||||
.discover-new-releases-view {
|
||||
.intro {
|
||||
position: relative;
|
||||
padding-top: 50px;
|
||||
padding-top: 80px;
|
||||
|
||||
header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.parallax {
|
||||
position: absolute;
|
||||
@ -132,12 +138,13 @@
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
.actions {
|
||||
padding-left: 240px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
h2 {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
@ -160,7 +167,7 @@
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
.actions {
|
||||
padding-left: 120px;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
.library-albums-view {
|
||||
.albums-detail-subview > .album {
|
||||
position: relative;
|
||||
padding-bottom: 40px;
|
||||
padding-bottom: 60px;
|
||||
min-height: 220px;
|
||||
|
||||
.thumbnail {
|
||||
@ -11,19 +11,12 @@
|
||||
}
|
||||
|
||||
.detail {
|
||||
padding-left: 220px;
|
||||
|
||||
h3 {
|
||||
padding: 0 0 0 40px;
|
||||
}
|
||||
|
||||
h4{
|
||||
padding: 0 0 10px 40px;
|
||||
}
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.detail,
|
||||
.track-list {
|
||||
padding-left: 220px;
|
||||
padding-left: 260px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,54 +1,77 @@
|
||||
|
||||
.playlist-view {
|
||||
main .playlist-view {
|
||||
padding-left: 30%;
|
||||
position: relative;
|
||||
|
||||
.title {
|
||||
padding: 30px 40px;
|
||||
|
||||
h3 {
|
||||
color: $mid_grey;
|
||||
h2 {
|
||||
a:hover {
|
||||
color: darken($mid_grey, 20%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.source {
|
||||
text-transform: uppercase;
|
||||
padding-bottom: 15px;
|
||||
.details {
|
||||
padding: 25px 0 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
@include clearfix();
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.description {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.thumbnail-wrapper {
|
||||
position: absolute;
|
||||
padding: 40px;
|
||||
width: 30%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
.thumbnail {
|
||||
float: left;
|
||||
max-width: 230px;
|
||||
margin: 0 30px 30px 0;
|
||||
.thumbnail {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
.title {
|
||||
padding: 20px 20px;
|
||||
padding-top: 0;
|
||||
padding-left: 10px;
|
||||
|
||||
.thumbnail-wrapper {
|
||||
width: 40%;
|
||||
padding: 10px 20px 10px 0;
|
||||
float: left;
|
||||
position: static;
|
||||
|
||||
.thumbnail {
|
||||
float: none;
|
||||
max-width: none;
|
||||
margin: 0 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
float: none;
|
||||
max-width: none;
|
||||
margin: 0 0 0 0;
|
||||
.title {
|
||||
padding: 30px 0 20px 40%;
|
||||
|
||||
.image {
|
||||
padding-bottom: 70%;
|
||||
.details {
|
||||
padding: 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
text-align: center;
|
||||
padding-bottom: 20px;
|
||||
@include clearfix();
|
||||
padding: 10px 0 20px 40%;
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive( $bp_small ){
|
||||
.title {
|
||||
@include clearfix();
|
||||
padding: 20px 20px 10px 40%;
|
||||
min-height: 20vw;
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,52 +1,48 @@
|
||||
|
||||
.queue-view {
|
||||
|
||||
.player {
|
||||
.current-track {
|
||||
text-align: center;
|
||||
padding: 40px 20px 10px;
|
||||
padding-bottom: 40px;
|
||||
|
||||
.artwork,
|
||||
.controls a {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.artwork,
|
||||
.controls,
|
||||
.progress {
|
||||
display: block;
|
||||
max-width: 400px;
|
||||
.artwork {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
}
|
||||
width: 60%;
|
||||
max-width: 400px;
|
||||
display: block;
|
||||
border: 0 !important;
|
||||
|
||||
.controls {
|
||||
padding: 20px 0 10px;
|
||||
a {
|
||||
font-size: 18px;
|
||||
.thumbnail {
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&.radio-enabled {
|
||||
.thumbnail {
|
||||
box-sizing: border-box;
|
||||
padding: 10%;
|
||||
}
|
||||
|
||||
.radio-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
max-width: none;
|
||||
.title {
|
||||
font-size: 18px;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.current-track {
|
||||
padding: 20px 0 0 0;
|
||||
font-weight: 700;
|
||||
.artist-sentence {
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.slider {
|
||||
margin-top: 0;
|
||||
.track {
|
||||
background: rgba(0,0,0,0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.radio {
|
||||
margin: 20px auto 0;
|
||||
padding: 10px 12px;
|
||||
max-width: 400px;
|
||||
background: $faint_grey;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,48 @@
|
||||
|
||||
.search-view {
|
||||
|
||||
.search-result-sections {
|
||||
padding: 20px 20px 0;
|
||||
.search-form {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 90px;
|
||||
|
||||
input {
|
||||
@include feature_font();
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
height: 3rem;
|
||||
background: transparent;
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
border-color: $turquoise;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-result-sections {
|
||||
section {
|
||||
.inner {
|
||||
padding: 10px 20px 0 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
|
||||
.search-form {
|
||||
top: 18px;
|
||||
left: 50px;
|
||||
right: 60px;
|
||||
|
||||
input {
|
||||
font-size: 1.8rem;
|
||||
height: 1.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
.search-result-sections {
|
||||
padding: 10px 10px 0;
|
||||
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
|
||||
.settings-view {
|
||||
section {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.status {
|
||||
.item {
|
||||
|
||||
@ -2,20 +2,42 @@
|
||||
.user-view {
|
||||
.intro {
|
||||
@include clearfix();
|
||||
padding-bottom: 20px;
|
||||
position: relative;
|
||||
|
||||
.thumbnail {
|
||||
max-width: 200px;
|
||||
margin: 40px 40px 0 40px;
|
||||
float: left;
|
||||
.parallax {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding-top: 80px;
|
||||
}
|
||||
.liner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 30vh 40px 40px 260px;
|
||||
|
||||
.actions {
|
||||
padding-top: 20px;
|
||||
.thumbnail {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
left: 40px;
|
||||
bottom: 20px;
|
||||
max-width: 184px;
|
||||
|
||||
.image {
|
||||
background-color: $darkest_grey;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding-bottom: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +47,7 @@
|
||||
|
||||
.thumbnail {
|
||||
float: none;
|
||||
margin: 20px auto 20px;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
||||
Reference in New Issue
Block a user