2022-04-15 14:24:30 +00:00
|
|
|
<template>
|
2022-05-09 15:09:55 +00:00
|
|
|
<div class="song-list-controls" data-testid="song-list-controls" ref="el">
|
2022-04-15 17:00:08 +00:00
|
|
|
<BtnGroup uppercased>
|
2022-04-15 14:24:30 +00:00
|
|
|
<template v-if="mergedConfig.play">
|
|
|
|
<template v-if="altPressed">
|
2022-04-15 17:00:08 +00:00
|
|
|
<Btn
|
2022-04-21 16:06:45 +00:00
|
|
|
v-if="selectedSongs.length < 2 && songs.length"
|
2022-04-15 14:24:30 +00:00
|
|
|
class="btn-play-all"
|
|
|
|
orange
|
|
|
|
title="Play all songs"
|
2022-04-21 16:06:45 +00:00
|
|
|
@click.prevent="playAll"
|
2022-04-15 14:24:30 +00:00
|
|
|
>
|
|
|
|
<i class="fa fa-play"></i> All
|
2022-04-15 17:00:08 +00:00
|
|
|
</Btn>
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
<Btn
|
2022-04-21 16:06:45 +00:00
|
|
|
v-if="selectedSongs.length > 1"
|
2022-04-15 14:24:30 +00:00
|
|
|
class="btn-play-selected"
|
|
|
|
orange
|
|
|
|
title="Play selected songs"
|
2022-04-21 16:06:45 +00:00
|
|
|
@click.prevent="playSelected"
|
2022-04-15 14:24:30 +00:00
|
|
|
>
|
|
|
|
<i class="fa fa-play"></i> Selected
|
2022-04-15 17:00:08 +00:00
|
|
|
</Btn>
|
2022-04-15 14:24:30 +00:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<template v-else>
|
2022-04-15 17:00:08 +00:00
|
|
|
<Btn
|
2022-04-21 16:06:45 +00:00
|
|
|
v-if="selectedSongs.length < 2 && songs.length"
|
2022-04-15 14:24:30 +00:00
|
|
|
class="btn-shuffle-all"
|
2022-05-09 15:09:55 +00:00
|
|
|
data-testid="btn-shuffle-all"
|
2022-04-15 14:24:30 +00:00
|
|
|
orange
|
|
|
|
title="Shuffle all songs"
|
2022-04-21 16:06:45 +00:00
|
|
|
@click.prevent="shuffle"
|
2022-04-15 14:24:30 +00:00
|
|
|
>
|
|
|
|
<i class="fa fa-random"></i> All
|
2022-04-15 17:00:08 +00:00
|
|
|
</Btn>
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
<Btn
|
2022-04-21 16:06:45 +00:00
|
|
|
v-if="selectedSongs.length > 1"
|
2022-04-15 14:24:30 +00:00
|
|
|
class="btn-shuffle-selected"
|
2022-05-10 23:01:48 +00:00
|
|
|
data-testid="btn-shuffle-selected"
|
2022-04-15 14:24:30 +00:00
|
|
|
orange
|
|
|
|
title="Shuffle selected songs"
|
2022-04-21 16:06:45 +00:00
|
|
|
@click.prevent="shuffleSelected"
|
2022-04-15 14:24:30 +00:00
|
|
|
>
|
|
|
|
<i class="fa fa-random"></i> Selected
|
2022-04-15 17:00:08 +00:00
|
|
|
</Btn>
|
2022-04-15 14:24:30 +00:00
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
<Btn
|
2022-05-09 15:09:55 +00:00
|
|
|
v-if="selectedSongs.length"
|
2022-04-15 14:24:30 +00:00
|
|
|
:title="`${showingAddToMenu ? 'Cancel' : 'Add selected songs to…'}`"
|
|
|
|
class="btn-add-to"
|
2022-05-10 23:01:48 +00:00
|
|
|
data-testid="add-to-btn"
|
2022-05-09 15:09:55 +00:00
|
|
|
green
|
|
|
|
@click.prevent.stop="toggleAddToMenu"
|
2022-04-15 14:24:30 +00:00
|
|
|
>
|
|
|
|
{{ showingAddToMenu ? 'Cancel' : 'Add To…' }}
|
2022-04-15 17:00:08 +00:00
|
|
|
</Btn>
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-05-09 15:09:55 +00:00
|
|
|
<Btn v-if="showClearQueueButton" red title="Clear current queue" @click.prevent="clearQueue">Clear</Btn>
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
<Btn
|
2022-04-21 16:06:45 +00:00
|
|
|
v-if="showDeletePlaylistButton"
|
2022-04-15 14:24:30 +00:00
|
|
|
class="del btn-delete-playlist"
|
|
|
|
red
|
|
|
|
title="Delete this playlist"
|
2022-04-21 16:06:45 +00:00
|
|
|
@click.prevent="deletePlaylist"
|
2022-04-15 14:24:30 +00:00
|
|
|
>
|
|
|
|
<i class="fa fa-times"></i> Playlist
|
2022-04-15 17:00:08 +00:00
|
|
|
</Btn>
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
</BtnGroup>
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
<AddToMenu
|
2022-04-21 16:06:45 +00:00
|
|
|
v-koel-clickaway="closeAddToMenu"
|
2022-04-15 14:24:30 +00:00
|
|
|
:config="mergedConfig.addTo"
|
|
|
|
:showing="showingAddToMenu"
|
2022-04-21 16:06:45 +00:00
|
|
|
:songs="selectedSongs"
|
|
|
|
@closing="closeAddToMenu"
|
2022-04-15 14:24:30 +00:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
<script lang="ts" setup>
|
|
|
|
import { computed, defineAsyncComponent, nextTick, onMounted, onUnmounted, ref, toRefs } from 'vue'
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-20 10:20:09 +00:00
|
|
|
const AddToMenu = defineAsyncComponent(() => import('./AddToMenu.vue'))
|
2022-04-21 18:39:18 +00:00
|
|
|
const Btn = defineAsyncComponent(() => import('@/components/ui/Btn.vue'))
|
2022-04-21 16:06:45 +00:00
|
|
|
const BtnGroup = defineAsyncComponent(() => import('@/components/ui/BtnGroup.vue'))
|
|
|
|
|
|
|
|
const props = withDefaults(
|
2022-05-09 14:27:17 +00:00
|
|
|
defineProps<{ songs?: Song[], selectedSongs?: Song[], config?: Partial<SongListControlsConfig> }>(),
|
2022-04-21 16:06:45 +00:00
|
|
|
{
|
|
|
|
songs: () => [],
|
|
|
|
selectedSongs: () => [],
|
|
|
|
config: () => ({})
|
|
|
|
}
|
|
|
|
)
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
const { config, songs, selectedSongs } = toRefs(props)
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-21 16:06:45 +00:00
|
|
|
const el = ref<HTMLElement>()
|
2022-04-15 17:00:08 +00:00
|
|
|
const showingAddToMenu = ref(false)
|
|
|
|
const altPressed = ref(false)
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
const mergedConfig = computed((): SongListControlsConfig => Object.assign({
|
|
|
|
play: true,
|
|
|
|
addTo: {
|
|
|
|
queue: true,
|
|
|
|
favorites: true,
|
|
|
|
playlists: true,
|
|
|
|
newPlaylist: true
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
2022-04-15 17:00:08 +00:00
|
|
|
clearQueue: false,
|
|
|
|
deletePlaylist: false
|
2022-04-21 16:06:45 +00:00
|
|
|
}, config.value)
|
2022-04-15 17:00:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const showClearQueueButton = computed(() => mergedConfig.value.clearQueue)
|
|
|
|
const showDeletePlaylistButton = computed(() => mergedConfig.value.deletePlaylist)
|
|
|
|
|
|
|
|
const emit = defineEmits(['playAll', 'playSelected', 'clearQueue', 'deletePlaylist'])
|
|
|
|
|
|
|
|
const shuffle = () => emit('playAll', true)
|
|
|
|
const shuffleSelected = () => emit('playSelected', true)
|
|
|
|
const playAll = () => emit('playAll', false)
|
|
|
|
const playSelected = () => emit('playSelected', false)
|
|
|
|
const clearQueue = () => emit('clearQueue')
|
|
|
|
const deletePlaylist = () => emit('deletePlaylist')
|
|
|
|
const closeAddToMenu = () => (showingAddToMenu.value = false)
|
2022-04-21 16:28:12 +00:00
|
|
|
const registerKeydown = (event: KeyboardEvent) => event.key === 'Alt' && (altPressed.value = true)
|
|
|
|
const registerKeyup = (event: KeyboardEvent) => event.key === 'Alt' && (altPressed.value = false)
|
2022-04-15 17:00:08 +00:00
|
|
|
|
|
|
|
const toggleAddToMenu = async () => {
|
|
|
|
showingAddToMenu.value = !showingAddToMenu.value
|
|
|
|
|
|
|
|
if (!showingAddToMenu.value) {
|
|
|
|
return
|
|
|
|
}
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
await nextTick()
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-21 16:06:45 +00:00
|
|
|
const btnAddTo = el.value?.querySelector<HTMLButtonElement>('.btn-add-to')!
|
2022-04-15 17:00:08 +00:00
|
|
|
const { left: btnLeft, bottom: btnBottom, width: btnWidth } = btnAddTo.getBoundingClientRect()
|
2022-04-21 16:06:45 +00:00
|
|
|
const contextMenu = el.value?.querySelector<HTMLElement>('.add-to')!
|
2022-04-15 17:00:08 +00:00
|
|
|
const menuWidth = contextMenu.getBoundingClientRect().width
|
|
|
|
contextMenu.style.top = `${btnBottom + 10}px`
|
|
|
|
contextMenu.style.left = `${btnLeft + btnWidth / 2 - menuWidth / 2}px`
|
|
|
|
}
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
onMounted(() => {
|
|
|
|
window.addEventListener('keydown', registerKeydown)
|
|
|
|
window.addEventListener('keyup', registerKeyup)
|
|
|
|
})
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
onUnmounted(() => {
|
|
|
|
window.removeEventListener('keydown', registerKeydown)
|
|
|
|
window.removeEventListener('keyup', registerKeyup)
|
2022-04-15 14:24:30 +00:00
|
|
|
})
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.song-list-controls {
|
|
|
|
position: relative;
|
|
|
|
}
|
|
|
|
</style>
|