Skip to content
Snippets Groups Projects
Commit 252fe516 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Merge branch 'artist-cards' into 'develop'

Enhance artists list by using our new artist card and remove masonry

See merge request funkwhale/funkwhale!840
parents eed411de 05f5ca3b
No related branches found
No related tags found
No related merge requests found
<template> <template>
<div class="ui card"> <div class="flat inline card">
<div class="content"> <div :class="['ui', 'image', 'with-overlay', {'default-cover': !cover.original}]" v-lazy:background-image="imageUrl">
<div class="header"> <play-button class="play-overlay" :icon-only="true" :is-playable="artist.is_playable" :button-classes="['ui', 'circular', 'large', 'orange', 'icon', 'button']" :artist="artist"></play-button>
<router-link class="discrete link" :to="{name: 'library.artists.detail', params: {id: artist.id }}">
{{ artist.name }}
</router-link>
</div>
<div class="description">
<table class="ui compact very basic fixed single line unstackable table">
<tbody>
<tr v-for="album in albums">
<td>
<img class="ui mini image" v-if="album.cover.original" v-lazy="$store.getters['instance/absoluteUrl'](album.cover.small_square_crop)">
<img class="ui mini image" v-else src="../../../assets/audio/default-cover.png">
</td>
<td colspan="4">
<router-link :title="album.title" class="discrete link" :to="{name: 'library.albums.detail', params: {id: album.id }}">
<strong>{{ album.title }}</strong>
</router-link><br />
{{ album.tracks_count }} tracks
</td>
<td>
<play-button class="right floated basic icon" :is-playable="album.is_playable" :discrete="true" :album="album"></play-button>
</td>
</tr>
</tbody>
</table>
<div class="center aligned segment" v-if="artist.albums.length > initialAlbums">
<em v-if="!showAllAlbums" @click="showAllAlbums = true" class="expand">
<translate translate-context="Content/Artist/Card.Link" :translate-params="{count: artist.albums.length - initialAlbums}" :translate-n="artist.albums.length - initialAlbums" translate-plural="Show %{ count } more albums">Show 1 more album</translate>
</em>
<em v-else @click="showAllAlbums = false" class="expand">
<translate translate-context="Content/*/Card.Link/Verb">Collapse</translate>
</em>
</div>
</div>
</div> </div>
<div class="extra content"> <div class="content">
<span> <router-link :title="artist.name" :to="{name: 'library.artists.detail', params: {id: artist.id}}">
<i class="sound icon"></i> {{ artist.name|truncate(30) }}
<translate translate-context="Content/Artist/Card" :translate-params="{count: artist.albums.length}" :translate-n="artist.albums.length" translate-plural="%{ count } albums">1 album</translate> </router-link>
</span> <div>
<play-button :is-playable="isPlayable" class="mini basic orange right floated" :artist="artist"> <i class="small sound icon"></i>
<translate translate-context="Content/Queue/Button.Label/Short, Verb">Play all</translate> <translate translate-context="Content/Artist/Card" :translate-params="{count: artist.albums.length}" :translate-n="artist.albums.length" translate-plural="%{ count } albums">1 album</translate>
</play-button>
</div> </div>
<tags-list label-classes="tiny" :truncate-size="20" :limit="2" :show-more="false" :tags="artist.tags"></tags-list>
<play-button
class="play-button basic icon"
:dropdown-only="true"
:is-playable="artist.is_playable"
:dropdown-icon-classes="['ellipsis', 'vertical', 'large', 'grey']"
:artist="artist"></play-button>
</div> </div>
</div>
</template> </template>
<script> <script>
import backend from '@/audio/backend' import backend from '@/audio/backend'
import PlayButton from '@/components/audio/PlayButton' import PlayButton from '@/components/audio/PlayButton'
import TagsList from "@/components/tags/List"
export default { export default {
props: ['artist'], props: ['artist'],
components: { components: {
PlayButton PlayButton,
TagsList
}, },
data () { data () {
return { return {
...@@ -65,16 +42,23 @@ export default { ...@@ -65,16 +42,23 @@ export default {
} }
}, },
computed: { computed: {
albums () {
if (this.showAllAlbums) { imageUrl () {
return this.artist.albums let url = '../../../assets/audio/default-cover.png'
let cover = this.cover
if (cover.original) {
url = this.$store.getters['instance/absoluteUrl'](cover.medium_square_crop)
} else {
return null
} }
return this.artist.albums.slice(0, this.initialAlbums) return url
}, },
isPlayable () { cover () {
return this.artist.albums.filter((a) => { return this.artist.albums.map((a) => {
return a.is_playable return a.cover
}).length > 0 }).filter((c) => {
return !!c
})[0] || {}
} }
} }
} }
...@@ -82,4 +66,27 @@ export default { ...@@ -82,4 +66,27 @@ export default {
<!-- Add "scoped" attribute to limit CSS to this component only --> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped> <style scoped>
.default-cover {
background-image: url("../../../assets/audio/default-cover.png") !important;
}
.play-button {
position: absolute;
right: 0;
bottom: 40%;
}
.with-overlay {
background-size: cover !important;
background-position: center !important;
height: 8em;
width: 8em;
display: flex !important;
justify-content: center !important;
align-items: center !important;
}
.flat.card .with-overlay.image {
border-radius: 50% !important;
margin: 0 auto;
}
</style> </style>
<template> <template>
<div class="wrapper"> <div class="wrapper">
<h3 class="ui header"> <h3 v-if="header" class="ui header">
<slot name="title"></slot> <slot name="title"></slot>
<span class="ui tiny circular label">{{ count }}</span> <span class="ui tiny circular label">{{ count }}</span>
</h3> </h3>
...@@ -12,28 +12,7 @@ ...@@ -12,28 +12,7 @@
<div v-if="isLoading" class="ui inverted active dimmer"> <div v-if="isLoading" class="ui inverted active dimmer">
<div class="ui loader"></div> <div class="ui loader"></div>
</div> </div>
<div class="flat inline card" v-for="object in objects" :key="object.id"> <artist-card :artist="artist" v-for="artist in objects" :key="artist.id"></artist-card>
<div :class="['ui', 'image', 'with-overlay', {'default-cover': !getCover(object).original}]" v-lazy:background-image="getImageUrl(object)">
<play-button class="play-overlay" :icon-only="true" :is-playable="object.is_playable" :button-classes="['ui', 'circular', 'large', 'orange', 'icon', 'button']" :artist="object"></play-button>
</div>
<div class="content">
<router-link :title="object.name" :to="{name: 'library.artists.detail', params: {id: object.id}}">
{{ object.name|truncate(30) }}
</router-link>
<div>
<i class="small sound icon"></i>
<translate translate-context="Content/Artist/Card" :translate-params="{count: object.albums.length}" :translate-n="object.albums.length" translate-plural="%{ count } albums">1 album</translate>
</div>
<tags-list label-classes="tiny" :truncate-size="20" :limit="2" :show-more="false" :tags="object.tags"></tags-list>
<play-button
class="play-button basic icon"
:dropdown-only="true"
:is-playable="object.is_playable"
:dropdown-icon-classes="['ellipsis', 'vertical', 'large', 'grey']"
:artist="object"></play-button>
</div>
</div>
</div> </div>
<div v-if="!isLoading && objects.length === 0">No results matching your query.</div> <div v-if="!isLoading && objects.length === 0">No results matching your query.</div>
</div> </div>
...@@ -42,17 +21,16 @@ ...@@ -42,17 +21,16 @@
<script> <script>
import _ from '@/lodash' import _ from '@/lodash'
import axios from 'axios' import axios from 'axios'
import PlayButton from '@/components/audio/PlayButton' import ArtistCard from "@/components/audio/artist/Card"
import TagsList from "@/components/tags/List"
export default { export default {
props: { props: {
filters: {type: Object, required: true}, filters: {type: Object, required: true},
controls: {type: Boolean, default: true}, controls: {type: Boolean, default: true},
header: {type: Boolean, default: true},
}, },
components: { components: {
PlayButton, ArtistCard,
TagsList
}, },
data () { data () {
return { return {
...@@ -96,23 +74,6 @@ export default { ...@@ -96,23 +74,6 @@ export default {
this.offset = Math.max(this.offset - this.limit, 0) this.offset = Math.max(this.offset - this.limit, 0)
} }
}, },
getImageUrl (object) {
let url = '../../../assets/audio/default-cover.png'
let cover = this.getCover(object)
if (cover.original) {
url = this.$store.getters['instance/absoluteUrl'](cover.medium_square_crop)
} else {
return null
}
return url
},
getCover (object) {
return object.albums.map((a) => {
return a.cover
}).filter((c) => {
return !!c
})[0] || {}
}
}, },
watch: { watch: {
offset () { offset () {
...@@ -127,21 +88,12 @@ export default { ...@@ -127,21 +88,12 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
@import "../../../style/vendor/media"; @import "../../../style/vendor/media";
.default-cover {
background-image: url("../../../assets/audio/default-cover.png") !important;
}
.wrapper { .wrapper {
width: 100%; width: 100%;
} }
.ui.cards { .ui.cards {
justify-content: flex-start; justify-content: flex-start;
} }
.play-button {
position: absolute;
right: 0;
bottom: 0;
}
.ui.three.cards .card { .ui.three.cards .card {
width: 100%; width: 100%;
...@@ -151,19 +103,6 @@ export default { ...@@ -151,19 +103,6 @@ export default {
width: 25em; width: 25em;
} }
} }
.with-overlay {
background-size: cover !important;
background-position: center !important;
height: 8em;
width: 8em;
display: flex !important;
justify-content: center !important;
align-items: center !important;
}
.flat.card .with-overlay.image {
border-radius: 50% !important;
margin: 0 auto;
}
</style> </style>
<style> <style>
.ui.cards .ui.button { .ui.cards .ui.button {
......
...@@ -35,27 +35,18 @@ ...@@ -35,27 +35,18 @@
<label><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label>
<select class="ui dropdown" v-model="paginateBy"> <select class="ui dropdown" v-model="paginateBy">
<option :value="parseInt(12)">12</option> <option :value="parseInt(12)">12</option>
<option :value="parseInt(25)">25</option> <option :value="parseInt(30)">30</option>
<option :value="parseInt(50)">50</option> <option :value="parseInt(50)">50</option>
</select> </select>
</div> </div>
</div> </div>
</div> </div>
<div class="ui hidden divider"></div> <div class="ui hidden divider"></div>
<div <div v-if="result && result.results.length > 0" class="ui three cards">
v-if="result" <div v-if="isLoading" class="ui inverted active dimmer">
v-masonry <div class="ui loader"></div>
transition-duration="0"
item-selector=".card"
percent-position="true"
stagger="0">
<div v-if="result.results.length > 0" class="ui cards">
<artist-card
v-masonry-tile
v-for="artist in result.results"
:key="artist.id"
:artist="artist"></artist-card>
</div> </div>
<artist-card :artist="artist" v-for="artist in result.results" :key="artist.id"></artist-card>
</div> </div>
<div class="ui center aligned basic segment"> <div class="ui center aligned basic segment">
<pagination <pagination
...@@ -108,7 +99,7 @@ export default { ...@@ -108,7 +99,7 @@ export default {
page: parseInt(this.defaultPage), page: parseInt(this.defaultPage),
query: this.defaultQuery, query: this.defaultQuery,
tags: this.defaultTags.filter((t) => { return t.length > 0 }) || [], tags: this.defaultTags.filter((t) => { return t.length > 0 }) || [],
paginateBy: parseInt(this.defaultPaginateBy || 12), paginateBy: parseInt(this.defaultPaginateBy || 30),
orderingDirection: defaultOrdering.direction || "+", orderingDirection: defaultOrdering.direction || "+",
ordering: defaultOrdering.field, ordering: defaultOrdering.field,
orderingOptions: [["creation_date", "creation_date"], ["name", "name"]] orderingOptions: [["creation_date", "creation_date"], ["name", "name"]]
...@@ -205,5 +196,22 @@ export default { ...@@ -205,5 +196,22 @@ export default {
</script> </script>
<!-- Add "scoped" attribute to limit CSS to this component only --> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped> <style scoped lang="scss">
@import "../../style/vendor/media";
.wrapper {
width: 100%;
}
.ui.cards {
justify-content: flex-start;
}
.ui.three.cards .card {
width: 100%;
}
@include media(">tablet") {
.ui.three.cards .card {
width: 25em;
}
}
</style> </style>
...@@ -8,40 +8,56 @@ $discrete-text-color: rgba(223, 235, 240, 0.904); ...@@ -8,40 +8,56 @@ $discrete-text-color: rgba(223, 235, 240, 0.904);
$border-color: rgb(63, 88, 102); $border-color: rgb(63, 88, 102);
$light-shadow-color: rgba(223, 235, 240, 0.15); $light-shadow-color: rgba(223, 235, 240, 0.15);
$shadow-color: rgba(63, 102, 97, 0.95); $shadow-color: rgba(63, 102, 97, 0.95);
$box-shadow: 0px 1px 3px 0px rgba(63, 88, 102, 0.95), 0px 0px 0px 1px rgba(63, 88, 102, 0.98); $box-shadow: 0px 1px 3px 0px rgba(63, 88, 102, 0.95),
0px 0px 0px 1px rgba(63, 88, 102, 0.98);
$link-color: rgb(255, 144, 0); $link-color: rgb(255, 144, 0);
.theme-dark { .theme-dark {
background-color: $background-color; background-color: $background-color;
.ui.labeled.input { .ui.labeled.input {
input, .label { input,
.label {
background-color: $input-background-color; background-color: $input-background-color;
&::placeholder { &::placeholder {
color: $light-background-color; color: $light-background-color;
} }
} }
} }
.ui.statistics .statistic { .ui.statistics .statistic {
> .label, > .value { > .label,
> .value {
color: $text-color; color: $text-color;
} }
} }
.ui.link.list.list .active.item, .ui.link.list.list .active.item a:not(.ui) { .ui.link.list.list .active.item,
.ui.link.list.list .active.item a:not(.ui) {
color: inherit; color: inherit;
} }
.ui.form textarea, .ui.form select, .ui.selection.dropdown, .ui.dropdown.selected, .ui.dropdown .menu .selected.item, .ui.form input:not([type]), .ui.form input[type="date"], .ui.form input[type="datetime-local"], .ui.form input[type="email"], .ui.form input[type="number"], .ui.form input[type="password"], .ui.form input[type="search"], .ui.form input[type="tel"], .ui.form input[type="time"], .ui.form input[type="text"], .ui.form input[type="file"], .ui.form input[type="url"] { .ui.form textarea,
.ui.form select,
.ui.selection.dropdown,
.ui.dropdown.selected,
.ui.dropdown .menu .selected.item,
.ui.form input:not([type]),
.ui.form input[type="date"],
.ui.form input[type="datetime-local"],
.ui.form input[type="email"],
.ui.form input[type="number"],
.ui.form input[type="password"],
.ui.form input[type="search"],
.ui.form input[type="tel"],
.ui.form input[type="time"],
.ui.form input[type="text"],
.ui.form input[type="file"],
.ui.form input[type="url"] {
background-color: $input-background-color; background-color: $input-background-color;
&::placeholder { &::placeholder {
color: $light-background-color; color: $light-background-color;
} }
} }
.ui.dropdown .menu .item:hover { .ui.dropdown .menu .item:hover {
background-color: $light-background-color; background-color: $light-background-color;
color: $text-color; color: $text-color;
} }
.main.pusher > .ui.secondary.menu { .main.pusher > .ui.secondary.menu {
background-color: $background-color; background-color: $background-color;
...@@ -54,7 +70,9 @@ $link-color: rgb(255, 144, 0); ...@@ -54,7 +70,9 @@ $link-color: rgb(255, 144, 0);
} }
} }
.ui.modal { .ui.modal {
> .header, > .content, > .actions { > .header,
> .content,
> .actions {
background-color: $background-color; background-color: $background-color;
} }
> .header { > .header {
...@@ -65,29 +83,36 @@ $link-color: rgb(255, 144, 0); ...@@ -65,29 +83,36 @@ $link-color: rgb(255, 144, 0);
border-top: 1px solid $border-color; border-top: 1px solid $border-color;
} }
} }
main, .main, footer, .modal { main,
.main,
footer,
.modal {
.ui.menu { .ui.menu {
background-color: $light-background-color; background-color: $light-background-color;
.item { .item {
color: $text-color; color: $text-color;
} }
} }
.ui.secondary.menu .dropdown.item:hover, .ui.secondary.menu .link.item:hover, .ui.secondary.menu a.item:hover { .ui.secondary.menu .dropdown.item:hover,
.ui.secondary.menu .link.item:hover,
.ui.secondary.menu a.item:hover {
background: $background-color; background: $background-color;
color: $text-color; color: $text-color;
} }
.header, .ui.form .field > label, .sub.header { .header,
.ui.form .field > label,
.sub.header {
color: $text-color; color: $text-color;
} }
.ui.attached.header { .ui.attached.header {
background-color: transparent; background-color: transparent;
} }
.ui.toggle.checkbox input:checked ~ .box, .ui.toggle.checkbox input:checked ~ label { .ui.toggle.checkbox input:checked ~ .box,
.ui.toggle.checkbox input:checked ~ label {
color: $text-color !important; color: $text-color !important;
} }
.ui.toggle.checkbox .box::before, .ui.toggle.checkbox label::before { .ui.toggle.checkbox .box::before,
.ui.toggle.checkbox label::before {
background-color: $light-background-color; background-color: $light-background-color;
} }
a:not(.ui):not(.discrete) { a:not(.ui):not(.discrete) {
...@@ -96,8 +121,12 @@ $link-color: rgb(255, 144, 0); ...@@ -96,8 +121,12 @@ $link-color: rgb(255, 144, 0);
.ui.segment:not(.basic) { .ui.segment:not(.basic) {
background-color: $light-background-color; background-color: $light-background-color;
} }
.ui.list, .ui.dropdown { .ui.list,
.item, div.item, a.item, .button.item { .ui.dropdown {
.item,
div.item,
a.item,
.button.item {
background-color: $background-color; background-color: $background-color;
color: $discrete-text-color; color: $discrete-text-color;
} }
...@@ -113,10 +142,13 @@ $link-color: rgb(255, 144, 0); ...@@ -113,10 +142,13 @@ $link-color: rgb(255, 144, 0);
color: $discrete-text-color; color: $discrete-text-color;
} }
} }
label, .toggle label { label,
.toggle label {
color: $text-color !important; color: $text-color !important;
} }
&, .main.pusher, .ui.vertical.segment { &,
.main.pusher,
.ui.vertical.segment {
color: $text-color; color: $text-color;
background-color: $background-color; background-color: $background-color;
} }
...@@ -125,21 +157,28 @@ $link-color: rgb(255, 144, 0); ...@@ -125,21 +157,28 @@ $link-color: rgb(255, 144, 0);
color: $discrete-text-color; color: $discrete-text-color;
} }
.ui.table thead th, .ui.table { .ui.table thead th,
.ui.table {
color: $text-color; color: $text-color;
} }
.ui.divider:not(.vertical):not(.horizontal) { .ui.divider:not(.vertical):not(.horizontal) {
border-top: 1px solid $border-color; border-top: 1px solid $border-color;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
} }
.ui.cards > .card, .ui.card { .ui.cards > .card,
.ui.card {
color: $text-color; color: $text-color;
background-color: $background-color; background-color: $background-color;
box-shadow: $box-shadow; &:not(.flat) {
.content, .header, .description { box-shadow: $box-shadow;
}
.content,
.header,
.description {
color: $text-color; color: $text-color;
} }
.extra, .meta { .extra,
.meta {
color: $discrete-text-color; color: $discrete-text-color;
} }
} }
...@@ -150,31 +189,40 @@ $link-color: rgb(255, 144, 0); ...@@ -150,31 +189,40 @@ $link-color: rgb(255, 144, 0);
} }
// buttons // buttons
[class='ui button ui button'], [class='ui button'], [class='ui icon button'], [class='ui fluid button'], [class='ui cancel button'] { [class="ui button ui button"],
[class="ui button"],
[class="ui icon button"],
[class="ui fluid button"],
[class="ui cancel button"] {
background-color: $light-background-color; background-color: $light-background-color;
color: $text-color; color: $text-color;
&:hover { &:hover {
background-color: $button-hover-color; background-color: $button-hover-color;
} }
} }
.ui.buttons > .ui.button:not(.basic):not(.inverted), .ui.buttons:not(.basic):not(.inverted) > .button { .ui.buttons > .ui.button:not(.basic):not(.inverted),
.ui.buttons:not(.basic):not(.inverted) > .button {
box-shadow: 0px 0px 0px 1px $light-shadow-color inset; box-shadow: 0px 0px 0px 1px $light-shadow-color inset;
} }
.ui.basic.buttons:not(:hover):not(.green):not(.orange):not(.yellow):not(.red) .button, .ui.basic.button { .ui.basic.buttons:not(:hover):not(.green):not(.orange):not(.yellow):not(.red)
.button,
.ui.basic.button {
box-shadow: 0px 0px 0px 1px $text-color inset; box-shadow: 0px 0px 0px 1px $text-color inset;
&:not(:hover):not(.green):not(.orange):not(.yellow):not(.red) { &:not(:hover):not(.green):not(.orange):not(.yellow):not(.red) {
color: $text-color !important; color: $text-color !important;
} }
} }
.ui.basic.buttons .button, .ui.basic.button { .ui.basic.buttons .button,
.ui.basic.button {
&:hover { &:hover {
color: $text-color !important; color: $text-color !important;
} }
} }
.ui.basic.buttons:not(.green):not(.orange):not(.yellow):not(.red) .button:hover, .ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):hover, .ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):active, .ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):focus { .ui.basic.buttons:not(.green):not(.orange):not(.yellow):not(.red)
.button:hover,
.ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):hover,
.ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):active,
.ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):focus {
color: $background-color !important; color: $background-color !important;
} }
// loading /dimmers // loading /dimmers
...@@ -185,7 +233,8 @@ $link-color: rgb(255, 144, 0); ...@@ -185,7 +233,8 @@ $link-color: rgb(255, 144, 0);
background-color: $loading-background-color; background-color: $loading-background-color;
} }
// table // table
.ui.basic.table tbody tr, .ui.table tr td { .ui.basic.table tbody tr,
.ui.table tr td {
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
} }
.ui.table thead th { .ui.table thead th {
...@@ -193,13 +242,15 @@ $link-color: rgb(255, 144, 0); ...@@ -193,13 +242,15 @@ $link-color: rgb(255, 144, 0);
} }
.ui.table { .ui.table {
&:not(.basic) { &:not(.basic) {
&, thead th { &,
thead th {
background-color: $light-background-color; background-color: $light-background-color;
} }
} }
} }
} }
.ui.link.list.list a.item:hover, .ui.link.list.list .item a:not(.ui):not(.button):hover { .ui.link.list.list a.item:hover,
.ui.link.list.list .item a:not(.ui):not(.button):hover {
color: $link-color; color: $link-color;
} }
[data-tooltip]::after { [data-tooltip]::after {
...@@ -208,13 +259,13 @@ $link-color: rgb(255, 144, 0); ...@@ -208,13 +259,13 @@ $link-color: rgb(255, 144, 0);
} }
.ui.progress > .label { .ui.progress > .label {
color: $text-color; color: $text-color;
} }
i.grey.icon { i.grey.icon {
color: $text-color !important; color: $text-color !important;
} }
input { input {
&::selection, &::-moz-selection { &::selection,
&::-moz-selection {
background: $background-color; background: $background-color;
color: $text-color; color: $text-color;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment