Context menu in action
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import { Link } from 'react-router'
|
||||
import { Link, hashHistory } from 'react-router'
|
||||
import { connect } from 'react-redux'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
@ -30,12 +30,17 @@ class ContextMenu extends React.Component{
|
||||
window.removeEventListener("click", this.handleClick, false)
|
||||
}
|
||||
|
||||
handleScroll(){
|
||||
if (this.props.menu) this.props.uiActions.hideContextMenu()
|
||||
handleScroll(e){
|
||||
if (this.props.menu){
|
||||
this.props.uiActions.hideContextMenu()
|
||||
}
|
||||
}
|
||||
|
||||
handleClick(){
|
||||
if (this.props.menu) this.props.uiActions.hideContextMenu()
|
||||
handleClick(e){
|
||||
var target = e.target
|
||||
if (this.props.menu && !target.classList.contains('context-menu-trigger')){
|
||||
this.props.uiActions.hideContextMenu()
|
||||
}
|
||||
}
|
||||
|
||||
playQueueItem(){
|
||||
@ -89,6 +94,10 @@ class ContextMenu extends React.Component{
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
goToUser(){
|
||||
hashHistory.push( global.baseURL +'user/'+ this.props.menu.item.owner.uri );
|
||||
}
|
||||
|
||||
copyURIs(e){
|
||||
var temp = $("<input>");
|
||||
$("body").append(temp);
|
||||
@ -100,6 +109,17 @@ class ContextMenu extends React.Component{
|
||||
this.props.uiActions.hideContextMenu()
|
||||
}
|
||||
|
||||
copyLinks(e){
|
||||
var temp = $("<input>");
|
||||
$("body").append(temp);
|
||||
temp.val(this.props.menu.item.external_urls.spotify).select();
|
||||
document.execCommand("copy");
|
||||
temp.remove();
|
||||
|
||||
this.props.uiActions.createNotification( "Copied link" )
|
||||
this.props.uiActions.hideContextMenu()
|
||||
}
|
||||
|
||||
closeAndDeselectTracks(){
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
@ -137,8 +157,10 @@ class ContextMenu extends React.Component{
|
||||
{ handleClick: 'playURIs', label: 'Play' },
|
||||
{ handleClick: 'playURIsNext', label: 'Play next' },
|
||||
{ handleClick: 'addToQueue', label: 'Add to queue' },
|
||||
{ handleClick: 'goToArtist', label: 'Go to artist' },
|
||||
// { handleClick: 'toggleFollow', label: 'Follow/unfollow' }, TODO
|
||||
{ handleClick: 'copyURIs', label: 'Copy URI' }
|
||||
{ handleClick: 'copyURIs', label: 'Copy URI' },
|
||||
{ handleClick: 'copyLinks', label: 'Copy link' }
|
||||
]
|
||||
break
|
||||
|
||||
@ -146,15 +168,18 @@ class ContextMenu extends React.Component{
|
||||
var items = [
|
||||
//{ handleClick: 'toggleFollow', label: 'Follow/unfollow' }, TODO
|
||||
{ handleClick: 'startRadio', label: 'Start radio' },
|
||||
{ handleClick: 'copyURIs', label: 'Copy URI' }
|
||||
{ handleClick: 'copyURIs', label: 'Copy URI' },
|
||||
{ handleClick: 'copyLinks', label: 'Copy link' }
|
||||
]
|
||||
break
|
||||
|
||||
case 'playlist':
|
||||
var items = [
|
||||
{ handleClick: 'playURIs', label: 'Play' },
|
||||
{ handleClick: 'goToUser', label: 'Go to user' },
|
||||
// { handleClick: 'toggleFollow', label: 'Follow/unfollow' }, TODO
|
||||
{ handleClick: 'copyURIs', label: 'Copy URI' }
|
||||
{ handleClick: 'copyURIs', label: 'Copy URI' },
|
||||
{ handleClick: 'copyLinks', label: 'Copy link' }
|
||||
]
|
||||
break
|
||||
|
||||
@ -205,6 +230,11 @@ class ContextMenu extends React.Component{
|
||||
return (
|
||||
<Link className="title" to={global.baseURL+helpers.uriType(item.uri)+'/'+item.uri}>
|
||||
{style ? <div className="background" style={style}></div> : null}
|
||||
<div className="type">
|
||||
{helpers.uriSource(item.uri)}
|
||||
|
||||
{this.props.menu.context}
|
||||
</div>
|
||||
<div className="text">{item.name}</div>
|
||||
</Link>
|
||||
)
|
||||
@ -264,8 +294,10 @@ class ContextMenu extends React.Component{
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
{this.props.menu.item ? this.renderTitle() : null}
|
||||
{this.renderItems()}
|
||||
<div className="liner">
|
||||
{this.props.menu.item ? this.renderTitle() : null}
|
||||
{this.renderItems()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -49,6 +49,16 @@ class Album extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
handleContextMenu(e){
|
||||
var data = {
|
||||
e: e,
|
||||
context: 'album',
|
||||
item: this.props.album,
|
||||
uris: [this.props.params.uri]
|
||||
}
|
||||
this.props.uiActions.showContextMenu(data)
|
||||
}
|
||||
|
||||
loadAlbum( props = this.props ){
|
||||
switch( helpers.uriSource( props.params.uri ) ){
|
||||
|
||||
@ -119,6 +129,7 @@ class Album extends React.Component{
|
||||
<div className="actions">
|
||||
<button className="primary" onClick={e => this.play()}>Play</button>
|
||||
{ helpers.uriSource(this.props.params.uri) == 'spotify' ? <FollowButton className="secondary" uri={this.props.params.uri} addText="Add to library" removeText="Remove from library" is_following={this.props.album.is_following} /> : null }
|
||||
<button className="context-menu-trigger" onClick={e => this.handleContextMenu(e)}><FontAwesome name="ellipsis-v" /></button>
|
||||
</div>
|
||||
|
||||
<section className="list-wrapper">
|
||||
|
||||
@ -122,7 +122,11 @@ class App extends React.Component{
|
||||
if (this.props.dragger && this.props.dragger.active) className += ' dragging'
|
||||
if (this.props.sidebar_open) className += ' sidebar-open'
|
||||
if (this.props.modal) className += ' modal-open'
|
||||
if (helpers.isTouchDevice() || this.props.emulate_touch) className += ' can-touch'
|
||||
if (helpers.isTouchDevice() || this.props.emulate_touch){
|
||||
className += ' touch'
|
||||
} else {
|
||||
className += ' notouch'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
|
||||
@ -54,6 +54,16 @@ class Artist extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
handleContextMenu(e){
|
||||
var data = {
|
||||
e: e,
|
||||
context: 'artist',
|
||||
item: this.props.artist,
|
||||
uris: [this.props.params.uri]
|
||||
}
|
||||
this.props.uiActions.showContextMenu(data)
|
||||
}
|
||||
|
||||
loadArtist( props = this.props ){
|
||||
switch( helpers.uriSource( props.params.uri ) ){
|
||||
|
||||
@ -204,7 +214,7 @@ class Artist extends React.Component{
|
||||
<div className="actions">
|
||||
{ can_play_radio ? <button className="primary" onClick={e => this.props.pusherActions.startRadio([this.props.artist.uri])}>Start radio</button> : null}
|
||||
{ can_follow ? <FollowButton className="white" uri={this.props.params.uri} removeText="Unfollow" addText="Follow" is_following={this.props.artist.is_following} /> : null}
|
||||
<button>...</button>
|
||||
<button className="context-menu-trigger white" onClick={e => this.handleContextMenu(e)}><FontAwesome name="ellipsis-v" /></button>
|
||||
</div>
|
||||
{ this.renderSubViewMenu() }
|
||||
</div>
|
||||
|
||||
@ -39,6 +39,16 @@ class Playlist extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
handleContextMenu(e){
|
||||
var data = {
|
||||
e: e,
|
||||
context: (this.props.playlist.can_edit ? 'editable-playlist' : 'playlist'),
|
||||
item: this.props.playlist,
|
||||
uris: [this.props.params.uri]
|
||||
}
|
||||
this.props.uiActions.showContextMenu(data)
|
||||
}
|
||||
|
||||
loadPlaylist( props = this.props ){
|
||||
switch( helpers.uriSource( props.params.uri ) ){
|
||||
|
||||
@ -97,7 +107,7 @@ class Playlist extends React.Component{
|
||||
<div className="actions">
|
||||
<button className="primary" onClick={ e => this.play() }>Play</button>
|
||||
<button className="secondary" onClick={ e => this.props.uiActions.openModal('edit_playlist', { uri: this.props.playlist.uri, name: this.props.playlist.name }) }>Edit</button>
|
||||
<ConfirmationButton className="destructive" content="Delete" confirmingContent="Are you sure?" onConfirm={ e => this.delete() } />
|
||||
<button className="context-menu-trigger" onClick={e => this.handleContextMenu(e)}><FontAwesome name="ellipsis-v" /></button>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -107,7 +117,7 @@ class Playlist extends React.Component{
|
||||
<div className="actions">
|
||||
<button className="primary" onClick={ e => this.play() }>Play</button>
|
||||
<button className="secondary" onClick={ e => this.props.uiActions.openModal('edit_playlist', { uri: this.props.playlist.uri, name: this.props.playlist.name, is_public: this.props.playlist.public }) }>Edit</button>
|
||||
<ConfirmationButton className="destructive" content="Delete" confirmingContent="Are you sure?" onConfirm={ e => this.unfollow() } />
|
||||
<button className="context-menu-trigger" onClick={e => this.handleContextMenu(e)}><FontAwesome name="ellipsis-v" /></button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -115,6 +125,7 @@ class Playlist extends React.Component{
|
||||
<div className="actions">
|
||||
<button className="primary" onClick={ e => this.play() }>Play</button>
|
||||
<FollowButton className="secondary" uri={this.props.playlist.uri} addText="Add to library" removeText="Remove from library" is_following={this.props.playlist.is_following} />
|
||||
<button className="context-menu-trigger" onClick={e => this.handleContextMenu(e)}><FontAwesome name="ellipsis-v" /></button>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -122,6 +133,7 @@ class Playlist extends React.Component{
|
||||
return (
|
||||
<div className="actions">
|
||||
<button className="primary" onClick={ e => this.play() }>Play</button>
|
||||
<button className="context-menu-trigger" onClick={e => this.handleContextMenu(e)}><FontAwesome name="ellipsis-v" /></button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -4,169 +4,154 @@
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 99;
|
||||
background: $dark_grey;
|
||||
color: #FFFFFF;
|
||||
|
||||
&.right-align {
|
||||
margin-left: -140px;
|
||||
}
|
||||
.liner {
|
||||
background: $dark_grey;
|
||||
color: #FFFFFF;
|
||||
|
||||
&.right-align-submenu .submenu {
|
||||
left: auto !important;
|
||||
right: 140px !important;
|
||||
}
|
||||
|
||||
&.bottom-align-submenu .submenu {
|
||||
top: auto !important;
|
||||
bottom: 0 !important;
|
||||
}
|
||||
|
||||
.title {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding: 8px 12px;
|
||||
width: 140px;
|
||||
font-weight: bold;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
background: $darkest_grey;
|
||||
|
||||
.background {
|
||||
@include blur(5px);
|
||||
background-size: cover;
|
||||
background-position: 50% 20%;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: -10px;
|
||||
bottom: -10px;
|
||||
right: -10px;
|
||||
z-index: 1;
|
||||
&.right-align {
|
||||
margin-left: -140px;
|
||||
}
|
||||
|
||||
.text {
|
||||
@include one_line_text();
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
color: #FFFFFF;
|
||||
&.right-align-submenu .submenu {
|
||||
left: auto !important;
|
||||
right: 140px !important;
|
||||
}
|
||||
|
||||
&:hover{
|
||||
.background {
|
||||
opacity: 0.7;
|
||||
}
|
||||
&.bottom-align-submenu .submenu {
|
||||
top: auto !important;
|
||||
bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item-wrapper {
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
.menu-item {
|
||||
.title {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding: 8px 12px;
|
||||
width: 140px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
font-weight: bold;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
background: $darkest_grey;
|
||||
|
||||
.icon {
|
||||
display: none;
|
||||
}
|
||||
.background {
|
||||
@include blur(5px);
|
||||
background-size: cover;
|
||||
background-position: 50% 20%;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: -10px;
|
||||
bottom: -10px;
|
||||
right: -10px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.submenu-icon {
|
||||
font-size: 10px;
|
||||
float: right;
|
||||
padding-top: 4px;
|
||||
}
|
||||
.text {
|
||||
@include one_line_text();
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.submenu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 140px;
|
||||
top: 0;
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
background: lighten($dark_grey, 5%);
|
||||
.type {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
color: #FFFFFF;
|
||||
opacity: 0.5;
|
||||
font-weight: 200;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
width: 140px;
|
||||
&:hover {
|
||||
background: lighten($dark_grey, 10%);
|
||||
&:hover{
|
||||
.background {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
border-top: 1px solid lighten($dark_grey, 8%);
|
||||
}
|
||||
.menu-item-wrapper {
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
.menu-item {
|
||||
background: lighten($dark_grey, 5%);
|
||||
}
|
||||
.submenu {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding: 8px 12px;
|
||||
width: 140px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.submenu-icon {
|
||||
font-size: 10px;
|
||||
float: right;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.submenu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 140px;
|
||||
top: 0;
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
background: lighten($dark_grey, 5%);
|
||||
|
||||
.menu-item {
|
||||
width: 140px;
|
||||
&:hover {
|
||||
background: lighten($dark_grey, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
border-top: 1px solid lighten($dark_grey, 8%);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.menu-item {
|
||||
background: lighten($dark_grey, 5%);
|
||||
}
|
||||
.submenu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive( $bp_medium ){
|
||||
top: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
left: 0 !important;
|
||||
margin: 0 !important;
|
||||
background: rgba(10, 10, 10, 0.95);
|
||||
overflow-y: scroll;
|
||||
|
||||
.liner {
|
||||
width: 250px;
|
||||
margin: 30px auto;
|
||||
max-width: 90vw;
|
||||
|
||||
.title {
|
||||
padding: 16px 20px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.menu-item-wrapper {
|
||||
.menu-item {
|
||||
padding: 16px 20px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.touch-context-menu {
|
||||
position: fixed;
|
||||
z-index: 99;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: auto;
|
||||
background: $blue;
|
||||
color: #FFFFFF;
|
||||
|
||||
.menu-item-wrapper {
|
||||
display: inline-block;
|
||||
|
||||
.menu-item {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
width: 50px;
|
||||
height: 44px;
|
||||
padding-top: 6px;
|
||||
border: 0;
|
||||
vertical-align: top;
|
||||
|
||||
&:hover {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
background: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding: 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.submenu-icon {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.cancel {
|
||||
float: right;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:not(:first-child) .menu-item {
|
||||
border-left: 1px solid rgba(255,255,255,0.2) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,6 +144,39 @@ input[type="submit"] {
|
||||
}
|
||||
}
|
||||
|
||||
&.context-menu-trigger {
|
||||
border-color: transparent;
|
||||
padding: 6px;
|
||||
font-size: 20px;
|
||||
min-width: 0;
|
||||
line-height: 20px;
|
||||
color: #000000;
|
||||
|
||||
&: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],
|
||||
&[disabled="disabled"]{
|
||||
opacity: 0.5;
|
||||
|
||||
Reference in New Issue
Block a user