fix(front): consistent playbutton and style in all cards

This commit is contained in:
ArneBo 2025-01-27 13:47:48 +01:00
parent 275094bfee
commit 6f728036ec
8 changed files with 111 additions and 103 deletions

View File

@ -50,7 +50,15 @@ const imageUrl = computed(() => props.album.cover?.urls.original
:tags="album.tags" :tags="album.tags"
:to="{name: 'library.albums.detail', params: {id: album.id}}" :to="{name: 'library.albums.detail', params: {id: album.id}}"
> >
<template <template #topright>
<PlayButton
icon-only
:is-playable="album.is_playable"
:album="album"
/>
</template>
<template #default
v-for="ac in album.artist_credit" v-for="ac in album.artist_credit"
:key="ac.artist.id" :key="ac.artist.id"
> >
@ -62,14 +70,6 @@ const imageUrl = computed(() => props.album.cover?.urls.original
<span>{{ ac.joinphrase }}</span> <span>{{ ac.joinphrase }}</span>
</template> </template>
<TagsList
label-classes="tiny"
:truncate-size="20"
:limit="2"
:show-more="false"
:tags="album.tags"
/>
<template #action> <template #action>
<Spacer :size="8" /> <Spacer :size="8" />
<span v-if="album.release_date"> <span v-if="album.release_date">
@ -90,30 +90,10 @@ const imageUrl = computed(() => props.album.cover?.urls.original
</Card> </Card>
</template> </template>
<style scoped> <style lang="scss" scoped>
.funkwhale { .play-button {
&.card.album-card { position: absolute;
--fw-border-radius: 12px; top: 214px;
--Card-width: 208px; right: 16px;
--Card-image-width: var(--Card-width);
--Card-padding: 16px;
> .card-image {
border-radius: 0 !important;
width: var(--Card-image-width);
margin: calc(-1 * var(--Card-padding)) calc(-1 * var(--Card-padding)) 0;
}
> .card-title {
font-size: 1rem;
text-align: left !important;
padding-top: 16px !important;
}
> .card-content {
padding-top: 0 !important;
text-align: left !important;
}
}
} }
</style> </style>

View File

@ -5,7 +5,6 @@ import { useStore } from '~/store'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import PlayButton from '~/components/audio/PlayButton.vue' import PlayButton from '~/components/audio/PlayButton.vue'
import TagsList from '~/components/tags/List.vue'
import Card from '~/components/ui/Card.vue' import Card from '~/components/ui/Card.vue'
import Spacer from '~/components/ui/Spacer.vue' import Spacer from '~/components/ui/Spacer.vue'
@ -47,10 +46,17 @@ const imageUrl = computed(() => cover.value?.urls.original
<Card <Card
:title="artist.name" :title="artist.name"
:image="imageUrl" :image="imageUrl"
class="card artist-card" class="artist-card"
:tags="artist.tags" :tags="artist.tags"
:to="{name: 'library.artists.detail', params: {id: artist.id}}" :to="{name: 'library.artists.detail', params: {id: artist.id}}"
> >
<template #topright>
<PlayButton
icon-only
:is-playable="artist.is_playable"
:artist="artist"
/>
</template>
<template #action> <template #action>
<Spacer :size="8" /> <Spacer :size="8" />
@ -73,20 +79,10 @@ const imageUrl = computed(() => cover.value?.urls.original
</Card> </Card>
</template> </template>
<style lang="scss"> <style lang="scss" scoped>
.play-button {
.funkwhale { position: absolute;
.card.artist-card { top: 214px;
right: 16px;
> .card-image {
border-radius: 50%;
width: 248px;
margin: 16px;
}
.play-button {
top: calc(var(--fw-card-width) - 44px - 8px) !important;
}
}
} }
</style> </style>

View File

@ -48,9 +48,18 @@ const updatedAgo = computed(() => moment(props.object.artist?.modification_date)
:title="object.artist?.name" :title="object.artist?.name"
:image="imageUrl" :image="imageUrl"
:tags="object.artist?.tags ?? []" :tags="object.artist?.tags ?? []"
class="artist-card"
:to="{name: 'channels.detail', params: {id: urlId}}" :to="{name: 'channels.detail', params: {id: urlId}}"
> >
<div class="content"> <template #topright>
<PlayButton
icon-only
:is-playable="object.artist?.is_playable"
:artist="object.artist"
/>
</template>
<template #default>
<span <span
v-if="object.artist?.content_category === 'podcast'" v-if="object.artist?.content_category === 'podcast'"
> >
@ -61,7 +70,8 @@ const updatedAgo = computed(() => moment(props.object.artist?.modification_date)
<i class="bi bi-music-note-list" /> <i class="bi bi-music-note-list" />
{{ t('components.audio.ChannelCard.meta.tracks', object.artist?.tracks_count ?? 0) }} {{ t('components.audio.ChannelCard.meta.tracks', object.artist?.tracks_count ?? 0) }}
</span> </span>
</div> </template>
<template #action> <template #action>
<time <time
:datetime="object.artist?.modification_date" :datetime="object.artist?.modification_date"
@ -80,3 +90,11 @@ const updatedAgo = computed(() => moment(props.object.artist?.modification_date)
</template> </template>
</Card> </Card>
</template> </template>
<style lang="scss" scoped>
.play-button {
position: absolute;
top: 214px;
right: 16px;
}
</style>

View File

@ -96,15 +96,6 @@ const labels = computed(() => ({
: t('components.audio.PlayButton.button.playTracks') : t('components.audio.PlayButton.button.playTracks')
})) }))
const title = computed(() => {
if (props.track) {
return t('components.audio.PlayButton.title.unavailable')
}
return ''
})
const isOpen = ref(false) const isOpen = ref(false)
</script> </script>
@ -124,11 +115,10 @@ const isOpen = ref(false)
disabled: !playable, disabled: !playable,
primary: playable, primary: playable,
split: true, split: true,
splitIcon: 'bi-caret-down-fill', splitIcon: 'bi-caret-down-fill'
splitTitle: title
}" }"
:aria-label="labels.replacePlay" :aria-label="labels.replacePlay"
:class="[...buttonClasses]" :class="[...buttonClasses, 'play-button']"
:isloading="isLoading" :isloading="isLoading"
:dropdownOnly="dropdownOnly" :dropdownOnly="dropdownOnly"
@click.stop.prevent="replacePlay()" @click.stop.prevent="replacePlay()"
@ -238,12 +228,12 @@ const isOpen = ref(false)
primary: playable, primary: playable,
}" }"
:aria-label="labels.replacePlay" :aria-label="labels.replacePlay"
:class="[...buttonClasses]" :class="[...buttonClasses, 'play-button']"
:isloading="isLoading" :isloading="isLoading"
:tiny="iconOnly && discrete" :square="iconOnly"
:icon="!playing ? playIconClass : 'bi-pause-fill'" :icon="!playing ? playIconClass : 'bi-pause-fill'"
:round="iconOnly" :round="iconOnly"
:secondary="iconOnly && !discrete" :primary="iconOnly && !discrete"
:ghost="discrete" :ghost="discrete"
@click.stop.prevent="replacePlay()" @click.stop.prevent="replacePlay()"
> >

View File

@ -59,6 +59,14 @@ const goToPlaylist = () => {
:title="playlist.name" :title="playlist.name"
:to="{ name: 'library.playlists.detail', params: { id: playlist.id } }" :to="{ name: 'library.playlists.detail', params: { id: playlist.id } }"
> >
<template #topright>
<PlayButton
iconOnly
:is-playable="playlist.is_playable"
:playlist="playlist"
/>
</template>
<template #image> <template #image>
<div class="playlist-grid"> <div class="playlist-grid">
<img <img
@ -71,18 +79,11 @@ const goToPlaylist = () => {
</div> </div>
</template> </template>
<template #topright>
<PlayButton
iconOnly
:is-playable="playlist.is_playable"
:playlist="playlist"
/>
</template>
<template #default> <template #default>
<div class="playlist-meta"> <div class="playlist-meta">
<ActorLink <ActorLink
:actor="playlist.actor" :actor="playlist.actor"
discrete
/> />
</div> </div>
</template> </template>
@ -91,6 +92,7 @@ const goToPlaylist = () => {
<div class="playlist-action"> <div class="playlist-action">
<span>{{ t('components.playlists.Card.meta.tracks', playlist.tracks_count) }}</span> <span>{{ t('components.playlists.Card.meta.tracks', playlist.tracks_count) }}</span>
<PlayButton <PlayButton
v-if="playlist.is_playable"
dropdown-only dropdown-only
:is-playable="playlist.is_playable" :is-playable="playlist.is_playable"
:playlist="playlist" :playlist="playlist"
@ -114,10 +116,15 @@ const goToPlaylist = () => {
} }
} }
.play-button {
position: absolute;
top: 214px;
right: 16px;
}
.playlist-meta { .playlist-meta {
display: flex; display: flex;
align-items: center; align-self: center;
} }
.playlist-action { .playlist-action {

View File

@ -85,6 +85,7 @@ const toggleRadio = () => {
:secondary="playOnly" :secondary="playOnly"
:round="playOnly" :round="playOnly"
icon="bi-play-fill" icon="bi-play-fill"
:square="store.state.auth.authenticated && type === 'custom'"
@click="toggleRadio" @click="toggleRadio"
> >
<div v-if="!playOnly">{{ buttonLabel }}</div> <div v-if="!playOnly">{{ buttonLabel }}</div>

View File

@ -42,14 +42,6 @@ const customRadioId = computed(() => props.customRadio?.id ?? null)
:title="radio.name" :title="radio.name"
:to="{name: 'library.radios.detail', params: {id: radio.id}}" :to="{name: 'library.radios.detail', params: {id: radio.id}}"
> >
<div
class="description"
:class="{expanded: isDescriptionExpanded}"
@click="isDescriptionExpanded = !isDescriptionExpanded"
>
{{ radio.description }}
</div>
<Spacer />
<template #topright> <template #topright>
<radio-button <radio-button
:type="type" :type="type"
@ -58,13 +50,24 @@ const customRadioId = computed(() => props.customRadio?.id ?? null)
playOnly playOnly
/> />
</template> </template>
<div class="extra content">
<template #default>
<user-link <user-link
v-if="radio.user" v-if="radio.user"
:user="radio.user" :user="radio.user"
discrete
class="left floated" class="left floated"
/> />
<div
class="description"
:class="{expanded: isDescriptionExpanded}"
@click="isDescriptionExpanded = !isDescriptionExpanded"
>
{{ radio.description }}
</div> </div>
<Spacer />
</template>
<template #action> <template #action>
<Button <Button
v-if="store.state.auth.authenticated && type === 'custom' && radio.user.id === store.state.auth.profile?.id" v-if="store.state.auth.authenticated && type === 'custom' && radio.user.id === store.state.auth.profile?.id"
@ -81,6 +84,20 @@ const customRadioId = computed(() => props.customRadio?.id ?? null)
large large
:title="radio.name" :title="radio.name"
> >
<template #topright>
<radio-button
:type="type"
:custom-radio-id="customRadioId"
:object-id="objectId"
/>
</template>
<template #default>
<user-link
v-if="radio.user"
discrete
:user="radio.user"
/>
<div <div
class="description" class="description"
:class="{expanded: isDescriptionExpanded}" :class="{expanded: isDescriptionExpanded}"
@ -89,17 +106,8 @@ const customRadioId = computed(() => props.customRadio?.id ?? null)
{{ radio.description }} {{ radio.description }}
</div> </div>
<Spacer /> <Spacer />
<div class="extra content"> </template>
<user-link
v-if="radio.user"
:user="radio.user"
/>
<radio-button
:type="type"
:custom-radio-id="customRadioId"
:object-id="objectId"
/>
</div>
<template #action> <template #action>
<Button <Button
v-if="store.state.auth.authenticated && type === 'custom' && radio.user.id === store.state.auth.profile?.id" v-if="store.state.auth.authenticated && type === 'custom' && radio.user.id === store.state.auth.profile?.id"
@ -112,3 +120,11 @@ const customRadioId = computed(() => props.customRadio?.id ?? null)
</template> </template>
</Card> </Card>
</template> </template>
<style lang="scss" scoped>
a.username {
display: flex;
align-self: center;
}
</style>

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, useSlots, onMounted } from 'vue' import { ref, computed, useSlots, onMounted } from 'vue'
import { type ColorProps, type VariantProps, type DefaultProps, type RaisedProps, color } from '~/composables/color'; import { type ColorProps, type VariantProps, type DefaultProps, type RaisedProps, type PastelProps, color } from '~/composables/color';
import { type WidthProps, width } from '~/composables/width' import { type WidthProps, width } from '~/composables/width'
import { type AlignmentProps, align } from '~/composables/alignment' import { type AlignmentProps, align } from '~/composables/alignment'
@ -30,7 +30,7 @@ const props = defineProps<{
autofocus? : boolean autofocus? : boolean
ariaPressed? : true ariaPressed? : true
} & (ColorProps | DefaultProps) } & (ColorProps | DefaultProps | PastelProps )
& VariantProps & VariantProps
& RaisedProps & RaisedProps
& WidthProps & WidthProps