Context menus now at the view level

This commit is contained in:
James Barnsley
2017-03-06 09:03:20 +13:00
parent 644858ebf7
commit 69d535e941
16 changed files with 127 additions and 142 deletions

View File

@ -345,10 +345,10 @@ class ContextMenu extends React.Component{
}
return (
<div className={className} style={style}>
<div id="context-menu" className={className} style={style}>
<div className="liner">
{this.renderTitle()}
{this.renderItems()}
{this.props.menu.context == 'custom' ? this.props.menu.options : this.renderItems()}
</div>
<div className="background" onClick={e => this.props.uiActions.hideContextMenu()}></div>
</div>

View File

@ -4,6 +4,7 @@ import FontAwesome from 'react-fontawesome'
import Icon from './Icon'
import SidebarToggleButton from './SidebarToggleButton'
import ContextMenuTrigger from './ContextMenuTrigger'
export default class Header extends React.Component{
@ -15,17 +16,25 @@ export default class Header extends React.Component{
}
}
renderActions(){
if (!this.props.actions) return null
handleContextMenuTrigger(e,options){
e.preventDefault()
var data = {
e: e,
context: 'custom',
options: options
}
this.props.uiActions.showContextMenu(data)
}
renderOptions(){
if (!this.props.options) return null
return (
<div className={this.state.expanded ? 'expanded actions' : 'actions'}>
<button className="trigger" onClick={e => this.setState({expanded: !this.state.expanded})}>
<FontAwesome name="ellipsis-v" />
</button>
<div className='options'>
<ContextMenuTrigger onTrigger={e => this.handleContextMenuTrigger(e,this.props.options)} />
<span className="items">
<span className="liner">
{ this.props.actions }
{ this.props.options }
</span>
</span>
</div>
@ -38,7 +47,7 @@ export default class Header extends React.Component{
<Icon name={ this.props.icon } />
<SidebarToggleButton />
<h1>{ this.props.title }</h1>
{this.renderActions()}
{this.renderOptions()}
</header>
);
}

View File

@ -80,15 +80,6 @@ class List extends React.Component{
var class_name = 'list-item'
if( row.type ) class_name += ' '+row.type
var source_icon = null
if (this.props.show_source_icon){
if (row.source){
source_icon = <FontAwesome className="source" name={helpers.sourceIcon(false,row.source)} />
}else{
source_icon = <FontAwesome className="source" name={helpers.sourceIcon(row.uri)} />
}
}
return (
<div
onClick={e => this.handleClick(e, row.uri)}
@ -104,7 +95,6 @@ class List extends React.Component{
)
})
}
{source_icon}
</div>
)
})

View File

@ -44,6 +44,7 @@ function handleUpdate() {
ReactGA.set({ page: window.location.hash })
ReactGA.pageview(window.location.hash)
$(window).scrollTop(0)
store.dispatch({type: 'HIDE_CONTEXT_MENU'})
}
global.baseURL = '/'

View File

