forked from platypush/platypush
Improved support for multiple modals and added ability to search for albums and artists on the fly from tracks in music.mpd - vue.js refactoring WIP
This commit is contained in:
parent
7df0cec14e
commit
0b6b29f043
7 changed files with 215 additions and 35 deletions
|
@ -21,13 +21,13 @@
|
|||
&.selected { background: $selected-bg !important; }
|
||||
|
||||
.artist {
|
||||
font-size: $artist-font-size;
|
||||
font-size: .9em;
|
||||
}
|
||||
}
|
||||
|
||||
* > .duration {
|
||||
color: $duration-color;
|
||||
font-size: $duration-font-size;
|
||||
font-size: .7em;
|
||||
}
|
||||
|
||||
* > button {
|
||||
|
@ -65,7 +65,7 @@
|
|||
background: $browser-panel-bg;
|
||||
border-right: $default-border-2;
|
||||
padding: .3rem 1rem 6rem 1rem;
|
||||
font-size: $browser-font-size;
|
||||
font-size: .9em;
|
||||
|
||||
.item {
|
||||
background: none;
|
||||
|
@ -163,6 +163,15 @@
|
|||
.artist {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
a {
|
||||
color: initial;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: $track-info-hover-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,8 +235,8 @@
|
|||
|
||||
* > .elapsed-time,
|
||||
* > .total-time {
|
||||
font-size: $control-time-font-size;
|
||||
color: $control-time-color;
|
||||
font-size: .7em;
|
||||
color: .7em;
|
||||
}
|
||||
|
||||
* > .elapsed-time {
|
||||
|
@ -267,10 +276,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
z-index: 503;
|
||||
}
|
||||
|
||||
.results-controls {
|
||||
position: fixed;
|
||||
padding: 0;
|
||||
|
@ -322,6 +327,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
#music-mpd-search-modal {
|
||||
.dropdown {
|
||||
z-index: 503;
|
||||
}
|
||||
}
|
||||
|
||||
@media #{map-get($widths, 's')} {
|
||||
#music-mpd-info {
|
||||
.modal {
|
||||
|
|
|
@ -2,18 +2,13 @@ $button-enabled-color: #59df3e;
|
|||
$button-hover-color: $button-enabled-color;
|
||||
$play-button-hover-color: #64ef4a;
|
||||
|
||||
$artist-font-size: $font-size * 0.9333;
|
||||
|
||||
$duration-color: #666;
|
||||
$duration-font-size: $font-size * 0.86666;
|
||||
|
||||
$control-panel-bg: rgba(245,245,245,0.95);
|
||||
$control-panel-shadow: 0 -2.5px 4px 0 #c0c0c0;
|
||||
$control-time-color: #666;
|
||||
$control-time-font-size: $font-size * 0.666666;
|
||||
|
||||
$browser-panel-bg: rgba(248,250,250,0.95);
|
||||
$browser-font-size: $font-size * 0.8666;
|
||||
|
||||
$empty-playlist-color: rgba(200,200,200,0.7);
|
||||
$empty-playlist-shadow: 2px 1px rgb(235,235,235);
|
||||
|
@ -30,4 +25,5 @@ $search-modal-footer-border: 1px solid #ccc;
|
|||
|
||||
$info-modal-row-border: 1px solid #ddd;
|
||||
$info-modal-attr-color: #777;
|
||||
$track-info-hover-color: rgb(46,190,110);
|
||||
|
||||
|
|
|
@ -69,6 +69,28 @@ Vue.component('modal', {
|
|||
this.prevValue = this.value;
|
||||
}
|
||||
|
||||
if (this.value) {
|
||||
// Make sure that a newly opened or visible+updated modal always comes to the front
|
||||
const myZIndex = parseInt(getComputedStyle(this.$el).zIndex);
|
||||
var maxZIndex = myZIndex;
|
||||
var outermostModals = [];
|
||||
|
||||
for (const modal of document.querySelectorAll('.modal-container:not(.hidden)')) {
|
||||
const zIndex = parseInt(getComputedStyle(modal).zIndex);
|
||||
|
||||
if (zIndex > maxZIndex) {
|
||||
maxZIndex = zIndex;
|
||||
outermostModals = [modal];
|
||||
} else if (zIndex == maxZIndex) {
|
||||
outermostModals.push(modal);
|
||||
}
|
||||
}
|
||||
|
||||
if (outermostModals.indexOf(this.$el) < 0 || outermostModals.length > 1) {
|
||||
this.$el.style.zIndex = maxZIndex+1;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.value && this.timeout && !this.timeoutId) {
|
||||
var handler = (self) => {
|
||||
return () => {
|
||||
|
|
|
@ -46,6 +46,8 @@ Vue.component('music-mpd', {
|
|||
var items = [];
|
||||
|
||||
if (Object.keys(this.selectedPlaylistItems).length === 1) {
|
||||
const track = Object.values(this.selectedPlaylistItems)[0];
|
||||
|
||||
items.push({
|
||||
text: 'Play',
|
||||
icon: 'play',
|
||||
|
@ -53,9 +55,32 @@ Vue.component('music-mpd', {
|
|||
await self.playpos();
|
||||
self.selectedPlaylistItems = {};
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (track.artist && track.artist.length) {
|
||||
items.push({
|
||||
text: 'View artist',
|
||||
icon: 'user',
|
||||
click: async function() {
|
||||
await self.searchArtist(track);
|
||||
self.selectedPlaylistItems = {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (track.album && track.album.length) {
|
||||
items.push({
|
||||
text: 'View album',
|
||||
icon: 'compact-disc',
|
||||
click: async function() {
|
||||
await self.searchAlbum(track);
|
||||
self.selectedPlaylistItems = {};
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
items.push({
|
||||
text: 'Add to playlist',
|
||||
icon: 'list',
|
||||
|
@ -85,8 +110,7 @@ Vue.component('music-mpd', {
|
|||
text: 'View track info',
|
||||
icon: 'info',
|
||||
click: async function() {
|
||||
self.infoItem = Object.values(self.selectedPlaylistItems)[0];
|
||||
self.modalVisible.info = true;
|
||||
await self.info(Object.values(self.selectedPlaylistItems)[0]);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -111,6 +135,8 @@ Vue.component('music-mpd', {
|
|||
}
|
||||
|
||||
if (Object.keys(this.selectedBrowserItems).length === 1) {
|
||||
const item = Object.values(this.selectedBrowserItems)[0];
|
||||
|
||||
items.push(
|
||||
{
|
||||
text: 'Play',
|
||||
|
@ -169,6 +195,28 @@ Vue.component('music-mpd', {
|
|||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (item.artist && item.artist.length) {
|
||||
items.push({
|
||||
text: 'View artist',
|
||||
icon: 'user',
|
||||
click: async function() {
|
||||
await self.searchArtist(item);
|
||||
self.selectedPlaylistItems = {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (item.album && item.album.length) {
|
||||
items.push({
|
||||
text: 'View album',
|
||||
icon: 'compact-disc',
|
||||
click: async function() {
|
||||
await self.searchAlbum(item);
|
||||
self.selectedPlaylistItems = {};
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
items.push(
|
||||
|
@ -547,6 +595,41 @@ Vue.component('music-mpd', {
|
|||
this._parseBrowserItems(items);
|
||||
},
|
||||
|
||||
info: async function(item) {
|
||||
var info = item;
|
||||
|
||||
if (typeof(item) === 'string') {
|
||||
item = await request('music.mpd.search', {filter: {file: info}});
|
||||
}
|
||||
|
||||
this.infoItem = item;
|
||||
this.modalVisible.info = true;
|
||||
},
|
||||
|
||||
searchArtist: async function(item) {
|
||||
await this.search({artist: item.artist});
|
||||
},
|
||||
|
||||
searchAlbum: async function(item) {
|
||||
var query = {};
|
||||
|
||||
if (item['x-albumuri']) {
|
||||
query.file = item['x-albumuri'];
|
||||
} else {
|
||||
query.artist = item.albumartist || item.artist;
|
||||
query.album = item.album;
|
||||
}
|
||||
|
||||
await this.search(query);
|
||||
},
|
||||
|
||||
search: async function(query) {
|
||||
this.$refs.search.resetQuery();
|
||||
this.$refs.search.query = query;
|
||||
this.$refs.search.visible = true;
|
||||
await this.$refs.search.search();
|
||||
},
|
||||
|
||||
onNewPlayingTrack: async function(event) {
|
||||
var previousTrack = {
|
||||
file: this.track.file,
|
||||
|
@ -791,7 +874,21 @@ Vue.component('music-mpd', {
|
|||
scrollToActiveTrack: function() {
|
||||
if (this.$refs.activePlaylistTrack && this.$refs.activePlaylistTrack.length) {
|
||||
this.$refs.activePlaylistTrack[0].$el.scrollIntoView({behavior: 'smooth'});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
const self = this;
|
||||
setTimeout(() => {
|
||||
var parent = self.$refs.activePlaylistTrack[0].$el.parentElement;
|
||||
if (parent.clientHeight + parent.scrollTop < parent.scrollHeight) {
|
||||
if (parent.scrollTop-50 > 0) {
|
||||
parent.scrollTop -= 50;
|
||||
} else {
|
||||
parent.scrollTop = 0;
|
||||
}
|
||||
}
|
||||
}, 750);
|
||||
},
|
||||
|
||||
addToPlaylistPrompt: async function() {
|
||||
|
|
|
@ -5,13 +5,14 @@ Vue.component('music-mpd-search', {
|
|||
return {
|
||||
visible: false,
|
||||
showResults: false,
|
||||
results: false,
|
||||
results: [],
|
||||
filter: '',
|
||||
selectionMode: false,
|
||||
selectedItems: {},
|
||||
|
||||
query: {
|
||||
any: '',
|
||||
file: '',
|
||||
artist: '',
|
||||
title: '',
|
||||
album: '',
|
||||
|
@ -66,9 +67,36 @@ Vue.component('music-mpd-search', {
|
|||
);
|
||||
|
||||
if (Object.keys(this.selectedItems).length === 1) {
|
||||
const item = Object.values(this.selectedItems)[0];
|
||||
|
||||
if (item.artist && item.artist.length) {
|
||||
items.push({
|
||||
text: 'View artist',
|
||||
icon: 'user',
|
||||
click: async function() {
|
||||
await self.mpd.searchArtist(item);
|
||||
self.selectedItems = {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (item.album && item.album.length) {
|
||||
items.push({
|
||||
text: 'View album',
|
||||
icon: 'compact-disc',
|
||||
click: async function() {
|
||||
await self.mpd.searchAlbum(item);
|
||||
self.selectedItems = {};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
text: 'View info',
|
||||
icon: 'info',
|
||||
click: function() {
|
||||
self.$emit('info', item);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -86,7 +114,9 @@ Vue.component('music-mpd-search', {
|
|||
return items;
|
||||
}, []);
|
||||
|
||||
this.results = [];
|
||||
var results = await request('music.mpd.search', {filter: filter});
|
||||
|
||||
this.results = results.sort((a,b) => {
|
||||
const tokenize = (t) => {
|
||||
return ''.concat(t.artist || '', '-', t.album || '', '-', t.disc || '', '-', t.track || '', t.title || '').toLocaleLowerCase();
|
||||
|
@ -135,11 +165,18 @@ Vue.component('music-mpd-search', {
|
|||
this.selectionMode = true;
|
||||
|
||||
for (var item of this.results) {
|
||||
this.selectedItems[item.id] = item;
|
||||
this.selectedItems[item.file] = item;
|
||||
}
|
||||
|
||||
openDropdown(this.$refs.dropdown.$el);
|
||||
},
|
||||
|
||||
resetQuery: function() {
|
||||
this.filter = '';
|
||||
for (const attr of Object.keys(this.query)) {
|
||||
this.query[attr] = '';
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
|
||||
<script type="text/x-template" id="tmpl-music-mpd">
|
||||
<div class="row music-mpd-container">
|
||||
<music-mpd-search ref="search" :mpd="this"></music-mpd-search>
|
||||
<music-mpd-search ref="search"
|
||||
@info="info"
|
||||
:mpd="this">
|
||||
</music-mpd-search>
|
||||
|
||||
<modal id="music-mpd-info" title="Info" v-model="modalVisible.info" ref="modal">
|
||||
<div class="info-container">
|
||||
|
@ -30,6 +33,11 @@
|
|||
<div class="col-8 value" v-text="infoItem.album"></div>
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="infoItem.year">
|
||||
<div class="col-4 attr">Year</div>
|
||||
<div class="col-8 value" v-text="infoItem.year"></div>
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="infoItem.disc">
|
||||
<div class="col-4 attr">Disc</div>
|
||||
<div class="col-8 value" v-text="infoItem.disc"></div>
|
||||
|
@ -174,8 +182,14 @@
|
|||
<div class="row controls">
|
||||
<div class="col-3 track-container">
|
||||
<div class="track-info" v-if="status.state == 'play' || status.state == 'pause'">
|
||||
<div class="row artist" v-text="track.artist"></div>
|
||||
<div class="row title" v-text="track.title"></div>
|
||||
<div class="row artist">
|
||||
<a href="#" v-text="track.artist" @click="searchArtist(track)" v-if="track.artist"></a>
|
||||
<span v-text="track.artist" v-else></span>
|
||||
</div>
|
||||
<div class="row title">
|
||||
<a href="#" v-text="track.title" @click="searchAlbum(track)" v-if="track.album"></a>
|
||||
<span v-text="track.title" v-else></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -4,10 +4,19 @@
|
|||
<modal id="music-mpd-search-modal" title="Search" v-model="visible"
|
||||
:width="showResults ? '90vw' : 'initial'" ref="modal"
|
||||
@open="$refs.form.querySelector('input[type=text]:first-child').focus()">
|
||||
<dropdown id="music-mpd-search-dropdown"
|
||||
v-if="results.length > 0"
|
||||
ref="dropdown"
|
||||
:items="dropdownItems">
|
||||
</dropdown>
|
||||
|
||||
<div class="search">
|
||||
<form ref="form" @submit.prevent="search" :class="{hidden: showResults}">
|
||||
<div class="row">
|
||||
<input type="text" v-model.lazy.trim="query.any" placeholder="Free-text search">
|
||||
<input type="text" v-model.lazy.trim="query.any" placeholder="Any field">
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="text" v-model.lazy.trim="query.file" placeholder="Filename or URI">
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="text" v-model.lazy.trim="query.artist" placeholder="Artist">
|
||||
|
@ -56,12 +65,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<dropdown id="music-mpd-search-dropdown"
|
||||
v-if="results.length > 0"
|
||||
ref="dropdown"
|
||||
:items="dropdownItems">
|
||||
</dropdown>
|
||||
|
||||
<div class="results" :class="{hidden: !showResults}">
|
||||
<div class="no-results" v-if="results.length === 0">No results</div>
|
||||
<div v-else>
|
||||
|
|
Loading…
Reference in a new issue