mirror of
https://github.com/koel/koel
synced 2024-12-25 12:03:06 +00:00
190 lines
5.6 KiB
Vue
190 lines
5.6 KiB
Vue
<template>
|
|
<div ref="el" class="song-list-controls" data-testid="song-list-controls">
|
|
<div class="wrapper">
|
|
<BtnGroup uppercased>
|
|
<template v-if="altPressed">
|
|
<Btn
|
|
v-if="selectedSongs.length < 2 && songs.length"
|
|
class="btn-play-all"
|
|
orange
|
|
title="Play all songs"
|
|
@click.prevent="playAll"
|
|
>
|
|
<icon :icon="faPlay" fixed-width />
|
|
All
|
|
</Btn>
|
|
|
|
<Btn
|
|
v-if="selectedSongs.length > 1"
|
|
class="btn-play-selected"
|
|
orange
|
|
title="Play selected songs"
|
|
@click.prevent="playSelected"
|
|
>
|
|
<icon :icon="faPlay" fixed-width />
|
|
Selected
|
|
</Btn>
|
|
</template>
|
|
|
|
<template v-else>
|
|
<Btn
|
|
v-if="selectedSongs.length < 2 && songs.length"
|
|
class="btn-shuffle-all"
|
|
data-testid="btn-shuffle-all"
|
|
orange
|
|
title="Shuffle all songs"
|
|
@click.prevent="shuffle"
|
|
>
|
|
<icon :icon="faRandom" fixed-width />
|
|
All
|
|
</Btn>
|
|
|
|
<Btn
|
|
v-if="selectedSongs.length > 1"
|
|
class="btn-shuffle-selected"
|
|
data-testid="btn-shuffle-selected"
|
|
orange
|
|
title="Shuffle selected songs"
|
|
@click.prevent="shuffleSelected"
|
|
>
|
|
<icon :icon="faRandom" fixed-width />
|
|
Selected
|
|
</Btn>
|
|
</template>
|
|
|
|
<Btn
|
|
v-if="showAddToButton"
|
|
ref="addToButton"
|
|
green @click.prevent.stop="toggleAddToMenu"
|
|
>
|
|
{{ showingAddToMenu ? 'Cancel' : 'Add To…' }}
|
|
</Btn>
|
|
|
|
<Btn v-if="config.clearQueue" red title="Clear current queue" @click.prevent="clearQueue">Clear</Btn>
|
|
</BtnGroup>
|
|
|
|
<BtnGroup v-if="config.refresh || config.deletePlaylist">
|
|
<Btn v-if="config.refresh" v-koel-tooltip green title="Refresh" @click.prevent="refresh">
|
|
<icon :icon="faRotateRight" fixed-width />
|
|
</Btn>
|
|
|
|
<Btn
|
|
v-if="config.deletePlaylist"
|
|
v-koel-tooltip
|
|
class="del btn-delete-playlist"
|
|
red
|
|
title="Delete this playlist"
|
|
@click.prevent="deletePlaylist"
|
|
>
|
|
<icon :icon="faTrashCan" />
|
|
</Btn>
|
|
</BtnGroup>
|
|
|
|
<BtnGroup v-if="config.filter && songs.length">
|
|
<SongListFilter @change="filter" />
|
|
</BtnGroup>
|
|
</div>
|
|
|
|
<div ref="addToMenu" v-koel-clickaway="closeAddToMenu" class="menu-wrapper">
|
|
<AddToMenu :config="config.addTo" :songs="selectedSongs" @closing="closeAddToMenu" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { faPlay, faRandom, faRotateRight, faTrashCan } from '@fortawesome/free-solid-svg-icons'
|
|
import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue'
|
|
import { SelectedSongsKey, SongsKey } from '@/symbols'
|
|
import { requireInjection } from '@/utils'
|
|
import { useFloatingUi, useSongListControls } from '@/composables'
|
|
|
|
import AddToMenu from '@/components/song/AddToMenu.vue'
|
|
import Btn from '@/components/ui/Btn.vue'
|
|
import BtnGroup from '@/components/ui/BtnGroup.vue'
|
|
|
|
const SongListFilter = defineAsyncComponent(() => import('@/components/song/SongListFilter.vue'))
|
|
|
|
const config = useSongListControls().getSongListControlsConfig()
|
|
|
|
const [songs] = requireInjection<[Ref<Song[]>]>(SongsKey)
|
|
const [selectedSongs] = requireInjection(SelectedSongsKey)
|
|
|
|
const el = ref<HTMLElement>()
|
|
const addToButton = ref<InstanceType<typeof Btn>>()
|
|
const addToMenu = ref<HTMLDivElement>()
|
|
const showingAddToMenu = ref(false)
|
|
const altPressed = ref(false)
|
|
|
|
const showAddToButton = computed(() => Boolean(selectedSongs.value.length))
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'playAll' | 'playSelected', shuffle: boolean): void,
|
|
(e: 'filter', keywords: string): void,
|
|
(e: 'clearQueue' | 'deletePlaylist' | 'refresh'): void,
|
|
}>()
|
|
|
|
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 refresh = () => emit('refresh')
|
|
const filter = (keywords: string) => emit('filter', keywords)
|
|
const registerKeydown = (event: KeyboardEvent) => event.key === 'Alt' && (altPressed.value = true)
|
|
const registerKeyup = (event: KeyboardEvent) => event.key === 'Alt' && (altPressed.value = false)
|
|
|
|
let usedFloatingUi: ReturnType<typeof useFloatingUi>
|
|
|
|
watch(showAddToButton, async showingButton => {
|
|
await nextTick()
|
|
|
|
if (showingButton) {
|
|
usedFloatingUi = useFloatingUi(addToButton.value!.button!, addToMenu, { autoTrigger: false })
|
|
usedFloatingUi.setup()
|
|
} else {
|
|
usedFloatingUi?.teardown()
|
|
}
|
|
}, { immediate: true })
|
|
|
|
const closeAddToMenu = () => {
|
|
usedFloatingUi?.hide()
|
|
showingAddToMenu.value = false
|
|
}
|
|
|
|
const toggleAddToMenu = () => {
|
|
showingAddToMenu.value ? usedFloatingUi?.hide() : usedFloatingUi?.show()
|
|
showingAddToMenu.value = !showingAddToMenu.value
|
|
}
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('keydown', registerKeydown)
|
|
window.addEventListener('keyup', registerKeyup)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
window.removeEventListener('keydown', registerKeydown)
|
|
window.removeEventListener('keyup', registerKeyup)
|
|
|
|
usedFloatingUi?.teardown()
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.song-list-controls {
|
|
position: relative;
|
|
|
|
.wrapper {
|
|
display: flex;
|
|
gap: .5rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.menu-wrapper {
|
|
@include context-menu();
|
|
|
|
padding: 0;
|
|
display: none;
|
|
}
|
|
}
|
|
</style>
|