mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-26 15:09:09 +00:00
VolumeSlider combined with volume mute button in VolumeBar
This commit is contained in:
parent
cc83ac40cc
commit
1bb91b3db8
8 changed files with 162 additions and 126 deletions
|
|
@ -1,13 +1,20 @@
|
|||
import React, { Component, Fragment } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import Icon from 'stremio-icons/dom';
|
||||
import { Popup } from 'stremio-common';
|
||||
import SeekBar from './SeekBar';
|
||||
import VolumeSlider from './VolumeSlider';
|
||||
import VolumeBar from './VolumeBar';
|
||||
import SubtitlesPicker from './SubtitlesPicker';
|
||||
import styles from './styles';
|
||||
|
||||
//TODO move this in separate file
|
||||
const ControlBarButton = React.forwardRef(({ active, icon, onClick }, ref) => (
|
||||
<div ref={ref} className={classnames(styles['control-bar-button'], { [styles['active']]: active })} onClick={onClick}>
|
||||
<Icon className={styles['icon']} icon={icon} />
|
||||
</div>
|
||||
));
|
||||
|
||||
class ControlBar extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -42,11 +49,15 @@ class ControlBar extends Component {
|
|||
this.props.setSelectedSubtitleTrackId(selectedSubtitleTrackId);
|
||||
}
|
||||
|
||||
toogleVolumeMute = () => {
|
||||
this.props.volume === 0 ? this.props.unmute() : this.props.mute();
|
||||
mute = () => {
|
||||
this.props.mute();
|
||||
}
|
||||
|
||||
onPlayPauseButtonClick = () => {
|
||||
unmute = () => {
|
||||
this.props.unmute();
|
||||
}
|
||||
|
||||
togglePaused = () => {
|
||||
this.props.paused ? this.props.play() : this.props.pause();
|
||||
}
|
||||
|
||||
|
|
@ -84,32 +95,23 @@ class ControlBar extends Component {
|
|||
|
||||
const icon = this.props.paused ? 'ic_play' : 'ic_pause';
|
||||
return (
|
||||
<div className={styles['control-bar-button']} onClick={this.onPlayPauseButtonClick}>
|
||||
<Icon className={styles['icon']} icon={icon} />
|
||||
</div>
|
||||
<ControlBarButton
|
||||
icon={icon}
|
||||
onClick={this.togglePaused}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderVolumeBar() {
|
||||
if (this.props.volume === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const icon = this.props.volume === 0 ? 'ic_volume0' :
|
||||
this.props.volume < 50 ? 'ic_volume1' :
|
||||
this.props.volume < 100 ? 'ic_volume2' :
|
||||
'ic_volume3';
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={styles['control-bar-button']} onClick={this.toogleVolumeMute}>
|
||||
<Icon className={styles['icon']} icon={icon} />
|
||||
</div>
|
||||
<VolumeSlider
|
||||
className={styles['volume-slider']}
|
||||
volume={this.props.volume}
|
||||
setVolume={this.setVolume}
|
||||
/>
|
||||
</Fragment>
|
||||
<VolumeBar
|
||||
className={styles['volume-bar']}
|
||||
toggleButtonComponent={ControlBarButton}
|
||||
volume={this.props.volume}
|
||||
setVolume={this.setVolume}
|
||||
mute={this.mute}
|
||||
unmute={this.unmute}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -117,9 +119,7 @@ class ControlBar extends Component {
|
|||
return (
|
||||
<Popup className={styles['popup-container']} border={true} onOpen={this.onSharePopupOpen} onClose={this.onSharePopupClose}>
|
||||
<Popup.Label>
|
||||
<div className={classnames(styles['control-bar-button'], { [styles['active']]: this.state.sharePopupOpen })}>
|
||||
<Icon className={styles['icon']} icon={'ic_share'} />
|
||||
</div>
|
||||
<ControlBarButton active={this.state.sharePopupOpen} icon={'ic_share'} />
|
||||
</Popup.Label>
|
||||
<Popup.Menu>
|
||||
<div className={classnames(styles['popup-content'], styles['share-popup-content'])} />
|
||||
|
|
@ -136,9 +136,7 @@ class ControlBar extends Component {
|
|||
return (
|
||||
<Popup className={styles['popup-container']} border={true} onOpen={this.onSubtitlesPopupOpen} onClose={this.onSubtitlesPopupClose}>
|
||||
<Popup.Label>
|
||||
<div className={classnames(styles['control-bar-button'], { [styles['active']]: this.state.subtitlesPopupOpen })}>
|
||||
<Icon className={styles['icon']} icon={'ic_sub'} />
|
||||
</div>
|
||||
<ControlBarButton active={this.state.subtitlesPopupOpen} icon={'ic_sub'} />
|
||||
</Popup.Label>
|
||||
<Popup.Menu>
|
||||
<SubtitlesPicker
|
||||
|
|
@ -153,10 +151,6 @@ class ControlBar extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (['paused', 'time', 'duration', 'volume', 'subtitleTracks'].every(propName => this.props[propName] === null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classnames(styles['control-bar-container'], this.props.className)}>
|
||||
{this.renderSeekBar()}
|
||||
|
|
|
|||
109
src/routes/Player/ControlBar/VolumeBar/VolumeBar.js
Normal file
109
src/routes/Player/ControlBar/VolumeBar/VolumeBar.js
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import debounce from 'lodash.debounce';
|
||||
import { Slider } from 'stremio-common';
|
||||
import styles from './styles';
|
||||
|
||||
class VolumeBar extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
volume: null
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return nextState.volume !== this.state.volume ||
|
||||
nextProps.volume !== this.props.volume ||
|
||||
nextProps.className !== this.props.className;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.resetVolumeDebounced.cancel();
|
||||
}
|
||||
|
||||
toogleVolumeMute = () => {
|
||||
this.props.volume === 0 ? this.props.unmute() : this.props.mute();
|
||||
}
|
||||
|
||||
resetVolumeDebounced = debounce(() => {
|
||||
this.setState({ volume: null });
|
||||
}, 100)
|
||||
|
||||
onSlide = (volume) => {
|
||||
this.resetVolumeDebounced.cancel();
|
||||
this.setState({ volume });
|
||||
}
|
||||
|
||||
onComplete = (volume) => {
|
||||
this.resetVolumeDebounced();
|
||||
this.setState({ volume });
|
||||
this.props.setVolume(volume);
|
||||
}
|
||||
|
||||
onCancel = () => {
|
||||
this.resetVolumeDebounced.cancel();
|
||||
this.setState({ volume: null });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.volume === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const volume = this.state.volume !== null ? this.state.volume : this.props.volume;
|
||||
const icon = volume === 0 ? 'ic_volume0' :
|
||||
volume < 30 ? 'ic_volume1' :
|
||||
volume < 70 ? 'ic_volume2' :
|
||||
'ic_volume3';
|
||||
return (
|
||||
<div className={classnames(styles['volume-bar-container'], this.props.className)}>
|
||||
{React.createElement(this.props.toggleButtonComponent, { icon, onClick: this.toogleVolumeMute }, null)}
|
||||
<Slider
|
||||
className={styles['slider']}
|
||||
value={volume}
|
||||
minimumValue={0}
|
||||
maximumValue={100}
|
||||
orientation={'horizontal'}
|
||||
onSlide={this.onSlide}
|
||||
onComplete={this.onComplete}
|
||||
onCancel={this.onCancel}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
VolumeBar.propTypes = {
|
||||
className: PropTypes.string,
|
||||
volume: PropTypes.number,
|
||||
toggleButtonComponent: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired,
|
||||
setVolume: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default VolumeBar;
|
||||
|
||||
|
||||
|
||||
// if (this.props.volume === null) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// const icon = this.props.volume === 0 ? 'ic_volume0' :
|
||||
// this.props.volume < 50 ? 'ic_volume1' :
|
||||
// this.props.volume < 100 ? 'ic_volume2' :
|
||||
// 'ic_volume3';
|
||||
// return (
|
||||
// <Fragment>
|
||||
// <div className={styles['control-bar-button']} onClick={this.toogleVolumeMute}>
|
||||
// <Icon className={styles['icon']} icon={icon} />
|
||||
// </div>
|
||||
// <VolumeSlider
|
||||
// className={styles['volume-slider']}
|
||||
// volume={this.props.volume}
|
||||
// setVolume={this.setVolume}
|
||||
// />
|
||||
// </Fragment>
|
||||
// );
|
||||
3
src/routes/Player/ControlBar/VolumeBar/index.js
Normal file
3
src/routes/Player/ControlBar/VolumeBar/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import VolumeBar from './VolumeBar';
|
||||
|
||||
export default VolumeBar;
|
||||
15
src/routes/Player/ControlBar/VolumeBar/styles.less
Normal file
15
src/routes/Player/ControlBar/VolumeBar/styles.less
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
.volume-bar-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.slider {
|
||||
--thumb-size: var(--volume-bar-thumb-size);
|
||||
--track-color: var(--color-surfacelight);
|
||||
--thumb-color: var(--color-surfacelight);
|
||||
--thumb-active-color: var(--color-surfacelighter);
|
||||
--track-active-color: var(--color-surfacelighter);
|
||||
flex: 1;
|
||||
margin: 0 calc(var(--volume-slider-thumb-size) * 0.5);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import debounce from 'lodash.debounce';
|
||||
import { Slider } from 'stremio-common';
|
||||
import styles from './styles';
|
||||
|
||||
class VolumeSlider extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
volume: null
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return nextState.volume !== this.state.volume ||
|
||||
nextProps.volume !== this.props.volume ||
|
||||
nextProps.className !== this.props.className;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.resetVolumeDebounced.cancel();
|
||||
}
|
||||
|
||||
resetVolumeDebounced = debounce(() => {
|
||||
this.setState({ volume: null });
|
||||
}, 100)
|
||||
|
||||
onSlide = (volume) => {
|
||||
this.resetVolumeDebounced.cancel();
|
||||
this.setState({ volume });
|
||||
}
|
||||
|
||||
onComplete = (volume) => {
|
||||
this.resetVolumeDebounced();
|
||||
this.setState({ volume });
|
||||
this.props.setVolume(volume);
|
||||
}
|
||||
|
||||
onCancel = () => {
|
||||
this.resetVolumeDebounced.cancel();
|
||||
this.setState({ volume: null });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.volume === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const volume = this.state.volume !== null ? this.state.volume : this.props.volume;
|
||||
return (
|
||||
<Slider
|
||||
className={classnames(styles['slider'], this.props.className)}
|
||||
value={volume}
|
||||
minimumValue={0}
|
||||
maximumValue={100}
|
||||
orientation={'horizontal'}
|
||||
onSlide={this.onSlide}
|
||||
onComplete={this.onComplete}
|
||||
onCancel={this.onCancel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
VolumeSlider.propTypes = {
|
||||
className: PropTypes.string,
|
||||
volume: PropTypes.number,
|
||||
setVolume: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default VolumeSlider;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import VolumeSlider from './VolumeSlider';
|
||||
|
||||
export default VolumeSlider;
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.slider {
|
||||
--thumb-size: var(--volume-slider-thumb-size);
|
||||
--track-color: var(--color-surfacelight);
|
||||
--thumb-color: var(--color-surfacelight);
|
||||
--thumb-active-color: var(--color-surfacelighter);
|
||||
--track-active-color: var(--color-surfacelighter);
|
||||
margin: 0 calc(var(--volume-slider-thumb-size) * 0.5);
|
||||
}
|
||||
|
|
@ -52,10 +52,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.volume-slider {
|
||||
--volume-slider-thumb-size: calc(var(--control-bar-button-height) * 0.3);
|
||||
height: var(--volume-slider-thumb-size);
|
||||
width: calc(var(--control-bar-button-height) * 3);
|
||||
.volume-bar {
|
||||
--volume-bar-thumb-size: calc(var(--control-bar-button-height) * 0.3);
|
||||
height: var(--control-bar-button-height);
|
||||
width: calc(var(--control-bar-button-height) * 4);
|
||||
}
|
||||
|
||||
.flex-spacing {
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
box-shadow: 0 0 calc(var(--control-bar-button-height) * 2) calc(var(--control-bar-button-height) * 2) var(--color-backgrounddarker);
|
||||
box-shadow: 0 0 calc(var(--control-bar-button-height) * 2) calc(var(--control-bar-button-height) * 2.3) var(--color-backgrounddarker);
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue