Files
site/resources/js/Shared/AudioPlayer.vue

316 lines
9.8 KiB
Vue
Executable File

<template>
<div class="col-span-7 md:col-span-5 lg:col-span-3 player-widget contain-content">
<div
ref="topbar"
class="
player-line
rounded-md
transition-colors
bg-transparent
hover:bg-orange-min
flex
items-center
space-x-3
p-2
cursor-pointer
"
@click="openFull"
>
<button class="focus:outline-none text-gray-600 hover:text-gray-800" @click.stop="skipTrack('prev')">
<svg
class="w-4 h-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="19 20 9 12 19 4 19 20"></polygon>
<line x1="5" y1="19"
x2="5" y2="5"
></line>
</svg>
</button>
<button
class="
flex
h-8
items-center
justify-center
w-8
transition-colors
text-gray-600
hover:text-gray-800
"
@click.stop="toggleAudio"
>
<PlayIcon v-show="!playing" class="w-8 h-8" />
<StopIcon v-show="playing" class="w-8 h-8" />
</button>
<button class="focus:outline-none text-gray-600 hover:text-gray-800" @click.stop="skipTrack('next')">
<svg
class="w-4 h-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="5 4 15 12 5 20 5 4"></polygon>
<line x1="19" y1="5"
x2="19" y2="19"
></line>
</svg>
</button>
<div v-show="count_playlist" class="text-base truncate select-none">
{{ currentSong.name }}
</div>
</div>
<teleport to="body">
<div v-if="open_drop">
<div
style="
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: 9998;
background: black;
opacity: 0.2;
"
@click="open_drop = false"
/>
<div
ref="dropdown"
class="z-[9999] player-widget-list w-full lg:w-[48rem] "
>
<div class="shadow-classic rounded-md bg-indigo-200">
<div
v-show="count_playlist"
class="overflow-hidden bg-orange relative h-2 mt-2 mb-2"
@click.prevent="updateSeek"
>
<div
:style="{ transform: `translate3d(${playerProgress}, 0, 0)` }"
class="will-change-transform absolute transition-transform h-full bg-white flex items-center justify-end w-full"
>
</div>
</div>
<div
class="
p-2
lg:p-4
rounded-md
items-center
grid grid-cols-1
md:grid-cols-10
cursor-pointer
"
>
<div class="col-span-7 flex items-center space-x-3">
<button class="focus:outline-none text-gray" @click.stop="skipTrack('prev')">
<svg
class="w-4 h-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="19 20 9 12 19 4 19 20"></polygon>
<line x1="5" y1="19"
x2="5" y2="5"
></line>
</svg>
</button>
<button
class="
flex
h-8
items-center
justify-center
w-8
transition-colors
text-gray
"
@click.stop="toggleAudio"
>
<PlayIcon v-show="!playing" class="w-8 h-8" />
<StopIcon v-show="playing" class="w-8 h-8" />
</button>
<button class="focus:outline-none text-gray" @click.stop="skipTrack('next')">
<svg
class="w-4 h-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="5 4 15 12 5 20 5 4"></polygon>
<line x1="19" y1="5"
x2="19" y2="19"
></line>
</svg>
</button>
<div class="text-gray-light text-base truncate select-none">
{{ currentSong.name }}
</div>
</div>
<div v-show="count_playlist" class="col-span-1 text-center text-gray-light text-xs select-none">
{{ seek }}
</div>
<div v-show="count_playlist" class="col-span-2 mt-2 md:mt-0 md:ml-auto flex items-center">
<input v-model="volume" class="w-full custom-input-range"
type="range" max="1"
step="0.1" @click.stop=""
@input="updateVolume(volume)"
/>
<span class="text-gray-light ml-1.5 text-xs">{{ volume * 100 + "%" }}</span>
</div>
</div>
<div class="choices-tabs">
<div class="border-b border-indigo-300">
<nav class="-mb-px flex" aria-label="Tabs">
<a v-for="tab in tabs" :key="tab.key"
href="#" :class="[
current_tab === tab.key ? 'border-orange text-white' : 'border-transparent text-gray-light hover:text-white',
'w-1/2 lg:w-1/4 py-3 lg:py-4 px-1 text-center text-sm border-b-2',
]"
@click.prevent="changeTab(tab.key)"
>{{ tab.name }}</a>
</nav>
</div>
</div>
<div>
<keep-alive>
<component :is="currentTabComponent"></component>
</keep-alive>
</div>
</div>
</div>
</div>
</teleport>
</div>
</template>
<script>
import Popper from 'popper.js'
import { PlayIcon, StopIcon } from '@heroicons/vue/solid'
import { mapActions, mapGetters, mapState } from 'vuex'
import AudioTabNow from '@/Shared/Tabs/AudioTabNow.vue'
import AudioTabFavorite from '@/Shared/Tabs/AudioTabFavorite.vue'
import AudioTabLoaded from '@/Shared/Tabs/AudioTabLoaded.vue'
export default {
name: 'Player',
components: {
AudioTabNow,
AudioTabLoaded,
AudioTabFavorite,
PlayIcon,
StopIcon
},
data() {
return {
tabs: [
{key: 'now', name: 'Текущая'},
{key: 'loaded', name: 'Загруженная'},
{key: 'favorite', name: 'Понравившийся'},
],
open_drop: false,
popper: null,
volume: 0.5,
}
},
computed: {
currentTabComponent() {
return 'audio-tab-' + this.current_tab
},
...mapGetters(['playing', 'count_playlist', 'volume_step']),
...mapState({
current_tab: (state) => state.player.tab,
seek: (state) => state.player.seek,
duration: (state) => state.player.duration,
playerProgress: (state) => state.player.playerProgress,
currentSong: (state) => state.player.currentSong,
}),
},
watch: {
open_drop(open_drop) {
if (open_drop) {
this.$nextTick(() => {
if (window.matchMedia('(min-width: 1024px)').matches) {
this.popper = new Popper(this.$refs.topbar, this.$refs.dropdown, {
placement: 'bottom-start',
modifiers: {
preventOverflow: { boundariesElement: 'scrollParent' },
},
})
}else {
this.popper = new Popper(this.$refs.topbar, this.$refs.dropdown, {
placement: 'bottom-end',
modifiers: {
preventOverflow: {
boundariesElement: 'scrollParent',
padding: 0,
},
offset: {
enabled: true,
offset: '0, 10'
}
},
})
}
})
} else if (this.popper) {
setTimeout(() => this.popper.destroy(), 100)
}
},
},
created() {
this.loadExsitPlaylist()
this.changeVolume(this.volume_step)
this.volume = this.volume_step
},
methods: {
...mapActions([
'toggleAudio',
'updateSeek',
'skipTrack',
'changeVolume',
'tabName',
'loadExsitPlaylist',
]),
openFull() {
this.open_drop = !this.open_drop
},
updateVolume(volume) {
this.changeVolume(volume)
},
changeTab(name){
this.tabName(name)
},
},
}
</script>