From 8d1436996c488fb319b540dcc0c9f57c0f1588c6 Mon Sep 17 00:00:00 2001 From: James Barnsley Date: Mon, 21 Nov 2016 10:06:25 +1300 Subject: [PATCH] Dropdown field; preliminary --- src/js/components/Dater.js | 17 +++++++- src/js/components/DropdownField.js | 53 +++++++++++++++++++++++ src/js/services/spotify/reducer.js | 2 +- src/js/services/ui/actions.js | 7 +++ src/js/services/ui/reducer.js | 9 ++++ src/js/views/library/LibraryArtists.js | 55 ++++++++++++++++++++++-- src/js/views/library/LibraryTracks.js | 2 +- src/scss/app.scss | 1 + src/scss/components/_dropdown-field.scss | 51 ++++++++++++++++++++++ 9 files changed, 189 insertions(+), 8 deletions(-) create mode 100755 src/js/components/DropdownField.js create mode 100755 src/scss/components/_dropdown-field.scss diff --git a/src/js/components/Dater.js b/src/js/components/Dater.js index f2216c76..e7ce2704 100755 --- a/src/js/components/Dater.js +++ b/src/js/components/Dater.js @@ -87,8 +87,21 @@ export default class Dater extends React.Component{ case 'ago': var date = new Date(this.props.data) var diff = new Date() - date - // todo: calculate nice diff - return diff + + var seconds = Math.floor(diff / 1000) + var minutes = Math.floor(diff / (1000 * 60)) + var hours = Math.floor(diff / (1000 * 60 * 60)) + var days = Math.floor(diff / (1000 * 60 * 60 * 24)) + + if (seconds < 60){ + return seconds + " seconds"; + }else if (minutes < 60){ + return minutes + " minutes"; + }else if (hours < 24){ + return hours + " hours"; + }else{ + return days + " days" + } break default: diff --git a/src/js/components/DropdownField.js b/src/js/components/DropdownField.js new file mode 100755 index 00000000..8db73064 --- /dev/null +++ b/src/js/components/DropdownField.js @@ -0,0 +1,53 @@ + +import React, { PropTypes } from 'react' +import { connect } from 'react-redux' +import { bindActionCreators } from 'redux' +import FontAwesome from 'react-fontawesome' + +export default class DropdownField extends React.Component{ + + constructor(props) { + super(props); + + this.state = { + expanded: false + } + } + + handleChange(value){ + this.setState({ expanded: !this.state.expanded }) + return this.props.handleChange( value ) + } + + handleToggle(){ + this.setState({ expanded: !this.state.expanded }) + } + + render(){ + if( !this.props.options ) return null + + var classname = 'dropdown-field' + if( this.state.expanded ) classname += ' expanded' + + return ( +
+
this.handleToggle() }> + +   { this.props.name } +
+
+ { + this.props.options.map( option => { + return ( +
this.handleChange(option.value) }> + { option.value == this.props.value ? : null } + { option.label } +
+ ) + }) + } +
+
+ ) + } +} \ No newline at end of file diff --git a/src/js/services/spotify/reducer.js b/src/js/services/spotify/reducer.js index 2e7305a4..b39d5c07 100755 --- a/src/js/services/spotify/reducer.js +++ b/src/js/services/spotify/reducer.js @@ -119,7 +119,7 @@ export default function reducer(spotify = {}, action){ ) } return Object.assign({}, spotify, { - library_tracks: tracks, + library_tracks: [...spotify.library_tracks, ...tracks], library_tracks_more: action.data.next }); diff --git a/src/js/services/ui/actions.js b/src/js/services/ui/actions.js index ac025682..0da0ea5f 100755 --- a/src/js/services/ui/actions.js +++ b/src/js/services/ui/actions.js @@ -59,6 +59,13 @@ export function dragEnd(){ return { type: 'DRAG_END' } } +export function setView( view ){ + return { + type: 'SET_VIEW', + view: view + } +} + export function reorderPlaylistTracks( uri, indexes, insert_before, snapshot_id = false ){ var range = helpers.createRange( indexes ); switch( helpers.uriSource( uri ) ){ diff --git a/src/js/services/ui/reducer.js b/src/js/services/ui/reducer.js index d8758145..df23d39d 100755 --- a/src/js/services/ui/reducer.js +++ b/src/js/services/ui/reducer.js @@ -7,6 +7,14 @@ export default function reducer(ui = {}, action){ case 'LAZY_LOADING': return Object.assign({}, ui, { lazy_loading: action.start }); + + /** + * View views + **/ + case 'SET_VIEW': + return Object.assign({}, ui, action.view) + + /** * Context menu **/ @@ -286,6 +294,7 @@ export default function reducer(ui = {}, action){ var playlist = Object.assign({}, ui.playlist, { following: action.is_following }) return Object.assign({}, ui, { playlist: playlist, following_loading: false }); + /** * Library Playlists **/ diff --git a/src/js/views/library/LibraryArtists.js b/src/js/views/library/LibraryArtists.js index 428aa614..44581da0 100755 --- a/src/js/views/library/LibraryArtists.js +++ b/src/js/views/library/LibraryArtists.js @@ -7,7 +7,10 @@ import { Link } from 'react-router' import LazyLoadListener from '../../components/LazyLoadListener' import Header from '../../components/Header' import ArtistGrid from '../../components/ArtistGrid' +import List from '../../components/List' +import DropdownField from '../../components/DropdownField' +import * as uiActions from '../../services/ui/actions' import * as mopidyActions from '../../services/mopidy/actions' import * as spotifyActions from '../../services/spotify/actions' @@ -27,13 +30,55 @@ class LibraryArtists extends React.Component{ this.props.spotifyActions.getURL( this.props.artists_more, 'SPOTIFY_LIBRARY_ARTISTS_LOADED_MORE' ); } + renderView(){ + if( !this.props.artists ) return null + + if( this.props.view == 'list' ){ + var columns = [ + { + width: 30, + name: 'name' + }, + { + width: 10, + name: 'followers.total' + } + ] + return ( +
+ +
+ ) + }else{ + return ( +
+ +
+ ) + } + } + render(){ + + var view_options = [ + { + value: 'thumbnails', + label: 'Thumbnails' + }, + { + value: 'list', + label: 'List' + } + ] + + var actions = ( + this.props.uiActions.setView({ library_artists_view: value }) } /> + ) + return (
-
-
- { this.props.artists ? : null } -
+
+ { this.renderView() } this.loadMore() }/>
); @@ -49,6 +94,7 @@ class LibraryArtists extends React.Component{ const mapStateToProps = (state, ownProps) => { return { + view: state.ui.library_artists_view, artists: state.spotify.library_artists, artists_more: state.spotify.library_artists_more } @@ -56,6 +102,7 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = (dispatch) => { return { + uiActions: bindActionCreators(uiActions, dispatch), mopidyActions: bindActionCreators(mopidyActions, dispatch), spotifyActions: bindActionCreators(spotifyActions, dispatch) } diff --git a/src/js/views/library/LibraryTracks.js b/src/js/views/library/LibraryTracks.js index 71ab13ac..e1f0c5aa 100755 --- a/src/js/views/library/LibraryTracks.js +++ b/src/js/views/library/LibraryTracks.js @@ -33,8 +33,8 @@ class LibraryTracks extends React.Component{
{ this.props.tracks ? : null } + this.loadMore() }/>
- this.loadMore() }/> ); } diff --git a/src/scss/app.scss b/src/scss/app.scss index 2000d62d..2976536a 100755 --- a/src/scss/app.scss +++ b/src/scss/app.scss @@ -18,6 +18,7 @@ @import 'components/lists'; @import 'components/grid'; @import 'components/header'; +@import 'components/dropdown-field'; @import 'views/artist'; @import 'views/user'; diff --git a/src/scss/components/_dropdown-field.scss b/src/scss/components/_dropdown-field.scss new file mode 100755 index 00000000..8d02aa2b --- /dev/null +++ b/src/scss/components/_dropdown-field.scss @@ -0,0 +1,51 @@ + +.dropdown-field { + font-size: 14px; + font-weight: 600; + text-transform: uppercase; + position: relative; + + .label { + padding: 18px 10px; + cursor: pointer; + } + + &.expanded { + .label { + background: $dark_grey; + color: #FFFFFF; + } + .options { + display: block; + } + } + + .options { + color: #FFFFFF; + position: absolute; + top: 54px; + right: 0px; + z-index: 97; + background: $dark_grey; + display: none; + + .option { + position: relative; + padding: 10px 16px 10px 34px; + border-top: 1px solid lighten($dark_grey, 10%); + cursor: pointer; + + .fa { + position: absolute; + top: 13px; + left: 12px; + font-size: 12px; + } + + &:hover { + background: lighten($dark_grey, 10%); + } + } + } +} +