Keyboard selecting of tracks, now native
This commit is contained in:
@ -10,6 +10,11 @@
|
|||||||
"babel-loader": "6.2.5",
|
"babel-loader": "6.2.5",
|
||||||
"babel-preset-es2015": "*",
|
"babel-preset-es2015": "*",
|
||||||
"babel-preset-react": "*",
|
"babel-preset-react": "*",
|
||||||
|
"sass-loader": "*",
|
||||||
|
"style-loader": "*",
|
||||||
|
"css-loader": "*",
|
||||||
|
"url-loader": "*",
|
||||||
|
"file-loader": "*",
|
||||||
"flux": "^2.1.1",
|
"flux": "^2.1.1",
|
||||||
"redux": "*",
|
"redux": "*",
|
||||||
"react": "*",
|
"react": "*",
|
||||||
@ -26,7 +31,6 @@
|
|||||||
"jquery": "3.1.1",
|
"jquery": "3.1.1",
|
||||||
"mopidy": "^0.5.0",
|
"mopidy": "^0.5.0",
|
||||||
"expose-loader": "*",
|
"expose-loader": "*",
|
||||||
"kioc": "1.0.4",
|
|
||||||
"react-fontawesome": "*"
|
"react-fontawesome": "*"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
2
src/js/bootstrap.js
vendored
2
src/js/bootstrap.js
vendored
@ -9,7 +9,7 @@ import spotify from './services/spotify/reducer'
|
|||||||
import thunk from 'redux-thunk'
|
import thunk from 'redux-thunk'
|
||||||
import mopidyMiddleware from './services/mopidy/middleware'
|
import mopidyMiddleware from './services/mopidy/middleware'
|
||||||
import spotifyMiddleware from './services/spotify/middleware'
|
import spotifyMiddleware from './services/spotify/middleware'
|
||||||
import localstorageMiddleware from './services/localstorageMiddleware'
|
import localstorageMiddleware from './services/localstorage/middleware'
|
||||||
|
|
||||||
let reducers = combineReducers({
|
let reducers = combineReducers({
|
||||||
mopidy,
|
mopidy,
|
||||||
|
|||||||
@ -13,6 +13,10 @@ export default class ConfirmationButton extends React.Component{
|
|||||||
this.unconfirmTimer = false;
|
this.unconfirmTimer = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(){
|
||||||
|
clearTimeout( this.unconfirmTimer );
|
||||||
|
}
|
||||||
|
|
||||||
handleClick(e){
|
handleClick(e){
|
||||||
if( this.state.confirming ){
|
if( this.state.confirming ){
|
||||||
this.setState({ confirming: false });
|
this.setState({ confirming: false });
|
||||||
@ -27,12 +31,14 @@ export default class ConfirmationButton extends React.Component{
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleMouseLeave(e){
|
handleMouseLeave(e){
|
||||||
this.unconfirmTimer = setTimeout(
|
if( this.state.confirming ){
|
||||||
function(){
|
this.unconfirmTimer = setTimeout(
|
||||||
this.setState({ confirming: false });
|
function(){
|
||||||
}.bind(this),
|
this.setState({ confirming: false });
|
||||||
1500
|
}.bind(this),
|
||||||
);
|
1500
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
|
|||||||
@ -10,12 +10,15 @@ export default class Track extends React.Component{
|
|||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSelected( e ){
|
handleClick( e ){
|
||||||
return this.props.toggleSelected();
|
var target = $(e.target);
|
||||||
|
if( !target.is('a') && target.closest('a').length <= 0 ){
|
||||||
|
this.props.handleClick(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playTrack( e ){
|
handleDoubleClick( e ){
|
||||||
return this.props.playTrack();
|
return this.props.handleDoubleClick(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
@ -28,11 +31,21 @@ export default class Track extends React.Component{
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="track"
|
className="track"
|
||||||
onDoubleClick={ (e) => this.playTrack(e) }>
|
onDoubleClick={ (e) => this.handleDoubleClick(e) }
|
||||||
<FontAwesome name={selectedIcon} fixedWidth onClick={ (e) => this.toggleSelected(e) } />
|
onClick={ (e) => this.handleClick(e) }>
|
||||||
{this.props.track.name}
|
<FontAwesome name={selectedIcon} fixedWidth />
|
||||||
<ArtistList artists={this.props.track.artists} />
|
<span className="name">
|
||||||
{ this.props.track.album ? <AlbumLink album={this.props.track.album} /> : null }
|
{this.props.track.name}
|
||||||
|
</span>
|
||||||
|
<span className="artists">
|
||||||
|
<ArtistList artists={this.props.track.artists} />
|
||||||
|
</span>
|
||||||
|
<span className="album">
|
||||||
|
{ this.props.track.album ? <AlbumLink album={this.props.track.album} /> : null }
|
||||||
|
</span>
|
||||||
|
<span className="duration">
|
||||||
|
{ this.props.track.duration }
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,17 +12,44 @@ class TrackList extends React.Component{
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
tracks: this.props.tracks
|
tracks: this.props.tracks,
|
||||||
|
lastSelectedTrack: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSelected( index ){
|
handleClick( e, index ){
|
||||||
var tracks = this.state.tracks;
|
var tracks = this.state.tracks;
|
||||||
tracks[index].selected = !tracks[index].selected;
|
|
||||||
this.setState({ tracks: tracks });
|
if( e.ctrlKey ){
|
||||||
|
|
||||||
|
tracks[index].selected = !tracks[index].selected;
|
||||||
|
|
||||||
|
}else if( e.shiftKey ){
|
||||||
|
|
||||||
|
if( this.state.lastSelectedTrack < index ){
|
||||||
|
var start = this.state.lastSelectedTrack;
|
||||||
|
var end = index;
|
||||||
|
}else{
|
||||||
|
var start = index;
|
||||||
|
var end = this.state.lastSelectedTrack;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( var i = start; i <= end; i++ ){
|
||||||
|
tracks[i].selected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
|
||||||
|
for( var i = 0; i < tracks.length; i++ ){
|
||||||
|
tracks[i].selected = false;
|
||||||
|
}
|
||||||
|
tracks[index].selected = !tracks[index].selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ tracks: tracks, lastSelectedTrack: index });
|
||||||
}
|
}
|
||||||
|
|
||||||
playTrack( index ){
|
handleDoubleClick( e, index ){
|
||||||
var tracks = this.state.tracks;
|
var tracks = this.state.tracks;
|
||||||
this.props.actions.changeTrack( tracks[index].tlid )
|
this.props.actions.changeTrack( tracks[index].tlid )
|
||||||
}
|
}
|
||||||
@ -67,8 +94,8 @@ class TrackList extends React.Component{
|
|||||||
return <Track
|
return <Track
|
||||||
key={index+'_'+track.uri}
|
key={index+'_'+track.uri}
|
||||||
track={track}
|
track={track}
|
||||||
playTrack={() => self.playTrack(index)}
|
handleDoubleClick={(e) => self.handleDoubleClick(e, index)}
|
||||||
toggleSelected={() => self.toggleSelected(index)} />
|
handleClick={(e) => self.handleClick(e, index)} />
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import { Router, Route, Link, hashHistory } from 'react-router'
|
|||||||
|
|
||||||
import store from './bootstrap.js'
|
import store from './bootstrap.js'
|
||||||
|
|
||||||
|
require('../scss/app.scss');
|
||||||
|
|
||||||
import App from './views/App'
|
import App from './views/App'
|
||||||
import Album from './views/Album'
|
import Album from './views/Album'
|
||||||
import Artist from './views/Artist'
|
import Artist from './views/Artist'
|
||||||
|
|||||||
19
src/js/services/keyboard/actions.js
Executable file
19
src/js/services/keyboard/actions.js
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Actions and Action Creators
|
||||||
|
**/
|
||||||
|
|
||||||
|
export function shiftKey( down ){
|
||||||
|
return {
|
||||||
|
type: 'SHIFT_KEY',
|
||||||
|
down: down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ctrlKey( down ){
|
||||||
|
return {
|
||||||
|
type: 'CTRL_KEY',
|
||||||
|
down: down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
18
src/js/services/keyboard/reducer.js
Executable file
18
src/js/services/keyboard/reducer.js
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
export default function reducer(keyboard = {}, action){
|
||||||
|
console.log(action);
|
||||||
|
switch (action.type) {
|
||||||
|
|
||||||
|
case 'SHIFT_KEY':
|
||||||
|
return Object.assign({}, keyboard, { shift: action.down });
|
||||||
|
|
||||||
|
case 'CTRL_KEY':
|
||||||
|
return Object.assign({}, keyboard, { ctrl: action.down });
|
||||||
|
|
||||||
|
default:
|
||||||
|
return keyboard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,2 +1,4 @@
|
|||||||
|
|
||||||
@import 'vendor/font-awesome/index';
|
@import 'vendor/font-awesome/index';
|
||||||
|
@import 'components/track';
|
||||||
|
|
||||||
|
|||||||
25
src/scss/components/_track.scss
Executable file
25
src/scss/components/_track.scss
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
.track {
|
||||||
|
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
.name,
|
||||||
|
.artists,
|
||||||
|
.album,
|
||||||
|
.duration {
|
||||||
|
width: 20%;
|
||||||
|
min-height: 1px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
clear: both;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
var dev = process.env.NODE_ENV !== "production";
|
var dev = process.env.NODE_ENV !== "production";
|
||||||
var webpack = require('webpack');
|
var webpack = require('webpack');
|
||||||
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
@ -17,7 +18,9 @@ var config = {
|
|||||||
|
|
||||||
module: {
|
module: {
|
||||||
loaders: [
|
loaders: [
|
||||||
{ test: require.resolve('jquery'), loader: 'expose?jQuery!expose?$' },
|
{
|
||||||
|
test: require.resolve('jquery'), loader: 'expose?jQuery!expose?$'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// loading JSX (aka Babel) into browser-friendly ES6
|
// loading JSX (aka Babel) into browser-friendly ES6
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
@ -37,6 +40,11 @@ var config = {
|
|||||||
'css'+(dev? '?sourceMap=true': ''),
|
'css'+(dev? '?sourceMap=true': ''),
|
||||||
'sass'+(dev? '?outputStyle=expanded&sourceMap=true&sourceMapContents=true': '')
|
'sass'+(dev? '?outputStyle=expanded&sourceMap=true&sourceMapContents=true': '')
|
||||||
])
|
])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// load external resources (ie Google fonts)
|
||||||
|
test: /.(png|woff(2)?|eot|ttf|svg)(\?[a-z0-9=\.]+)?$/,
|
||||||
|
loader: 'url-loader?limit=100000'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user