Skip to content
Snippets Groups Projects
Commit fd6c2557 authored by Kasper Seweryn's avatar Kasper Seweryn 🥞
Browse files

Add playlist card

parent d80cb43a
No related branches found
No related tags found
1 merge request!1Implement all components
Pipeline #23457 failed with stages
in 3 minutes and 12 seconds
......@@ -24,6 +24,7 @@ export default defineConfig({
{ text: "Radio Card", link: "/components/card/radio" },
{ text: "Artist Card", link: "/components/card/artist" },
{ text: "Album Card", link: "/components/card/album" },
{ text: "Playlist Card", link: "/components/card/playlist" },
] },
]
}
......
<script setup lang="ts">
const playlist = {
name: 'Incredible Playlist',
user: {
full_username: '@username:example.com',
username: '@username'
},
tracks_count: 27,
album_covers: [
'https://images.unsplash.com/photo-1524650359799-842906ca1c06?ixlib=rb-1.2.1&dl=te-nguyen-Wt7XT1R6sjU-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb',
'https://unsplash.com/photos/SVGan4GCopM/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8MTV8fG11c2ljaWFufGVufDB8Mnx8fDE2NjI5NzQwNjI&force=true&w=640',
'https://unsplash.com/photos/P4gXe-RsEXI/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8Nnx8cGVyZm9ybWVyfGVufDB8Mnx8fDE2NjMwMTM3OTE&force=true&w=640'
]
}
</script>
# Playlist card
## Normal card
```html
<fw-playlist-card :playlist="playlist" />
```
<div style="display: grid; grid-template-columns: auto auto 1fr; grid-gap: 2rem;">
<fw-playlist-card :playlist="playlist" />
<fw-playlist-card :playlist="{ ...playlist, album_covers: [] }" />
</div>
......@@ -16,13 +16,21 @@ defineProps<Props>()
<div
@click="(event) => !buttonTitle ? onClick?.(event) : undefined"
class="funkwhale card"
:class="{ 'has-image': !!image, 'is-link': !!onClick, 'is-category': category, 'is-cta': !!buttonTitle }"
:class="{ 'has-image': !!image || $slots.image, 'is-link': !!onClick, 'is-category': category, 'is-cta': !!buttonTitle }"
>
<div
v-if="$slots.image"
class="card-image"
>
<slot name="image" :src="image" />
</div>
<img
v-if="image"
v-else-if="image"
:src="image"
class="card-image"
/>
<div class="card-title">{{ title }}</div>
<div
v-if="$slots.default"
......
<script setup lang="ts">
import { FwCard, FwPlayButton, FwOptionsButton } from '~/components'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { computed } from 'vue'
const { t } = useI18n()
interface Playlist {
id: number
name: string
user: {
full_username: string
username: string
}
tracks_count: number
album_covers: string[]
}
interface Events {
(e: 'play', playlist: Playlist): void
}
interface Props {
playlist: Playlist
}
const emit = defineEmits<Events>()
const props = defineProps<Props>()
const covers = computed(() => props.playlist.album_covers
.filter((src, index, array) => array.indexOf(src) === index)
.slice(0, 4)
)
const profileParams = computed(() => {
const [username, domain] = props.playlist.user.full_username.split('@')
return { username, domain }
})
let navigate = (to: 'playlist' | 'user') => {}
if (import.meta.env.PROD) {
const router = useRouter()
navigate = (to: 'playlist' | 'user') => to === 'playlist'
? router.push({ name: 'library.playlists.detail', params: { id: props.playlist.id } })
: router.push({ name: 'profile.full', params: profileParams.value })
}
</script>
<template>
<fw-card
:title="playlist.name"
@click="navigate('playlist')"
class="playlist-card"
>
<template #image>
<img
v-for="src in covers"
:key="src"
:src="src"
/>
<div
v-for="i in Math.max(0, 4 - covers.length)"
:key="i"
/>
</template>
<fw-play-button @play="emit('play', playlist)" />
<a
@click.stop="navigate('user')"
class="funkwhale link artist-link"
>
{{ t('vui.playlist-by', playlist.user) }}
</a>
<div class="card-footer">
{{ t('vui.tracks', playlist.tracks_count) }}
<fw-options-button />
</div>
</fw-card>
</template>
<style lang="scss">
@import './style.scss'
</style>
.funkwhale {
&.card.playlist-card {
--fw-card-width: 208px;
position: relative;
width: var(--fw-card-width);
padding-bottom: 14px;
&:hover {
.play-button {
opacity: 1 !important;
transform: translateX(6px) !important;
}
.options-button {
opacity: 1 !important;
transform: translate(12px) !important;
}
}
.play-button,
.options-button {
opacity: 0;
}
.options-button {
transform: translateX(6px) !important;
transition-delay: .1s;
}
--fw-image-width: var(--fw-card-width);
> .card-image {
border-radius: 0 !important;
height: var(--fw-image-width);
width: var(--fw-image-width);
max-width: var(--fw-image-width);
margin: -24px -24px 0;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
> div {
--fw-bg-color: var(--fw-pastel-blue-3);
background-color: var(--fw-bg-color);
&:nth-child(2) {
--fw-bg-color: var(--fw-pastel-blue-1);
}
&:nth-child(3) {
--fw-bg-color: var(--fw-pastel-blue-2);
}
&:nth-child(4) {
--fw-bg-color: var(--fw-pastel-blue-4);
}
}
}
> .card-title {
font-size: 1rem;
text-align: left !important;
padding-top: 16px !important;
}
> .card-content {
padding-top: 0 !important;
text-align: left !important;
> .artist-link {
font-size: 0.875rem;
--fw-link-color: var(--fw-text-color);
}
> .play-button {
position: absolute;
right: 8px;
top: calc(var(--fw-image-width) - 8px - 44px);
}
> .card-footer {
display: flex;
align-items: center;
color: var(--fw-grey-700);
font-size: 0.8125rem;
padding-top: 6px;
> .options-button {
margin-left: auto;
}
}
}
}
}
......@@ -12,7 +12,8 @@
cursor: pointer;
}
> img.card-image {
> .card-image {
overflow: hidden;
border-radius: var(--fw-border-radius);
object-fit: cover;
height: 160px;
......
// Buttons
export { default as FwOptionsButton } from './button/options/Button.vue'
export { default as FwPlayButton } from './button/play/Button.vue'
export { default as FwButton } from './button/Button.vue'
// Cards
export { default as FwPlaylistCard } from './card/playlist/Card.vue'
export { default as FwArtistCard } from './card/artist/Card.vue'
export { default as FwAlbumCard } from './card/album/Card.vue'
export { default as FwRadioCard } from './card/radio/Card.vue'
export { default as FwCard } from './card/Card.vue'
export { default as FwLoader } from './loader/Loader.vue'
// Pills
export { default as FwPill } from './pill/Pill.vue'
// Loader
export { default as FwLoader } from './loader/Loader.vue'
......@@ -2,3 +2,4 @@ vui:
radio: Radio
albums: '{n} album | {n} albums'
tracks: '{n} track | {n} tracks'
playlist-by: 'by {username}'
......@@ -3,7 +3,7 @@
a {
--fw-link-color: var(--fw-blue-500);
&:not(.VPLink):not(.vp-doc .header-anchor):not(.VPDocAsideOutline .outline-link):not(.VPNavBarTitle .title) {
&:not(.VPLink):not(.vp-doc .header-anchor):not(.VPDocAsideOutline .outline-link):not(.VPNavBarTitle .title):not(.VPDocFooter .pager-link) {
color: var(--fw-link-color);
&:hover,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment