workflow: reset feature/search-page-improvement branch
This commit is contained in:
parent
66b1ebe244
commit
7f0d621f62
2
.gitignore
vendored
2
.gitignore
vendored
@ -20,8 +20,8 @@ yarn-debug.log*
|
|||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
|
# .vscode
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
|
||||||
*.suo
|
*.suo
|
||||||
*.ntvs*
|
*.ntvs*
|
||||||
*.njsproj
|
*.njsproj
|
||||||
|
28
.vscode/tasks.json
vendored
Normal file
28
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "build",
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"problemMatcher": [],
|
||||||
|
"label": "npm: build",
|
||||||
|
"detail": "npm-run-all --sequential clean build:js"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Workaround for dev script
|
||||||
|
"type": "npm",
|
||||||
|
"script": "dev:gui",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"label": "npm: dev:gui",
|
||||||
|
"detail": "npm-run-all --parallel serve:gui watch:js",
|
||||||
|
"group": {
|
||||||
|
"kind": "test",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
2991
package-lock.json
generated
2991
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,12 +4,12 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>deemix</title>
|
<title>deemix</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/css/vendor/material-icons.css">
|
|
||||||
<link rel="stylesheet" type="text/css" href="/css/vendor/OpenSans.css">
|
|
||||||
|
|
||||||
<link rel="shortcut icon" href="/favicon.ico">
|
<link rel="shortcut icon" href="/favicon.ico">
|
||||||
|
|
||||||
<meta name="viewport"
|
<meta name="viewport"
|
||||||
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=0">
|
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=0">
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
if (localStorage.getItem('selectedTheme')) {
|
if (localStorage.getItem('selectedTheme')) {
|
||||||
document.documentElement.setAttribute('data-theme', localStorage.getItem('selectedTheme'))
|
document.documentElement.setAttribute('data-theme', localStorage.getItem('selectedTheme'))
|
||||||
|
File diff suppressed because one or more lines are too long
10
src/App.vue
10
src/App.vue
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div id="app">
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<TheSidebar />
|
<TheSidebar />
|
||||||
|
|
||||||
@ -12,8 +12,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BaseLoadingPlaceholder
|
<BaseLoadingPlaceholder
|
||||||
:hidden="isSocketConnected"
|
|
||||||
text="Connecting to the server..."
|
text="Connecting to the server..."
|
||||||
|
:hidden="isSocketConnected"
|
||||||
additionalClasses="absolute top-0 left-0 w-screen h-screen bg-black bg-opacity-50 z-50"
|
additionalClasses="absolute top-0 left-0 w-screen h-screen bg-black bg-opacity-50 z-50"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ import BaseLoadingPlaceholder from '@components/globals/BaseLoadingPlaceholder.v
|
|||||||
import TheContextMenu from '@components/globals/TheContextMenu.vue'
|
import TheContextMenu from '@components/globals/TheContextMenu.vue'
|
||||||
import TheTrackPreview from '@components/globals/TheTrackPreview.vue'
|
import TheTrackPreview from '@components/globals/TheTrackPreview.vue'
|
||||||
import TheQualityModal from '@components/globals/TheQualityModal.vue'
|
import TheQualityModal from '@components/globals/TheQualityModal.vue'
|
||||||
import ConfirmModal from '@components/globals/ConfirmModal.vue'
|
// import ConfirmModal from '@components/globals/ConfirmModal.vue'
|
||||||
|
|
||||||
import TheSidebar from '@components/TheSidebar.vue'
|
import TheSidebar from '@components/TheSidebar.vue'
|
||||||
import TheSearchBar from '@components/TheSearchBar.vue'
|
import TheSearchBar from '@components/TheSearchBar.vue'
|
||||||
@ -65,8 +65,8 @@ export default {
|
|||||||
TheQualityModal,
|
TheQualityModal,
|
||||||
BaseLoadingPlaceholder,
|
BaseLoadingPlaceholder,
|
||||||
TheContextMenu,
|
TheContextMenu,
|
||||||
TheContent,
|
TheContent
|
||||||
ConfirmModal
|
// ConfirmModal
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
socket.on('connect', () => {
|
socket.on('connect', () => {
|
||||||
|
@ -5,8 +5,12 @@ window.vol = {
|
|||||||
preview_max_volume: 100
|
preview_max_volume: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import '@/styles/css/material-icons.css'
|
||||||
|
import '@/styles/css/OpenSans.css'
|
||||||
|
|
||||||
import '@/styles/scss/style.scss'
|
import '@/styles/scss/style.scss'
|
||||||
import '@/styles/css/components.css'
|
import '@/styles/css/components.css'
|
||||||
|
import '@/styles/css/helpers.css'
|
||||||
|
|
||||||
import App from '@/App.vue'
|
import App from '@/App.vue'
|
||||||
import i18n from '@/plugins/i18n'
|
import i18n from '@/plugins/i18n'
|
||||||
@ -174,9 +178,9 @@ socket.on('errorMessage', function(error) {
|
|||||||
|
|
||||||
socket.on('queueError', function(queueItem) {
|
socket.on('queueError', function(queueItem) {
|
||||||
if (queueItem.errid) {
|
if (queueItem.errid) {
|
||||||
toast(queueItem.link+ " - " +i18n.t(`errors.ids.${queueItem.errid}`), 'error')
|
toast(queueItem.link + ' - ' + i18n.t(`errors.ids.${queueItem.errid}`), 'error')
|
||||||
} else {
|
} else {
|
||||||
toast(queueItem.link+ " - " +queueItem.error, 'error')
|
toast(queueItem.link + ' - ' + queueItem.error, 'error')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -31,8 +31,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../styles/scss/base/_variables.scss';
|
|
||||||
// src/components/TheContent.vue
|
|
||||||
#container {
|
#container {
|
||||||
--container-width: 95%;
|
--container-width: 95%;
|
||||||
|
|
||||||
@ -41,11 +39,11 @@
|
|||||||
width: var(--container-width);
|
width: var(--container-width);
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
|
|
||||||
@media only screen and (min-width: $small) {
|
@media only screen and (min-width: 601px) {
|
||||||
--container-width: 85%;
|
--container-width: 85%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: $medium) {
|
@media only screen and (min-width: 993px) {
|
||||||
--container-width: 70%;
|
--container-width: 70%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,8 @@ export default {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isShowingSearch && sameAsLastSearch) {
|
if (isShowingSearch && sameAsLastSearch) {
|
||||||
this.$root.$emit('mainSearch:updateResults', term)
|
// ? Has this any sense since we're not performing any call?
|
||||||
|
// this.$root.$emit('mainSearch:updateResults', term)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="hasFails"
|
v-if="hasFails"
|
||||||
class="inline-flex"
|
class="flex items-center"
|
||||||
:class="{ clickable: finishedWithFails }"
|
:class="{ clickable: finishedWithFails }"
|
||||||
style="justify-content: center"
|
style="justify-content: center"
|
||||||
@click="finishedWithFails ? $emit('show-errors', queueItem) : null"
|
@click="finishedWithFails ? $emit('show-errors', queueItem) : null"
|
||||||
@ -181,6 +181,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes indeterminate {
|
||||||
|
0% {
|
||||||
|
left: -35%;
|
||||||
|
right: 100%;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
left: 100%;
|
||||||
|
right: -90%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
left: 100%;
|
||||||
|
right: -90%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes indeterminate-short {
|
||||||
|
0% {
|
||||||
|
left: -200%;
|
||||||
|
right: 100%;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
left: 107%;
|
||||||
|
right: -8%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
left: 107%;
|
||||||
|
right: -8%;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
<template functional>
|
<template functional>
|
||||||
<div
|
<div
|
||||||
:id="props.id"
|
|
||||||
class="flex justify-center items-center flex-col flex-1 h-full"
|
class="flex justify-center items-center flex-col flex-1 h-full"
|
||||||
:class="props.additionalClasses"
|
:class="props.additionalClasses"
|
||||||
v-show="!props.hidden"
|
v-show="!props.hidden"
|
||||||
>
|
>
|
||||||
<span class="mb-5">{{ props.text }}</span>
|
<span class="mb-5">{{ props.text || 'Loading...' }}</span>
|
||||||
|
|
||||||
<div class="lds-ring">
|
<div class="lds-ring">
|
||||||
<div></div>
|
<div></div>
|
||||||
@ -60,27 +59,3 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
text: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: 'Loading...'
|
|
||||||
},
|
|
||||||
id: {
|
|
||||||
type: String,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
hidden: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
additionalClasses: {
|
|
||||||
type: String,
|
|
||||||
required: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
@ -23,8 +23,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../styles/scss/base/_variables.scss';
|
|
||||||
|
|
||||||
.smallmodal {
|
.smallmodal {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 1250;
|
z-index: 1250;
|
||||||
@ -47,11 +45,11 @@
|
|||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
|
|
||||||
@media only screen and (min-width: $small) {
|
@media only screen and (min-width: 601px) {
|
||||||
--modal-content-width: 85%;
|
--modal-content-width: 85%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: $medium) {
|
@media only screen and (min-width: 993px) {
|
||||||
--modal-content-width: 70%;
|
--modal-content-width: 70%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,10 +23,10 @@
|
|||||||
<a href="https://deemix.app" target="_blank">🌍 {{ $t('about.officialWebsite') }}</a>
|
<a href="https://deemix.app" target="_blank">🌍 {{ $t('about.officialWebsite') }}</a>
|
||||||
</li> -->
|
</li> -->
|
||||||
<li>
|
<li>
|
||||||
<a href="https://git.fuwafuwa.moe/RemixDev/deemix" target="_blank">🚀 {{ $t('about.officialRepo') }}</a>
|
<a href="https://codeberg.org/RemixDev/deemix" target="_blank">🚀 {{ $t('about.officialRepo') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://git.fuwafuwa.moe/RemixDev/deemix-webui" target="_blank">💻 {{ $t('about.officialWebuiRepo') }}</a>
|
<a href="https://codeberg.org/RemixDev/deemix-webui" target="_blank">💻 {{ $t('about.officialWebuiRepo') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://www.reddit.com/r/deemix" target="_blank">🤖 {{ $t('about.officialSubreddit') }}</a>
|
<a href="https://www.reddit.com/r/deemix" target="_blank">🤖 {{ $t('about.officialSubreddit') }}</a>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="artist_tab" class="relative image-header" ref="root">
|
<div id="artist_tab" class="relative image-header" ref="root">
|
||||||
<header
|
<header
|
||||||
class="inline-flex"
|
class="flex items-center"
|
||||||
:style="{
|
:style="{
|
||||||
'background-image':
|
'background-image':
|
||||||
'linear-gradient(to bottom, transparent 0%, var(--main-background) 100%), url(\'' + image + '\')'
|
'linear-gradient(to bottom, transparent 0%, var(--main-background) 100%), url(\'' + image + '\')'
|
||||||
@ -14,7 +14,7 @@
|
|||||||
aria-label="download"
|
aria-label="download"
|
||||||
@click.stop="addToQueue"
|
@click.stop="addToQueue"
|
||||||
:data-link="link"
|
:data-link="link"
|
||||||
class="rounded-full bg-primary text-grayscale-870 cursor-pointer w-16 h-16 grid place-items-center right"
|
class="rounded-full bg-primary text-grayscale-870 cursor-pointer w-16 h-16 grid place-items-center ml-auto"
|
||||||
>
|
>
|
||||||
<i class="material-icons text-4xl" :title="$t('globals.download_hint')">get_app</i>
|
<i class="material-icons text-4xl" :title="$t('globals.download_hint')">get_app</i>
|
||||||
</div>
|
</div>
|
||||||
@ -54,13 +54,13 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="release in showTable" :key="release.id">
|
<tr v-for="release in showTable" :key="release.id">
|
||||||
<router-link tag="td" class="inline-flex clickable" :to="{ name: 'Album', params: { id: release.id } }">
|
<router-link tag="td" class="flex items-center clickable" :to="{ name: 'Album', params: { id: release.id } }">
|
||||||
<img
|
<img
|
||||||
class="rounded coverart"
|
class="rounded coverart"
|
||||||
:src="release.cover_small"
|
:src="release.cover_small"
|
||||||
style="margin-right: 16px; width: 56px; height: 56px"
|
style="margin-right: 16px; width: 56px; height: 56px"
|
||||||
/>
|
/>
|
||||||
<i v-if="release.explicit_lyrics" class="material-icons explicit_icon"> explicit </i>
|
<i v-if="release.explicit_lyrics" class="material-icons explicit-icon"> explicit </i>
|
||||||
{{ release.title }}
|
{{ release.title }}
|
||||||
<i v-if="checkNewRelease(release.release_date)" class="material-icons" style="color: #ff7300">
|
<i v-if="checkNewRelease(release.release_date)" class="material-icons" style="color: #ff7300">
|
||||||
fiber_new
|
fiber_new
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
<table class="table table--charts">
|
<table class="table table--charts">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="track in chart" class="track_row">
|
<tr v-for="track in chart" class="track_row">
|
||||||
<td class="top-tracks-position" :class="{ first: track.position === 1 }">
|
<td class="p-3 text-center cursor-default" :class="{ first: track.position === 1 }">
|
||||||
{{ track.position }}
|
{{ track.position }}
|
||||||
</td>
|
</td>
|
||||||
<td class="table__icon table__icon--big">
|
<td class="table__icon table__icon--big">
|
||||||
|
@ -26,9 +26,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<button class="btn btn-primary" v-if="!activeTabEmpty" style="margin-bottom: 2rem" @click="downloadAllOfType">
|
<button class="btn btn-primary" v-if="!activeTabEmpty" style="margin-bottom: 2rem" @click="downloadAllOfType">
|
||||||
{{
|
{{ $t('globals.downloadAll', { thing: $tc(`globals.listTabs.${activeTab}`, 2) }) }}
|
||||||
$t('globals.download', { thing: $tc(`globals.listTabs.${activeTab}N`, getTabLenght() )})
|
|
||||||
}}
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="favorites_tabcontent" :class="{ 'favorites_tabcontent--active': activeTab === 'playlist' }">
|
<div class="favorites_tabcontent" :class="{ 'favorites_tabcontent--active': activeTab === 'playlist' }">
|
||||||
@ -166,7 +164,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<table v-if="tracks.length > 0" class="table">
|
<table v-if="tracks.length > 0" class="table">
|
||||||
<tr v-for="track in tracks" class="track_row">
|
<tr v-for="track in tracks" class="track_row">
|
||||||
<td class="top-tracks-position" :class="{ first: track.position === 1 }">
|
<td class="p-3 text-center cursor-default" :class="{ first: track.position === 1 }">
|
||||||
{{ track.position }}
|
{{ track.position }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@ -389,12 +387,6 @@ export default {
|
|||||||
|
|
||||||
return toDownload
|
return toDownload
|
||||||
},
|
},
|
||||||
getTabLenght(tab = this.activeTab) {
|
|
||||||
let total = this[`${tab}s`].length
|
|
||||||
// TODO: Add Spotify playlists to downlaod queue as well
|
|
||||||
//if (tab === "playlist") total += this.spotifyPlaylists.length
|
|
||||||
return total
|
|
||||||
},
|
|
||||||
getLovedTracksPlaylist() {
|
getLovedTracksPlaylist() {
|
||||||
let lovedTracks = this.playlists.filter(playlist => {
|
let lovedTracks = this.playlists.filter(playlist => {
|
||||||
return playlist.is_loved_track
|
return playlist.is_loved_track
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<section class="py-6 border-0 border-t border-solid border-grayscale-500" ref="notLogged" v-if="!isLoggedIn">
|
<section class="py-6 border-0 border-t border-solid border-grayscale-500" ref="notLogged" v-if="!isLoggedIn">
|
||||||
<p id="home_not_logged_text" class="mb-4">{{ $t('home.needTologin') }}</p>
|
<p id="home_not_logged_text" class="mb-4">{{ $t('home.needTologin') }}</p>
|
||||||
<router-link tag="button" name="button" :to="{ name: 'Settings' }" class="btn btn-primary">
|
<router-link tag="button" class="btn btn-primary" name="button" :to="{ name: 'Settings' }">
|
||||||
{{ $t('home.openSettings') }}
|
{{ $t('home.openSettings') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</section>
|
</section>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<header
|
<header
|
||||||
class="inline-flex"
|
class="flex items-center"
|
||||||
:style="{
|
:style="{
|
||||||
'background-image':
|
'background-image':
|
||||||
'linear-gradient(to bottom, transparent 0%, var(--main-background) 100%), url(\'' + image + '\')'
|
'linear-gradient(to bottom, transparent 0%, var(--main-background) 100%), url(\'' + image + '\')'
|
||||||
@ -68,7 +68,7 @@
|
|||||||
@contextmenu.prevent="openQualityModal"
|
@contextmenu.prevent="openQualityModal"
|
||||||
@click.stop="addToQueue"
|
@click.stop="addToQueue"
|
||||||
:data-link="link"
|
:data-link="link"
|
||||||
class="rounded-full bg-primary text-grayscale-870 cursor-pointer w-16 h-16 grid place-items-center right"
|
class="rounded-full bg-primary text-grayscale-870 cursor-pointer w-16 h-16 grid place-items-center ml-auto"
|
||||||
>
|
>
|
||||||
<i class="material-icons text-4xl" :title="$t('globals.download_hint')">get_app</i>
|
<i class="material-icons text-4xl" :title="$t('globals.download_hint')">get_app</i>
|
||||||
</div>
|
</div>
|
||||||
@ -121,10 +121,11 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div v-if="type == 'album'">
|
<div v-if="type == 'album'">
|
||||||
<router-link tag="button" :to="{ name: 'Album', params: { id } }">
|
<router-link tag="button" class="btn btn-primary" name="button" :to="{ name: 'Album', params: { id } }">
|
||||||
{{ $t('linkAnalyzer.table.tracklist') }}
|
{{ $t('linkAnalyzer.table.tracklist') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="countries.length">
|
<div v-if="countries.length">
|
||||||
<p v-for="country in countries">{{ country[0] }} - {{ country[1] }}</p>
|
<p v-for="country in countries">{{ country[0] }} - {{ country[1] }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
<keep-alive>
|
<keep-alive>
|
||||||
<component
|
<component
|
||||||
:is="currentTab.component"
|
:is="currentTab.component"
|
||||||
:results="results"
|
:viewInfo="getViewInfo()"
|
||||||
|
want-headers
|
||||||
@add-to-queue="addToQueue"
|
@add-to-queue="addToQueue"
|
||||||
@change-search-tab="changeSearchTab"
|
@change-search-tab="changeSearchTab"
|
||||||
></component>
|
></component>
|
||||||
@ -43,10 +44,20 @@ import { sendAddToQueue } from '@/utils/downloads'
|
|||||||
import { numberWithDots, convertDuration } from '@/utils/utils'
|
import { numberWithDots, convertDuration } from '@/utils/utils'
|
||||||
import EventBus from '@/utils/EventBus'
|
import EventBus from '@/utils/EventBus'
|
||||||
|
|
||||||
|
import { reduceSearchResults, formatSingleTrack, formatAlbums, formatArtist, formatPlaylist } from '@/data/search'
|
||||||
|
|
||||||
|
const resetObj = { data: [], next: 0, total: 0, hasLoaded: false }
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
BaseLoadingPlaceholder
|
BaseLoadingPlaceholder
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
performScrolledSearch: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
const $t = this.$t.bind(this)
|
const $t = this.$t.bind(this)
|
||||||
const $tc = this.$tc.bind(this)
|
const $tc = this.$tc.bind(this)
|
||||||
@ -54,33 +65,45 @@ export default {
|
|||||||
return {
|
return {
|
||||||
currentTab: {
|
currentTab: {
|
||||||
name: '',
|
name: '',
|
||||||
component: {}
|
searchType: '',
|
||||||
|
component: {},
|
||||||
|
viewInfo: '',
|
||||||
|
formatFunc: () => {}
|
||||||
},
|
},
|
||||||
tabs: [
|
tabs: [
|
||||||
{
|
{
|
||||||
name: $t('globals.listTabs.all'),
|
name: $t('globals.listTabs.all'),
|
||||||
searchType: 'all',
|
searchType: 'all',
|
||||||
component: ResultsAll
|
component: ResultsAll,
|
||||||
|
viewInfo: 'allTab'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: $tc('globals.listTabs.track', 2),
|
name: $tc('globals.listTabs.track', 2),
|
||||||
searchType: 'track',
|
searchType: 'track',
|
||||||
component: ResultsTracks
|
component: ResultsTracks,
|
||||||
|
viewInfo: 'trackTab',
|
||||||
|
formatFunc: formatSingleTrack
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: $tc('globals.listTabs.album', 2),
|
name: $tc('globals.listTabs.album', 2),
|
||||||
searchType: 'album',
|
searchType: 'album',
|
||||||
component: ResultsAlbums
|
component: ResultsAlbums,
|
||||||
|
viewInfo: 'albumTab',
|
||||||
|
formatFunc: formatAlbums
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: $tc('globals.listTabs.artist', 2),
|
name: $tc('globals.listTabs.artist', 2),
|
||||||
searchType: 'artist',
|
searchType: 'artist',
|
||||||
component: ResultsArtists
|
component: ResultsArtists,
|
||||||
|
viewInfo: 'artistTab',
|
||||||
|
formatFunc: formatArtist
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: $tc('globals.listTabs.playlist', 2),
|
name: $tc('globals.listTabs.playlist', 2),
|
||||||
searchType: 'playlist',
|
searchType: 'playlist',
|
||||||
component: ResultsPlaylists
|
component: ResultsPlaylists,
|
||||||
|
viewInfo: 'playlistTab',
|
||||||
|
formatFunc: formatPlaylist
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
results: {
|
results: {
|
||||||
@ -88,35 +111,23 @@ export default {
|
|||||||
allTab: {
|
allTab: {
|
||||||
ORDER: [],
|
ORDER: [],
|
||||||
TOP_RESULT: [],
|
TOP_RESULT: [],
|
||||||
ALBUM: {},
|
ALBUM: {
|
||||||
ARTIST: {},
|
hasLoaded: false
|
||||||
TRACK: {},
|
|
||||||
PLAYLIST: {}
|
|
||||||
},
|
},
|
||||||
trackTab: {
|
ARTIST: {
|
||||||
data: [],
|
hasLoaded: false
|
||||||
next: 0,
|
|
||||||
total: 0,
|
|
||||||
loaded: false
|
|
||||||
},
|
},
|
||||||
albumTab: {
|
TRACK: {
|
||||||
data: [],
|
hasLoaded: false
|
||||||
next: 0,
|
|
||||||
total: 0,
|
|
||||||
loaded: false
|
|
||||||
},
|
},
|
||||||
artistTab: {
|
PLAYLIST: {
|
||||||
data: [],
|
hasLoaded: false
|
||||||
next: 0,
|
|
||||||
total: 0,
|
|
||||||
loaded: false
|
|
||||||
},
|
|
||||||
playlistTab: {
|
|
||||||
data: [],
|
|
||||||
next: 0,
|
|
||||||
total: 0,
|
|
||||||
loaded: false
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
trackTab: { ...resetObj },
|
||||||
|
albumTab: { ...resetObj },
|
||||||
|
artistTab: { ...resetObj },
|
||||||
|
playlistTab: { ...resetObj }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -125,87 +136,82 @@ export default {
|
|||||||
return this.results.query !== ''
|
return this.results.query !== ''
|
||||||
},
|
},
|
||||||
loadedTabs() {
|
loadedTabs() {
|
||||||
const loaded = []
|
const tabsLoaded = []
|
||||||
|
|
||||||
for (const resultKey in this.results) {
|
for (const resultKey in this.results) {
|
||||||
if (this.results.hasOwnProperty(resultKey)) {
|
if (this.results.hasOwnProperty(resultKey)) {
|
||||||
const result = this.results[resultKey]
|
const currentResult = this.results[resultKey]
|
||||||
|
|
||||||
if (result.loaded) {
|
if (currentResult.hasLoaded) {
|
||||||
loaded.push(resultKey.replace(/Tab/g, ''))
|
tabsLoaded.push(resultKey.replace(/Tab/g, ''))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return loaded
|
return tabsLoaded
|
||||||
}
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
performScrolledSearch: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.currentTab = this.tabs[0]
|
this.currentTab = this.tabs[0]
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
EventBus.$on('mainSearch:checkLoadMoreContent', this.checkLoadMoreContent)
|
this.$root.$on('mainSearch:showNewResults', this.checkIfPerformNewMainSearch)
|
||||||
this.$root.$on('mainSearch:showNewResults', this.checkIfShowNewResults)
|
|
||||||
this.$root.$on('mainSearch:updateResults', this.checkIfUpdateResults)
|
this.$root.$on('mainSearch:updateResults', this.checkIfUpdateResults)
|
||||||
|
|
||||||
socket.on('mainSearch', this.handleMainSearch)
|
socket.on('mainSearch', this.saveMainSearchResult)
|
||||||
socket.on('search', this.handleSearch)
|
socket.on('search', this.handleSearch)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
changeSearchTab(sectionName) {
|
numberWithDots,
|
||||||
sectionName = sectionName.toLowerCase()
|
convertDuration,
|
||||||
|
addToQueue(e) {
|
||||||
|
sendAddToQueue(e.currentTarget.dataset.link)
|
||||||
|
},
|
||||||
|
getViewInfo() {
|
||||||
|
if (this.currentTab.searchType === 'all') {
|
||||||
|
return this.results.allTab
|
||||||
|
}
|
||||||
|
|
||||||
let newTab = this.tabs.find(tab => {
|
return reduceSearchResults(this.results[this.currentTab.viewInfo], this.currentTab.formatFunc)
|
||||||
return tab.searchType === sectionName
|
},
|
||||||
|
changeSearchTab(tabName) {
|
||||||
|
tabName = tabName.toLowerCase()
|
||||||
|
|
||||||
|
const newTab = this.tabs.find(tab => {
|
||||||
|
return tab.searchType === tabName
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!newTab) {
|
if (!newTab) {
|
||||||
console.error(`No tab ${sectionName} found`)
|
console.error(`No tab ${tabName} found`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
window.scrollTo(0, 0)
|
window.scrollTo(0, 0)
|
||||||
this.currentTab = newTab
|
this.currentTab = newTab
|
||||||
},
|
},
|
||||||
checkIfShowNewResults(term, mainSelected) {
|
checkIfPerformNewMainSearch(searchTerm) {
|
||||||
let needToPerformNewSearch = term !== this.results.query /* || mainSelected == 'search_tab' */
|
let needToPerformNewMainSearch = searchTerm !== this.results.query
|
||||||
|
|
||||||
if (needToPerformNewSearch) {
|
if (needToPerformNewMainSearch) {
|
||||||
this.showNewResults(term)
|
this.performNewMainSearch(searchTerm)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
checkIfUpdateResults(term) {
|
performNewMainSearch(term) {
|
||||||
let needToUpdateSearch = term === this.results.query && this.currentTab.searchType !== 'all'
|
|
||||||
|
|
||||||
if (needToUpdateSearch) {
|
|
||||||
let resetObj = { data: [], next: 0, total: 0, loaded: false }
|
|
||||||
this.results[this.currentTab.searchType + 'Tab'] = { ...resetObj }
|
|
||||||
this.search(this.currentTab.searchType)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
showNewResults(term) {
|
|
||||||
socket.emit('mainSearch', { term })
|
socket.emit('mainSearch', { term })
|
||||||
|
|
||||||
// Showing loading placeholder
|
// Showing loading placeholder
|
||||||
this.$root.$emit('updateSearchLoadingState', true)
|
this.$root.$emit('updateSearchLoadingState', true)
|
||||||
this.currentTab = this.tabs[0]
|
this.currentTab = this.tabs[0]
|
||||||
},
|
},
|
||||||
checkLoadMoreContent(searchSelected) {
|
// ! Updates search only if the search term is the same as before AND we're not in the ALL tab. Wtf
|
||||||
if (this.results[searchSelected.split('_')[0] + 'Tab'].data.length !== 0) return
|
checkIfUpdateResults(term) {
|
||||||
|
let needToUpdateSearch = term === this.results.query && this.currentTab.searchType !== 'all'
|
||||||
|
|
||||||
this.search(searchSelected.split('_')[0])
|
if (needToUpdateSearch) {
|
||||||
|
this.results[this.currentTab.searchType + 'Tab'] = { ...resetObj }
|
||||||
|
this.search(this.currentTab.searchType)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
addToQueue(e) {
|
|
||||||
sendAddToQueue(e.currentTarget.dataset.link)
|
|
||||||
},
|
|
||||||
numberWithDots,
|
|
||||||
convertDuration,
|
|
||||||
search(type) {
|
search(type) {
|
||||||
socket.emit('search', {
|
socket.emit('search', {
|
||||||
term: this.results.query,
|
term: this.results.query,
|
||||||
@ -217,29 +223,34 @@ export default {
|
|||||||
scrolledSearch() {
|
scrolledSearch() {
|
||||||
if (this.currentTab.searchType === 'all') return
|
if (this.currentTab.searchType === 'all') return
|
||||||
|
|
||||||
let currentTab = `${this.currentTab.searchType}Tab`
|
const currentTabKey = `${this.currentTab.searchType}Tab`
|
||||||
|
const needToPerformScrolledSearch = this.results[currentTabKey].next < this.results[currentTabKey].total
|
||||||
|
|
||||||
if (this.results[currentTab].next < this.results[currentTab].total) {
|
if (needToPerformScrolledSearch) {
|
||||||
this.search(this.currentTab.searchType)
|
this.search(this.currentTab.searchType)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleMainSearch(result) {
|
saveMainSearchResult(searchResult) {
|
||||||
// Hiding loading placeholder
|
// Hide loading placeholder
|
||||||
this.$root.$emit('updateSearchLoadingState', false)
|
this.$root.$emit('updateSearchLoadingState', false)
|
||||||
|
|
||||||
let resetObj = { data: [], next: 0, total: 0, loaded: false }
|
this.results.query = searchResult.QUERY
|
||||||
|
|
||||||
|
this.results.allTab = searchResult
|
||||||
|
this.results.allTab.TRACK.hasLoaded = true
|
||||||
|
this.results.allTab.ALBUM.hasLoaded = true
|
||||||
|
this.results.allTab.ARTIST.hasLoaded = true
|
||||||
|
this.results.allTab.PLAYLIST.hasLoaded = true
|
||||||
|
|
||||||
this.results.allTab = result
|
|
||||||
this.results.trackTab = { ...resetObj }
|
this.results.trackTab = { ...resetObj }
|
||||||
this.results.albumTab = { ...resetObj }
|
this.results.albumTab = { ...resetObj }
|
||||||
this.results.artistTab = { ...resetObj }
|
this.results.artistTab = { ...resetObj }
|
||||||
this.results.playlistTab = { ...resetObj }
|
this.results.playlistTab = { ...resetObj }
|
||||||
this.results.query = result.QUERY
|
|
||||||
},
|
},
|
||||||
handleSearch(result) {
|
handleSearch(result) {
|
||||||
const { next: nextResult, total, type, data } = result
|
const { next: nextResult, total, type, data: newData } = result
|
||||||
|
|
||||||
let currentTab = type + 'Tab'
|
const currentTabKey = type + 'Tab'
|
||||||
let next = 0
|
let next = 0
|
||||||
|
|
||||||
if (nextResult) {
|
if (nextResult) {
|
||||||
@ -248,16 +259,16 @@ export default {
|
|||||||
next = total
|
next = total
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.results[currentTab].total != total) {
|
if (this.results[currentTabKey].total !== total) {
|
||||||
this.results[currentTab].total = total
|
this.results[currentTabKey].total = total
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.results[currentTab].next != next) {
|
if (this.results[currentTabKey].next !== next) {
|
||||||
this.results[currentTab].next = next
|
this.results[currentTabKey].next = next
|
||||||
this.results[currentTab].data = this.results[currentTab].data.concat(data)
|
this.results[currentTabKey].data = this.results[currentTabKey].data.concat(newData)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.results[currentTab].loaded = true
|
this.results[currentTabKey].hasLoaded = true
|
||||||
},
|
},
|
||||||
isTabLoaded(tab) {
|
isTabLoaded(tab) {
|
||||||
return this.loadedTabs.indexOf(tab.searchType) !== -1 || tab.searchType === 'all'
|
return this.loadedTabs.indexOf(tab.searchType) !== -1 || tab.searchType === 'all'
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<h3 class="settings-group__header settings-group__header--with-icon">
|
<h3 class="settings-group__header settings-group__header--with-icon">
|
||||||
<i class="material-icons">person</i>{{ $t('settings.login.title') }}
|
<i class="material-icons">person</i>{{ $t('settings.login.title') }}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="inline-flex">
|
<div class="flex items-center">
|
||||||
<input
|
<input
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
type="password"
|
type="password"
|
||||||
@ -68,11 +68,11 @@
|
|||||||
<h3 class="settings-group__header settings-group__header--with-icon">
|
<h3 class="settings-group__header settings-group__header--with-icon">
|
||||||
<i class="material-icons">web</i>{{ $t('settings.appearance.title') }}
|
<i class="material-icons">web</i>{{ $t('settings.appearance.title') }}
|
||||||
</h3>
|
</h3>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="changeSlimDownloads" />
|
<input type="checkbox" v-model="changeSlimDownloads" />
|
||||||
<span class="checkbox_text">{{ $t('settings.appearance.slimDownloadTab') }}</span>
|
<span class="checkbox_text">{{ $t('settings.appearance.slimDownloadTab') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="changeSlimSidebar" />
|
<input type="checkbox" v-model="changeSlimSidebar" />
|
||||||
<span class="checkbox_text">{{ $t('settings.appearance.slimSidebar') }}</span>
|
<span class="checkbox_text">{{ $t('settings.appearance.slimSidebar') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -82,7 +82,7 @@
|
|||||||
<h3 class="settings-group__header settings-group__header--with-icon">
|
<h3 class="settings-group__header settings-group__header--with-icon">
|
||||||
<i class="material-icons">folder</i>{{ $t('settings.downloadPath.title') }}
|
<i class="material-icons">folder</i>{{ $t('settings.downloadPath.title') }}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="inline-flex">
|
<div class="flex items-center">
|
||||||
<input autocomplete="off" type="text" v-model="settings.downloadLocation" />
|
<input autocomplete="off" type="text" v-model="settings.downloadLocation" />
|
||||||
<button
|
<button
|
||||||
id="select_downloads_folder"
|
id="select_downloads_folder"
|
||||||
@ -116,7 +116,7 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<div class="settings-container">
|
<div class="settings-container">
|
||||||
<div class="settings-container__third">
|
<div class="settings-container__third">
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.createPlaylistFolder" />
|
<input type="checkbox" v-model="settings.createPlaylistFolder" />
|
||||||
<span class="checkbox_text">{{ $t('settings.folders.createPlaylistFolder') }}</span>
|
<span class="checkbox_text">{{ $t('settings.folders.createPlaylistFolder') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -126,7 +126,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-container__third">
|
<div class="settings-container__third">
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.createArtistFolder" />
|
<input type="checkbox" v-model="settings.createArtistFolder" />
|
||||||
<span class="checkbox_text">{{ $t('settings.folders.createArtistFolder') }}</span>
|
<span class="checkbox_text">{{ $t('settings.folders.createArtistFolder') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -137,7 +137,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-container__third">
|
<div class="settings-container__third">
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.createAlbumFolder" />
|
<input type="checkbox" v-model="settings.createAlbumFolder" />
|
||||||
<span class="checkbox_text">{{ $t('settings.folders.createAlbumFolder') }}</span>
|
<span class="checkbox_text">{{ $t('settings.folders.createAlbumFolder') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -149,17 +149,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.createCDFolder" />
|
<input type="checkbox" v-model="settings.createCDFolder" />
|
||||||
<span class="checkbox_text">{{ $t('settings.folders.createCDFolder') }}</span>
|
<span class="checkbox_text">{{ $t('settings.folders.createCDFolder') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.createStructurePlaylist" />
|
<input type="checkbox" v-model="settings.createStructurePlaylist" />
|
||||||
<span class="checkbox_text">{{ $t('settings.folders.createStructurePlaylist') }}</span>
|
<span class="checkbox_text">{{ $t('settings.folders.createStructurePlaylist') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.createSingleFolder" />
|
<input type="checkbox" v-model="settings.createSingleFolder" />
|
||||||
<span class="checkbox_text">{{ $t('settings.folders.createSingleFolder') }}</span>
|
<span class="checkbox_text">{{ $t('settings.folders.createSingleFolder') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -172,7 +172,7 @@
|
|||||||
|
|
||||||
<div class="settings-container">
|
<div class="settings-container">
|
||||||
<div class="settings-container__third settings-container__third--only-checkbox">
|
<div class="settings-container__third settings-container__third--only-checkbox">
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.padTracks" />
|
<input type="checkbox" v-model="settings.padTracks" />
|
||||||
<span class="checkbox_text">{{ $t('settings.trackTitles.padTracks') }}</span>
|
<span class="checkbox_text">{{ $t('settings.trackTitles.padTracks') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -224,34 +224,34 @@
|
|||||||
|
|
||||||
<div class="settings-container">
|
<div class="settings-container">
|
||||||
<div class="settings-container__third settings-container__third--only-checkbox">
|
<div class="settings-container__third settings-container__third--only-checkbox">
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.fallbackBitrate" />
|
<input type="checkbox" v-model="settings.fallbackBitrate" />
|
||||||
<span class="checkbox_text">{{ $t('settings.downloads.fallbackBitrate') }}</span>
|
<span class="checkbox_text">{{ $t('settings.downloads.fallbackBitrate') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.fallbackSearch" />
|
<input type="checkbox" v-model="settings.fallbackSearch" />
|
||||||
<span class="checkbox_text">{{ $t('settings.downloads.fallbackSearch') }}</span>
|
<span class="checkbox_text">{{ $t('settings.downloads.fallbackSearch') }}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-container__third settings-container__third--only-checkbox">
|
<div class="settings-container__third settings-container__third--only-checkbox">
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.logErrors" />
|
<input type="checkbox" v-model="settings.logErrors" />
|
||||||
<span class="checkbox_text">{{ $t('settings.downloads.logErrors') }}</span>
|
<span class="checkbox_text">{{ $t('settings.downloads.logErrors') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.logSearched" />
|
<input type="checkbox" v-model="settings.logSearched" />
|
||||||
<span class="checkbox_text">{{ $t('settings.downloads.logSearched') }}</span>
|
<span class="checkbox_text">{{ $t('settings.downloads.logSearched') }}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-container__third settings-container__third--only-checkbox">
|
<div class="settings-container__third settings-container__third--only-checkbox">
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.syncedLyrics" />
|
<input type="checkbox" v-model="settings.syncedLyrics" />
|
||||||
<span class="checkbox_text">{{ $t('settings.downloads.syncedLyrics') }}</span>
|
<span class="checkbox_text">{{ $t('settings.downloads.syncedLyrics') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.createM3U8File" />
|
<input type="checkbox" v-model="settings.createM3U8File" />
|
||||||
<span class="checkbox_text">{{ $t('settings.downloads.createM3U8File') }}</span>
|
<span class="checkbox_text">{{ $t('settings.downloads.createM3U8File') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -263,7 +263,7 @@
|
|||||||
<input type="text" v-model="settings.playlistFilenameTemplate" />
|
<input type="text" v-model="settings.playlistFilenameTemplate" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.saveDownloadQueue" />
|
<input type="checkbox" v-model="settings.saveDownloadQueue" />
|
||||||
<span class="checkbox_text">{{ $t('settings.downloads.saveDownloadQueue') }}</span>
|
<span class="checkbox_text">{{ $t('settings.downloads.saveDownloadQueue') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -274,7 +274,7 @@
|
|||||||
<i class="material-icons">album</i>{{ $t('settings.covers.title') }}
|
<i class="material-icons">album</i>{{ $t('settings.covers.title') }}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.saveArtwork" />
|
<input type="checkbox" v-model="settings.saveArtwork" />
|
||||||
<span class="checkbox_text">{{ $t('settings.covers.saveArtwork') }}</span>
|
<span class="checkbox_text">{{ $t('settings.covers.saveArtwork') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -284,7 +284,7 @@
|
|||||||
<input type="text" v-model="settings.coverImageTemplate" />
|
<input type="text" v-model="settings.coverImageTemplate" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.saveArtworkArtist" />
|
<input type="checkbox" v-model="settings.saveArtworkArtist" />
|
||||||
<span class="checkbox_text">{{ $t('settings.covers.saveArtworkArtist') }}</span>
|
<span class="checkbox_text">{{ $t('settings.covers.saveArtworkArtist') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -319,7 +319,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.embeddedArtworkPNG" />
|
<input type="checkbox" v-model="settings.embeddedArtworkPNG" />
|
||||||
<span class="checkbox_text">{{ $t('settings.covers.embeddedArtworkPNG') }}</span>
|
<span class="checkbox_text">{{ $t('settings.covers.embeddedArtworkPNG') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -327,7 +327,7 @@
|
|||||||
⚠️ {{ $t('settings.covers.embeddedPNGWarning') }}
|
⚠️ {{ $t('settings.covers.embeddedPNGWarning') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.coverDescriptionUTF8" />
|
<input type="checkbox" v-model="settings.tags.coverDescriptionUTF8" />
|
||||||
<span class="checkbox_text">{{ $t('settings.covers.coverDescriptionUTF8') }}</span>
|
<span class="checkbox_text">{{ $t('settings.covers.coverDescriptionUTF8') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -345,106 +345,106 @@
|
|||||||
|
|
||||||
<div class="settings-container">
|
<div class="settings-container">
|
||||||
<div class="settings-container__half">
|
<div class="settings-container__half">
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.title" />
|
<input type="checkbox" v-model="settings.tags.title" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.title') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.title') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.artist" />
|
<input type="checkbox" v-model="settings.tags.artist" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.artist') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.artist') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.album" />
|
<input type="checkbox" v-model="settings.tags.album" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.album') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.album') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.cover" />
|
<input type="checkbox" v-model="settings.tags.cover" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.cover') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.cover') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.trackNumber" />
|
<input type="checkbox" v-model="settings.tags.trackNumber" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.trackNumber') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.trackNumber') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.trackTotal" />
|
<input type="checkbox" v-model="settings.tags.trackTotal" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.trackTotal') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.trackTotal') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.discNumber" />
|
<input type="checkbox" v-model="settings.tags.discNumber" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.discNumber') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.discNumber') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.discTotal" />
|
<input type="checkbox" v-model="settings.tags.discTotal" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.discTotal') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.discTotal') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.albumArtist" />
|
<input type="checkbox" v-model="settings.tags.albumArtist" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.albumArtist') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.albumArtist') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.genre" />
|
<input type="checkbox" v-model="settings.tags.genre" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.genre') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.genre') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.year" />
|
<input type="checkbox" v-model="settings.tags.year" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.year') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.year') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.date" />
|
<input type="checkbox" v-model="settings.tags.date" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.date') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.date') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.explicit" />
|
<input type="checkbox" v-model="settings.tags.explicit" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.explicit') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.explicit') }}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-container__half">
|
<div class="settings-container__half">
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.isrc" />
|
<input type="checkbox" v-model="settings.tags.isrc" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.isrc') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.isrc') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.length" />
|
<input type="checkbox" v-model="settings.tags.length" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.length') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.length') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.barcode" />
|
<input type="checkbox" v-model="settings.tags.barcode" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.barcode') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.barcode') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.bpm" />
|
<input type="checkbox" v-model="settings.tags.bpm" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.bpm') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.bpm') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.replayGain" />
|
<input type="checkbox" v-model="settings.tags.replayGain" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.replayGain') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.replayGain') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.label" />
|
<input type="checkbox" v-model="settings.tags.label" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.label') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.label') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.lyrics" />
|
<input type="checkbox" v-model="settings.tags.lyrics" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.lyrics') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.lyrics') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.syncedLyrics" />
|
<input type="checkbox" v-model="settings.tags.syncedLyrics" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.syncedLyrics') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.syncedLyrics') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.copyright" />
|
<input type="checkbox" v-model="settings.tags.copyright" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.copyright') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.copyright') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.composer" />
|
<input type="checkbox" v-model="settings.tags.composer" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.composer') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.composer') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.involvedPeople" />
|
<input type="checkbox" v-model="settings.tags.involvedPeople" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.involvedPeople') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.involvedPeople') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.source" />
|
<input type="checkbox" v-model="settings.tags.source" />
|
||||||
<span class="checkbox_text">{{ $t('settings.tags.source') }}</span>
|
<span class="checkbox_text">{{ $t('settings.tags.source') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -457,17 +457,17 @@
|
|||||||
<i class="material-icons">list</i>{{ $t('settings.other.title') }}
|
<i class="material-icons">list</i>{{ $t('settings.other.title') }}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.savePlaylistAsCompilation" />
|
<input type="checkbox" v-model="settings.tags.savePlaylistAsCompilation" />
|
||||||
<span class="checkbox_text">{{ $t('settings.other.savePlaylistAsCompilation') }}</span>
|
<span class="checkbox_text">{{ $t('settings.other.savePlaylistAsCompilation') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.useNullSeparator" />
|
<input type="checkbox" v-model="settings.tags.useNullSeparator" />
|
||||||
<span class="checkbox_text">{{ $t('settings.other.useNullSeparator') }}</span>
|
<span class="checkbox_text">{{ $t('settings.other.useNullSeparator') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.saveID3v1" />
|
<input type="checkbox" v-model="settings.tags.saveID3v1" />
|
||||||
<span class="checkbox_text">{{ $t('settings.other.saveID3v1') }}</span>
|
<span class="checkbox_text">{{ $t('settings.other.saveID3v1') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -488,22 +488,22 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.tags.singleAlbumArtist" />
|
<input type="checkbox" v-model="settings.tags.singleAlbumArtist" />
|
||||||
<span class="checkbox_text">{{ $t('settings.other.singleAlbumArtist') }}</span>
|
<span class="checkbox_text">{{ $t('settings.other.singleAlbumArtist') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.albumVariousArtists" />
|
<input type="checkbox" v-model="settings.albumVariousArtists" />
|
||||||
<span class="checkbox_text">{{ $t('settings.other.albumVariousArtists') }}</span>
|
<span class="checkbox_text">{{ $t('settings.other.albumVariousArtists') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.removeAlbumVersion" />
|
<input type="checkbox" v-model="settings.removeAlbumVersion" />
|
||||||
<span class="checkbox_text">{{ $t('settings.other.removeAlbumVersion') }}</span>
|
<span class="checkbox_text">{{ $t('settings.other.removeAlbumVersion') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="with_checkbox">
|
<label class="with-checkbox">
|
||||||
<input type="checkbox" v-model="settings.removeDuplicateArtists" />
|
<input type="checkbox" v-model="settings.removeDuplicateArtists" />
|
||||||
<span class="checkbox_text">{{ $t('settings.other.removeDuplicateArtists') }}</span>
|
<span class="checkbox_text">{{ $t('settings.other.removeDuplicateArtists') }}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -643,7 +643,7 @@
|
|||||||
|
|
||||||
.locale-flag {
|
.locale-flag {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
display: inline-flex;
|
display: flex items-center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -724,10 +724,6 @@ export default {
|
|||||||
set(wantSlimSidebar) {
|
set(wantSlimSidebar) {
|
||||||
this.slimSidebar = wantSlimSidebar
|
this.slimSidebar = wantSlimSidebar
|
||||||
document.getElementById('sidebar').classList.toggle('slim', wantSlimSidebar)
|
document.getElementById('sidebar').classList.toggle('slim', wantSlimSidebar)
|
||||||
// Moves all toast messages when the option changes
|
|
||||||
Array.from(document.getElementsByClassName('toastify')).forEach((toast)=>{
|
|
||||||
toast.style.transform = `translate(${wantSlimSidebar ? '3rem' : '14rem'}, 0)`;
|
|
||||||
})
|
|
||||||
localStorage.setItem('slimSidebar', wantSlimSidebar)
|
localStorage.setItem('slimSidebar', wantSlimSidebar)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
'linear-gradient(to bottom, transparent 0%, var(--main-background) 100%), url(\'' + image + '\')'
|
'linear-gradient(to bottom, transparent 0%, var(--main-background) 100%), url(\'' + image + '\')'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<h1 class="inline-flex m-0 text-5xl">
|
<h1 class="flex items-center m-0 text-5xl">
|
||||||
{{ title }} <i v-if="explicit" class="material-icons explicit_icon explicit_icon--right">explicit</i>
|
{{ title }} <i v-if="explicit" class="material-icons explicit-icon explicit-icon--right">explicit</i>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<h2 class="m-0 mb-3 text-lg">
|
<h2 class="m-0 mb-3 text-lg">
|
||||||
@ -60,7 +60,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="table__cell--large table__cell--with-icon">
|
<td class="table__cell--large table__cell--with-icon">
|
||||||
<div class="table__cell-content table__cell-content--vertical-center">
|
<div class="table__cell-content table__cell-content--vertical-center">
|
||||||
<i v-if="track.explicit_lyrics" class="material-icons explicit_icon"> explicit </i>
|
<i v-if="track.explicit_lyrics" class="material-icons explicit-icon"> explicit </i>
|
||||||
{{
|
{{
|
||||||
track.title +
|
track.title +
|
||||||
(track.title_version && track.title.indexOf(track.title_version) == -1
|
(track.title_version && track.title.indexOf(track.title_version) == -1
|
||||||
@ -126,8 +126,8 @@
|
|||||||
<i v-else class="material-icons disabled">play_arrow</i>
|
<i v-else class="material-icons disabled">play_arrow</i>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ i + 1 }}</td>
|
<td>{{ i + 1 }}</td>
|
||||||
<td class="inline-flex">
|
<td class="flex items-center">
|
||||||
<i v-if="track.explicit" class="material-icons explicit_icon">explicit</i>
|
<i v-if="track.explicit" class="material-icons explicit-icon">explicit</i>
|
||||||
{{ track.name }}
|
{{ track.name }}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ track.artists[0].name }}</td>
|
<td>{{ track.artists[0].name }}</td>
|
||||||
|
@ -1,51 +1,79 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="album_search" class="search_tabcontent">
|
<section>
|
||||||
<BaseLoadingPlaceholder v-if="!results.albumTab.loaded" />
|
<BaseLoadingPlaceholder v-if="isLoading" />
|
||||||
<div v-else-if="results.albumTab.data.length == 0">
|
|
||||||
|
<template v-else>
|
||||||
|
<div v-if="viewInfo.data.length === 0">
|
||||||
<h1>{{ $t('search.noResultsAlbum') }}</h1>
|
<h1>{{ $t('search.noResultsAlbum') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="release_grid" v-if="results.albumTab.data.length > 0">
|
|
||||||
|
<div class="release_grid" v-else>
|
||||||
<router-link
|
<router-link
|
||||||
tag="div"
|
tag="div"
|
||||||
v-for="release in results.albumTab.data"
|
v-for="release in viewInfo.data.slice(0, itemsToShow)"
|
||||||
:key="release.id"
|
:key="release.albumID"
|
||||||
class="release clickable"
|
class="release clickable"
|
||||||
:to="{ name: 'Album', params: { id: release.id } }"
|
:to="{ name: 'Album', params: { id: release.albumID } }"
|
||||||
>
|
>
|
||||||
<div class="cover_container">
|
<div class="cover_container">
|
||||||
<img aria-hidden="true" class="rounded coverart" :src="release.cover_medium" />
|
<img aria-hidden="true" class="rounded coverart" :src="release.albumCoverMedium" />
|
||||||
<button
|
<button
|
||||||
role="button"
|
role="button"
|
||||||
aria-label="download"
|
aria-label="download"
|
||||||
@click.stop="$emit('add-to-queue', $event)"
|
@click.stop="$emit('add-to-queue', $event)"
|
||||||
:data-link="release.link"
|
:data-link="release.albumLink"
|
||||||
class="download_overlay"
|
class="download_overlay"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
|
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="primary-text inline-flex">
|
<p class="primary-text flex items-center">
|
||||||
<i v-if="release.explicit_lyrics" class="material-icons explicit_icon">explicit</i>
|
<i v-if="release.isAlbumExplicit" class="material-icons explicit-icon">explicit</i>
|
||||||
{{ release.title }}
|
{{ release.albumTitle }}
|
||||||
</p>
|
</p>
|
||||||
<p class="secondary-text">
|
<p class="secondary-text">
|
||||||
{{
|
{{
|
||||||
$t('globals.by', { artist: release.artist.name }) +
|
$t('globals.by', { artist: release.artistName }) +
|
||||||
' - ' +
|
' - ' +
|
||||||
$tc('globals.listTabs.trackN', release.nb_tracks)
|
$tc('globals.listTabs.trackN', release.albumTracks)
|
||||||
}}
|
}}
|
||||||
</p>
|
</p>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BaseLoadingPlaceholder from '@components/globals/BaseLoadingPlaceholder.vue'
|
import BaseLoadingPlaceholder from '@components/globals/BaseLoadingPlaceholder.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['results'],
|
props: {
|
||||||
|
viewInfo: {
|
||||||
|
validator: function (value) {
|
||||||
|
let isNull = Object.is(value, null)
|
||||||
|
let isObject = Object.prototype.toString.call(value) === '[object Object]'
|
||||||
|
|
||||||
|
return isNull || isObject
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
itemsToShow: {
|
||||||
|
type: Number,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
wantHeaders: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isLoading() {
|
||||||
|
return !this.viewInfo || !this.viewInfo.hasLoaded
|
||||||
|
}
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
BaseLoadingPlaceholder
|
BaseLoadingPlaceholder
|
||||||
}
|
}
|
||||||
|
@ -1,221 +1,66 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="main_search" class="search_tabcontent">
|
<section>
|
||||||
<template v-for="section in results.allTab.ORDER">
|
<div v-if="!thereAreResults">
|
||||||
|
<h1>{{ $t('search.noResults') }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
<section
|
<section
|
||||||
v-if="
|
v-for="section in viewInfo.ORDER"
|
||||||
(section != 'TOP_RESULT' && results.allTab[section].data.length > 0) || results.allTab[section].length > 0
|
:key="section"
|
||||||
"
|
class="float-none py-5 border-grayscale-500 border-t first:border-t-0"
|
||||||
class="search_section"
|
|
||||||
>
|
>
|
||||||
<h2
|
<h2
|
||||||
@click="$emit('change-search-tab', section)"
|
@click="$emit('change-search-tab', section)"
|
||||||
class="search_header"
|
class="mb-6 capitalize"
|
||||||
:class="{ top_result_header: section === 'TOP_RESULT' }"
|
:class="{
|
||||||
|
'text-4xl text-center': section === 'TOP_RESULT',
|
||||||
|
'inline-block cursor-pointer text-3xl hover:text-primary transition-colors duration-200 ease-in-out':
|
||||||
|
section !== 'TOP_RESULT'
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
{{ $tc(`globals.listTabs.${section.toLowerCase()}`, 2) }}
|
{{ $tc(`globals.listTabs.${section.toLowerCase()}`, 2) }}
|
||||||
</h2>
|
</h2>
|
||||||
<!-- Top result -->
|
|
||||||
<router-link
|
<TopResult
|
||||||
tag="div"
|
v-if="section === 'TOP_RESULT'"
|
||||||
v-if="section == 'TOP_RESULT'"
|
:info="viewInfo.TOP_RESULT[0]"
|
||||||
class="top_result clickable"
|
@add-to-queue="$emit('add-to-queue', $event)"
|
||||||
:to="{ name: upperCaseFirstLowerCaseRest(topResultType), params: { id: results.allTab.TOP_RESULT[0].id } }"
|
|
||||||
>
|
|
||||||
<div class="cover_container">
|
|
||||||
<img
|
|
||||||
aria-hidden="true"
|
|
||||||
:src="results.allTab.TOP_RESULT[0].picture"
|
|
||||||
:class="(results.allTab.TOP_RESULT[0].type == 'artist' ? 'circle' : 'rounded') + ' coverart'"
|
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
role="button"
|
<ResultsTracks
|
||||||
aria-label="download"
|
v-else-if="section === 'TRACK'"
|
||||||
@click.stop="$emit('add-to-queue', $event)"
|
:viewInfo="reduceSearchResults(viewInfo.TRACK, formatSingleTrack)"
|
||||||
:data-link="results.allTab.TOP_RESULT[0].link"
|
:itemsToShow="6"
|
||||||
class="download_overlay"
|
@add-to-queue="$emit('add-to-queue', $event)"
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="info_box">
|
|
||||||
<p class="primary-text">{{ results.allTab.TOP_RESULT[0].title }}</p>
|
|
||||||
<p class="secondary-text">
|
|
||||||
{{ fansNumber }}
|
|
||||||
</p>
|
|
||||||
<span class="tag">{{ $tc(`globals.listTabs.${results.allTab.TOP_RESULT[0].type}`, 1) }}</span>
|
|
||||||
</div>
|
|
||||||
</router-link>
|
|
||||||
<div v-else-if="section == 'TRACK'">
|
|
||||||
<table class="table table--tracks">
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="track in results.allTab.TRACK.data.slice(0, 6)" :key="track.SNG_ID">
|
|
||||||
<td class="table__icon" aria-hidden="true">
|
|
||||||
<img
|
|
||||||
class="rounded coverart"
|
|
||||||
:src="
|
|
||||||
'https://e-cdns-images.dzcdn.net/images/cover/' + track.ALB_PICTURE + '/32x32-000000-80-0-0.jpg'
|
|
||||||
"
|
|
||||||
/>
|
/>
|
||||||
</td>
|
|
||||||
<td class="table__cell table__cell--large breakline">
|
<ResultsAlbums
|
||||||
<div class="table__cell-content table__cell-content--vertical-center">
|
v-else-if="section == 'ALBUM'"
|
||||||
<i v-if="track.EXPLICIT_LYRICS == 1" class="material-icons explicit_icon"> explicit </i>
|
:viewInfo="reduceSearchResults(viewInfo.ALBUM, formatAlbums)"
|
||||||
{{ track.SNG_TITLE + (track.VERSION ? ' ' + track.VERSION : '') }}
|
:itemsToShow="6"
|
||||||
</div>
|
@add-to-queue="$emit('add-to-queue', $event)"
|
||||||
</td>
|
|
||||||
<td class="table__cell table__cell--medium table__cell--center breakline">
|
|
||||||
<router-link
|
|
||||||
tag="span"
|
|
||||||
v-for="artist in track.ARTISTS"
|
|
||||||
:key="artist.ART_ID"
|
|
||||||
class="clickable"
|
|
||||||
:to="{
|
|
||||||
name: 'Artist',
|
|
||||||
params: { id: artist.ART_ID }
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ artist.ART_NAME }}
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
<router-link
|
|
||||||
tag="td"
|
|
||||||
class="table__cell--medium table__cell--center breakline clickable"
|
|
||||||
:to="{ name: 'Album', params: { id: track.ALB_ID } }"
|
|
||||||
>
|
|
||||||
{{ track.ALB_TITLE }}
|
|
||||||
</router-link>
|
|
||||||
<td class="table__cell table__cell--center">
|
|
||||||
{{ convertDuration(track.DURATION) }}
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="table__cell--download table__cell--center clickable"
|
|
||||||
@click.stop="$emit('add-to-queue', $event)"
|
|
||||||
:data-link="'https://www.deezer.com/track/' + track.SNG_ID"
|
|
||||||
role="button"
|
|
||||||
aria-label="download"
|
|
||||||
>
|
|
||||||
<i class="material-icons" :title="$t('globals.download_hint')"> get_app </i>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="section == 'ARTIST'" class="release_grid firstrow_only">
|
|
||||||
<router-link
|
|
||||||
tag="div"
|
|
||||||
v-for="release in results.allTab.ARTIST.data.slice(0, 10)"
|
|
||||||
class="release clickable"
|
|
||||||
:key="release.ART_ID"
|
|
||||||
:to="{ name: 'Artist', params: { id: release.ART_ID } }"
|
|
||||||
>
|
|
||||||
<div class="cover_container">
|
|
||||||
<img
|
|
||||||
aria-hidden="true"
|
|
||||||
class="circle coverart"
|
|
||||||
:src="
|
|
||||||
'https://e-cdns-images.dzcdn.net/images/artist/' + release.ART_PICTURE + '/156x156-000000-80-0-0.jpg'
|
|
||||||
"
|
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
role="button"
|
<ResultsPlaylists
|
||||||
aria-label="download"
|
v-else-if="section == 'PLAYLIST'"
|
||||||
@click.stop="$emit('add-to-queue', $event)"
|
:viewInfo="reduceSearchResults(viewInfo.PLAYLIST, formatPlaylist)"
|
||||||
:data-link="'https://deezer.com/artist/' + release.ART_ID"
|
:itemsToShow="6"
|
||||||
class="download_overlay"
|
@add-to-queue="$emit('add-to-queue', $event)"
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p class="primary-text">{{ release.ART_NAME }}</p>
|
|
||||||
<p class="secondary-text">{{ $t('search.fans', { n: $n(release.NB_FAN) }) }}</p>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="section == 'ALBUM'" class="release_grid firstrow_only">
|
|
||||||
<router-link
|
|
||||||
tag="div"
|
|
||||||
v-for="release in results.allTab.ALBUM.data.slice(0, 10)"
|
|
||||||
:key="release.ALB_ID"
|
|
||||||
class="release clickable"
|
|
||||||
:to="{ name: 'Album', params: { id: release.ALB_ID } }"
|
|
||||||
>
|
|
||||||
<div class="cover_container">
|
|
||||||
<img
|
|
||||||
aria-hidden="true"
|
|
||||||
class="rounded coverart"
|
|
||||||
:src="
|
|
||||||
'https://e-cdns-images.dzcdn.net/images/cover/' + release.ALB_PICTURE + '/156x156-000000-80-0-0.jpg'
|
|
||||||
"
|
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
role="button"
|
<ResultsArtists
|
||||||
aria-label="download"
|
v-else-if="section === 'ARTIST'"
|
||||||
@click.stop="$emit('add-to-queue', $event)"
|
:viewInfo="reduceSearchResults(viewInfo.ARTIST, formatArtist)"
|
||||||
:data-link="'https://deezer.com/album/' + release.ALB_ID"
|
:itemsToShow="6"
|
||||||
class="download_overlay"
|
@add-to-queue="$emit('add-to-queue', $event)"
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p class="primary-text inline-flex">
|
|
||||||
<i
|
|
||||||
v-if="[1, 4].indexOf(release.EXPLICIT_ALBUM_CONTENT.EXPLICIT_LYRICS_STATUS) != -1"
|
|
||||||
class="material-icons explicit_icon"
|
|
||||||
>explicit</i
|
|
||||||
>
|
|
||||||
{{ release.ALB_TITLE }}
|
|
||||||
</p>
|
|
||||||
<p class="secondary-text">
|
|
||||||
{{ release.ART_NAME + ' - ' + $tc('globals.listTabs.trackN', release.NUMBER_TRACK) }}
|
|
||||||
</p>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="section == 'PLAYLIST'" class="release_grid firstrow_only">
|
|
||||||
<router-link
|
|
||||||
tag="div"
|
|
||||||
v-for="release in results.allTab.PLAYLIST.data.slice(0, 10)"
|
|
||||||
class="release clickable"
|
|
||||||
:key="release.PLAYLIST_ID"
|
|
||||||
:to="{ name: 'Playlist', params: { id: release.PLAYLIST_ID } }"
|
|
||||||
>
|
|
||||||
<div class="cover_container">
|
|
||||||
<img
|
|
||||||
aria-hidden="true"
|
|
||||||
class="rounded coverart"
|
|
||||||
:src="
|
|
||||||
'https://e-cdns-images.dzcdn.net/images/' +
|
|
||||||
release.PICTURE_TYPE +
|
|
||||||
'/' +
|
|
||||||
release.PLAYLIST_PICTURE +
|
|
||||||
'/156x156-000000-80-0-0.jpg'
|
|
||||||
"
|
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
role="button"
|
|
||||||
aria-label="download"
|
|
||||||
@click.stop="$emit('add-to-queue', $event)"
|
|
||||||
:data-link="'https://deezer.com/playlist/' + release.PLAYLIST_ID"
|
|
||||||
class="download_overlay"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p class="primary-text">{{ release.TITLE }}</p>
|
|
||||||
<p class="secondary-text">{{ $tc('globals.listTabs.trackN', release.NB_SONG) }}</p>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<div v-if="noResults">
|
</section>
|
||||||
<h1>{{ $t('search.noResults') }}</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style scoped>
|
||||||
.tag {
|
.tag {
|
||||||
background-color: var(--tag-background);
|
background-color: var(--tag-background);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
@ -230,39 +75,66 @@
|
|||||||
<script>
|
<script>
|
||||||
import { convertDuration } from '@/utils/utils'
|
import { convertDuration } from '@/utils/utils'
|
||||||
import { upperCaseFirstLowerCaseRest } from '@/utils/texts'
|
import { upperCaseFirstLowerCaseRest } from '@/utils/texts'
|
||||||
|
import TopResult from '@/components/search/TopResult.vue'
|
||||||
|
import ResultsTracks from '@components/search/ResultsTracks.vue'
|
||||||
|
import ResultsAlbums from '@components/search/ResultsAlbums.vue'
|
||||||
|
import ResultsArtists from '@components/search/ResultsArtists.vue'
|
||||||
|
import ResultsPlaylists from '@components/search/ResultsPlaylists.vue'
|
||||||
|
|
||||||
|
import { reduceSearchResults, formatSingleTrack, formatAlbums, formatArtist, formatPlaylist } from '@/data/search'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['results'],
|
components: {
|
||||||
computed: {
|
TopResult,
|
||||||
topResultType() {
|
ResultsTracks,
|
||||||
return this.results.allTab.TOP_RESULT[0].type
|
ResultsAlbums,
|
||||||
|
ResultsArtists,
|
||||||
|
ResultsPlaylists
|
||||||
},
|
},
|
||||||
noResults() {
|
props: {
|
||||||
return this.results.allTab.ORDER.every(section =>
|
viewInfo: {
|
||||||
section == 'TOP_RESULT'
|
type: Object,
|
||||||
? this.results.allTab[section].length == 0
|
required: false
|
||||||
: this.results.allTab[section].data.length == 0
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
thereAreResults() {
|
||||||
|
let areInfosLoaded = !!this.viewInfo
|
||||||
|
|
||||||
|
if (!areInfosLoaded) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let noResultsPresent = this.viewInfo.ORDER.every(section =>
|
||||||
|
section === 'TOP_RESULT' ? this.viewInfo[section].length === 0 : this.viewInfo[section].data.length === 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return !noResultsPresent
|
||||||
},
|
},
|
||||||
fansNumber() {
|
fansNumber() {
|
||||||
let number
|
let number
|
||||||
|
|
||||||
try {
|
try {
|
||||||
number = this.$n(this.results.allTab.TOP_RESULT[0].nb_fan)
|
number = this.$n(this.viewInfo.TOP_RESULT[0].nb_fan)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
number = this.$n(this.results.allTab.TOP_RESULT[0].nb_fan, { locale: 'en' })
|
number = this.$n(this.viewInfo.TOP_RESULT[0].nb_fan, { locale: 'en' })
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.results.allTab.TOP_RESULT[0].type == 'artist'
|
return this.viewInfo.TOP_RESULT[0].type == 'artist'
|
||||||
? this.$t('search.fans', { n: number })
|
? this.$t('search.fans', { n: number })
|
||||||
: this.$t('globals.by', { artist: this.results.allTab.TOP_RESULT[0].artist }) +
|
: this.$t('globals.by', { artist: this.viewInfo.TOP_RESULT[0].artist }) +
|
||||||
' - ' +
|
' - ' +
|
||||||
this.$tc('globals.listTabs.trackN', this.results.allTab.TOP_RESULT[0].nb_song)
|
this.$tc('globals.listTabs.trackN', this.viewInfo.TOP_RESULT[0].nb_song)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
convertDuration,
|
convertDuration,
|
||||||
upperCaseFirstLowerCaseRest
|
upperCaseFirstLowerCaseRest,
|
||||||
|
reduceSearchResults,
|
||||||
|
formatSingleTrack,
|
||||||
|
formatAlbums,
|
||||||
|
formatArtist,
|
||||||
|
formatPlaylist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,44 +1,72 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="artist_search" class="search_tabcontent">
|
<section>
|
||||||
<BaseLoadingPlaceholder v-if="!results.artistTab.loaded"></BaseLoadingPlaceholder>
|
<BaseLoadingPlaceholder v-if="isLoading" />
|
||||||
<div v-else-if="results.artistTab.data.length == 0">
|
|
||||||
|
<template v-else>
|
||||||
|
<div v-if="viewInfo.data.length === 0">
|
||||||
<h1>{{ $t('search.noResultsArtist') }}</h1>
|
<h1>{{ $t('search.noResultsArtist') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="release_grid" v-if="results.artistTab.data.length > 0">
|
|
||||||
|
<div v-else class="release_grid">
|
||||||
<router-link
|
<router-link
|
||||||
tag="div"
|
tag="div"
|
||||||
v-for="release in results.artistTab.data"
|
v-for="release in viewInfo.data.slice(0, itemsToShow)"
|
||||||
class="release clickable"
|
class="release clickable"
|
||||||
:key="release.id"
|
:key="release.artistID"
|
||||||
:to="{ name: 'Artist', params: { id: release.id } }"
|
:to="{ name: 'Artist', params: { id: release.artistID } }"
|
||||||
>
|
>
|
||||||
<div class="cover_container">
|
<div class="cover_container">
|
||||||
<img aria-hidden="true" class="circle coverart" :src="release.picture_medium" />
|
<img aria-hidden="true" class="circle coverart" :src="release.artistPictureMedium" />
|
||||||
<button
|
<button
|
||||||
role="button"
|
role="button"
|
||||||
aria-label="download"
|
aria-label="download"
|
||||||
@click.stop="$emit('add-to-queue', $event)"
|
@click.stop="$emit('add-to-queue', $event)"
|
||||||
:data-link="release.link"
|
:data-link="release.artistLink"
|
||||||
class="download_overlay"
|
class="download_overlay"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
|
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="primary-text">{{ release.name }}</p>
|
<p class="primary-text">{{ release.artistName }}</p>
|
||||||
<p class="secondary-text">{{ $tc('globals.listTabs.releaseN', release.nb_album) }}</p>
|
<p class="secondary-text">{{ $tc('globals.listTabs.releaseN', release.artistAlbumsNumber) }}</p>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BaseLoadingPlaceholder from '@components/globals/BaseLoadingPlaceholder.vue'
|
import BaseLoadingPlaceholder from '@components/globals/BaseLoadingPlaceholder.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['results'],
|
|
||||||
components: {
|
components: {
|
||||||
BaseLoadingPlaceholder
|
BaseLoadingPlaceholder
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
viewInfo: {
|
||||||
|
validator: function (value) {
|
||||||
|
let isNull = Object.is(value, null)
|
||||||
|
let isObject = Object.prototype.toString.call(value) === '[object Object]'
|
||||||
|
|
||||||
|
return isNull || isObject
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
itemsToShow: {
|
||||||
|
type: Number,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
wantHeaders: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isLoading() {
|
||||||
|
return !this.viewInfo || !this.viewInfo.hasLoaded
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,48 +1,78 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="playlist_search" class="search_tabcontent">
|
<section>
|
||||||
<BaseLoadingPlaceholder v-if="!results.playlistTab.loaded" />
|
<BaseLoadingPlaceholder v-if="isLoading" />
|
||||||
<div v-else-if="results.playlistTab.data.length == 0">
|
|
||||||
|
<template v-else>
|
||||||
|
<div v-if="viewInfo.data.length === 0">
|
||||||
<h1>{{ $t('search.noResultsPlaylist') }}</h1>
|
<h1>{{ $t('search.noResultsPlaylist') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="release_grid" v-if="results.playlistTab.data.length > 0">
|
<div class="release_grid" v-else>
|
||||||
<router-link
|
<router-link
|
||||||
tag="div"
|
tag="div"
|
||||||
v-for="release in results.playlistTab.data"
|
v-for="playlist in viewInfo.data.slice(0, itemsToShow)"
|
||||||
class="release clickable"
|
class="release clickable"
|
||||||
:key="release.id"
|
:key="playlist.playlistID"
|
||||||
:to="{ name: 'Playlist', params: { id: release.id } }"
|
:to="{ name: 'Playlist', params: { id: playlist.playlistID } }"
|
||||||
>
|
>
|
||||||
<div class="cover_container">
|
<div class="cover_container">
|
||||||
<img aria-hidden="true" class="rounded coverart" :src="release.picture_medium" />
|
<img aria-hidden="true" class="rounded coverart" :src="playlist.playlistPictureMedium" />
|
||||||
<button
|
<button
|
||||||
role="button"
|
role="button"
|
||||||
aria-label="download"
|
aria-label="download"
|
||||||
@click.stop="$emit('add-to-queue', $event)"
|
@click.stop="$emit('add-to-queue', $event)"
|
||||||
:data-link="release.link"
|
:data-link="playlist.playlistLink"
|
||||||
class="download_overlay"
|
class="download_overlay"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
|
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="primary-text">{{ release.title }}</p>
|
<p class="primary-text">{{ playlist.playlistTitle }}</p>
|
||||||
<p class="secondary-text">
|
<p class="secondary-text">
|
||||||
{{
|
{{
|
||||||
`${$t('globals.by', { artist: release.user.name })} - ${$tc('globals.listTabs.trackN', release.nb_tracks)}`
|
`${$t('globals.by', { artist: playlist.artistName })} - ${$tc(
|
||||||
|
'globals.listTabs.trackN',
|
||||||
|
playlist.playlistTracksNumber
|
||||||
|
)}`
|
||||||
}}
|
}}
|
||||||
</p>
|
</p>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BaseLoadingPlaceholder from '@components/globals/BaseLoadingPlaceholder.vue'
|
import BaseLoadingPlaceholder from '@components/globals/BaseLoadingPlaceholder.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['results'],
|
|
||||||
components: {
|
components: {
|
||||||
BaseLoadingPlaceholder
|
BaseLoadingPlaceholder
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
viewInfo: {
|
||||||
|
validator: function (value) {
|
||||||
|
let isNull = Object.is(value, null)
|
||||||
|
let isObject = Object.prototype.toString.call(value) === '[object Object]'
|
||||||
|
|
||||||
|
return isNull || isObject
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
itemsToShow: {
|
||||||
|
type: Number,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
wantHeaders: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isLoading() {
|
||||||
|
return !this.viewInfo || !this.viewInfo.hasLoaded
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,66 +1,67 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="track_search" class="search_tabcontent">
|
<section>
|
||||||
<BaseLoadingPlaceholder v-if="!results.trackTab.loaded" />
|
<BaseLoadingPlaceholder v-if="isLoading" />
|
||||||
<div v-else-if="results.trackTab.data.length == 0">
|
|
||||||
|
<template v-else>
|
||||||
|
<div v-if="viewInfo.data.length === 0">
|
||||||
<h1>{{ $t('search.noResultsTrack') }}</h1>
|
<h1>{{ $t('search.noResultsTrack') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table--tracks" v-if="results.trackTab.data.length > 0">
|
|
||||||
<thead>
|
<table v-else class="table table--tracks">
|
||||||
<tr>
|
<thead v-if="wantHeaders">
|
||||||
|
<tr class="capitalize">
|
||||||
<th colspan="2">{{ $tc('globals.listTabs.title', 1) }}</th>
|
<th colspan="2">{{ $tc('globals.listTabs.title', 1) }}</th>
|
||||||
<th>{{ $tc('globals.listTabs.artist', 1) }}</th>
|
<th>{{ $tc('globals.listTabs.artist', 1) }}</th>
|
||||||
<th>{{ $tc('globals.listTabs.album', 1) }}</th>
|
<th>{{ $tc('globals.listTabs.album', 1) }}</th>
|
||||||
<th>
|
<th>
|
||||||
<i class="material-icons">timer</i>
|
<i class="material-icons">timer</i>
|
||||||
</th>
|
</th>
|
||||||
<th style="width: 56px"></th>
|
<th style="width: 3.5rem"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="track in results.trackTab.data">
|
<tr v-for="track in viewInfo.data.slice(0, itemsToShow)" :key="track.trackLink">
|
||||||
<td class="table__icon table__icon--big">
|
<td class="table__icon table__icon--big">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
@click="playPausePreview"
|
@click="playPausePreview"
|
||||||
class="rounded"
|
class="rounded"
|
||||||
:class="{ 'single-cover': !!track.preview }"
|
:class="{ 'single-cover': !!track.trackPreview }"
|
||||||
:data-preview="track.preview"
|
:data-preview="track.trackPreview"
|
||||||
>
|
>
|
||||||
<PreviewControls v-if="track.preview" />
|
<PreviewControls v-if="track.trackPreview" />
|
||||||
|
|
||||||
<img class="rounded coverart" :src="track.album.cover_small" />
|
<img class="rounded coverart" :src="track.albumPicture" />
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="table__cell table__cell--large breakline">
|
<td class="table__cell table__cell--large breakline">
|
||||||
<div class="table__cell-content table__cell-content--vertical-center">
|
<div class="table__cell-content table__cell-content--vertical-center">
|
||||||
<i v-if="track.explicit_lyrics" class="material-icons explicit_icon"> explicit </i>
|
<i v-if="track.isTrackExplicit" class="material-icons explicit-icon">explicit</i>
|
||||||
{{
|
{{ getTitle(track) }}
|
||||||
track.title +
|
|
||||||
(track.title_version && track.title.indexOf(track.title_version) == -1 ? ' ' + track.title_version : '')
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<router-link
|
<router-link
|
||||||
tag="td"
|
tag="td"
|
||||||
class="table__cell table__cell--medium table__cell--center breakline clickable"
|
class="table__cell table__cell--medium table__cell--center breakline clickable"
|
||||||
:to="{ name: 'Artist', params: { id: track.artist.id } }"
|
:to="{ name: 'Artist', params: { id: track.artistID } }"
|
||||||
>
|
>
|
||||||
{{ track.artist.name }}
|
{{ track.artistName }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link
|
<router-link
|
||||||
tag="td"
|
tag="td"
|
||||||
class="table__cell table__cell--medium table__cell--center breakline clickable"
|
class="table__cell table__cell--medium table__cell--center breakline clickable"
|
||||||
:to="{ name: 'Album', params: { id: track.album.id } }"
|
:to="{ name: 'Album', params: { id: track.albumID } }"
|
||||||
>
|
>
|
||||||
{{ track.album.title }}
|
{{ track.albumTitle }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<td class="table__cell table__cell--small table__cell--center">
|
<td class="table__cell table__cell--small table__cell--center">
|
||||||
{{ convertDuration(track.duration) }}
|
{{ convertDuration(track.trackDuration) }}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class="table__cell--download table__cell--center clickable"
|
class="table__cell--download table__cell--center clickable"
|
||||||
@click.stop="$emit('add-to-queue', $event)"
|
@click.stop="$emit('add-to-queue', $event)"
|
||||||
:data-link="track.link"
|
:data-link="track.trackLink"
|
||||||
role="button"
|
role="button"
|
||||||
aria-label="download"
|
aria-label="download"
|
||||||
>
|
>
|
||||||
@ -69,7 +70,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</template>
|
||||||
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -80,15 +82,44 @@ import EventBus from '@/utils/EventBus'
|
|||||||
import { convertDuration } from '@/utils/utils'
|
import { convertDuration } from '@/utils/utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['results'],
|
|
||||||
components: {
|
components: {
|
||||||
BaseLoadingPlaceholder,
|
BaseLoadingPlaceholder,
|
||||||
PreviewControls
|
PreviewControls
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
viewInfo: {
|
||||||
|
validator: function (value) {
|
||||||
|
let isNull = Object.is(value, null)
|
||||||
|
let isObject = Object.prototype.toString.call(value) === '[object Object]'
|
||||||
|
|
||||||
|
return isNull || isObject
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
itemsToShow: {
|
||||||
|
type: Number,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
wantHeaders: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isLoading() {
|
||||||
|
return !this.viewInfo || !this.viewInfo.hasLoaded
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
convertDuration,
|
convertDuration,
|
||||||
playPausePreview(e) {
|
playPausePreview(e) {
|
||||||
EventBus.$emit('trackPreview:playPausePreview', e)
|
EventBus.$emit('trackPreview:playPausePreview', e)
|
||||||
|
},
|
||||||
|
getTitle(track) {
|
||||||
|
const hasTitleVersion = track.trackTitleVersion && track.trackTitle.indexOf(track.trackTitleVersion) === -1
|
||||||
|
|
||||||
|
return `${track.trackTitle}${hasTitleVersion ? ` ${track.trackTitleVersion}` : ''}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
73
src/components/search/TopResult.vue
Normal file
73
src/components/search/TopResult.vue
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<router-link
|
||||||
|
tag="div"
|
||||||
|
class="top_result cursor-pointer flex items-center flex-col"
|
||||||
|
:to="{ name: upperCaseFirstLowerCaseRest($attrs.info.type), params: { id: $attrs.info.id } }"
|
||||||
|
>
|
||||||
|
<div class="cover_container">
|
||||||
|
<img
|
||||||
|
aria-hidden="true"
|
||||||
|
class="coverart"
|
||||||
|
:src="$attrs.info.picture"
|
||||||
|
:class="$attrs.info.type == 'artist' ? 'circle' : 'rounded'"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
role="button"
|
||||||
|
aria-label="download"
|
||||||
|
@click.stop="$emit('add-to-queue', $event)"
|
||||||
|
:data-link="$attrs.info.link"
|
||||||
|
class="download_overlay"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<i class="material-icons" :title="$t('globals.download_hint')">get_app</i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="info_box">
|
||||||
|
<p class="primary-text">{{ $attrs.info.title }}</p>
|
||||||
|
<p class="secondary-text">
|
||||||
|
{{ fansNumber }}
|
||||||
|
</p>
|
||||||
|
<span class="tag">{{ $tc(`globals.listTabs.${$attrs.info.type}`, 1) }}</span>
|
||||||
|
</div>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.tag {
|
||||||
|
background-color: var(--tag-background);
|
||||||
|
border-radius: 2px;
|
||||||
|
color: var(--tag-text);
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 3px 6px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { upperCaseFirstLowerCaseRest } from '@/utils/texts'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
upperCaseFirstLowerCaseRest
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
fansNumber() {
|
||||||
|
let number
|
||||||
|
|
||||||
|
try {
|
||||||
|
number = this.$n(this.$attrs.info.nb_fan)
|
||||||
|
} catch (error) {
|
||||||
|
number = this.$n(this.$attrs.info.nb_fan, { locale: 'en' })
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.$attrs.info.type == 'artist'
|
||||||
|
? this.$t('search.fans', { n: number })
|
||||||
|
: this.$t('globals.by', { artist: this.$attrs.info.artist }) +
|
||||||
|
' - ' +
|
||||||
|
this.$tc('globals.listTabs.trackN', this.$attrs.info.nb_song)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
117
src/data/search.js
Normal file
117
src/data/search.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { getProperty } from '@/utils/utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {object} ReducedSearchResult
|
||||||
|
* @property {FormattedData} data
|
||||||
|
* @property {boolean} hasLoaded
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {object} FormattedData
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {function} Formatter
|
||||||
|
* @returns {FormattedData} formattedData
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduces passed data to a specific format decied by the formatter passed.
|
||||||
|
*
|
||||||
|
* @param {object} rawObj
|
||||||
|
* @param {Formatter} formatFunc
|
||||||
|
* @returns {null|ReducedSearchResult}
|
||||||
|
*/
|
||||||
|
export function reduceSearchResults(rawObj, formatFunc) {
|
||||||
|
if (!rawObj.hasLoaded) {
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
const { data: rawData } = rawObj
|
||||||
|
const formattedData = []
|
||||||
|
|
||||||
|
for (const dataElement of rawData) {
|
||||||
|
let formatted = formatFunc(dataElement)
|
||||||
|
|
||||||
|
formattedData.push(formatted)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: formattedData,
|
||||||
|
hasLoaded: rawObj.hasLoaded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {FormattedData} track
|
||||||
|
*/
|
||||||
|
export function formatSingleTrack(track) {
|
||||||
|
return {
|
||||||
|
/* Track */
|
||||||
|
trackTitle: getProperty(track, 'title', 'SNG_TITLE'),
|
||||||
|
trackTitleVersion: getProperty(track, 'title_version', 'VERSION'),
|
||||||
|
trackPreview: getProperty(track, 'preview'),
|
||||||
|
trackDuration: getProperty(track, 'duration', 'DURATION'),
|
||||||
|
trackLink: getProperty(track, 'link') || `https://www.deezer.com/track/${track.SNG_ID}`,
|
||||||
|
isTrackExplicit: getProperty(track, 'explicit_lyrics', 'EXPLICIT_LYRICS'),
|
||||||
|
|
||||||
|
/* Artist */
|
||||||
|
artistID: getProperty(track, 'artist.id', 'ART_ID'),
|
||||||
|
artistName: getProperty(track, 'artist.name', 'ART_NAME'),
|
||||||
|
|
||||||
|
/* Album */
|
||||||
|
albumID: getProperty(track, 'album.id', 'ALB_ID'),
|
||||||
|
albumTitle: getProperty(track, 'album.title', 'ALB_TITLE'),
|
||||||
|
albumPicture:
|
||||||
|
getProperty(track, 'album.cover_small') ||
|
||||||
|
`https://e-cdns-images.dzcdn.net/images/cover/${track.ALB_PICTURE}/32x32-000000-80-0-0.jpg`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatAlbums(album) {
|
||||||
|
return {
|
||||||
|
/* Album */
|
||||||
|
albumID: getProperty(album, 'id', 'ALB_ID'),
|
||||||
|
albumTitle: getProperty(album, 'title', 'ALB_TITLE'),
|
||||||
|
albumCoverMedium:
|
||||||
|
getProperty(album, 'cover_medium') ||
|
||||||
|
`https://e-cdns-images.dzcdn.net/images/cover/${album.ALB_PICTURE}/156x156-000000-80-0-0.jpg`,
|
||||||
|
albumLink: getProperty(album, 'link') || `https://deezer.com/album/${album.ALB_ID}`,
|
||||||
|
albumTracks: getProperty(album, 'nb_tracks', 'NUMBER_TRACK'),
|
||||||
|
isAlbumExplicit: getProperty(album, 'explicit_lyrics', 'EXPLICIT_ALBUM_CONTENT.EXPLICIT_LYRICS_STATUS'),
|
||||||
|
|
||||||
|
/* Artist */
|
||||||
|
artistName: getProperty(album, 'artist.name', 'ART_NAME')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatArtist(artist) {
|
||||||
|
return {
|
||||||
|
/* Artist */
|
||||||
|
artistID: getProperty(artist, 'id', 'ART_ID'),
|
||||||
|
artistName: getProperty(artist, 'name', 'ART_NAME'),
|
||||||
|
artistPictureMedium:
|
||||||
|
getProperty(artist, 'picture_medium') ||
|
||||||
|
`https://e-cdns-images.dzcdn.net/images/artist/${artist.ART_PICTURE}/156x156-000000-80-0-0.jpg`,
|
||||||
|
artistLink: getProperty(artist, 'link') || `https://deezer.com/artist/${artist.ART_ID}`,
|
||||||
|
artistAlbumsNumber: getProperty(artist, 'nb_album', 'NB_FAN')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatPlaylist(playlist) {
|
||||||
|
return {
|
||||||
|
/* Playlist */
|
||||||
|
playlistID: getProperty(playlist, 'id', 'PLAYLIST_ID'),
|
||||||
|
playlistTitle: getProperty(playlist, 'title', 'TITLE'),
|
||||||
|
playlistPictureMedium:
|
||||||
|
getProperty(playlist, 'picture_medium') ||
|
||||||
|
`https://e-cdns-images.dzcdn.net/images/${playlist.PICTURE_TYPE}/${
|
||||||
|
playlist.PLAYLIST_PICTURE
|
||||||
|
}/156x156-000000-80-0-0.jpg`,
|
||||||
|
playlistLink: getProperty(playlist, 'link') || `https://deezer.com/playlist/${playlist.PLAYLIST_ID}`,
|
||||||
|
playlistTracksNumber: getProperty(playlist, 'nb_tracks', 'NB_SONG'),
|
||||||
|
|
||||||
|
/* Artist */
|
||||||
|
artistName: getProperty(playlist, 'user.name')
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ const en = {
|
|||||||
back: 'back',
|
back: 'back',
|
||||||
loading: 'loading',
|
loading: 'loading',
|
||||||
download: 'Download {thing}',
|
download: 'Download {thing}',
|
||||||
|
downloadAll: 'Download all {thing}',
|
||||||
by: 'by {artist}',
|
by: 'by {artist}',
|
||||||
in: 'in {album}',
|
in: 'in {album}',
|
||||||
download_hint: 'Download',
|
download_hint: 'Download',
|
||||||
@ -27,6 +28,7 @@ const en = {
|
|||||||
single: 'single | singles',
|
single: 'single | singles',
|
||||||
title: 'title | titles',
|
title: 'title | titles',
|
||||||
track: 'track | tracks',
|
track: 'track | tracks',
|
||||||
|
trackN: '0 tracks | {n} track | {n} tracks',
|
||||||
releaseN: '0 releases | {n} release | {n} releases',
|
releaseN: '0 releases | {n} release | {n} releases',
|
||||||
playlist: 'playlist | playlists',
|
playlist: 'playlist | playlists',
|
||||||
compile: 'compilation | compilations',
|
compile: 'compilation | compilations',
|
||||||
@ -36,11 +38,7 @@ const en = {
|
|||||||
featured: 'Featured in',
|
featured: 'Featured in',
|
||||||
spotifyPlaylist: 'spotify playlist | spotify playlists',
|
spotifyPlaylist: 'spotify playlist | spotify playlists',
|
||||||
releaseDate: 'release date',
|
releaseDate: 'release date',
|
||||||
error: 'error',
|
error: 'error'
|
||||||
trackN: '0 tracks | {n} track | {n} tracks',
|
|
||||||
albumN: '0 albums | {n} album | {n} albums',
|
|
||||||
artistN: '0 artists | {n} artist | {n} artists',
|
|
||||||
playlistN: '0 playlists | {n} playlist | {n} playlists',
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
about: {
|
about: {
|
||||||
|
@ -4,6 +4,7 @@ const fr = {
|
|||||||
back: 'retour',
|
back: 'retour',
|
||||||
loading: 'chargement en cours',
|
loading: 'chargement en cours',
|
||||||
download: 'Télécharger {thing}',
|
download: 'Télécharger {thing}',
|
||||||
|
downloadAll: "Télécharger l'intégralité des {thing}",
|
||||||
by: 'par {artist}',
|
by: 'par {artist}',
|
||||||
in: 'dans {album}',
|
in: 'dans {album}',
|
||||||
download_hint: 'Télécharger',
|
download_hint: 'Télécharger',
|
||||||
@ -27,6 +28,7 @@ const fr = {
|
|||||||
single: 'single | singles',
|
single: 'single | singles',
|
||||||
title: 'titre | titres',
|
title: 'titre | titres',
|
||||||
track: 'piste | pistes',
|
track: 'piste | pistes',
|
||||||
|
trackN: '0 piste | {n} piste | {n} pistes',
|
||||||
releaseN: '0 sortie | {n} sortie | {n} sorties',
|
releaseN: '0 sortie | {n} sortie | {n} sorties',
|
||||||
playlist: 'playlist | playlists',
|
playlist: 'playlist | playlists',
|
||||||
compile: 'compilation | compilations',
|
compile: 'compilation | compilations',
|
||||||
@ -36,11 +38,7 @@ const fr = {
|
|||||||
featured: 'Apparaît dans',
|
featured: 'Apparaît dans',
|
||||||
spotifyPlaylist: 'playlist spotify | playlists spotify',
|
spotifyPlaylist: 'playlist spotify | playlists spotify',
|
||||||
releaseDate: 'date de sortie',
|
releaseDate: 'date de sortie',
|
||||||
error: 'erreur',
|
error: 'erreur'
|
||||||
trackN: '0 piste | {n} piste | {n} pistes',
|
|
||||||
albumN: '0 album | {n} album | {n} albums',
|
|
||||||
artistN: '0 artiste | {n} artiste | {n} artistes',
|
|
||||||
playlistN: '0 playlist | {n} playlist | {n} playlists'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
about: {
|
about: {
|
||||||
@ -201,8 +199,7 @@ const fr = {
|
|||||||
},
|
},
|
||||||
appearance: {
|
appearance: {
|
||||||
title: 'Apparence',
|
title: 'Apparence',
|
||||||
slimDownloadTab: 'Onglet de téléchargement compact',
|
slimDownloadTab: 'Onglet de téléchargement compact'
|
||||||
slimSidebar: 'Barre latérale compacte'
|
|
||||||
},
|
},
|
||||||
downloadPath: {
|
downloadPath: {
|
||||||
title: 'Emplacement De Téléchargement'
|
title: 'Emplacement De Téléchargement'
|
||||||
@ -302,8 +299,7 @@ const fr = {
|
|||||||
syncedLyrics: 'Paroles Synchronisées',
|
syncedLyrics: 'Paroles Synchronisées',
|
||||||
copyright: "Droits d'Auteur (Copyright)",
|
copyright: "Droits d'Auteur (Copyright)",
|
||||||
composer: 'Compositeur',
|
composer: 'Compositeur',
|
||||||
involvedPeople: 'Personnes Impliquées',
|
involvedPeople: 'Personnes Impliquées'
|
||||||
source: 'ID de la source et de la piste'
|
|
||||||
},
|
},
|
||||||
other: {
|
other: {
|
||||||
title: 'Autre',
|
title: 'Autre',
|
||||||
|
@ -4,6 +4,7 @@ const it = {
|
|||||||
back: 'indietro',
|
back: 'indietro',
|
||||||
loading: 'caricamento',
|
loading: 'caricamento',
|
||||||
download: 'Scarica {thing}',
|
download: 'Scarica {thing}',
|
||||||
|
downloadAll: 'Scarica ogni {thing}',
|
||||||
by: 'di {artist}',
|
by: 'di {artist}',
|
||||||
in: 'in {album}',
|
in: 'in {album}',
|
||||||
download_hint: 'Scarica',
|
download_hint: 'Scarica',
|
||||||
@ -26,6 +27,7 @@ const it = {
|
|||||||
single: 'singolo | singoli',
|
single: 'singolo | singoli',
|
||||||
title: 'titolo | titoli',
|
title: 'titolo | titoli',
|
||||||
track: 'brano | brani',
|
track: 'brano | brani',
|
||||||
|
trackN: '0 brani | {n} brano | {n} brani',
|
||||||
releaseN: '0 dischi | {n} disco | {n} dischi',
|
releaseN: '0 dischi | {n} disco | {n} dischi',
|
||||||
playlist: 'playlist',
|
playlist: 'playlist',
|
||||||
compile: 'compilation',
|
compile: 'compilation',
|
||||||
@ -36,11 +38,7 @@ const it = {
|
|||||||
spotifyPlaylist: 'playlist spotify',
|
spotifyPlaylist: 'playlist spotify',
|
||||||
releaseDate: 'data di uscita',
|
releaseDate: 'data di uscita',
|
||||||
error: 'errore',
|
error: 'errore',
|
||||||
empty: '',
|
empty: ''
|
||||||
trackN: '0 brani | {n} brano | {n} brani',
|
|
||||||
albumN: '{n} album',
|
|
||||||
artistN: '0 artisti | {n} artista | {n} artisti',
|
|
||||||
playlistN: '{n} playlist',
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
about: {
|
about: {
|
||||||
|
7
src/styles/css/helpers.css
Normal file
7
src/styles/css/helpers.css
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.changing-theme {
|
||||||
|
transition: all 200ms ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
[v-cloak] {
|
||||||
|
display: none;
|
||||||
|
}
|
@ -25,3 +25,22 @@
|
|||||||
font-feature-settings: 'liga';
|
font-feature-settings: 'liga';
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.material-icons.explicit-icon {
|
||||||
|
margin-right: 0.3125em;
|
||||||
|
margin-left: -3px;
|
||||||
|
color: hsl(240, 5%, 59%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons.explicit-icon.explicit-icon--right {
|
||||||
|
margin-right: 0px;
|
||||||
|
margin-left: 0.3125em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons.disabled {
|
||||||
|
@apply opacity-50 cursor-default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons.mirrored {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
@ -6,33 +6,3 @@
|
|||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes indeterminate {
|
|
||||||
0% {
|
|
||||||
left: -35%;
|
|
||||||
right: 100%;
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
left: 100%;
|
|
||||||
right: -90%;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
left: 100%;
|
|
||||||
right: -90%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes indeterminate-short {
|
|
||||||
0% {
|
|
||||||
left: -200%;
|
|
||||||
right: 100%;
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
left: 107%;
|
|
||||||
right: -8%;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
left: 107%;
|
|
||||||
right: -8%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
// Breakpoints
|
|
||||||
// TODO Change them in more proper values
|
|
||||||
$small: 601px;
|
|
||||||
$medium: 993px;
|
|
||||||
|
|
||||||
// Static variables (not an oxymoron)
|
|
||||||
$explicit-separator: 0.3125em;
|
|
@ -2,55 +2,55 @@ input[type='text'],
|
|||||||
input[type='password'],
|
input[type='password'],
|
||||||
input[type='number'] {
|
input[type='number'] {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
width: calc(100% - 16px);
|
margin-bottom: 8px;
|
||||||
border: 0px solid black;
|
border: 0px solid black;
|
||||||
line-height: 36px;
|
|
||||||
padding: 0px 8px;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: var(--secondary-background);
|
background-color: var(--secondary-background);
|
||||||
|
padding: 0px 8px;
|
||||||
|
width: calc(100% - 16px);
|
||||||
|
line-height: 36px;
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='checkbox'] {
|
input[type='checkbox'] {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
background-color: none;
|
|
||||||
border: 2px solid gray;
|
|
||||||
opacity: 0.5;
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 7px;
|
|
||||||
margin: 3px;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
opacity: 0.5;
|
||||||
|
margin: 3px;
|
||||||
|
border: 2px solid gray;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: none;
|
||||||
|
padding: 7px;
|
||||||
|
|
||||||
&:checked {
|
&:checked {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
margin: 3px;
|
||||||
|
border: 0px solid var(--primary-color);
|
||||||
|
border-radius: 2px;
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='18' viewBox='3 3 18 18' width='18'%3E%3Cpath fill='%23ffffff' d='M 10,17 5,12 6.41,10.59 10,14.17 17.59,6.58 19,8 Z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='18' viewBox='3 3 18 18' width='18'%3E%3Cpath fill='%23ffffff' d='M 10,17 5,12 6.41,10.59 10,14.17 17.59,6.58 19,8 Z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E");
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
border: 0px solid var(--primary-color);
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 9px;
|
padding: 9px;
|
||||||
margin: 3px;
|
|
||||||
color: var(--primary-text);
|
color: var(--primary-text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
width: 100%;
|
margin-bottom: 8px;
|
||||||
border: 0px solid black;
|
border: 0px solid black;
|
||||||
line-height: 36px;
|
|
||||||
padding: 0px 40px 0px 8px;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
background-clip: border-box;
|
||||||
background-color: var(--secondary-background);
|
background-color: var(--secondary-background);
|
||||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 0 24 24' width='24'%3E%3Cpath style='fill%3A%23000000%3Bfill-opacity%3A0.25' d='M7 10l5 5 5-5z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 0 24 24' width='24'%3E%3Cpath style='fill%3A%23000000%3Bfill-opacity%3A0.25' d='M7 10l5 5 5-5z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E");
|
||||||
|
background-position: calc(100% - 8px) center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 24px;
|
background-size: 24px;
|
||||||
background-position: calc(100% - 8px) center;
|
padding: 0px 40px 0px 8px;
|
||||||
background-clip: border-box;
|
width: 100%;
|
||||||
|
line-height: 36px;
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@ -67,27 +67,9 @@ img {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i {
|
|
||||||
&.disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.explicit_icon {
|
|
||||||
color: hsl(240, 5%, 59%);
|
|
||||||
margin-right: $explicit-separator;
|
|
||||||
margin-left: -3px;
|
|
||||||
|
|
||||||
&.explicit_icon--right {
|
|
||||||
margin-left: $explicit-separator;
|
|
||||||
margin-right: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.single-cover {
|
.single-cover {
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,61 +87,42 @@ i {
|
|||||||
cursor: pointer !important;
|
cursor: pointer !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table--tracklist .clickable:hover,
|
|
||||||
.table--charts .clickable:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.with_checkbox {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
[type='checkbox'] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox_text {
|
|
||||||
margin-left: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.coverart {
|
.coverart {
|
||||||
background-color: var(--secondary-background);
|
background-color: var(--secondary-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ? Maybe make a component?
|
||||||
.cover_container {
|
.cover_container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.coverart {
|
.coverart {
|
||||||
opacity: 1;
|
|
||||||
display: block;
|
display: block;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
transition: 0.5s ease;
|
||||||
|
opacity: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
transition: 0.5s ease;
|
|
||||||
backface-visibility: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.download_overlay {
|
.download_overlay {
|
||||||
transition: 0.5s ease;
|
|
||||||
opacity: 0;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
text-align: center;
|
transition: 0.5s ease;
|
||||||
background-color: #000000;
|
opacity: 0;
|
||||||
border-radius: 50%;
|
|
||||||
min-width: 32px;
|
|
||||||
padding: 0px;
|
|
||||||
height: 44px;
|
|
||||||
border: 0px;
|
border: 0px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #000000;
|
||||||
|
padding: 0px;
|
||||||
|
min-width: 32px;
|
||||||
|
height: 44px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
color: white;
|
|
||||||
padding: 10px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
padding: 10px;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
@ -178,45 +141,3 @@ i {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Remove
|
|
||||||
.inline-flex {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.right {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Remove
|
|
||||||
.right {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hide {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.changing-theme {
|
|
||||||
transition: all 200ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
[v-cloak] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.material-icons {
|
|
||||||
@apply select-none;
|
|
||||||
$sizes: 18, 24, 36, 48;
|
|
||||||
|
|
||||||
@each $size in $sizes {
|
|
||||||
&.md-#{$size} {
|
|
||||||
font-size: $size * 1px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.mirrored {
|
|
||||||
transform: scaleX(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -228,9 +228,7 @@ $table-border-radius: 3px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo Remove
|
.table--tracklist .clickable:hover,
|
||||||
.top-tracks-position {
|
.table--charts .clickable:hover {
|
||||||
padding: 12px;
|
text-decoration: underline;
|
||||||
text-align: center;
|
|
||||||
cursor: default;
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
@import '~tailwindcss/utilities';
|
@import '~tailwindcss/utilities';
|
||||||
|
|
||||||
@import './base/base';
|
@import './base/base';
|
||||||
@import './base/variables';
|
|
||||||
|
|
||||||
html {
|
html {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
@ -1,47 +1,13 @@
|
|||||||
#main_search {
|
|
||||||
.search_section {
|
|
||||||
float: none;
|
|
||||||
padding-top: 20px;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
|
|
||||||
&:not(:first-child) {
|
|
||||||
border-top: 1px solid theme('colors.grayscale.500');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.top_result_header {
|
|
||||||
display: block;
|
|
||||||
cursor: default;
|
|
||||||
font-size: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search_header {
|
|
||||||
display: inline-block;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 1.75rem;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
text-transform: capitalize;
|
|
||||||
|
|
||||||
&:not(.top_result_header) {
|
|
||||||
transition: color 200ms ease-in-out;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Top Result */
|
/* Top Result */
|
||||||
.top_result {
|
.top_result {
|
||||||
display: flex;
|
@apply flex items-center flex-col;
|
||||||
align-items: center;
|
// display: flex;
|
||||||
flex-direction: column;
|
// align-items: center;
|
||||||
|
// flex-direction: column;
|
||||||
|
|
||||||
> .cover_container {
|
> .cover_container {
|
||||||
width: 156px;
|
width: 9.75rem;
|
||||||
height: 156px;
|
height: 9.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info_box {
|
.info_box {
|
||||||
@ -49,24 +15,24 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 15px;
|
margin-top: 0.9375rem;
|
||||||
|
|
||||||
.primary-text,
|
.primary-text,
|
||||||
.secondary-text {
|
.secondary-text {
|
||||||
font-size: 18px;
|
font-size: 1.125rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-text {
|
.primary-text {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 0.3125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.secondary-text {
|
.secondary-text {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 0.625rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag {
|
.tag {
|
||||||
width: 40px;
|
width: 2.5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,17 +42,17 @@
|
|||||||
.release {
|
.release {
|
||||||
.primary-text,
|
.primary-text,
|
||||||
.secondary-text {
|
.secondary-text {
|
||||||
margin: 0px;
|
margin: 0rem;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.secondary-text {
|
.secondary-text {
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
|
|
||||||
.material-icons {
|
.material-icons {
|
||||||
font-size: 17px !important;
|
font-size: 1.0625rem !important;
|
||||||
margin-left: 4px;
|
margin-left: 0.25rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,7 +65,7 @@
|
|||||||
&.firstrow_only {
|
&.firstrow_only {
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
grid-auto-rows: 0;
|
grid-auto-rows: 0;
|
||||||
grid-row-gap: 0px;
|
grid-row-gap: 0rem;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.with-checkbox {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
[type='checkbox'] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox_text {
|
||||||
|
margin-left: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Input group */
|
/* Input group */
|
||||||
.input_group {
|
.input_group {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
@ -86,7 +101,7 @@
|
|||||||
margin-bottom: 7px;
|
margin-bottom: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.with_checkbox + & {
|
.with-checkbox + & {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,10 @@ import { socket } from '@/utils/socket'
|
|||||||
|
|
||||||
const sharedOptions = {
|
const sharedOptions = {
|
||||||
gravity: 'bottom',
|
gravity: 'bottom',
|
||||||
position: 'left'
|
position: 'left',
|
||||||
|
offset: {
|
||||||
|
x: '14rem'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let toastsWithId = {}
|
let toastsWithId = {}
|
||||||
@ -84,9 +87,6 @@ export const toast = function(msg, icon = null, dismiss = true, id = null) {
|
|||||||
delete toastsWithId[id]
|
delete toastsWithId[id]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
offset: {
|
|
||||||
x: 'true' === localStorage.getItem('slimSidebar') ? '3rem': '14rem'
|
|
||||||
}
|
}
|
||||||
}).showToast()
|
}).showToast()
|
||||||
if (id) {
|
if (id) {
|
||||||
|
@ -95,6 +95,28 @@ export function copyToClipboard(text) {
|
|||||||
ghostInput.remove()
|
ghostInput.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getProperty(obj, ...props) {
|
||||||
|
for (const prop of props) {
|
||||||
|
// Example: this.is.an.example
|
||||||
|
let hasDotNotation = /\./.test(prop)
|
||||||
|
|
||||||
|
// Searching the properties in the object
|
||||||
|
let valueToTest = hasDotNotation
|
||||||
|
? prop.split('.').reduce((o, i) => {
|
||||||
|
if (o) {
|
||||||
|
return o[i]
|
||||||
|
}
|
||||||
|
}, obj)
|
||||||
|
: obj[prop]
|
||||||
|
|
||||||
|
if (!!valueToTest) {
|
||||||
|
return valueToTest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
isValidURL,
|
isValidURL,
|
||||||
convertDuration,
|
convertDuration,
|
||||||
|
@ -48,7 +48,8 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
textColor: ({ after }) => after(['group-hover'])
|
textColor: ({ after }) => after(['group-hover']),
|
||||||
|
borderWidth: ['responsive', 'first', 'hover', 'focus']
|
||||||
},
|
},
|
||||||
corePlugins: {
|
corePlugins: {
|
||||||
preflight: false
|
preflight: false
|
||||||
|
Loading…
Reference in New Issue
Block a user