Nicer context menus
This commit is contained in:
@ -166,6 +166,14 @@ class ContextMenu extends React.Component{
|
||||
]
|
||||
break
|
||||
|
||||
case 'playlist':
|
||||
var items = [
|
||||
{ handleClick: 'playURIs', label: 'Play', icon: 'play' },
|
||||
{ handleClick: 'toggleFollow', label: 'Follow/unfollow', icon: 'toggle-on' },
|
||||
{ handleClick: 'copyURIs', label: 'Copy URI', icon: 'copy' }
|
||||
]
|
||||
break
|
||||
|
||||
default:
|
||||
var items = [
|
||||
{ handleClick: 'playURIs', label: 'Play', icon: 'play' },
|
||||
@ -180,6 +188,21 @@ class ContextMenu extends React.Component{
|
||||
return items
|
||||
}
|
||||
|
||||
renderTitle(){
|
||||
var style = {}
|
||||
var item = this.props.context_menu.data.item
|
||||
var image = null
|
||||
if (item && item.images){
|
||||
image = helpers.sizedImages(item.images).medium
|
||||
}
|
||||
return (
|
||||
<div className="title" style={style}>
|
||||
{image ? <img src={image} /> : null}
|
||||
{this.props.context_menu.data.item.name}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderItems(trigger){
|
||||
var items = this.getItems(trigger)
|
||||
|
||||
@ -254,7 +277,8 @@ class ContextMenu extends React.Component{
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
{ this.renderItems(trigger) }
|
||||
{this.renderTitle()}
|
||||
{this.renderItems(trigger)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import { Link, hashHistory } from 'react-router'
|
||||
import { connect } from 'react-redux'
|
||||
import { createStore, bindActionCreators } from 'redux'
|
||||
import { hashHistory, Link } from 'react-router'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
|
||||
import ArtistSentence from './ArtistSentence'
|
||||
import Dater from './Dater'
|
||||
|
||||
import * as helpers from '../helpers'
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
|
||||
export default class List extends React.Component{
|
||||
class List extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -22,6 +25,12 @@ export default class List extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
handleContextMenu(e,uri){
|
||||
e.preventDefault()
|
||||
var data = { uris: [uri] }
|
||||
this.props.uiActions.showContextMenu(e, data, helpers.uriType(uri), 'click')
|
||||
}
|
||||
|
||||
renderHeader(){
|
||||
if (!this.props.columns || this.props.noheader) return null
|
||||
|
||||
@ -71,7 +80,11 @@ export default class List extends React.Component{
|
||||
if( row.type ) className += ' '+row.type
|
||||
|
||||
return (
|
||||
<div onClick={ e => this.handleClick(e, row.uri)} className={className} key={row_index}>
|
||||
<div
|
||||
onClick={e => this.handleClick(e, row.uri)}
|
||||
onContextMenu={e => this.handleContextMenu(e,row.uri)}
|
||||
className={className}
|
||||
key={row_index}>
|
||||
{
|
||||
this.props.columns.map( (col, col_index) => {
|
||||
return (
|
||||
@ -89,4 +102,16 @@ export default class List extends React.Component{
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
uiActions: bindActionCreators(uiActions, dispatch)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(List)
|
||||
|
||||
@ -1,13 +1,29 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import GridItem from './GridItem'
|
||||
import { connect } from 'react-redux'
|
||||
import { createStore, bindActionCreators } from 'redux'
|
||||
import { Link } from 'react-router'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
|
||||
export default class PlaylistGrid extends React.Component{
|
||||
import Thumbnail from './Thumbnail'
|
||||
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
|
||||
class PlaylistGrid extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
handleContextMenu(e,item){
|
||||
e.preventDefault()
|
||||
var data = {
|
||||
uris: [item.uri],
|
||||
item: item
|
||||
}
|
||||
this.props.uiActions.showContextMenu( e, data, 'playlist', 'click' )
|
||||
}
|
||||
|
||||
render(){
|
||||
if( !this.props.playlists ) return null
|
||||
|
||||
@ -17,8 +33,21 @@ export default class PlaylistGrid extends React.Component{
|
||||
<div className={className}>
|
||||
{
|
||||
this.props.playlists.map(
|
||||
(playlist, index) => {
|
||||
return <GridItem item={playlist} key={index} link={global.baseURL+'playlist/'+playlist.uri} />
|
||||
(playlist, index) => {
|
||||
return (
|
||||
<Link
|
||||
className="grid-item"
|
||||
to={global.baseURL+'playlist/'+playlist.uri}
|
||||
key={index}
|
||||
onContextMenu={e => this.handleContextMenu(e,playlist)}>
|
||||
<Thumbnail size="medium" images={playlist.images} />
|
||||
<div className="name">{ playlist.name }</div>
|
||||
<div className="secondary">
|
||||
{ playlist.tracks_total ? playlist.tracks_total+' tracks' : <span>0 tracks</span> }
|
||||
{ playlist.can_edit ? <FontAwesome name="edit" /> : null }
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -27,3 +56,15 @@ export default class PlaylistGrid extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
uiActions: bindActionCreators(uiActions, dispatch)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(PlaylistGrid)
|
||||
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
import { Link } from 'react-router'
|
||||
|
||||
import ListItem from './ListItem'
|
||||
import ItemSource from './ItemSource'
|
||||
|
||||
export default class PlaylistListItem extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
renderOwner(){
|
||||
if( !this.props.item.owner ) return null;
|
||||
return (
|
||||
<Link to={global.baseURL+'user/'+this.props.item.owner.id}>
|
||||
{ this.props.item.owner.id }
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
render(){
|
||||
if( !this.props.item ) return null
|
||||
|
||||
return (
|
||||
<ListItem extraClasses="playlist" link={global.baseURL+'playlist/'+this.props.item.uri}>
|
||||
<span className="col name">
|
||||
{this.props.item.name}
|
||||
</span>
|
||||
<span className="col owner">
|
||||
{ this.renderOwner() }
|
||||
</span>
|
||||
<span className="col source">
|
||||
<ItemSource uri={this.props.item.uri} />
|
||||
</span>
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ import Mopidy from 'mopidy'
|
||||
import * as helpers from '../../helpers'
|
||||
|
||||
var mopidyActions = require('./actions.js')
|
||||
var uiActions = require('../ui/actions.js')
|
||||
var lastfmActions = require('../lastfm/actions.js')
|
||||
var pusherActions = require('../pusher/actions.js')
|
||||
|
||||
|
||||
@ -28,6 +28,12 @@ class LibraryAlbums extends React.Component{
|
||||
if (!this.props.library_albums) this.props.spotifyActions.getLibraryAlbums();
|
||||
}
|
||||
|
||||
handleContextMenu(e,uri){
|
||||
e.preventDefault()
|
||||
var data = { uris: [uri] }
|
||||
this.props.uiActions.showContextMenu( e, data, 'album', 'click' )
|
||||
}
|
||||
|
||||
loadMore(){
|
||||
this.props.spotifyActions.getURL( this.props.library_albums_more, 'SPOTIFY_LIBRARY_ALBUMS_LOADED' );
|
||||
}
|
||||
@ -76,13 +82,19 @@ class LibraryAlbums extends React.Component{
|
||||
]
|
||||
return (
|
||||
<section className="list-wrapper">
|
||||
<List rows={albums} columns={columns} link_prefix={global.baseURL+"album/"} />
|
||||
<List
|
||||
handleContextMenu={(e,uri) => this.handleContextMenu(e,uri)}
|
||||
rows={albums}
|
||||
columns={columns}
|
||||
link_prefix={global.baseURL+"album/"} />
|
||||
</section>
|
||||
)
|
||||
}else if( this.props.view == 'thumbnails' ){
|
||||
return (
|
||||
<section className="grid-wrapper">
|
||||
<AlbumGrid albums={albums} />
|
||||
<AlbumGrid
|
||||
handleContextMenu={(e,uri) => this.handleContextMenu(e,uri)}
|
||||
albums={albums} />
|
||||
</section>
|
||||
)
|
||||
}else{
|
||||
|
||||
@ -21,6 +21,12 @@ class LibraryArtists extends React.Component{
|
||||
super(props);
|
||||
}
|
||||
|
||||
handleContextMenu(e,uri){
|
||||
e.preventDefault()
|
||||
var data = { uris: [uri] }
|
||||
this.props.uiActions.showContextMenu( e, data, 'artist', 'click' )
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
if (!this.props.library_artists) this.props.spotifyActions.getLibraryArtists();
|
||||
}
|
||||
@ -61,13 +67,19 @@ class LibraryArtists extends React.Component{
|
||||
]
|
||||
return (
|
||||
<section className="list-wrapper">
|
||||
<List rows={artists} columns={columns} link_prefix={global.baseURL+"artist/"} show_source_icon={true} />
|
||||
<List
|
||||
handleContextMenu={(e,uri) => this.handleContextMenu(e,uri)}
|
||||
rows={artists}
|
||||
columns={columns}
|
||||
link_prefix={global.baseURL+"artist/"} show_source_icon={true} />
|
||||
</section>
|
||||
)
|
||||
}else{
|
||||
return (
|
||||
<section className="grid-wrapper">
|
||||
<ArtistGrid artists={artists} />
|
||||
<ArtistGrid
|
||||
handleContextMenu={(e,uri) => this.handleContextMenu(e,uri)}
|
||||
artists={artists} />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@ -21,6 +21,28 @@
|
||||
bottom: 0 !important;
|
||||
}
|
||||
|
||||
.title {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding: 8px 12px;
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
@include blur(5px);
|
||||
background-size: cover;
|
||||
background-position: 50% 0%;
|
||||
opacity: 0.2;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item-wrapper {
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
Reference in New Issue
Block a user