Splitting out mute, cleaning up mobile output controls

This commit is contained in:
James Barnsley
2018-10-02 16:58:15 +13:00
parent 85c5786d9e
commit d67069c75c
15 changed files with 14298 additions and 10906 deletions

View File

@ -4445,7 +4445,7 @@ $icon-websocket: \e920;
position: relative;
cursor: pointer;
height: 20px; }
.slider input[type="range"] {
.slider__input {
position: absolute;
top: 0;
left: 0;
@ -4454,16 +4454,16 @@ $icon-websocket: \e920;
opacity: 0;
z-index: 2;
cursor: pointer; }
.slider input[type="range"]::-webkit-slider-thumb {
.slider__input::-webkit-slider-thumb {
width: 1px;
border: 0; }
.slider input[type="range"]::-moz-range-thumb {
.slider__input::-moz-range-thumb {
width: 1px;
border: 0; }
.slider input[type="range"]::-ms-thumb {
.slider__input::-ms-thumb {
width: 1px;
border: 0; }
.slider .track {
.slider__track {
position: absolute;
background: rgba(0, 0, 0, 0.2);
border-radius: 2px;
@ -4472,7 +4472,7 @@ $icon-websocket: \e920;
right: 0;
left: 0;
margin-top: -3px; }
.slider .track .progress {
.slider__track__progress {
position: absolute;
background: #08d58f;
pointer-events: none;
@ -4482,7 +4482,7 @@ $icon-websocket: \e920;
height: 100%;
max-width: 100%;
border-radius: 4px; }
.slider .track .progress:after {
.slider__track__progress:after {
-webkit-transition: all 0.1s ease-in-out;
-moz-transition: all 0.1s ease-in-out;
-o-transition: all 0.1s ease-in-out;
@ -4496,11 +4496,11 @@ $icon-websocket: \e920;
width: 4px;
height: 4px;
border-radius: 50%; }
.slider.disabled .progress {
.slider--disabled .slider__track__progress, .slider--muted .slider__track__progress {
background: #a3a19f; }
.slider.disabled .progress:after {
.slider--disabled .slider__track__progress:after, .slider--muted .slider__track__progress:after {
background: #a3a19f !important; }
.slider:hover .track .progress:after {
.slider:hover .slider__track__progress:after {
width: 12px;
height: 12px;
top: -4px;
@ -4684,15 +4684,11 @@ $icon-websocket: \e920;
right: 20px;
width: 150px;
font-size: 18px; }
.playback-controls section.volume .modal-trigger {
display: none; }
.playback-controls section.volume .slider-wrapper {
.playback-controls section.volume .slider__wrapper {
position: absolute;
bottom: 6px;
right: 0;
left: 30px; }
.playback-controls section.volume .muted .slider .progress {
background: #a3a19f; }
.playback-controls section.triggers {
display: none; }
@media (max-width: 800px) {
@ -4700,8 +4696,8 @@ $icon-websocket: \e920;
width: auto;
bottom: auto;
bottom: 8px;
left: 70px;
right: 70px;
left: 80px;
right: 80px;
text-align: center;
font-size: 12px;
color: #FFFFFF !important; }
@ -4743,20 +4739,29 @@ $icon-websocket: \e920;
height: 150px; }
.playback-controls.expanded section.settings {
display: block;
right: 20px;
bottom: 60px;
right: 0;
bottom: 100px;
text-align: right; }
.playback-controls.expanded section.volume {
width: auto;
display: block;
right: 130px;
right: 110px;
left: 22px;
bottom: 60px; }
bottom: 100px; }
.playback-controls.expanded section.progress {
display: block;
bottom: 110px;
left: 0;
right: 0; } }
bottom: 55px;
left: -10px;
right: -10px; }
.playback-controls.expanded .output-control {
position: absolute;
bottom: 150px;
left: 0px;
right: 0px; }
.playback-controls.expanded .output-control > .control {
display: none; }
.playback-controls.expanded .output-control__items {
width: 100%; } }
.output-control {
position: relative; }
@ -4798,8 +4803,6 @@ $icon-websocket: \e920;
position: relative; }
.output-control__item:not(:first-child) {
border-top: 1px solid rgba(0, 0, 0, 0.05); }
.output-control__item--has-power-button {
padding-left: 56px; }
.output-control__item__actions {
float: right; }
.output-control__item__actions .action {
@ -4811,16 +4814,56 @@ $icon-websocket: \e920;
color: #a3a19f; }
.output-control__item__actions .action:hover {
color: #181818; }
.output-control__item--has-power-button .output-control__item__volume {
padding-left: 50px; }
.output-control__item__volume {
padding-top: 2px;
padding-left: 25px; }
.output-control__item__mute.control {
position: relative;
padding: 2px;
color: #000000;
float: left;
font-size: 16px; }
@keyframes click_feedback {
0% {
top: 50%;
left: 0;
width: 100%;
padding-bottom: 100%;
opacity: 1; }
100% {
top: 25%;
left: -25%;
width: 150%;
padding-bottom: 150%;
opacity: 0; } }
.output-control__item__mute.control:after {
content: '';
display: block;
position: absolute;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
opacity: 0;
border-radius: 50%;
margin-top: -50%;
pointer-events: none; }
.output-control__item__mute.control:focus:after, .output-control__item__mute.control:active:after {
animation-name: click_feedback;
animation-duration: 0.2s;
animation-timing-function: ease-out;
animation-iteration-count: 1; }
.output-control__item__power {
position: relative;
position: absolute !important;
top: 12px;
left: 12px;
width: 30px;
height: 30px;
width: 18px;
height: 18px;
text-align: center;
color: #FFFFFF;
cursor: pointer; }
cursor: pointer;
float: left;
margin-top: -2px;
margin-right: 6px; }
@keyframes click_feedback {
0% {
@ -4851,13 +4894,13 @@ $icon-websocket: \e920;
animation-timing-function: ease-out;
animation-iteration-count: 1; }
.output-control__item__power .icon {
padding-top: 10px;
font-size: 16px; }
padding-top: 5px;
font-size: 12px; }
.output-control__item__power:before {
content: '';
display: block;
position: absolute;
top: 3px;
top: 2px;
left: 0;
width: 100%;
padding-bottom: 100%;
@ -4865,46 +4908,6 @@ $icon-websocket: \e920;
z-index: -1;
border-radius: 50%;
opacity: 1; }
.output-control__item__volume {
position: relative;
padding-left: 22px;
display: block; }
.output-control__item__volume .mute-control {
position: relative;
position: absolute !important;
top: -2px;
left: -2px;
padding: 2px;
color: #000000; }
@keyframes click_feedback {
0% {
top: 50%;
left: 0;
width: 100%;
padding-bottom: 100%;
opacity: 1; }
100% {
top: 25%;
left: -25%;
width: 150%;
padding-bottom: 150%;
opacity: 0; } }
.output-control__item__volume .mute-control:after {
content: '';
display: block;
position: absolute;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
opacity: 0;
border-radius: 50%;
margin-top: -50%;
pointer-events: none; }
.output-control__item__volume .mute-control:focus:after, .output-control__item__volume .mute-control:active:after {
animation-name: click_feedback;
animation-duration: 0.2s;
animation-timing-function: ease-out;
animation-iteration-count: 1; }
@media (max-width: 800px) {
.output-control__items {
width: 86vw;
@ -6155,13 +6158,13 @@ main header {
width: 1px;
background: #383734;
z-index: 0; }
.snapcast__client__latency .latency-control .progress.positive {
.snapcast__client__latency .latency-control .slider__track__progress--positive {
border-top-left-radius: 0;
border-bottom-left-radius: 0; }
.snapcast__client__latency .latency-control .progress.negative {
.snapcast__client__latency .latency-control .slider__track__progress--negative {
border-top-right-radius: 0;
border-bottom-right-radius: 0; }
.snapcast__client__latency .latency-control .progress.negative:after {
.snapcast__client__latency .latency-control .slider__track__progress--negative:after {
right: auto;
left: -1px; }
@media (max-width: 800px) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -98,7 +98,7 @@
// Release details
// These are automatically injected to built HTML
var build = "1538382441";
var build = "1538419845";
var version = "3.26.2";
// Construct the script tag

View File

@ -48,18 +48,19 @@ export default class LatencyControl extends React.Component{
return (
<span className="latency-control">
<div className="slider-wrapper">
<div className="slider horizontal">
<div className="slider__wrapper">
<div className="slider slider--latency">
<input
type="range"
min="-100"
max="100"
className="slider__input"
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>
<div className="slider__track">
<div className={"slider__track__progress slider__track__progress--"+(negative ? 'negative' : 'positive')} style={{ width: width+'%', left: left+'%' }}></div>
</div>
</div>
</div>

View File

@ -0,0 +1,30 @@
import React from 'react';
import Icon from '../Icon';
import * as helpers from '../../helpers';
export default class MuteControl extends React.Component{
constructor(props){
super(props);
}
render(){
if (this.props.mute){
return (
<a className={"control mute-control "+(this.props.noTooltip ? "" : "tooltip ")+(this.props.className ? this.props.className : "")} onClick={() => this.props.onMuteChange(false)}>
<Icon className="red-text" name="volume_off" />
{this.props.noTooltip ? null : <span className="tooltip__content">Unmute</span>}
</a>
)
} else {
return (
<a className={"control mute-control "+(this.props.noTooltip ? "" : "tooltip ")+(this.props.className ? this.props.className : "")} onClick={() => this.props.onMuteChange(true)}>
<Icon className="muted" name="volume_mute" />
{this.props.noTooltip ? null : <span className="tooltip__content">Mute</span>}
</a>
)
}
}
}

View File

@ -4,6 +4,7 @@ import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import VolumeControl from './VolumeControl'
import MuteControl from './MuteControl'
import SnapcastPowerButton from './SnapcastPowerButton'
import Icon from '../Icon'
@ -73,22 +74,29 @@ class OutputControl extends React.Component{
return (
<div className={"output-control__item outputs__item--snapcast"+(commands && commands.power ? " output-control__item--has-power-button": '')} key={client.id}>
<div className="output-control__item__name">
{client.name}
</div>
<div className="output-control__item__controls">
{commands && commands.power ? <SnapcastPowerButton
className="output-control__item__power"
client={client}
onClick={e => this.props.snapcastActions.sendClientCommand(client.id, commands.power)}
/> : null}
<div className="output-control__item__name">
{client.name}
</div>
<MuteControl
className="output-control__item__mute"
noTooltip={true}
mute={client.mute}
onMuteChange={mute => this.props.snapcastActions.setClientMute(client.id, mute)}
/>
<VolumeControl
className="output-control__item__volume"
volume={client.volume}
mute={client.mute}
onVolumeChange={percent => this.props.snapcastActions.setClientVolume(client.id, percent)}
onMuteChange={mute => this.props.snapcastActions.setClientMute(client.id, mute)}
/>
</div>
</div>
);
})
}
@ -138,7 +146,7 @@ class OutputControl extends React.Component{
}
render(){
if (this.state.expanded){
if (this.state.expanded || this.props.force_expanded){
return (
<span className="output-control">
<a className="control speakers active" onClick={e => this.setExpanded()}><Icon name="speaker" /></a>

View File

@ -29,16 +29,17 @@ class ProgressSlider extends React.Component{
}
return (
<div className={"progress slider horizontal "+this.props.play_state}>
<div className="track">
<div className={"slider slider--playback-progress slider--"+this.props.play_state}>
<div className="slider__track">
<input
type="range"
min="0"
max="100"
value={percent}
className="slider__input"
onChange={e => this.handleChange(parseInt(e.target.value))}
/>
<div className="progress" style={{ width: (percent)+'%' }}></div>
<div className="slider__track__progress" style={{ width: (percent)+'%' }}></div>
</div>
</div>
);

View File

@ -16,51 +16,23 @@ export default class VolumeControl extends React.Component{
this.props.onVolumeChange(value, this.props.volume);
}
renderMuteButton(){
if (this.props.mute){
return (
<a className="control mute-control tooltip" onClick={() => this.props.onMuteChange(false)}>
<Icon className="red-text" name="volume_off" />
<span className="tooltip__content">Unmute</span>
</a>
)
} else {
return (
<a className="control mute-control tooltip" onClick={() => this.props.onMuteChange(true)}>
<Icon className="muted" name="volume_mute" />
<span className="tooltip__content">Mute</span>
</a>
)
}
}
render(){
var className = "volume-control";
if (this.props.mute){
className += " muted";
}
if (this.props.className){
className += " "+this.props.className;
}
return (
<span className={className}>
{this.props.NoMuteButton ? null : this.renderMuteButton()}
<div className="slider-wrapper">
<div className={"slider horizontal"+(this.props.mute ? " disabled" : "")}>
<div className={"slider__wrapper "+(this.props.className ? this.props.className : "")}>
<div className={"slider slider--volume "+(this.props.mute ? "slider--muted" : "")}>
<input
className="slider__input"
type="range"
min="0"
max="25"
value={this.props.volume/4}
onChange={e => this.handleChange(parseInt(e.target.value)*4)}
/>
<div className="track">
<div className="progress" style={{ width: this.props.volume+'%' }}></div>
<div className="slider__track">
<div className="slider__track__progress" style={{ width: this.props.volume+'%' }}></div>
</div>
</div>
</div>
</span>
);
}
}

View File

@ -6,6 +6,7 @@ import { bindActionCreators } from 'redux'
import ProgressSlider from './Fields/ProgressSlider'
import VolumeControl from './Fields/VolumeControl'
import MuteControl from './Fields/MuteControl'
import OutputControl from './Fields/OutputControl'
import Dater from './Dater'
import ArtistSentence from './ArtistSentence'
@ -161,7 +162,7 @@ class PlaybackControls extends React.Component{
{this.renderConsumeButton()}
{this.renderRandomButton()}
{this.renderRepeatButton()}
<OutputControl />
<OutputControl force_expanded={this.state.expanded} />
</section>
<section className="progress">
@ -171,12 +172,15 @@ class PlaybackControls extends React.Component{
</section>
<section className="volume">
<MuteControl
mute={this.props.mute}
onMuteChange={mute => this.props.mopidyActions.setMute(mute)}
/>
<VolumeControl
scrollWheel
volume={this.props.volume}
mute={this.props.mute}
onVolumeChange={percent => this.props.mopidyActions.setVolume(percent)}
onMuteChange={mute => this.props.mopidyActions.setMute(mute)}
/>
</section>

View File

@ -0,0 +1 @@

View File

@ -53,10 +53,6 @@
border-top: 1px solid rgba(0,0,0,0.05);
}
&--has-power-button {
padding-left: 56px;
}
&__actions {
float: right;
@ -71,27 +67,46 @@
}
}
&--has-power-button {
.output-control__item__volume {
padding-left: 50px;
}
}
&__volume {
padding-top: 2px;
padding-left: 25px;
}
&__mute.control {
@include click_feedback(rgba(0,0,0,0.5));
padding: 2px;
color: $black;
float: left;
font-size: 16px;
}
&__power {
@include click_feedback(rgba(0,0,0,0.5));
position: absolute !important;
top: 12px;
left: 12px;
width: 30px;
height: 30px;
width: 18px;
height: 18px;
text-align: center;
color: $white;
cursor: pointer;
float: left;
margin-top: -2px;
margin-right: 6px;
.icon {
padding-top: 10px;
font-size: 16px;
padding-top: 5px;
font-size: 12px;
}
&:before {
content: '';
display: block;
position: absolute;
top: 3px;
top: 2px;
left: 0;
width: 100%;
padding-bottom: 100%;
@ -101,21 +116,6 @@
opacity: 1;
}
}
&__volume {
position: relative;
padding-left: 22px;
display: block;
.mute-control {
@include click_feedback(rgba(0,0,0,0.5));
position: absolute !important;
top: -2px;
left: -2px;
padding: 2px;
color: $black;
}
}
}
@include responsive($bp_medium){

View File

@ -176,22 +176,12 @@
width: 150px;
font-size: 18px;
.modal-trigger {
display: none;
}
.slider-wrapper {
.slider__wrapper {
position: absolute;
bottom: 6px;
right: 0;
left: 30px;
}
.muted {
.slider .progress {
background: $mid_grey;
}
}
}
&.triggers {
@ -205,8 +195,8 @@
width: auto;
bottom: auto;
bottom: 8px;
left: 70px;
right: 70px;
left: 80px;
right: 80px;
text-align: center;
font-size: 12px;
color: $white !important;
@ -277,27 +267,41 @@
height: 150px;
section {
&.settings {
display: block;
right: 20px;
bottom: 60px;
right: 0;
bottom: 100px;
text-align: right;
}
&.volume {
width: auto;
display: block;
right: 130px;
right: 110px;
left: 22px;
bottom: 60px;
bottom: 100px;
}
&.progress {
display: block;
bottom: 110px;
left: 0;
right: 0;
bottom: 55px;
left: -10px;
right: -10px;
}
}
.output-control {
position: absolute;
bottom: 150px;
left: 0px;
right: 0px;
& > .control {
display: none;
}
&__items {
width: 100%;
}
}
}

View File

@ -4,7 +4,7 @@
cursor: pointer;
height: 20px;
input[type="range"]{
&__input {
position: absolute;
top: 0;
left: 0;
@ -30,7 +30,7 @@
}
}
.track {
&__track {
position: absolute;
background: rgba(0,0,0,0.2);
border-radius: 2px;
@ -40,7 +40,7 @@
left: 0;
margin-top: -3px;
.progress {
&__progress {
position: absolute;
background: $turquoise;
pointer-events: none;
@ -66,8 +66,9 @@
}
}
&.disabled {
.progress {
&--disabled,
&--muted {
.slider__track__progress {
background: $mid_grey;
&:after {
@ -77,8 +78,8 @@
}
&:hover {
.track {
.progress {
.slider__track {
&__progress {
&:after {
width: 12px;
height: 12px;

View File

@ -145,12 +145,14 @@
z-index: 0;
}
.progress.positive {
.slider__track {
&__progress {
&--positive {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.progress.negative {
&--negative {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
@ -161,6 +163,8 @@
}
}
}
}
}
@include responsive($bp_medium){
padding-left: 0 !important;