Light interface

This commit is contained in:
James Barnsley
2018-08-27 08:21:41 +12:00
parent ce59cedfbd
commit 9d7ed8efdf
33 changed files with 11290 additions and 14411 deletions

View File

@ -1,73 +1,18 @@
import React, { PropTypes } from 'react';
import * as helpers from '../../helpers';
export default class LatencyControl extends React.Component{
constructor(props){
super(props);
this.handleChange = helpers.throttle(this.handleChange.bind(this), 100);
}
handleClick(e){
var slider = e.target;
if (slider.className != 'slider'){
slider = slider.parentElement;
}
var sliderX = e.clientX - slider.getBoundingClientRect().left;
var sliderWidth = slider.getBoundingClientRect().width;
// Clicked left half
if (sliderX < (sliderWidth/2)){
var percent = Math.round((sliderX / (sliderWidth/2) ) * 100);
// Invert our percentage
percent = 100 - percent;
var value = -(this.props.max * (percent / 100));
// Handle maximum value limits
if (value < -this.props.max){
value = -this.props.max;
}
// Second half
} else {
// Subtract half the slider's width from our click position
sliderX = sliderX - (sliderWidth/2);
var percent = Math.round((sliderX / (sliderWidth/2) ) * 100);
var value = this.props.max * (percent / 100);
// Handle maximum value limits
if (value > this.props.max){
value = this.props.max;
}
}
this.props.onChange(value);
}
handleWheel(e){
if (this.props.scrollWheel){
// Identify which direction we've scrolled (inverted)
// This is simplified and doesn't consider momentum as it varies wildly
// between browsers and devices
var direction = (e.deltaY > 0 ? -1 : 1)
var value = this.props.value;
value += direction * 5
if (value > this.props.max){
value = this.props.max;
} else if (value < -this.props.max){
value = -this.props.max;
}
this.props.onChange(value);
e.preventDefault();
}
handleChange(value){
this.props.onChange(value, this.props.value);
}
render(){
@ -83,6 +28,8 @@ export default class LatencyControl extends React.Component{
var width = percentage;
var negative = false;
if (width > 50) width = 50;
// Negative value
// We reverse it to a positive for easier maths and style rules
} else {
@ -94,12 +41,22 @@ export default class LatencyControl extends React.Component{
var left = 50 - percentage;
var width = percentage;
var negative = true;
if (left < 0) left = 0;
if (width > 50) width = 50;
}
return (
<span className="latency-control" onWheel={e => this.handleWheel(e)}>
<span className="latency-control">
<div className="slider-wrapper">
<div className="slider horizontal" onClick={e => this.handleClick(e)}>
<div className="slider horizontal">
<input
type="range"
min="-100"
max="100"
value={this.props.value}
onChange={e => this.handleChange(parseInt(e.target.value))}
/>
<div className="zero"></div>
<div className="track">
<div className={"progress "+(negative ? 'negative' : 'positive')} style={{ width: width+'%', left: left+'%' }}></div>

View File

@ -37,7 +37,7 @@ export default class TextField extends React.Component{
return (
<input
className={this.props.className ? this.props.className : ''}
type="text"
type={this.props.type ? this.props.type : "text"}
onChange={e => this.handleChange(e)}
onFocus={e => this.handleFocus(e)}
onBlur={e => this.handleBlur(e)}

View File

@ -47,7 +47,7 @@ export default class VolumeControl extends React.Component{
<span className={className}>
{this.props.NoMuteButton ? null : this.renderMuteButton()}
<div className="slider-wrapper">
<div className="slider horizontal">
<div className={"slider horizontal"+(this.props.mute ? " disabled" : "")}>
<input
type="range"
min="0"

View File

@ -139,7 +139,16 @@ export default class Parallax extends React.Component{
// Fill the background with mid-grey
context.beginPath();
context.rect(0, 0, this.state.canvas.width, this.state.canvas.height);
context.fillStyle = "#121212";
switch (this.props.theme){
case 'light':
context.fillStyle = "#EDEDED";
break;
case 'dark':
default:
context.fillStyle = "#121212";
}
context.fill();
if (image && !self.state.loading){
@ -175,8 +184,19 @@ export default class Parallax extends React.Component{
// Construct a gradient overlay
context.rect(0, 0, this.state.canvas.width, this.state.canvas.height);
let gradient = context.createLinearGradient(0, 0, 0, this.state.canvas.height);
gradient.addColorStop(0, 'rgba(24,24,24,0)');
gradient.addColorStop(0.9, 'rgba(24,24,24,1)');
switch (this.props.theme){
case 'light':
gradient.addColorStop(0.5, 'rgba(24,24,24,0)');
gradient.addColorStop(0.9, 'rgba(24,24,24,0.6)');
break;
case 'dark':
default:
gradient.addColorStop(0, 'rgba(24,24,24,0)');
gradient.addColorStop(0.9, 'rgba(24,24,24,1)');
}
context.fillStyle = gradient;
// And now drop it into place

View File

@ -103,11 +103,6 @@ class Snapcast extends React.Component{
onVolumeChange={percent => this.props.pusherActions.setSnapcastClientVolume(client.id, percent)}
onMuteChange={mute => this.props.pusherActions.setSnapcastClientMute(client.id, mute)}
/>
<TextField
className="tiny"
onChange={value => this.props.pusherActions.setSnapcastClientVolume(client.id, parseInt(value))}
value={client.config.volume.percent}
/>
</div>
<div className="col latency">
<LatencyControl
@ -117,6 +112,7 @@ class Snapcast extends React.Component{
/>
<TextField
className="tiny"
type="number"
onChange={value => this.props.pusherActions.setSnapcastClientLatency(client.id, parseInt(value))}
value={String(client.config.latency)}
/>

View File

@ -337,7 +337,7 @@ class Artist extends React.Component{
<div className="view artist-view">
<div className="intro">
<Parallax image={image} />
<Parallax image={image} theme={this.props.theme} />
<div className="liner">
<h1>{this.props.artist ? this.props.artist.name : null}</h1>
@ -367,6 +367,7 @@ class Artist extends React.Component{
const mapStateToProps = (state, ownProps) => {
var uri = ownProps.params.uri;
return {
theme: state.ui.theme,
slim_mode: state.ui.slim_mode,
load_queue: state.ui.load_queue,
artist: (state.core.artists[uri] !== undefined ? state.core.artists[uri] : false),

View File

@ -165,7 +165,7 @@ class Queue extends React.Component{
<Icon name="play_arrow" type="material" />
Now playing
</Header>
<Parallax blur image={current_track_image} />
<Parallax blur image={current_track_image} theme={this.props.theme} />
<div className="content-wrapper">
<div className="current-track">
@ -203,6 +203,7 @@ class Queue extends React.Component{
const mapStateToProps = (state, ownProps) => {
return {
theme: state.ui.theme,
spotify_enabled: state.spotify.enabled,
radio: state.core.radio,
radio_enabled: (state.core.radio && state.core.radio.enabled ? true : false),

View File

@ -8,7 +8,6 @@ import ConfirmationButton from '../components/Fields/ConfirmationButton'
import PusherConnectionList from '../components/PusherConnectionList'
import SourcesPriority from '../components/Fields/SourcesPriority'
import Header from '../components/Header'
import Parallax from '../components/Parallax'
import Icon from '../components/Icon'
import Thumbnail from '../components/Thumbnail'
import URILink from '../components/URILink'
@ -172,17 +171,11 @@ class Settings extends React.Component {
return (
<div className="view settings-view">
<Header className="overlay" options={options} uiActions={this.props.uiActions}>
<Header options={options} uiActions={this.props.uiActions}>
<Icon name="settings" type="material" />
Settings
</Header>
<div className="intro">
<div className="liner">
<Parallax image="assets/backgrounds/settings.jpg" />
</div>
</div>
<section className="content-wrapper">
<h4 className="underline">Server</h4>

View File

@ -47,7 +47,7 @@ class DiscoverFeatured extends React.Component{
if (playlist){
return (
<div className="intro">
<Parallax image={helpers.sizedImages(playlist.images).huge} blur />
<Parallax image={helpers.sizedImages(playlist.images).huge} blur theme={this.props.theme} />
<div className="content cf">
<Link
to={global.baseURL+'playlist/'+playlist.uri}
@ -134,6 +134,7 @@ class DiscoverFeatured extends React.Component{
const mapStateToProps = (state, ownProps) => {
return {
theme: state.ui.theme,
load_queue: state.ui.load_queue,
featured_playlists: state.spotify.featured_playlists,
playlists: state.core.playlists

View File

@ -61,7 +61,7 @@ class DiscoverNewReleases extends React.Component{
if (album){
return (
<div className="intro">
<Parallax image={helpers.sizedImages(album.images).huge} blur />
<Parallax image={helpers.sizedImages(album.images).huge} blur theme={this.props.theme} />
<div className="content cf">
<Link
to={global.baseURL+'album/'+album.uri}
@ -153,6 +153,7 @@ class DiscoverNewReleases extends React.Component{
const mapStateToProps = (state, ownProps) => {
return {
theme: state.ui.theme,
load_queue: state.ui.load_queue,
albums: state.core.albums,
new_releases: state.core.new_releases,

View File

@ -501,7 +501,7 @@ class Discover extends React.Component{
<div className="view discover-view">
<div className="intro">
<Parallax image="assets/backgrounds/discover.jpg" />
<Parallax image="assets/backgrounds/discover.jpg" theme={this.props.theme} />
<div className="liner">
<h1>Explore new music</h1>
@ -545,6 +545,7 @@ class Discover extends React.Component{
const mapStateToProps = (state, ownProps) => {
return {
theme: state.ui.theme,
albums: state.core.albums,
artists: state.core.artists,
tracks: state.core.tracks,

View File

@ -16,12 +16,12 @@
@include animate();
width: 0;
padding-left: 25px;
background: transparent;
}
&:not(.active){
input {
cursor: pointer;
background: transparent !important;
}
&:hover {

View File

@ -48,6 +48,10 @@ main {
display: none;
}
}
.light-theme & {
color: $darkest_grey;
}
}
}
@ -57,6 +61,17 @@ main {
left: 0;
width: 100%;
z-index: 98;
.light-theme & {
h1 {
color: $white;
}
.button,
button {
color: $white;
}
}
}
@include responsive($bp_medium){

View File

@ -17,7 +17,7 @@
border-radius: 3px;
.light-theme & {
background-color: $light_grey;
background-color: $faint_grey;
}
}
@ -37,7 +37,7 @@
border: 0;
&:hover {
background: #000000;
background: $black;
}
}

View File

@ -25,8 +25,10 @@
position: absolute;
top: 0;
left: 0;
border: {
color: lighten($dark_grey, 5%);
border-color: lighten($dark_grey, 5%);
.light-theme & {
border-color: $faint_grey;
}
}
@ -76,6 +78,10 @@
&:before {
border-color: darken($secondary_grey, 5%);
.light-theme & {
border-color: $faint_grey;
}
}
}
}

View File

@ -18,6 +18,10 @@
color: $white;
text-decoration: none;
.light-theme & {
color: $darkest_grey;
}
.thumbnail {
float: left;
margin: 12px;

View File

@ -80,6 +80,22 @@
font-weight: bold;
font-size: 16px;
}
.light-theme & {
background: $faint_grey;
&:hover{
background: darken($faint_grey, 4%);
}
&.active {
background: darken($faint_grey, 4%);
}
&:after {
background: darken($faint_grey, 4%);
}
}
}
}
}
@ -92,6 +108,10 @@
.field:last-child {
padding-bottom: 0;
}
.light-theme & {
background: darken($faint_grey, 4%);
}
}
@include responsive($bp_medium){

View File

@ -13,8 +13,8 @@ aside{
box-sizing: border-box;
.light-theme & {
background: $faint_grey;
border-right: 1px solid darken($faint_grey, 3%);
background: lighten($faint_grey, 2%);
border-right: none;
}
.liner {

View File

@ -2,6 +2,7 @@
.slider {
position: relative;
cursor: pointer;
height: 20px;
input[type="range"]{
position: absolute;
@ -33,83 +34,59 @@
position: absolute;
background: rgba(0,0,0,0.2);
border-radius: 2px;
height: 4px;
top: 42%;
right: 0;
left: 0;
margin-top: -3px;
.progress {
position: absolute;
background: $turquoise;
pointer-events: none;
top: 0;
left: 0;
border-radius: 2px;
height: 100%;
max-width: 100%;
border-radius: 4px;
&:after {
@include animate(0.1s);
content: '';
display: block;
background: $turquoise;
position: absolute;
top: 0;
right: -1px;
width: 4px;
height: 4px;
border-radius: 50%;
}
}
}
&.disabled {
.progress {
background: rgba(255,255,255,0.25);
background: $mid_grey;
&:after {
background: $mid_grey !important;
}
}
}
&.horizontal {
height: 20px;
&:hover {
.track {
height: 4px;
top: 42%;
right: 0;
left: 0;
margin-top: -3px;
.progress {
top: 0;
height: 100%;
max-width: 100%;
border-radius: 4px;
&:after {
@include animate(0.1s);
content: '';
display: block;
background: $turquoise;
position: absolute;
top: 0;
right: -1px;
width: 4px;
height: 4px;
border-radius: 50%;
width: 12px;
height: 12px;
top: -4px;
right: -6px;
background: lighten($turquoise, 10%);
}
}
}
&:hover {
.track {
.progress {
&:after {
width: 12px;
height: 12px;
top: -4px;
right: -6px;
background: lighten($turquoise, 10%);
}
}
}
}
}
&.vertical {
width: 20px;
height: 100%;
.track {
width: 5px;
top: 0;
bottom: 0;
left: 12px;
.progress {
bottom: 0;
width: 100%;
max-height: 100%;
}
}
}
}

View File

@ -17,12 +17,6 @@
.slider-wrapper {
margin-left: 30px;
}
&.muted {
.slider .progress {
background: $mid_grey;
}
}
&.group-volume-control {
padding: 10px 0 5px;
@ -32,10 +26,9 @@
}
&.client-volume-control {
padding-top: 4px;
width: 80%;
padding-top: 11px;
padding-right: 10px;
display: inline-block;
display: block;
vertical-align: middle;
box-sizing: border-box;
}
@ -96,11 +89,8 @@
& > .latency {
width: 28%;
padding-left: 2%;
}
& > .volume,
& > .latency {
input {
input[type="number"] {
width: 20%;
}
}
@ -120,7 +110,7 @@
bottom: 0;
width: 1px;
background: $grey;
z-index: 1;
z-index: 0;
}
.progress.positive {
@ -131,6 +121,11 @@
.progress.negative {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
&:after {
right: auto;
left: -1px;
}
}
}

View File

@ -34,5 +34,20 @@
background: $blue;
color: $white;
}
.light-theme & {
background: $faint_grey;
color: inherit;
&:hover {
background: darken($faint_grey, 4%);
}
&:focus,
&:active {
background: $blue;
color: $white;
}
}
}
}

View File

@ -37,6 +37,15 @@
bottom: -3px;
left: 9px;
}
.light-theme & {
background: $grey;
color: $white;
&:after {
background: $grey;
}
}
}
&:hover {

View File

@ -43,7 +43,7 @@ html {
.light-theme {
background: $white;
color: $dark_grey;
color: $darkest_grey;
}
}

View File

@ -38,6 +38,26 @@ select {
padding-right: 4px;
text-align: center;
}
.light-theme & {
background: $faint_grey;
color: $darkest_grey;
&:focus,
&:active {
background: darken($faint_grey, 3%);
}
}
}
input[type="number"]{
-moz-appearance: textfield;
&::-webkit-inner-spin-button,
&::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
}
input[type="radio"]{
@ -70,6 +90,22 @@ input[type="radio"]{
&:active + .label:before {
background: rgba(255,255,255,0.3);
}
.light-theme & {
& + .label:before {
background: $faint_grey;
}
&:checked + .label:after {
background: $darkest_grey;
}
&:focus + .label:before,
&:active + .label:before {
background: $grey;
}
}
}
input[type="checkbox"]{
@ -98,6 +134,18 @@ input[type="checkbox"]{
&:active + .label:before {
background: rgba(255,255,255,0.3);
}
.light-theme & {
& + .label:before {
background: $faint_grey;
color: $darkest_grey;
}
&:focus + .label:before,
&:active + .label:before {
background: $grey;
}
}
}
select {
@ -173,6 +221,15 @@ input[type="submit"] {
opacity: 0.8;
}
.light-theme & {
background: $dark_grey;
color: $white;
&:active {
background: $black;
}
}
&.small {
padding: 6px 18px 4px;
}
@ -417,6 +474,12 @@ input[type="submit"] {
}
}
}
.light-theme & {
.description {
color: rgba(0,0,0,0.6);
}
}
}
@include responsive($bp_medium){

View File

@ -28,6 +28,10 @@
padding-bottom: 25px;
font-size: 5rem;
line-height: 5rem;
.light-theme & {
color: $white;
}
}
.actions {

View File

@ -1,36 +1,6 @@
.settings-view {
.intro {
position: relative;
@include clearfix();
.parallax {
position: absolute;
z-index: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: auto;
opacity: 0.5;
}
.liner {
position: relative;
z-index: 1;
padding: 20vh 40px 0;
.sub-views {
position: relative;
}
}
}
.content-wrapper {
padding-top: 0;
}
.field.current-user {
.text {
.user {
@ -69,12 +39,6 @@
}
@include responsive($bp_medium){
.intro {
.liner {
padding: 15vh 20px 0;
}
}
.content-wrapper {
padding-bottom: 60px;
}