[web] Migration to Vue 3 and Vite

This commit is contained in:
chme
2022-02-19 06:18:01 +01:00
parent 92279ef33d
commit de097fcf94
104 changed files with 2904 additions and 36569 deletions

View File

@@ -1,17 +1,13 @@
<template>
<figure>
<img v-lazyload
:data-src="artwork_url_with_size"
:data-err="dataURI"
:key="artwork_url_with_size"
<img v-lazy="{ src: artwork_url_with_size, lifecycle: lazy_lifecycle }"
@click="$emit('click')">
</figure>
</template>
<script>
import webapi from '@/webapi'
import SVGRenderer from '@/lib/SVGRenderer'
import stringToColor from 'string-to-color'
import { renderSVG } from '@/lib/SVGRenderer'
export default {
name: 'CoverArtwork',
@@ -19,12 +15,16 @@ export default {
data () {
return {
svg: new SVGRenderer(),
width: 600,
height: 600,
font_family: 'sans-serif',
font_size: 200,
font_weight: 600
font_weight: 600,
lazy_lifecycle: {
error: (el) => {
el.src = this.dataURI()
}
}
}
},
@@ -48,47 +48,18 @@ export default {
return this.artist.substring(0, 2)
}
return ''
},
}
},
background_color () {
return stringToColor(this.alt_text)
},
is_background_light () {
// Based on https://stackoverflow.com/a/44615197
const hex = this.background_color.replace(/#/, '')
const r = parseInt(hex.substr(0, 2), 16)
const g = parseInt(hex.substr(2, 2), 16)
const b = parseInt(hex.substr(4, 2), 16)
const luma = [
0.299 * r,
0.587 * g,
0.114 * b
].reduce((a, b) => a + b) / 255
return luma > 0.5
},
text_color () {
return this.is_background_light ? '#000000' : '#ffffff'
},
rendererParams () {
return {
methods: {
dataURI: function () {
return renderSVG(this.caption, this.alt_text, {
width: this.width,
height: this.height,
textColor: this.text_color,
backgroundColor: this.background_color,
caption: this.caption,
fontFamily: this.font_family,
fontSize: this.font_size,
fontWeight: this.font_weight
}
},
dataURI () {
return this.svg.render(this.rendererParams)
font_family: this.font_family,
font_size: this.font_size,
font_weight: this.font_weight
})
}
}
}

View File

@@ -1,8 +1,8 @@
<template>
<div class="dropdown" :class="{ 'is-active': is_active }" v-click-outside="onClickOutside">
<div class="dropdown" :class="{ 'is-active': is_active }" v-click-away="onClickOutside">
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu" @click="is_active = !is_active">
<span>{{ value }}</span>
<span>{{ modelValue }}</span>
<span class="icon is-small">
<i class="mdi mdi-chevron-down" aria-hidden="true"></i>
</span>
@@ -12,7 +12,7 @@
<div class="dropdown-content">
<a class="dropdown-item"
v-for="option in options" :key="option"
:class="{'is-active': value === option}"
:class="{'is-active': modelValue === option}"
@click="select(option)">
{{ option }}
</a>
@@ -25,7 +25,8 @@
export default {
name: 'DropdownMenu',
props: ['value', 'options'],
props: ['modelValue', 'options'],
emits: ['update:modelValue'],
data () {
return {
@@ -40,7 +41,7 @@ export default {
select (option) {
this.is_active = false
this.$emit('input', option)
this.$emit('update:modelValue', option)
}
}
}

View File

@@ -21,7 +21,7 @@ export default {
methods: {
nav: function (id) {
this.$router.push({ path: this.$router.currentRoute.path + '#index_' + id })
this.$router.push({ hash: '#index_' + id })
},
scroll_to_top: function () {

View File

@@ -3,12 +3,14 @@
<div v-if="is_grouped">
<div v-for="idx in albums.indexList" :key="idx" class="mb-6">
<span class="tag is-info is-light is-small has-text-weight-bold" :id="'index_' + idx">{{ idx }}</span>
<list-item-album v-for="album in albums.grouped[idx]"
<div class="media" v-for="album in albums.grouped[idx]"
:key="album.id"
:album="album"
@click="open_album(album)">
<template slot="artwork" v-if="is_visible_artwork">
<p class="image is-64x64 fd-has-shadow fd-has-action">
<div class="media-left fd-has-action"
v-if="is_visible_artwork">
<p class="image is-64x64 fd-has-shadow fd-has-action">
<cover-artwork
:artwork_url="album.artwork_url"
:artist="album.artist"
@@ -16,13 +18,28 @@
:maxwidth="64"
:maxheight="64" />
</p>
</template>
<template slot="actions">
<a @click="open_dialog(album)">
</div>
<div class="media-content fd-has-action is-clipped">
<div style="margin-top:0.7rem;">
<h1 class="title is-6">{{ album.name }}</h1>
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artist }}</b></h2>
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal"
v-if="album.date_released && album.media_kind === 'music'">
{{ $filters.time(album.date_released, 'L') }}
</h2>
</div>
</div>
<div class="media-right" style="padding-top:0.7rem;">
<a @click.prevent.stop="open_dialog(album)">
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
</a>
</template>
</list-item-album>
</div>
</div>
</div>
</div>
<div v-else>
@@ -30,7 +47,7 @@
:key="album.id"
:album="album"
@click="open_album(album)">
<template slot="artwork" v-if="is_visible_artwork">
<template v-slot:artwork v-if="is_visible_artwork">
<p class="image is-64x64 fd-has-shadow fd-has-action">
<cover-artwork
:artwork_url="album.artwork_url"
@@ -40,8 +57,8 @@
:maxheight="64" />
</p>
</template>
<template slot="actions">
<a @click="open_dialog(album)">
<template v-slot:actions>
<a @click.prevent.stop="open_dialog(album)">
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
</a>
</template>
@@ -60,7 +77,7 @@
delete_action="Remove"
@close="show_remove_podcast_modal = false"
@delete="remove_podcast">
<template slot="modal-content">
<template v-slot:modal-content>
<p>Permanently remove this podcast from your library?</p>
<p class="is-size-7">(This will also remove the RSS playlist <b>{{ rss_playlist_to_remove.name }}</b>.)</p>
</template>
@@ -69,10 +86,10 @@
</template>
<script>
import ListItemAlbum from '@/components/ListItemAlbum'
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
import ModalDialog from '@/components/ModalDialog'
import CoverArtwork from '@/components/CoverArtwork'
import ListItemAlbum from '@/components/ListItemAlbum.vue'
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
import ModalDialog from '@/components/ModalDialog.vue'
import CoverArtwork from '@/components/CoverArtwork.vue'
import webapi from '@/webapi'
import Albums from '@/lib/Albums'
@@ -105,7 +122,10 @@ export default {
if (Array.isArray(this.albums)) {
return this.albums
}
return this.albums.sortedAndFiltered
if (this.albums) {
return this.albums.sortedAndFiltered
}
return []
},
is_grouped: function () {

View File

@@ -7,8 +7,8 @@
:key="artist.id"
:artist="artist"
@click="open_artist(artist)">
<template slot="actions">
<a @click="open_dialog(artist)">
<template v-slot:actions>
<a @click.prevent.stop="open_dialog(artist)">
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
</a>
</template>
@@ -20,8 +20,8 @@
:key="artist.id"
:artist="artist"
@click="open_artist(artist)">
<template slot="actions">
<a @click="open_dialog(artist)">
<template v-slot:actions>
<a @click.prevent.stop="open_dialog(artist)">
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
</a>
</template>
@@ -32,8 +32,8 @@
</template>
<script>
import ListItemArtist from '@/components/ListItemArtist'
import ModalDialogArtist from '@/components/ModalDialogArtist'
import ListItemArtist from '@/components/ListItemArtist.vue'
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
import Artists from '@/lib/Artists'
export default {

View File

@@ -7,8 +7,8 @@
:key="composer.id"
:composer="composer"
@click="open_composer(composer)">
<template slot="actions">
<a @click="open_dialog(composer)">
<template v-slot:actions>
<a @click.prevent.stop="open_dialog(composer)">
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
</a>
</template>
@@ -20,8 +20,8 @@
:key="composer.id"
:composer="composer"
@click="open_composer(composer)">
<template slot="actions">
<a @click="open_dialog(composer)">
<template v-slot:actions>
<a @click.prevent.stop="open_dialog(composer)">
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
</a>
</template>
@@ -32,8 +32,8 @@
</template>
<script>
import ListItemComposer from '@/components/ListItemComposer'
import ModalDialogComposer from '@/components/ModalDialogComposer'
import ListItemComposer from '@/components/ListItemComposer.vue'
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
import Composers from '@/lib/Composers'
export default {

View File

@@ -1,17 +1,16 @@
<template functional>
<div class="media" :id="'index_' + props.album.name_sort.charAt(0).toUpperCase()">
<template>
<div class="media" :id="'index_' + album.name_sort.charAt(0).toUpperCase()">
<div class="media-left fd-has-action"
v-if="$slots['artwork']"
@click="listeners.click">
v-if="$slots['artwork']">
<slot name="artwork"></slot>
</div>
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
<div class="media-content fd-has-action is-clipped">
<div style="margin-top:0.7rem;">
<h1 class="title is-6">{{ props.album.name }}</h1>
<h2 class="subtitle is-7 has-text-grey"><b>{{ props.album.artist }}</b></h2>
<h1 class="title is-6">{{ album.name }}</h1>
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artist }}</b></h2>
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal"
v-if="props.album.date_released && props.album.media_kind === 'music'">
{{ props.album.date_released | time('L') }}
v-if="album.date_released && album.media_kind === 'music'">
{{ $filters.time(album.date_released, 'L') }}
</h2>
</div>
</div>

View File

@@ -1,7 +1,7 @@
<template functional>
<template>
<div class="media">
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
<h1 class="title is-6">{{ props.artist.name }}</h1>
<div class="media-content fd-has-action is-clipped">
<h1 class="title is-6">{{ artist.name }}</h1>
</div>
<div class="media-right">
<slot name="actions"></slot>

View File

@@ -1,7 +1,7 @@
<template functional>
<div class="media" :id="'index_' + props.composer.name.charAt(0).toUpperCase()">
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
<h1 class="title is-6">{{ props.composer.name }}</h1>
<template>
<div class="media" :id="'index_' + composer.name.charAt(0).toUpperCase()">
<div class="media-content fd-has-action is-clipped">
<h1 class="title is-6">{{ composer.name }}</h1>
</div>
<div class="media-right">
<slot name="actions"></slot>

View File

@@ -1,13 +1,13 @@
<template functional>
<template>
<div class="media">
<figure class="media-left fd-has-action" @click="listeners.click">
<figure class="media-left fd-has-action">
<span class="icon">
<i class="mdi mdi-folder"></i>
</span>
</figure>
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
<h1 class="title is-6">{{ props.directory.path.substring(props.directory.path.lastIndexOf('/') + 1) }}</h1>
<h2 class="subtitle is-7 has-text-grey-light">{{ props.directory.path }}</h2>
<div class="media-content fd-has-action is-clipped">
<h1 class="title is-6">{{ directory.path.substring(directory.path.lastIndexOf('/') + 1) }}</h1>
<h2 class="subtitle is-7 has-text-grey-light">{{ directory.path }}</h2>
</div>
<div class="media-right">
<slot name="actions"></slot>

View File

@@ -1,7 +1,7 @@
<template functional>
<div class="media" :id="'index_' + props.genre.name.charAt(0).toUpperCase()">
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
<h1 class="title is-6">{{ props.genre.name }}</h1>
<template>
<div class="media" :id="'index_' + genre.name.charAt(0).toUpperCase()">
<div class="media-content fd-has-action is-clipped">
<h1 class="title is-6">{{ genre.name }}</h1>
</div>
<div class="media-right">
<slot name="actions"></slot>

View File

@@ -1,10 +1,10 @@
<template functional>
<template>
<div class="media">
<figure class="media-left fd-has-action" v-if="slots().icon" @click="listeners.click">
<figure class="media-left fd-has-action" v-if="$slots.icon">
<slot name="icon"></slot>
</figure>
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
<h1 class="title is-6">{{ props.playlist.name }}</h1>
<div class="media-content fd-has-action is-clipped">
<h1 class="title is-6">{{ playlist.name }}</h1>
</div>
<div class="media-right">
<slot name="actions"></slot>

View File

@@ -1,12 +1,12 @@
<template functional>
<div class="media" :id="'index_' + props.track.title_sort.charAt(0).toUpperCase()" :class="{ 'with-progress': slots().progress }">
<figure class="media-left fd-has-action" v-if="slots().icon" @click="listeners.click">
<template>
<div class="media" :id="'index_' + track.title_sort.charAt(0).toUpperCase()" :class="{ 'with-progress': $slots.progress }">
<figure class="media-left fd-has-action" v-if="$slots.icon">
<slot name="icon"></slot>
</figure>
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
<h1 class="title is-6" :class="{ 'has-text-grey': props.track.media_kind === 'podcast' && props.track.play_count > 0 }">{{ props.track.title }}</h1>
<h2 class="subtitle is-7 has-text-grey"><b>{{ props.track.artist }}</b></h2>
<h2 class="subtitle is-7 has-text-grey">{{ props.track.album }}</h2>
<div class="media-content fd-has-action is-clipped">
<h1 class="title is-6" :class="{ 'has-text-grey': track.media_kind === 'podcast' && track.play_count > 0 }">{{ track.title }}</h1>
<h2 class="subtitle is-7 has-text-grey"><b>{{ track.artist }}</b></h2>
<h2 class="subtitle is-7 has-text-grey">{{ track.album }}</h2>
<slot name="progress"></slot>
</div>
<div class="media-right">

View File

@@ -1,13 +1,13 @@
<template>
<div>
<list-item-playlist v-for="playlist in playlists" :key="playlist.id" :playlist="playlist" @click="open_playlist(playlist)">
<template slot="icon">
<template v-slot:icon>
<span class="icon">
<i class="mdi" :class="{ 'mdi-library-music': playlist.type !== 'folder', 'mdi-rss': playlist.type === 'rss', 'mdi-folder': playlist.type === 'folder' }"></i>
</span>
</template>
<template slot="actions">
<a @click="open_dialog(playlist)">
<template v-slot:actions>
<a @click.prevent.stop="open_dialog(playlist)">
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
</a>
</template>
@@ -17,8 +17,8 @@
</template>
<script>
import ListItemPlaylist from '@/components/ListItemPlaylist'
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist'
import ListItemPlaylist from '@/components/ListItemPlaylist.vue'
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist.vue'
export default {
name: 'ListPlaylists',

View File

@@ -1,8 +1,8 @@
<template>
<div>
<list-item-track v-for="(track, index) in tracks" :key="track.id" :track="track" @click="play_track(index, track)">
<template slot="actions">
<a @click="open_dialog(track)">
<template v-slot:actions>
<a @click.prevent.stop="open_dialog(track)">
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
</a>
</template>
@@ -12,8 +12,8 @@
</template>
<script>
import ListItemTrack from '@/components/ListItemTrack'
import ModalDialogTrack from '@/components/ModalDialogTrack'
import ListItemTrack from '@/components/ListItemTrack.vue'
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
import webapi from '@/webapi'
export default {

View File

@@ -25,7 +25,7 @@
</p>
<p v-if="album.date_released">
<span class="heading">Release date</span>
<span class="title is-6">{{ album.date_released | time('L') }}</span>
<span class="title is-6">{{ $filters.time(album.date_released, 'L') }}</span>
</p>
<p v-else-if="album.year > 0">
<span class="heading">Year</span>
@@ -37,7 +37,7 @@
</p>
<p>
<span class="heading">Length</span>
<span class="title is-6">{{ album.length_ms | duration }}</span>
<span class="title is-6">{{ $filters.duration(album.length_ms) }}</span>
</p>
<p>
<span class="heading">Type</span>
@@ -45,7 +45,7 @@
</p>
<p>
<span class="heading">Added at</span>
<span class="title is-6">{{ album.time_added | time('L LT') }}</span>
<span class="title is-6">{{ $filters.time(album.time_added, 'L LT') }}</span>
</p>
</div>
</div>
@@ -69,7 +69,7 @@
</template>
<script>
import CoverArtwork from '@/components/CoverArtwork'
import CoverArtwork from '@/components/CoverArtwork.vue'
import webapi from '@/webapi'
export default {

View File

@@ -24,7 +24,7 @@
</p>
<p>
<span class="heading">Added at</span>
<span class="title is-6">{{ artist.time_added | time('L LT') }}</span>
<span class="title is-6">{{ $filters.time(artist.time_added, 'L LT') }}</span>
</p>
</div>
</div>

View File

@@ -41,7 +41,7 @@
</p>
<p>
<span class="heading">Length</span>
<span class="title is-6">{{ item.length_ms | duration }}</span>
<span class="title is-6">{{ $filters.duration(item.length_ms) }}</span>
</p>
<p>
<span class="heading">Path</span>
@@ -56,7 +56,7 @@
<span class="title is-6">
{{ item.type }}
<span v-if="item.samplerate"> | {{ item.samplerate }} Hz</span>
<span v-if="item.channels"> | {{ item.channels | channels }}</span>
<span v-if="item.channels"> | {{ $filters.channels(item.channels) }}</span>
<span v-if="item.bitrate"> | {{ item.bitrate }} Kb/s</span>
</span>
</p>

View File

@@ -31,7 +31,7 @@
</p>
<p v-if="track.date_released">
<span class="heading">Release date</span>
<span class="title is-6">{{ track.date_released | time('L') }}</span>
<span class="title is-6">{{ $filters.time(track.date_released, 'L') }}</span>
</p>
<p v-else-if="track.year > 0">
<span class="heading">Year</span>
@@ -47,7 +47,7 @@
</p>
<p>
<span class="heading">Length</span>
<span class="title is-6">{{ track.length_ms | duration }}</span>
<span class="title is-6">{{ $filters.duration(track.length_ms) }}</span>
</p>
<p>
<span class="heading">Path</span>
@@ -62,13 +62,13 @@
<span class="title is-6">
{{ track.type }}
<span v-if="track.samplerate"> | {{ track.samplerate }} Hz</span>
<span v-if="track.channels"> | {{ track.channels | channels }}</span>
<span v-if="track.channels"> | {{ $filters.channels(track.channels) }}</span>
<span v-if="track.bitrate"> | {{ track.bitrate }} Kb/s</span>
</span>
</p>
<p>
<span class="heading">Added at</span>
<span class="title is-6">{{ track.time_added | time('L LT') }}</span>
<span class="title is-6">{{ $filters.time(track.time_added, 'L LT') }}</span>
</p>
<p>
<span class="heading">Rating</span>

View File

@@ -6,7 +6,7 @@
close_action="Close"
@ok="update_library"
@close="close()">
<template slot="modal-content">
<template v-slot:modal-content>
<div v-if="!library.updating">
<p class="mb-3">Scan for new, deleted and modified files</p>
<div class="field" v-if="spotify_enabled || rss.tracks > 0">
@@ -36,7 +36,7 @@
</template>
<script>
import ModalDialog from '@/components/ModalDialog'
import ModalDialog from '@/components/ModalDialog.vue'
import * as types from '@/store/mutation_types'
import webapi from '@/webapi'

View File

@@ -53,14 +53,21 @@
<div class="level-item fd-expanded">
<div class="fd-expanded">
<p class="heading">Volume</p>
<range-slider
<Slider v-model="player.volume"
:min="0"
:max="100"
:step="1"
:tooltips="false"
@change="set_volume"
:classes="{ target: 'slider'}" />
<!--range-slider
class="slider fd-has-action"
min="0"
max="100"
step="1"
:value="player.volume"
@change="set_volume">
</range-slider>
</range-slider-->
</div>
</div>
</div>
@@ -82,7 +89,15 @@
<div class="level-item fd-expanded">
<div class="fd-expanded">
<p class="heading" :class="{ 'has-text-grey-light': !playing }">HTTP stream <a href="stream.mp3"><span class="is-lowercase">(stream.mp3)</span></a></p>
<range-slider
<Slider v-model="stream_volume"
:min="0"
:max="100"
:step="1"
:tooltips="false"
:disabled="!playing"
@change="set_stream_volume"
:classes="{ target: 'slider'}" />
<!--range-slider
class="slider fd-has-action"
min="0"
max="100"
@@ -90,7 +105,7 @@
:disabled="!playing"
:value="stream_volume"
@change="set_stream_volume">
</range-slider>
</range-slider-->
</div>
</div>
</div>
@@ -142,14 +157,21 @@
<div class="level-item fd-expanded">
<div class="fd-expanded">
<p class="heading">Volume</p>
<range-slider
<Slider v-model="player.volume"
:min="0"
:max="100"
:step="1"
:tooltips="false"
@change="set_volume"
:classes="{ target: 'slider'}" />
<!--range-slider
class="slider fd-has-action"
min="0"
max="100"
step="1"
:value="player.volume"
@change="set_volume">
</range-slider>
</range-slider-->
</div>
</div>
</div>
@@ -175,7 +197,15 @@
<div class="level-item fd-expanded">
<div class="fd-expanded">
<p class="heading" :class="{ 'has-text-grey-light': !playing }">HTTP stream <a href="stream.mp3"><span class="is-lowercase">(stream.mp3)</span></a></p>
<range-slider
<Slider v-model="stream_volume"
:min="0"
:max="100"
:step="1"
:tooltips="false"
:disabled="!playing"
@change="set_stream_volume"
:classes="{ target: 'slider'}" />
<!-- range-slider
class="slider fd-has-action"
min="0"
max="100"
@@ -183,7 +213,7 @@
:disabled="!playing"
:value="stream_volume"
@change="set_stream_volume">
</range-slider>
</range-slider-->
</div>
</div>
</div>
@@ -197,17 +227,18 @@
<script>
import webapi from '@/webapi'
import _audio from '@/audio'
import NavbarItemLink from './NavbarItemLink'
import NavbarItemOutput from './NavbarItemOutput'
import PlayerButtonPlayPause from '@/components/PlayerButtonPlayPause'
import PlayerButtonNext from '@/components/PlayerButtonNext'
import PlayerButtonPrevious from '@/components/PlayerButtonPrevious'
import PlayerButtonShuffle from '@/components/PlayerButtonShuffle'
import PlayerButtonConsume from '@/components/PlayerButtonConsume'
import PlayerButtonRepeat from '@/components/PlayerButtonRepeat'
import PlayerButtonSeekBack from '@/components/PlayerButtonSeekBack'
import PlayerButtonSeekForward from '@/components/PlayerButtonSeekForward'
import RangeSlider from 'vue-range-slider'
import NavbarItemLink from './NavbarItemLink.vue'
import NavbarItemOutput from './NavbarItemOutput.vue'
import PlayerButtonPlayPause from '@/components/PlayerButtonPlayPause.vue'
import PlayerButtonNext from '@/components/PlayerButtonNext.vue'
import PlayerButtonPrevious from '@/components/PlayerButtonPrevious.vue'
import PlayerButtonShuffle from '@/components/PlayerButtonShuffle.vue'
import PlayerButtonConsume from '@/components/PlayerButtonConsume.vue'
import PlayerButtonRepeat from '@/components/PlayerButtonRepeat.vue'
import PlayerButtonSeekBack from '@/components/PlayerButtonSeekBack.vue'
import PlayerButtonSeekForward from '@/components/PlayerButtonSeekForward.vue'
//import RangeSlider from 'vue-range-slider'
import Slider from '@vueform/slider'
import * as types from '@/store/mutation_types'
export default {
@@ -215,7 +246,8 @@ export default {
components: {
NavbarItemLink,
NavbarItemOutput,
RangeSlider,
//RangeSlider,
Slider,
PlayerButtonPlayPause,
PlayerButtonNext,
PlayerButtonPrevious,

View File

@@ -14,7 +14,15 @@
<div class="level-item fd-expanded">
<div class="fd-expanded">
<p class="heading" :class="{ 'has-text-grey-light': !output.selected }">{{ output.name }}</p>
<range-slider
<Slider v-model="volume"
:min="0"
:max="100"
:step="1"
:tooltips="false"
:disabled="!output.selected"
@change="set_volume"
:classes="{ target: 'slider'}" />
<!--range-slider
class="slider fd-has-action"
min="0"
max="100"
@@ -22,7 +30,7 @@
:disabled="!output.selected"
:value="volume"
@change="set_volume" >
</range-slider>
</range-slider-->
</div>
</div>
</div>
@@ -31,12 +39,16 @@
</template>
<script>
import RangeSlider from 'vue-range-slider'
//import RangeSlider from 'vue-range-slider'
import Slider from '@vueform/slider'
import webapi from '@/webapi'
export default {
name: 'NavbarItemOutput',
components: { RangeSlider },
components: {
// RangeSlider
Slider
},
props: ['output'],

View File

@@ -79,7 +79,7 @@
</template>
<script>
import NavbarItemLink from './NavbarItemLink'
import NavbarItemLink from './NavbarItemLink.vue'
import * as types from '@/store/mutation_types'
export default {

View File

@@ -0,0 +1,22 @@
<template>
<div v-if="width > 0" class="progress-bar mt-2" :style="{ width: width_percent }" />
</template>
<script>
export default {
name: 'ProgressBar',
props: ['max', 'value'],
computed: {
width () {
if (this.value > 0 && this.max > 0) {
return parseInt(this.value * 100 / this.max)
}
return 0
},
width_percent () {
return this.width + '%'
}
}
}
</script>

View File

@@ -81,6 +81,7 @@ export default {
this.timerId = -1
const newValue = this.$refs.settings_checkbox.checked
console.log(this.$refs.settings_checkbox)
if (newValue === this.value) {
this.statusUpdate = ''
return

View File

@@ -1,14 +1,13 @@
<template functional>
<template>
<div class="media">
<div class="media-left fd-has-action"
v-if="$slots['artwork']"
@click="listeners.click">
v-if="$slots['artwork']">
<slot name="artwork"></slot>
</div>
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
<h1 class="title is-6">{{ props.album.name }}</h1>
<h2 class="subtitle is-7 has-text-grey"><b>{{ props.album.artists[0].name }}</b></h2>
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal">({{ props.album.album_type }}, {{ props.album.release_date | time('L') }})</h2>
<div class="media-content fd-has-action is-clipped">
<h1 class="title is-6">{{ album.name }}</h1>
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artists[0].name }}</b></h2>
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal">({{ album.album_type }}, {{ $filters.time(album.release_date, 'L') }})</h2>
</div>
<div class="media-right">
<slot name="actions"></slot>

View File

@@ -19,7 +19,7 @@
</p>
<p>
<span class="heading">Release date</span>
<span class="title is-6">{{ album.release_date | time('L') }}</span>
<span class="title is-6">{{ $filters.time(album.release_date, 'L') }}</span>
</p>
<p>
<span class="heading">Type</span>

View File

@@ -23,7 +23,7 @@
</p>
<p>
<span class="heading">Release date</span>
<span class="title is-6">{{ album.release_date | time('L') }}</span>
<span class="title is-6">{{ $filters.time(album.release_date, 'L') }}</span>
</p>
<p>
<span class="heading">Track / Disc</span>
@@ -31,7 +31,7 @@
</p>
<p>
<span class="heading">Length</span>
<span class="title is-6">{{ track.duration_ms | duration }}</span>
<span class="title is-6">{{ $filters.duration(track.duration_ms) }}</span>
</p>
<p>
<span class="heading">Path</span>

View File

@@ -5,17 +5,21 @@
<div class="column is-four-fifths">
<div class="tabs is-centered is-small">
<ul>
<router-link tag="li" to="/audiobooks/artists" active-class="is-active">
<a>
<span class="icon is-small"><i class="mdi mdi-artist"></i></span>
<span class="">Authors</span>
</a>
<router-link to="/audiobooks/artists" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="icon is-small"><i class="mdi mdi-artist"></i></span>
<span class="">Authors</span>
</a>
</li>
</router-link>
<router-link tag="li" to="/audiobooks/albums" active-class="is-active">
<a>
<span class="icon is-small"><i class="mdi mdi-album"></i></span>
<span class="">Audiobooks</span>
</a>
<router-link to="/audiobooks/albums" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="icon is-small"><i class="mdi mdi-album"></i></span>
<span class="">Audiobooks</span>
</a>
</li>
</router-link>
</ul>
</div>

View File

@@ -5,41 +5,53 @@
<div class="column is-four-fifths">
<div class="tabs is-centered is-small">
<ul>
<router-link tag="li" to="/music/browse" active-class="is-active">
<a>
<span class="icon is-small"><i class="mdi mdi-web"></i></span>
<span class="">Browse</span>
</a>
<router-link to="/music/browse" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="icon is-small"><i class="mdi mdi-web"></i></span>
<span class="">Browse</span>
</a>
</li>
</router-link>
<router-link tag="li" to="/music/artists" active-class="is-active">
<a>
<span class="icon is-small"><i class="mdi mdi-artist"></i></span>
<span class="">Artists</span>
</a>
<router-link to="/music/artists" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="icon is-small"><i class="mdi mdi-artist"></i></span>
<span class="">Artists</span>
</a>
</li>
</router-link>
<router-link tag="li" to="/music/albums" active-class="is-active">
<a>
<span class="icon is-small"><i class="mdi mdi-album"></i></span>
<span class="">Albums</span>
</a>
<router-link to="/music/albums" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="icon is-small"><i class="mdi mdi-album"></i></span>
<span class="">Albums</span>
</a>
</li>
</router-link>
<router-link tag="li" to="/music/genres" active-class="is-active">
<a>
<span class="icon is-small"><i class="mdi mdi-speaker"></i></span>
<span class="">Genres</span>
</a>
<router-link to="/music/genres" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="icon is-small"><i class="mdi mdi-speaker"></i></span>
<span class="">Genres</span>
</a>
</li>
</router-link>
<router-link tag="li" to="/music/composers" active-class="is-active">
<a>
<span class="icon is-small"><i class="mdi mdi-book-open-page-variant"></i></span>
<span class="">Composers</span>
</a>
<router-link to="/music/composers" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="icon is-small"><i class="mdi mdi-book-open-page-variant"></i></span>
<span class="">Composers</span>
</a>
</li>
</router-link>
<router-link tag="li" to="/music/spotify" v-if="spotify_enabled" active-class="is-active">
<a>
<span class="icon is-small"><i class="mdi mdi-spotify"></i></span>
<span class="">Spotify</span>
</a>
<router-link to="/music/spotify" v-if="spotify_enabled" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="icon is-small"><i class="mdi mdi-spotify"></i></span>
<span class="">Spotify</span>
</a>
</li>
</router-link>
</ul>
</div>

View File

@@ -5,25 +5,33 @@
<div class="column is-four-fifths">
<div class="tabs is-centered is-small">
<ul>
<router-link tag="li" to="/settings/webinterface" active-class="is-active">
<a>
<span class="">Webinterface</span>
</a>
<router-link to="/settings/webinterface" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="">Webinterface</span>
</a>
</li>
</router-link>
<router-link tag="li" to="/settings/remotes-outputs" active-class="is-active">
<a>
<span class="">Remotes &amp; Outputs</span>
</a>
<router-link to="/settings/remotes-outputs" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="">Remotes &amp; Outputs</span>
</a>
</li>
</router-link>
<router-link tag="li" to="/settings/artwork" active-class="is-active">
<a>
<span class="">Artwork</span>
</a>
<router-link to="/settings/artwork" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="">Artwork</span>
</a>
</li>
</router-link>
<router-link tag="li" to="/settings/online-services" active-class="is-active">
<a>
<span class="">Online Services</span>
</a>
<router-link to="/settings/online-services" custom v-slot="{ navigate, isActive }">
<li :class="{'is-active': isActive}">
<a @click="navigate" @keypress.enter="navigate">
<span class="">Online Services</span>
</a>
</li>
</router-link>
</ul>
</div>