[media UI] Component refactor.

- Fixed style of the floating control extensions panel.
- Extracted `ProgressBar` and `ExtraControls` into separate components.
This commit is contained in:
Fabio Manganiello 2023-11-06 01:54:12 +01:00
parent 8e8bd7fb9f
commit e0a9ccca24
Signed by: blacklight
GPG key ID: D90FBA7F76362774
6 changed files with 176 additions and 157 deletions

View file

@ -3,7 +3,7 @@
<div class="row"> <div class="row">
<div class="col-3"> <div class="col-3">
</div> </div>
<div class="col-6"> <div class="col-6 buttons">
<div class="buttons"> <div class="buttons">
<button @click="$emit('previous')" title="Play previous track" v-if="buttons_.previous"> <button @click="$emit('previous')" title="Play previous track" v-if="buttons_.previous">
<i class="icon fa fa-step-backward"></i> <i class="icon fa fa-step-backward"></i>
@ -21,43 +21,18 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-9 volume-container">
<VolumeSlider :value="status.volume" :range="volumeRange" :status="status" <VolumeSlider :value="status.volume" :range="volumeRange" :status="status"
@mute="$emit('mute')" @unmute="$emit('unmute')" @mute="$emit('mute')" @unmute="$emit('unmute')"
@set-volume="$emit('set-volume', $event)" /> @set-volume="$emit('set-volume', $event)" />
</div>
<div class="col-3 list-controls"> <ExtraControls :status="status" :buttons="buttons_"
<button @click="$emit('consume', !status.consume)" :class="{enabled: status.consume}" @consume="$emit('consume', !status.consume)"
title="Toggle consume mode" v-if="buttons_.consume"> @random="$emit('random', !status.random)"
<i class="icon fa fa-utensils"></i> @repeat="$emit('repeat', !status.repeat)" />
</button>
<button @click="$emit('random', !status.random)" :class="{enabled: status.random}"
title="Toggle shuffle" v-if="buttons_.random">
<i class="icon fa fa-random"></i>
</button>
<button @click="$emit('repeat', !status.repeat)" :class="{enabled: status.repeat}"
title="Toggle repeat" v-if="buttons_.repeat">
<i class="icon fa fa-redo"></i>
</button>
</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-s-2 col-m-1 time"> <ProgressBar :elapsed="elapsed" :duration="duration" :status="status" @seek="$emit('seek', $event)" />
<span class="elapsed-time"
v-text="elapsed != null && (status.state === 'play' || status.state === 'pause') ? convertTime(elapsed) : '-:--'"></span>
</div>
<div class="col-s-8 col-m-10 time-bar">
<Slider :value="elapsed" :range="[0, duration]" :disabled="!duration || status.state === 'stop'"
@mouseup="$emit('seek', $event.target.value)" />
</div>
<div class="col-s-2 col-m-1 time">
<span class="total-time"
v-text="duration && status.state !== 'stop' ? convertTime(duration) : '-:--'"></span>
</div>
</div> </div>
</div> </div>
@ -70,7 +45,7 @@
</button> </button>
</div> </div>
<div class="track-container col-s-8 col-m-8 col-l-3"> <div class="track-container col-s-9 col-m-9 col-l-3">
<div class="track-info" v-if="track && status?.state !== 'stop'"> <div class="track-info" v-if="track && status?.state !== 'stop'">
<div class="title" v-if="status.state === 'play' || status.state === 'pause'"> <div class="title" v-if="status.state === 'play' || status.state === 'pause'">
<a :href="$route.fullPath" v-text="track.title?.length ? track.title : '[No Title]'" <a :href="$route.fullPath" v-text="track.title?.length ? track.title : '[No Title]'"
@ -103,45 +78,25 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-1 time"> <ProgressBar :elapsed="elapsed" :duration="duration" :status="status" @seek="$emit('seek', $event)" />
<span class="elapsed-time"
v-text="elapsed != null && (status.state === 'play' || status.state === 'pause') ? convertTime(elapsed) : '-:--'"></span>
</div>
<div class="col-10">
<Slider :value="elapsed" :range="[0, duration]" :disabled="!duration || status.state === 'stop'"
@mouseup="$emit('seek', $event.target.value)" />
</div>
<div class="col-1 time">
<span class="total-time"
v-text="duration && status.state !== 'stop' ? convertTime(duration) : '-:--'"></span>
</div>
</div> </div>
</div> </div>
<div class="col-2 pull-right until tablet right-buttons"> <div class="col-1 until tablet right-controls">
<button @click="expanded = !expanded" :title="expanded ? 'Show more controls' : 'Hide extra controls'"> <button @click="expanded = !expanded" :title="expanded ? 'Show more controls' : 'Hide extra controls'">
<i class="fas" :class="[`fa-chevron-${expanded ? 'down' : 'up'}`]" /> <i class="fas" :class="[`fa-chevron-${expanded ? 'down' : 'up'}`]" />
</button> </button>
</div> </div>
<div class="col-3 pull-right from desktop"> <div class="col-3 from desktop right-controls">
<div class="row volume-container">
<VolumeSlider :value="status.volume" :range="volumeRange" :status="status" <VolumeSlider :value="status.volume" :range="volumeRange" :status="status"
@mute="$emit('mute')" @unmute="$emit('unmute')" @mute="$emit('mute')" @unmute="$emit('unmute')"
@set-volume="$emit('set-volume', $event)" /> @set-volume="$emit('set-volume', $event)" />
</div>
<div class="row list-controls">
<button @click="$emit('consume')" :class="{enabled: status.consume}" title="Toggle consume mode" v-if="buttons_.consume">
<i class="icon fa fa-utensils"></i>
</button>
<button @click="$emit('random')" :class="{enabled: status.random}" title="Toggle shuffle" v-if="buttons_.random">
<i class="icon fa fa-random"></i>
</button>
<button @click="$emit('repeat')" :class="{enabled: status.repeat}" title="Toggle repeat" v-if="buttons_.repeat">
<i class="icon fa fa-redo"></i>
</button>
</div>
<ExtraControls :status="status" :buttons="buttons_"
@consume="$emit('consume', !status.consume)"
@random="$emit('random', !status.random)"
@repeat="$emit('repeat', !status.repeat)" />
</div> </div>
</div> </div>
</template> </template>
@ -149,11 +104,12 @@
<script> <script>
import Utils from "@/Utils" import Utils from "@/Utils"
import MediaUtils from "@/components/Media/Utils"; import MediaUtils from "@/components/Media/Utils";
import Slider from "@/components/elements/Slider"; import ExtraControls from "./ExtraControls";
import ProgressBar from "./ProgressBar";
import VolumeSlider from "./VolumeSlider"; import VolumeSlider from "./VolumeSlider";
export default { export default {
components: {Slider, VolumeSlider}, components: {ExtraControls, ProgressBar, VolumeSlider},
mixins: [Utils, MediaUtils], mixins: [Utils, MediaUtils],
emits: [ emits: [
'consume', 'consume',
@ -282,10 +238,15 @@ button {
flex-direction: column; flex-direction: column;
display: none; display: none;
overflow: hidden; overflow: hidden;
padding: .5em;
@include until($desktop) { @include until($desktop) {
display: flex; display: flex;
padding-top: .5em; }
:deep(.extra-controls-container) {
@extend .pull-right;
flex: 1;
} }
.row { .row {
@ -293,34 +254,10 @@ button {
} }
.buttons { .buttons {
display: flex;
justify-content: center; justify-content: center;
margin: 0; margin: 0;
} }
.volume-container,
.list-controls {
display: flex;
align-items: center;
button {
padding: 0 .25em;
}
}
.list-controls {
margin-top: -.5em;
flex-flow: row-reverse;
}
.time {
&:first-child {
margin-left: .25em;
}
&:last-child {
margin-right: .25em;
}
}
} }
.controls { .controls {
@ -336,12 +273,6 @@ button {
display: flex; display: flex;
} }
.volume-container {
display: flex;
flex-direction: column;
align-items: center;
}
.track-container { .track-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -380,24 +311,6 @@ button {
} }
.playback-controls { .playback-controls {
&.mobile {
display: none;
@include until($tablet) {
display: flex !important;
align-items: center;
}
}
&.tablet {
display: none;
@media screen and (min-width: $tablet) and (max-width: $desktop - 1) {
display: flex !important;
align-items: center;
}
}
.row { .row {
justify-content: center; justify-content: center;
} }
@ -423,47 +336,25 @@ button {
} }
} }
.list-controls { .right-controls {
height: 50%; @extend .pull-right;
opacity: 0.7;
display: flex;
align-items: center;
margin-bottom: 1em;
flex-flow: row-reverse;
}
.mobile.right-buttons {
@include until ($desktop) {
display: flex; display: flex;
align-items: center; flex-direction: column;
justify-content: flex-end;
flex: 1; flex: 1;
} align-items: end;
}
.pull-right {
button { button {
padding: 0.5em; padding: 0.5em;
} }
:deep(.extra-controls-container) {
@extend .pull-right;
}
} }
.seek-slider { .seek-slider {
width: 75%; width: 75%;
} }
} }
.time {
font-size: .7em;
position: relative;
}
.elapsed-time {
text-align: right;
float: right;
}
.time-bar {
flex-grow: 1;
margin: 0 .5em;
}
</style> </style>

View file

@ -0,0 +1,55 @@
<template>
<div class="extra-controls-container">
<button @click="$emit('consume')" :class="{enabled: status.consume}" title="Toggle consume mode" v-if="buttons.consume">
<i class="icon fa fa-utensils"></i>
</button>
<button @click="$emit('random')" :class="{enabled: status.random}" title="Toggle shuffle" v-if="buttons.random">
<i class="icon fa fa-random"></i>
</button>
<button @click="$emit('repeat')" :class="{enabled: status.repeat}" title="Toggle repeat" v-if="buttons.repeat">
<i class="icon fa fa-redo"></i>
</button>
</div>
</template>
<script>
export default {
emits: ['consume', 'random', 'repeat'],
props: {
status: {
type: Object,
default: () => ({}),
},
buttons: {
type: Object,
default: () => ({}),
},
},
}
</script>
<style lang="scss" scoped>
.extra-controls-container {
display: flex;
align-items: center;
justify-content: center;
opacity: 0.7;
button {
margin: 0 .25em;
padding: 0;
background: none;
border: 0;
&.enabled {
color: $selected-fg;
}
&:not(:disabled):hover {
color: $default-hover-fg;
opacity: 1;
}
}
}
</style>

View file

@ -0,0 +1,71 @@
<template>
<div class="progress-bar-container">
<div class="col-s-2 col-m-1 time">
<span class="elapsed-time"
v-text="elapsed != null && (status.state === 'play' || status.state === 'pause') ? convertTime(elapsed) : '-:--'"></span>
</div>
<div class="col-s-8 col-m-10 time-bar">
<Slider :value="elapsed" :range="[0, duration]" :disabled="!duration || status.state === 'stop'"
@input="$emit('seek', $event.target.value)" />
</div>
<div class="col-s-2 col-m-1 time">
<span class="total-time"
v-text="duration && status.state !== 'stop' ? convertTime(duration) : '-:--'"></span>
</div>
</div>
</template>
<script>
import MediaUtils from "@/components/Media/Utils";
import Slider from "@/components/elements/Slider";
export default {
components: {Slider},
emits: ['seek'],
mixins: [MediaUtils],
props: {
elapsed: {
type: Number,
},
duration: {
type: Number,
},
// Current player status
status: {
type: Object,
default: () => ({}),
},
},
}
</script>
<style lang="scss" scoped>
$time-width: 2.5em;
.progress-bar-container {
width: 100%;
display: flex;
align-items: center;
.time {
width: $time-width;
font-size: .7em;
position: relative;
margin-left: 0;
}
.elapsed-time {
text-align: right;
float: right;
}
.time-bar {
width: calc(100% - #{$time-width * 2} - 2em);
flex-grow: 1;
margin: 0 .5em;
}
}
</style>

View file

@ -45,13 +45,15 @@ export default {
.volume-slider-container { .volume-slider-container {
min-width: 15em; min-width: 15em;
max-width: 25em;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
flex: 1;
.volume-slider { .volume-slider {
margin: 0; margin: 0;
padding: 0 .5em 0 1em; padding: 0 .5em 0 1em;
flex-grow: 1; flex: 1;
} }
button { button {

View file

@ -120,7 +120,7 @@ export default {
flex-direction: column; flex-direction: column;
button { button {
background: $default-bg-2; background: none;
border: 0; border: 0;
padding: 0.5em; padding: 0.5em;

View file

@ -159,23 +159,23 @@ $width-ranges: (
.#{$name} { .#{$name} {
&.from { &.from {
@include until(nth($range, 1)) { @include until(nth($range, 1)) {
display: none; display: none !important;
} }
} }
&.until { &.until {
@include from(nth($range, 2)) { @include from(nth($range, 2)) {
display: none; display: none !important;
} }
} }
&.only { &.only {
@include until(nth($range, 1)) { @include until(nth($range, 1)) {
display: none; display: none !important;
} }
@include from(nth($range, 2)) { @include from(nth($range, 2)) {
display: none; display: none !important;
} }
} }
} }
@ -183,13 +183,13 @@ $width-ranges: (
.mobile { .mobile {
@include from($tablet) { @include from($tablet) {
display: none; display: none !important;
} }
} }
.fullhd { .fullhd {
@include until($fullhd) { @include until($fullhd) {
display: none; display: none !important;
} }
} }