Beginning Draggability
This commit is contained in:
@ -14,9 +14,6 @@ class ContextMenu extends React.Component{
|
||||
super(props);
|
||||
}
|
||||
|
||||
handleClick(e){
|
||||
}
|
||||
|
||||
renderItems(){
|
||||
var items = [];
|
||||
|
||||
|
||||
68
src/js/components/Dragger.js
Executable file
68
src/js/components/Dragger.js
Executable file
@ -0,0 +1,68 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { bindActionCreators } from 'redux'
|
||||
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
import * as mopidyActions from '../services/mopidy/actions'
|
||||
import * as spotifyActions from '../services/spotify/actions'
|
||||
|
||||
class Dragger extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleMouseMove = this.handleMouseMove.bind(this)
|
||||
this.handleMouseUp = this.handleMouseUp.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
window.addEventListener("mousemove", this.handleMouseMove, false);
|
||||
window.addEventListener("mouseup", this.handleMouseUp, false);
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
window.removeEventListener("mousemove", this.handleMouseMove, false);
|
||||
window.removeEventListener("mouseup", this.handleMouseUp, false);
|
||||
}
|
||||
|
||||
handleMouseMove(e){
|
||||
if( !this.props.dragger || !this.props.dragger.dragging ) return null;
|
||||
this.props.uiActions.dragMove( e )
|
||||
}
|
||||
|
||||
handleMouseUp(e){
|
||||
if( !this.props.dragger || !this.props.dragger.dragging ) return null;
|
||||
this.props.uiActions.dragEnd( e )
|
||||
}
|
||||
|
||||
render(){
|
||||
if( !this.props.dragger || !this.props.dragger.dragging ) return null;
|
||||
|
||||
var style = {
|
||||
left: this.props.dragger.position_x,
|
||||
top: this.props.dragger.position_y,
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="dragger" style={style}>
|
||||
Dragging { this.props.dragger.victims.length } things
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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)(Dragger)
|
||||
@ -18,6 +18,18 @@ class Sidebar extends React.Component{
|
||||
super(props);
|
||||
}
|
||||
|
||||
renderDropzones(){
|
||||
if( !this.props.dragger || !this.props.dragger.dragging ) return null
|
||||
|
||||
return (
|
||||
<div className="dropzones">
|
||||
<div className="zone">
|
||||
<div className="title">Zone</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<aside>
|
||||
@ -89,6 +101,8 @@ class Sidebar extends React.Component{
|
||||
|
||||
</nav>
|
||||
|
||||
{ this.renderDropzones() }
|
||||
|
||||
<MiniPlayer />
|
||||
|
||||
</aside>
|
||||
@ -109,7 +123,8 @@ const mapStateToProps = (state, ownProps) => {
|
||||
pusher_connected: state.pusher.connected,
|
||||
spotify_connected: state.spotify.connected,
|
||||
spotify_authorized: state.spotify.authorized,
|
||||
current_track: state.ui.current_track
|
||||
current_track: state.ui.current_track,
|
||||
dragger: state.ui.dragger
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,13 @@ export default class Track extends React.Component{
|
||||
return this.props.handleDoubleClick(e);
|
||||
}
|
||||
|
||||
handleDragStart(e){
|
||||
// if we're not selected, perform click behavior [first] << this is assumed
|
||||
if( !this.props.track.selected ) this.handleClick(e)
|
||||
|
||||
this.props.handleDragStart(e)
|
||||
}
|
||||
|
||||
handleContextMenu(e){
|
||||
e.preventDefault();
|
||||
|
||||
@ -44,9 +51,11 @@ export default class Track extends React.Component{
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
onDoubleClick={ (e) => this.handleDoubleClick(e) }
|
||||
onClick={ (e) => this.handleClick(e) }
|
||||
onContextMenu={ (e) => this.handleContextMenu(e) }>
|
||||
draggable='true'
|
||||
onDragStart={ e => this.handleDragStart(e) }
|
||||
onClick={ e => this.handleClick(e) }
|
||||
onDoubleClick={ e => this.handleDoubleClick(e) }
|
||||
onContextMenu={ e => this.handleContextMenu(e) }>
|
||||
{ this.props.track.selected ? <FontAwesome name="check" className="select-state" fixedWidth /> : null }
|
||||
<span className="col name">
|
||||
{ track.name ? track.name : '-' }
|
||||
|
||||
@ -2,11 +2,12 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import * as mopidyActions from '../services/mopidy/actions'
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
|
||||
import Track from './Track'
|
||||
|
||||
import * as mopidyActions from '../services/mopidy/actions'
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
|
||||
class TrackList extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
@ -81,12 +82,16 @@ class TrackList extends React.Component{
|
||||
this.setState({ tracks: tracks, lastSelectedTrack: index });
|
||||
}
|
||||
|
||||
handleDoubleClick( e, index ){
|
||||
handleDoubleClick(e, index){
|
||||
if( this.props.context_menu.show ) this.props.uiActions.hideContextMenu();
|
||||
this.playTracks()
|
||||
}
|
||||
|
||||
handleContextMenu( e, index ){
|
||||
handleDragStart(e, index){
|
||||
this.props.uiActions.dragStart( e, 'tltracks', this.selectedTracks() )
|
||||
}
|
||||
|
||||
handleContextMenu(e, index){
|
||||
var data = {
|
||||
selected_tracks: this.selectedTracks()
|
||||
}
|
||||
@ -148,9 +153,10 @@ class TrackList extends React.Component{
|
||||
show_source_icon={ this.props.show_source_icon }
|
||||
key={index+'_'+track.uri}
|
||||
track={track}
|
||||
handleDoubleClick={(e) => self.handleDoubleClick(e, index)}
|
||||
handleClick={(e) => self.handleClick(e, index)}
|
||||
handleContextMenu={(e) => self.handleContextMenu(e, index)} />
|
||||
handleDoubleClick={ e => self.handleDoubleClick(e, index)}
|
||||
handleClick={ e => self.handleClick(e, index)}
|
||||
handleDragStart={ e => self.handleDragStart(e, index)}
|
||||
handleContextMenu={ e => self.handleContextMenu(e, index)} />
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ const localstorageMiddleware = (function(){
|
||||
|
||||
// append our state to a global variable. This gives us access to debug the store at any point
|
||||
window._store = store
|
||||
console.log(action)
|
||||
//console.log(action)
|
||||
|
||||
switch( action.type ){
|
||||
|
||||
|
||||
@ -39,4 +39,26 @@ export function lazyLoading( start ){
|
||||
}
|
||||
}
|
||||
|
||||
export function dragStart( e, context, victims ){
|
||||
return {
|
||||
type: 'DRAG_START',
|
||||
context: context,
|
||||
victims: victims,
|
||||
position_x: e.clientX,
|
||||
position_y: e.clientY
|
||||
}
|
||||
}
|
||||
|
||||
export function dragMove( e ){
|
||||
return {
|
||||
type: 'DRAG_MOVE',
|
||||
position_x: e.clientX,
|
||||
position_y: e.clientY,
|
||||
}
|
||||
}
|
||||
|
||||
export function dragEnd(){
|
||||
return { type: 'DRAG_END' }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -23,6 +23,40 @@ export default function reducer(ui = {}, action){
|
||||
return Object.assign({}, ui, { context_menu: { show: false } });
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Dragging
|
||||
**/
|
||||
|
||||
case 'DRAG_START':
|
||||
return Object.assign({}, ui, {
|
||||
dragger: {
|
||||
dragging: true,
|
||||
context: action.context,
|
||||
victims: action.victims,
|
||||
position_x: action.position_x,
|
||||
position_y: action.position_y
|
||||
}
|
||||
});
|
||||
|
||||
case 'DRAG_MOVE':
|
||||
var dragger = Object.assign({}, ui.dragger, {
|
||||
position_x: action.position_x,
|
||||
position_y: action.position_y
|
||||
})
|
||||
return Object.assign({}, ui, { dragger: dragger });
|
||||
|
||||
case 'DRAG_END':
|
||||
return Object.assign({}, ui, {
|
||||
dragger: {
|
||||
dragging: false,
|
||||
context: false,
|
||||
victims: false
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Albums
|
||||
**/
|
||||
|
||||
@ -7,6 +7,7 @@ import { connect } from 'react-redux'
|
||||
|
||||
import Sidebar from '../components/Sidebar'
|
||||
import ContextMenu from '../components/ContextMenu'
|
||||
import Dragger from '../components/Dragger'
|
||||
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
import * as pusherActions from '../services/pusher/actions'
|
||||
@ -93,13 +94,17 @@ class App extends React.Component{
|
||||
}
|
||||
|
||||
render(){
|
||||
var className = '';
|
||||
if( this.props.dragger && this.props.dragger.dragging ) className += ' dragging'
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={className}>
|
||||
<Sidebar />
|
||||
<main>
|
||||
{this.props.children}
|
||||
</main>
|
||||
<ContextMenu state={this.props.context_menu} />
|
||||
<Dragger />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -116,6 +121,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
mopidy_connected: state.mopidy.connected,
|
||||
spotify_authorized: state.spotify.authorized,
|
||||
play_state: state.mopidy.play_state,
|
||||
dragger: state.ui.dragger,
|
||||
context_menu: state.ui.context_menu
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
@import 'global/forms';
|
||||
|
||||
@import 'components/context-menu';
|
||||
@import 'components/dragger';
|
||||
@import 'components/images';
|
||||
@import 'components/icon';
|
||||
@import 'components/search-form';
|
||||
|
||||
30
src/scss/components/_dragger.scss
Executable file
30
src/scss/components/_dragger.scss
Executable file
@ -0,0 +1,30 @@
|
||||
|
||||
.dragger {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 9999;
|
||||
background: $blue;
|
||||
color: #FFFFFF;
|
||||
padding: 12px 20px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dropzones {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9998;
|
||||
background: rgba(25, 25, 25, 0.9);
|
||||
|
||||
.zone {
|
||||
margin: 15px;
|
||||
padding: 50px 20px;
|
||||
background: $turquoise;
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ $dark_grey: #333333;
|
||||
$secondary_grey: #888888;
|
||||
$red: #cf2d2d;
|
||||
$green: #47af2a;
|
||||
$blue: #32b5f2;
|
||||
|
||||
@mixin one_line_text {
|
||||
white-space: nowrap;
|
||||
|
||||
Reference in New Issue
Block a user