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 !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.
Finish editing this message first!
Please register or to comment