diff --git a/src/js/components/Dropzone.js b/src/js/components/Dropzone.js new file mode 100755 index 00000000..eb5e625e --- /dev/null +++ b/src/js/components/Dropzone.js @@ -0,0 +1,30 @@ + +import React, { PropTypes } from 'react' +import { connect } from 'react-redux' +import { bindActionCreators } from 'redux' + +import Icon from './Icon' + +export default class Dropzone extends React.Component{ + + constructor(props) { + super(props); + } + + handleMouseUp(e){ + // TODO: sanity check if this is a valid dropzone + + return this.props.handleMouseUp(e) + } + + render(){ + if( !this.props.data ) return null + + return ( +
this.handleMouseUp(e) }> + + { this.props.data.title } +
+ ); + } +} \ No newline at end of file diff --git a/src/js/components/Dropzones.js b/src/js/components/Dropzones.js new file mode 100755 index 00000000..e2819048 --- /dev/null +++ b/src/js/components/Dropzones.js @@ -0,0 +1,89 @@ + +import React, { PropTypes } from 'react' +import { connect } from 'react-redux' +import { bindActionCreators } from 'redux' + +import Dropzone from './Dropzone' + +import * as uiActions from '../services/ui/actions' +import * as mopidyActions from '../services/mopidy/actions' +import * as spotifyActions from '../services/spotify/actions' + +class Dropzones extends React.Component{ + + constructor(props) { + super(props); + + this._zones = [ + { + title: 'Add to queue', + icon: 'play', + action: 'enqueue' + }, + { + title: 'Add to library', + icon: 'music', + action: 'add_to_library', + accepts: ['track','album','playlist','artist'] + }, + { + title: 'Add to playlist', + icon: 'playlist', + action: 'add_to_playlist', + accepts: ['tltrack','track','album','playlist','artist'] + } + ] + } + + handleMouseMove(e){ + if( !this.props.dragger || !this.props.dragger.dragging ) return null; + this.props.uiActions.dragMove( e ) + } + + handleMouseUp(e, index){ + var target = this._zones[index] + var victims = this.props.dragger.victims + var uris = [] + for( var i = 0; i < victims.length; i++ ){ + uris.push( victims[i].uri ) + } + + switch( target.action ){ + + case 'enqueue': + this.props.mopidyActions.enqueueTracks( uris ) + break + + } + } + + render(){ + if( !this.props.dragger || !this.props.dragger.dragging ) return null + + return ( +
+ { + this._zones.map( (zone, index) => { + return this.handleMouseUp(e, index) }/> + }) + } +
+ ); + } +} + +const mapStateToProps = (state, ownProps) => { + return { + dragger: state.ui.dragger + } +} + +const mapDispatchToProps = (dispatch) => { + return { + uiActions: bindActionCreators(uiActions, dispatch), + spotifyActions: bindActionCreators(spotifyActions, dispatch), + mopidyActions: bindActionCreators(mopidyActions, dispatch) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(Dropzones) \ No newline at end of file diff --git a/src/js/components/Sidebar.js b/src/js/components/Sidebar.js index e564e7d2..d4836fc1 100755 --- a/src/js/components/Sidebar.js +++ b/src/js/components/Sidebar.js @@ -8,6 +8,7 @@ import MiniPlayer from './MiniPlayer' import Icon from './Icon' import Thumbnail from './Thumbnail' import SearchForm from './SearchForm' +import Dropzones from './Dropzones' import FontAwesome from 'react-fontawesome' import * as mopidyActions from '../services/mopidy/actions' @@ -18,18 +19,6 @@ class Sidebar extends React.Component{ super(props); } - renderDropzones(){ - if( !this.props.dragger || !this.props.dragger.dragging ) return null - - return ( -
-
-
Zone
-
-
- ) - } - render(){ return ( diff --git a/src/js/services/ui/actions.js b/src/js/services/ui/actions.js index 1ebf1aa7..475c6137 100755 --- a/src/js/services/ui/actions.js +++ b/src/js/services/ui/actions.js @@ -57,6 +57,10 @@ export function dragMove( e ){ } } +export function dragCancel(){ + return { type: 'DRAG_CANCEL' } +} + export function dragEnd(){ return { type: 'DRAG_END' } } diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index ff6012bd..d1cf3711 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -55,6 +55,15 @@ export default function reducer(ui = {}, action){ } }); + case 'DRAG_CANCEL': + return Object.assign({}, ui, { + dragger: { + dragging: false, + context: false, + victims: false + } + }); + /** diff --git a/src/js/views/App.js b/src/js/views/App.js index 998e3d0a..af2ab08b 100755 --- a/src/js/views/App.js +++ b/src/js/views/App.js @@ -63,7 +63,7 @@ class App extends React.Component{ shouldTriggerShortcut(e){ var ignoreNodes = ['INPUT', 'TEXTAREA']; - var keyCodes = [32]; + var keyCodes = [32,27]; if( ignoreNodes.indexOf(e.target.nodeName) > -1 ){ return false; @@ -82,13 +82,20 @@ class App extends React.Component{ handleKeyUp(e){ if( !this.shouldTriggerShortcut(e) ) return; - switch(e.keyCode){ + switch(e.keyCode){ + case 32: // spacebar if( this.props.play_state == 'playing' ){ this.props.mopidyActions.pause(); }else{ this.props.mopidyActions.play(); } + break; + + case 27: // esc + if( this.props.dragger.dragging ){ + this.props.uiActions.dragCancel(); + } break; } } diff --git a/src/scss/app.scss b/src/scss/app.scss index 935ea3ca..5c8f387b 100755 --- a/src/scss/app.scss +++ b/src/scss/app.scss @@ -1,5 +1,4 @@ - @import 'vendor/font-awesome/index'; @import 'global/variables'; diff --git a/src/scss/components/_dragger.scss b/src/scss/components/_dragger.scss index ea398ffe..d3b075ee 100755 --- a/src/scss/components/_dragger.scss +++ b/src/scss/components/_dragger.scss @@ -1,4 +1,10 @@ +.dragging * { + cursor: grabbing !important; + cursor: -moz-grabbing !important; + cursor: -webkit-grabbing !important; +} + .dragger { position: fixed; left: 0; @@ -19,12 +25,23 @@ z-index: 9998; background: rgba(25, 25, 25, 0.9); - .zone { + .dropzone { + @include animate(); margin: 15px; - padding: 50px 20px; - background: $turquoise; + padding: 40px 20px; + background: $dark_grey; color: #FFFFFF; - font-size: 16px; + font-size: 18px; text-align: center; + + .icon { + display: block; + margin: 0 auto 10px; + width: 32px; + } + + &:hover { + background: $turquoise; + } } } \ No newline at end of file