@ -41,7 +41,7 @@ class Debug extends React.Component{
render(){
var actions = (
var options = (
<button onClick={e => hashHistory.push(global.baseURL+'settings')}>
<FontAwesome name="reply" />&nbsp;
Back
@ -50,7 +50,7 @@ class Debug extends React.Component{
return (
<div className="view debugger-view">
<Header icon="cog" title="Debugger" actions={actions} />
<Header icon="cog" title="Debugger" options={options} uiActions={this.props.uiActions} />
<section>

View File

@ -57,7 +57,7 @@ class Queue extends React.Component{
}
render(){
var actions = (
var options = (
<span>
<button onClick={e => this.props.uiActions.openModal('kiosk_mode')}>
<FontAwesome name="television" />&nbsp;
@ -80,7 +80,7 @@ class Queue extends React.Component{
return (
<div className="view queue-view">
<Header icon="play" title="Now playing" actions={actions} />
<Header icon="play" title="Now playing" options={options} uiActions={this.props.uiActions} />
<FullPlayer />
<section className="list-wrapper">

View File

@ -37,7 +37,7 @@ class QueueHistory extends React.Component{
}
render(){
var actions = (
var options = (
<span>
<button onClick={e => hashHistory.push(global.baseURL+'queue')}>
<FontAwesome name="reply" />&nbsp;
@ -48,7 +48,7 @@ class QueueHistory extends React.Component{
return (
<div className="view playback-history-view">
<Header icon="play" title="Playback history" actions={actions} />
<Header icon="play" title="Playback history" options={options} uiActions={this.props.uiActions} />
<section className="list-wrapper">
<TrackList

View File

@ -163,7 +163,7 @@ class Settings extends React.Component{
render(){
var actions = (
var options = (
<button onClick={e => hashHistory.push(global.baseURL+'debug')}>
<FontAwesome name="flask" />&nbsp;
Debug
@ -172,7 +172,7 @@ class Settings extends React.Component{
return (
<div className="view settings-view">
<Header icon="cog" title="Settings" actions={actions} />
<Header icon="cog" title="Settings" options={options} uiActions={this.props.uiActions} />
<section>

View File

@ -183,16 +183,16 @@ class LibraryAlbums extends React.Component{
}
]
var actions = (
var options = (
<span>
<DropdownField icon="sort" name="Sort" value={this.props.sort} options={sort_options} reverse={this.props.sort_reverse} handleChange={val => this.setSort(val)} />
<DropdownField icon="eye" name="View" value={this.props.view} options={view_options} handleChange={val => this.props.uiActions.set({ library_albums_view: val })} />
<DropdownField icon="sort" name="Sort" value={this.props.sort} options={sort_options} reverse={this.props.sort_reverse} handleChange={val => {this.setSort(val); this.props.uiActions.hideContextMenu() }} />
<DropdownField icon="eye" name="View" value={this.props.view} options={view_options} handleChange={val => {this.props.uiActions.set({ library_albums_view: val }); this.props.uiActions.hideContextMenu() }} />
</span>
)
return (
<div className="view library-albums-view">
<Header icon="cd" title="My albums" actions={actions} />
<Header icon="cd" title="My albums" options={options} uiActions={this.props.uiActions} />
{ this.renderView(albums) }
<LazyLoadListener enabled={this.props.library_albums_more} loadMore={ () => this.loadMore() }/>
</div>

View File

@ -75,8 +75,7 @@ class LibraryArtists extends React.Component{
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
rows={artists}
columns={columns}
link_prefix={global.baseURL+"artist/"}
show_source_icon={true} />
link_prefix={global.baseURL+"artist/"} />
</section>
)
}else{
@ -132,16 +131,16 @@ class LibraryArtists extends React.Component{
}
]
var actions = (
var options = (
<span>
<DropdownField icon="sort" name="Sort" value={ this.props.sort } options={sort_options} reverse={this.props.sort_reverse} handleChange={ value => this.setSort(value) } />
<DropdownField icon="eye" name="View" value={ this.props.view } options={view_options} handleChange={ value => this.props.uiActions.set({ library_artists_view: value }) } />
<DropdownField icon="sort" name="Sort" value={ this.props.sort } options={sort_options} reverse={this.props.sort_reverse} handleChange={value => {this.setSort(value); this.props.uiActions.hideContextMenu() }} />
<DropdownField icon="eye" name="View" value={ this.props.view } options={view_options} handleChange={value => {this.props.uiActions.set({ library_artists_view: value }); this.props.uiActions.hideContextMenu()}} />
</span>
)
return (
<div className="view library-artists-view">
<Header icon="mic" title="My artists" actions={actions} />
<Header icon="mic" title="My artists" options={options} uiActions={this.props.uiActions} />
{ this.renderView(artists) }
<LazyLoadListener enabled={this.props.library_artists_more} loadMore={ () => this.loadMore() }/>
</div>

View File

@ -141,16 +141,16 @@ class LibraryLocalAlbums extends React.Component{
}
]
var actions = (
var options = (
<span>
<DropdownField icon="sort" name="Sort" value={this.props.sort} options={sort_options} reverse={this.props.sort_reverse} handleChange={val => this.setSort(val)} />
<DropdownField icon="eye" name="View" value={this.props.view} options={view_options} handleChange={val => this.props.uiActions.set({ library_local_albums_view: val })} />
<DropdownField icon="sort" name="Sort" value={this.props.sort} options={sort_options} reverse={this.props.sort_reverse} handleChange={val => {this.setSort(val); this.props.uiActions.hideContextMenu() }} />
<DropdownField icon="eye" name="View" value={this.props.view} options={view_options} handleChange={val => {this.props.uiActions.set({ library_local_albums_view: val }); this.props.uiActions.hideContextMenu() }} />
</span>
)
return (
<div className="view library-local-view">
<Header icon="music" title="Local albums" actions={actions} />
<Header icon="music" title="Local albums" options={options} uiActions={this.props.uiActions} />
{this.renderView(albums)}
<LazyLoadListener enabled={(this.moreURIsToLoad().length > 0)} loadMore={ () => this.loadMore() }/>
</div>

View File

@ -71,9 +71,9 @@ class LibraryLocalDirectory extends React.Component{
var items = this.arrange_directory( this.props.directory )
var actions = null
var options = null
if (this.props.params.uri != 'local:directory' ){
actions = (
options = (
<button onClick={ () => window.history.back() }>
<FontAwesome name="reply" />&nbsp;
Back
@ -83,7 +83,7 @@ class LibraryLocalDirectory extends React.Component{
return (
<div className="view library-local-view">
<Header icon="music" title="Local files" actions={actions} />
<Header icon="music" title="Local files" options={options} uiActions={this.props.uiActions} />
<section className="list-wrapper">
<List
columns={[{ name: 'name', width: '100'}]}

View File

@ -69,6 +69,11 @@ class LibraryPlaylists extends React.Component{
label: 'Owner',
name: 'owner'
},
{
width: 10,
label: 'Source',
name: 'source'
},
{
width: 10,
label: 'Tracks',
@ -76,7 +81,7 @@ class LibraryPlaylists extends React.Component{
},
{
width: 10,
label: 'Can edit',
label: 'Editable',
name: 'can_edit'
}
]
@ -86,8 +91,7 @@ class LibraryPlaylists extends React.Component{
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
rows={playlists}
columns={columns}
link_prefix={global.baseURL+"playlist/"}
show_source_icon={true} />
link_prefix={global.baseURL+"playlist/"} />
</section>
)
}else{
@ -137,20 +141,20 @@ class LibraryPlaylists extends React.Component{
}
]
var actions = (
<div>
<DropdownField icon="sort" name="Sort" value={this.props.sort} options={sort_options} reverse={this.props.sort_reverse} handleChange={val => this.setSort(val)} />
<DropdownField icon="eye" name="View" value={this.props.view} options={view_options} handleChange={val => this.props.uiActions.set({ library_playlists_view: val}) } />
var options = (
<span>
<DropdownField icon="sort" name="Sort" value={this.props.sort} options={sort_options} reverse={this.props.sort_reverse} handleChange={val => {this.setSort(val); this.props.uiActions.hideContextMenu() }} />
<DropdownField icon="eye" name="View" value={this.props.view} options={view_options} handleChange={val => {this.props.uiActions.set({ library_playlists_view: val}); this.props.uiActions.hideContextMenu() }} />
<button onClick={ () => this.props.uiActions.openModal('create_playlist', {} ) }>
<FontAwesome name="plus" />&nbsp;
New
</button>
</div>
</span>
)
return (
<div className="view library-playlists-view">
<Header icon="playlist" title="My playlists" actions={actions} />
<Header icon="playlist" title="My playlists" options={options} uiActions={this.props.uiActions} />
{ this.renderView() }
</div>
)

View File

@ -1,5 +1,5 @@
.context-menu {
#context-menu {
position: fixed;
left: 0;
top: 0;
@ -135,6 +135,65 @@
border-top: 1px solid lighten($dark_grey, 8%);
}
}
& > span {
display: block;
button,
.dropdown-field {
display: block;
border: 0;
margin: 0;
min-width: 100% !important;
max-width: 100% !important;
box-sizing: border-box;
font-size: inherit;
text-align: left;
text-transform: none;
font-weight: 300;
color: inherit;
border-top: 1px solid lighten($dark_grey, 8%);
.fa {
display: none;
}
.notouch &:hover {
background: lighten($dark_grey, 5%);
}
}
button {
padding: 16px 20px;
min-width: 100% !important;
max-width: 100% !important;
}
.dropdown-field {
padding: 0;
.label {
padding: 16px 20px;
}
.options {
display: block;
position: static;
background: lighten($dark_grey, 10%);
.option {
padding: 16px 20px 16px 50px;
border-top: 1px solid lighten($dark_grey, 8%);
.fa {
display: inline-block;
left: 25px;
top: 18px;
}
}
}
}
}
}
@include responsive( null, $bp_medium ){

View File

@ -48,34 +48,5 @@
}
}
}
@include responsive( $bp_medium ){
&.expanded {
background: none !important;
color: inherit !important;
}
.label {
background: none !important;
color: inherit !important;
.fa {
display: none;
}
}
.options {
display: block;
position: relative;
top: 0;
background: lighten($light_grey,5%);
.option {
color: #000000;
border-color: $light_grey;
background: none !important;
}
}
}
}

View File

@ -23,11 +23,17 @@ main {
}
}
.actions {
.options {
position: absolute;
top: 0;
right: 10px;
.context-menu-trigger {
display: none;
font-size: 18px;
padding: 16px 14px;
}
button {
background: transparent;
font-size: 14px;
@ -43,74 +49,20 @@ main {
&:active {
background: rgba(0,0,0,0.15);
}
&.trigger {
display: none;
font-size: 18px;
padding: 16px 14px;
}
}
}
@include responsive( $bp_medium ){
.actions {
.options {
right: 0;
button.trigger {
display: inline-block;
.context-menu-trigger {
display: block;
}
.items {
display: none;
}
&.expanded {
z-index: 98;
button.trigger {
z-index: 99;
color: #FFFFFF;
position: relative;
}
.items {
display: block;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0,0,0,0.9);
overflow-y: scroll;
.liner {
display: block;
width: 200px;
margin: 30px auto;
background: $light_grey;
}
button,
.dropdown-field {
display: block;
width: 100%;
text-align: left;
border-bottom: 1px solid rgba(0,0,0,0.1);
.fa {
width: 1.5em;
text-align: center;
}
&.dropdown-field {
.label {
padding-left: 14px;
padding-right: 14px;
}
}
}
}
}
}
}
}