refactor(front): Player

This commit is contained in:
ArneBo 2025-02-05 02:34:21 +01:00
parent fd83ebb287
commit 614cfeafc0
6 changed files with 148 additions and 178 deletions

View File

@ -15,6 +15,7 @@ import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
import PlayerControls from './PlayerControls.vue'
import VolumeControl from './VolumeControl.vue'
import Button from '~/components/ui/Button.vue'
const {
LoopingMode,
@ -160,10 +161,11 @@ const hideArtist = () => {
class="ui tiny image"
@click.stop.prevent="router.push({name: 'library.tracks.detail', params: {id: currentTrack.id }})"
>
<!-- TODO: Use smaller covers -->
<img
ref="cover"
alt=""
:src="store.getters['instance/absoluteUrl'](currentTrack.coverUrl)"
v-lazy="store.getters['instance/absoluteUrl'](currentTrack.coverUrl)"
>
</div>
<div
@ -172,7 +174,7 @@ const hideArtist = () => {
>
<strong>
<router-link
class="small header discrete link track"
class="header discrete link track"
:to="{name: 'library.tracks.detail', params: {id: currentTrack.id }}"
@click.stop.prevent=""
>
@ -186,7 +188,7 @@ const hideArtist = () => {
:key="ac.artist.id"
>
<router-link
class="discrete link"
class="small discrete link"
:to="{name: 'library.artists.detail', params: {id: ac.artist.id }}"
@click.stop.prevent=""
>
@ -198,7 +200,7 @@ const hideArtist = () => {
<template v-if="currentTrack.albumId !== -1">
<span class="middle slash symbol" />
<router-link
class="discrete link"
class="small discrete link"
:to="{name: 'library.albums.detail', params: {id: currentTrack.albumId }}"
@click.stop.prevent=""
>
@ -210,10 +212,11 @@ const hideArtist = () => {
</div>
<div class="controls track-controls queue-not-focused desktop-and-below">
<div class="ui tiny image">
<!-- TODO: Use smaller covers -->
<img
ref="cover"
alt=""
:src="store.getters['instance/absoluteUrl'](currentTrack.coverUrl)"
v-lazy="store.getters['instance/absoluteUrl'](currentTrack.coverUrl)"
>
</div>
<div class="middle aligned content ellipsis">
@ -240,21 +243,22 @@ const hideArtist = () => {
class="controls desktop-and-up fluid align-right"
>
<track-favorite-icon
class="control white"
ghost
:track="currentTrack"
/>
<track-playlist-icon
class="control white"
ghost
:track="currentTrack"
/>
<button
:class="['ui', 'really', 'basic', 'circular', 'icon', 'button', 'control']"
<!-- <Button
round
ghost
icon="bi-eye-slash"
:aria-label="labels.addArtistContentFilter"
:title="labels.addArtistContentFilter"
@click="hideArtist"
>
<i :class="['eye slash outline', 'basic', 'icon']" />
</button>
</Button> -->
</div>
<player-controls class="controls queue-not-focused" />
<div class="controls progress-controls queue-not-focused tablet-and-up small align-left">
@ -274,49 +278,37 @@ const hideArtist = () => {
<div class="controls queue-controls when-queue-focused align-right">
<div class="group">
<volume-control class="expandable" />
<button
class="circular control button"
<Button
:class="{ looping: looping !== LoopingMode.None }"
:title="loopingTitle"
ghost
round
:aria-label="loopingTitle"
:disabled="!currentTrack"
:icon="looping === LoopingMode.LoopTrack ? 'bi-repeat-1' : looping === LoopingMode.LoopQueue ? 'bi-repeat' : 'bi-arrow-clockwise'"
@click.prevent.stop="toggleLooping"
>
<i class="repeat icon">
<span
v-if="looping !== LoopingMode.None"
class="ui circular tiny vibrant label"
>
<span
v-if="looping === LoopingMode.LoopTrack"
class="symbol single"
/>
<span
v-else-if="looping === LoopingMode.LoopQueue"
class="infinity symbol"
/>
</span>
</i>
</button>
/>
<button
class="circular control button"
<Button
round
ghost
:class="{ shuffling: isShuffled }"
:disabled="queue.length === 0"
:title="labels.shuffle"
:aria-label="labels.shuffle"
icon="bi-shuffle"
@click.prevent.stop="shuffle()"
>
<i :class="['ui', 'random', { disabled: queue.length === 0, shuffling: isShuffled }, 'icon']" />
</button>
/>
</div>
<div class="group">
<div class="fake-dropdown">
<button
<Button
class="position circular control button desktop-and-up"
aria-expanded="true"
ghost
icon="bi-music-note-list"
@click.stop="toggleMobilePlayer"
>
<i class="stream icon" />
<i18n-t keypath="components.audio.Player.meta.position">
<template #index>
{{ currentIndex + 1 }}
@ -325,12 +317,12 @@ const hideArtist = () => {
{{ queue.length }}
</template>
</i18n-t>
</button>
<button
</Button>
<Button
class="position circular control button desktop-and-below"
@click.stop="switchTab"
icon="bi-music-note-list"
>
<i class="stream icon" />
<i18n-t keypath="components.audio.Player.meta.position">
<template #index>
{{ currentIndex + 1 }}
@ -339,46 +331,54 @@ const hideArtist = () => {
{{ queue.length }}
</template>
</i18n-t>
</button>
</Button>
<button
<Button
v-if="store.state.ui.queueFocused"
class="circular control button close-control desktop-and-up"
ghost
class="close-control desktop-and-up"
icon="bi-caret-down-fill"
@click.stop="toggleMobilePlayer"
>
<i class="large down angle icon" />
</button>
<button
</Button>
<Button
v-else
class="circular control button desktop-and-up"
ghost
class="desktop-and-up"
icon="bi-caret-up-fill"
@click.stop="toggleMobilePlayer"
>
<i class="large up angle icon" />
</button>
<button
</Button>
<Button
v-if="store.state.ui.queueFocused === 'player'"
class="circular control button close-control desktop-and-below"
ghost
class="close-control desktop-and-below"
icon="bi-caret-up-fill"
@click.stop="switchTab"
>
<i class="large up angle icon" />
</button>
<button
</Button>
<Button
v-if="store.state.ui.queueFocused === 'queue'"
class="circular control button desktop-and-below"
ghost
class="desktop-and-below"
icon="bi-caret-down-fill"
@click.stop="switchTab"
>
<i class="large down angle icon" />
</button>
</Button>
</div>
<button
class="circular control button close-control desktop-and-below"
<Button
class="close-control desktop-and-below"
icon="bi-x"
@click.stop="store.commit('ui/queueFocused', null)"
>
<i class="x icon" />
</button>
</Button>
</div>
</div>
</div>
</div>
</section>
</template>
<style lang="scss" scoped>
</style>

View File

@ -5,6 +5,8 @@ import { computed } from 'vue'
import { usePlayer } from '~/composables/audio/player'
import { useQueue } from '~/composables/audio/queue'
import Button from '~/components/ui/Button.vue'
const { playPrevious, hasNext, playNext, currentTrack } = useQueue()
const { isPlaying } = usePlayer()
@ -19,40 +21,36 @@ const labels = computed(() => ({
<template>
<div class="player-controls">
<button
<Button
:title="labels.previous"
:aria-label="labels.previous"
class="circular button control tablet-and-up"
round
ghost
alignSelf="center"
class="control tablet-and-up"
icon="bi-skip-backward-fill"
@click.prevent.stop="playPrevious()"
>
<i :class="['ui', 'large', 'backward step', 'icon']" />
</button>
<button
v-if="!isPlaying"
:title="labels.play"
:aria-label="labels.play"
class="circular button control"
@click.prevent.stop="isPlaying = true"
>
<i :class="['ui', 'big', 'play', {'disabled': !currentTrack}, 'icon']" />
</button>
<button
v-else
:title="labels.pause"
:aria-label="labels.pause"
class="circular button control"
@click.prevent.stop="isPlaying = false"
>
<i :class="['ui', 'big', 'pause', {'disabled': !currentTrack}, 'icon']" />
</button>
<button
/>
<Button
:title="isPlaying ? labels.pause : labels.play"
round
ghost
alignSelf="center"
:aria-label="isPlaying ? labels.pause : labels.play"
:class="['control', isPlaying ? 'pause' : 'play', 'large']"
:icon="isPlaying ? 'bi-pause-fill' : 'bi-play-fill'"
@click.prevent.stop="isPlaying = !isPlaying"
/>
<Button
:title="labels.next"
:aria-label="labels.next"
round
ghost
alignSelf="center"
:disabled="!hasNext"
class="circular button control"
class="control"
icon="bi-skip-forward-fill"
@click.prevent.stop="playNext()"
>
<i :class="['ui', 'large', {'disabled': !hasNext}, 'forward step', 'icon']" />
</button>
/>
</div>
</template>

View File

@ -4,6 +4,8 @@ import { useTimeoutFn } from '@vueuse/core'
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import Button from '~/components/ui/Button.vue'
const { volume, mute } = usePlayer()
const expanded = ref(false)
@ -32,8 +34,9 @@ const scroll = (event: WheelEvent) => {
</script>
<template>
<button
class="circular control button"
<Button
round
ghost
:class="['component-volume-control', {'expanded': expanded}]"
@click.prevent.stop=""
@mouseover="handleOver"
@ -47,7 +50,7 @@ const scroll = (event: WheelEvent) => {
:aria-label="labels.unmute"
@click.prevent.stop="mute"
>
<i class="volume off icon" />
<i class="bi bi-volume-mute-fill" />
</span>
<span
v-else-if="volume < 0.5"
@ -56,7 +59,7 @@ const scroll = (event: WheelEvent) => {
:aria-label="labels.mute"
@click.prevent.stop="mute"
>
<i class="volume down icon" />
<i class="bi bi-volume-down-fill" />
</span>
<span
v-else
@ -65,7 +68,7 @@ const scroll = (event: WheelEvent) => {
:aria-label="labels.mute"
@click.prevent.stop="mute"
>
<i class="volume up icon" />
<i class="bi bi-volume-up-fill" />
</span>
<div class="popup">
<label
@ -81,5 +84,5 @@ const scroll = (event: WheelEvent) => {
max="1"
>
</div>
</button>
</Button>
</template>

View File

@ -102,9 +102,6 @@ $dropdown-item-selected-background: var(--dropdown-item-hover-background) !defau
$segment-color: var(--text-color) !default;
$segment-background: var(--site-background) !default;
$player-color: rgba(255, 255, 255, 0.9) !default;
$player-background: #1B1C1D !default;
$table-background: transparent !default;
$table-border: var(--divider) !default;

View File

@ -107,6 +107,9 @@
--disabled-background-color:var(--fw-beige-100);
--disabled-border-color:var(--fw-beige-100);
--player-color: var(--fw-gray-700);
--player-background: var(--fw-blue-010);
&.raised {
--background-color:var(--fw-beige-300);
--border-color:color-mix(in oklab, var(--fw-beige-400) 90%, currentcolor);
@ -295,6 +298,9 @@
}
}
// Dark theme
:is(body.theme-dark, html.dark>body:not(.theme-light)), .force-dark-theme.force-dark-theme.force-dark-theme {
@ -323,6 +329,9 @@
--disabled-background-color:var(--fw-gray-950);
--disabled-border-color:var(--fw-gray-950);
--player-color: var(--fw-gray-300);
--player-background: var(--fw-gray-950);
&.raised{
--background-color:var(--fw-gray-950);
--border-color:var(--fw-gray-600);

View File

@ -6,9 +6,8 @@
.ui.top.attached.progress {
top: 0;
height: 1rem;
height: 3px;
z-index: 1;
padding-bottom: 0.8rem;
border-radius: 0;
.bar {
@ -18,7 +17,7 @@
}
.ui.bottom-player > .segment.fixed-controls {
color: var(--player-color);
color: var(--color);
background: var(--player-background);
width: 100%;
border-radius: 0;
@ -106,18 +105,44 @@
justify-content: flex-start;
flex-grow: 1;
.image {
padding: 0.5em;
margin: 0.5em;
width: auto;
margin-right: 0.5em;
margin-right: 1em;
> img {
max-height: 3.7em;
max-width: 4.7em;
max-height: 40px;
max-width: 40px;
}
}
}
.controls {
min-width: 8em;
font-size: 1.1em;
a {
text-decoration: none;
font-size: 16px;
}
.meta {
line-height: 1.5em;
}
.button {
border: none;
cursor: pointer;
&:hover {
background-color: transparent;
> i {
color: var(--color);
transform: scale(1.2);
}
}
}
#volume-slider {
accent-color: var(--vibrant-color);
}
@include media(">desktop") {
&:not(.fluid) {
width: 20%;
@ -129,7 +154,11 @@
width: 10%;
}
&.player-controls {
width: 15%;
gap: 8px;
& i {
font-size: 1.8em;
}
}
}
&.small, .small {
@ -137,11 +166,12 @@
font-size: 0.9em;
}
}
.icon {
font-size: 1.1em;
i {
font-size: 1.3em;
color: var(--player-color);
}
.icon.large {
font-size: 1.4em;
.large i {
font-size: 3.2em;
}
&:not(.track-controls) {
@include media(">desktop") {
@ -159,11 +189,6 @@
padding: 0.5em;
}
}
&.player-controls {
.icon {
margin: 0;
}
}
}
}
@ -171,74 +196,12 @@
.component-player {
.controls {
display: flex;
justify-content: space-between;
}
.controls .icon.big {
cursor: pointer;
font-size: 2em !important;
}
.controls .icon {
cursor: pointer;
vertical-align: middle;
}
.timer {
font-size: 1.2em;
}
.looping {
i {
position: relative;
}
.ui.circular.label {
font-family: sans-serif;
position: absolute;
font-size: 0.5em !important;
bottom: -0.7rem;
right: -0.7rem;
padding: 2px 0 !important;
width: 15px !important;
height: 15px !important;
min-width: 15px !important;
min-height: 15px !important;
@include media(">desktop") {
font-size: 0.6em !important;
}
}
}
.shuffling {
color: var(--vibrant-color);
}
.control.circular.button {
padding: 0;
border: none;
background-color: transparent;
color: inherit;
}
.fake-dropdown {
border: 1px solid gray;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: space-between;
min-width: 8em;
z-index: 2;
> .control.button {
padding: 0.5em;
}
.position.control {
flex-grow: 1;
i.stream.icon {
position: relative;
top: -2px;
left: -2px;
}
}
.angle.icon {
margin-right: 0;
}
}
}