koel/resources/assets/js/components/playlist/PlaylistSidebarItem.vue

176 lines
4.4 KiB
Vue
Raw Normal View History

2022-04-15 14:24:30 +00:00
<template>
<li
:class="['playlist', type, editing ? 'editing' : '', playlist.is_smart ? 'smart' : '']"
data-testid="playlist-sidebar-item"
@dblclick.prevent="makeEditable"
>
2022-04-15 14:24:30 +00:00
<a
2022-05-13 11:46:03 +00:00
v-if="contentEditable"
v-koel-droppable="handleDrop"
2022-04-15 14:24:30 +00:00
:class="{ active }"
:href="url"
2022-04-30 11:55:54 +00:00
@contextmenu.prevent="openContextMenu"
>
2022-07-15 07:23:55 +00:00
<icon v-if="type === 'favorites'" :icon="faHeart" class="text-maroon" fixed-width/>
<icon v-else :icon="faMusic" :mask="faFile" transform="shrink-7 down-2" fixed-width/>
{{ playlist.name }}
</a>
2022-07-15 07:23:55 +00:00
2022-05-13 11:46:03 +00:00
<a v-else :class="{ active }" :href="url" @contextmenu.prevent="openContextMenu">
2022-07-15 07:23:55 +00:00
<icon v-if="type === 'recently-played'" :icon="faClockRotateLeft" class="text-green" fixed-width/>
<icon v-else :icon="faBoltLightning" :mask="faFile" transform="shrink-7 down-2" fixed-width/>
2022-05-13 11:46:03 +00:00
{{ playlist.name }}
</a>
2022-04-15 14:24:30 +00:00
2022-04-15 17:00:08 +00:00
<NameEditor
v-if="nameEditable && editing"
2022-04-15 14:24:30 +00:00
:playlist="playlist"
@cancelled="cancelEditing"
@updated="onPlaylistNameUpdated"
/>
<ContextMenu v-if="hasContextMenu" ref="contextMenu" :playlist="playlist" @edit="makeEditable"/>
2022-04-15 14:24:30 +00:00
</li>
</template>
2022-04-15 17:00:08 +00:00
<script lang="ts" setup>
2022-07-15 07:23:55 +00:00
import { faBoltLightning, faClockRotateLeft, faFile, faHeart, faMusic } from '@fortawesome/free-solid-svg-icons'
2022-04-15 17:00:08 +00:00
import { computed, defineAsyncComponent, nextTick, ref, toRefs } from 'vue'
import { eventBus, pluralize, requireInjection, resolveSongsFromDragEvent } from '@/utils'
2022-06-10 10:47:46 +00:00
import { favoriteStore, playlistStore } from '@/stores'
import router from '@/router'
import { MessageToasterKey } from '@/symbols'
2022-04-15 14:24:30 +00:00
const ContextMenu = defineAsyncComponent(() => import('@/components/playlist/PlaylistContextMenu.vue'))
2022-04-24 08:29:14 +00:00
const NameEditor = defineAsyncComponent(() => import('@/components/playlist/PlaylistNameEditor.vue'))
2022-04-15 14:24:30 +00:00
const toaster = requireInjection(MessageToasterKey)
const contextMenu = ref<InstanceType<typeof ContextMenu>>()
2022-04-15 14:24:30 +00:00
const props = withDefaults(defineProps<{ playlist: Playlist, type?: PlaylistType }>(), { type: 'playlist' })
2022-04-15 17:00:08 +00:00
const { playlist, type } = toRefs(props)
2022-04-15 14:24:30 +00:00
2022-04-15 17:00:08 +00:00
const editing = ref(false)
const active = ref(false)
2022-04-15 14:24:30 +00:00
2022-04-15 17:00:08 +00:00
const url = computed(() => {
switch (type.value) {
case 'playlist':
return `#!/playlist/${playlist.value.id}`
2022-04-15 14:24:30 +00:00
2022-04-15 17:00:08 +00:00
case 'favorites':
return '#!/favorites'
2022-04-15 14:24:30 +00:00
2022-04-15 17:00:08 +00:00
case 'recently-played':
return '#!/recently-played'
2022-04-15 14:24:30 +00:00
2022-04-15 17:00:08 +00:00
default:
throw new Error('Invalid playlist type')
}
})
const nameEditable = computed(() => type.value === 'playlist')
const hasContextMenu = computed(() => type.value === 'playlist')
const contentEditable = computed(() => {
if (playlist.value.is_smart) {
return false
}
return type.value === 'playlist' || type.value === 'favorites'
})
const makeEditable = () => {
if (!nameEditable.value) {
return
}
editing.value = true
}
2022-06-10 10:47:46 +00:00
const handleDrop = async (event: DragEvent) => {
2022-04-15 17:00:08 +00:00
if (!contentEditable.value) {
return false
}
2022-06-10 10:47:46 +00:00
const songs = await resolveSongsFromDragEvent(event)
2022-04-15 17:00:08 +00:00
if (!songs.length) {
return false
}
if (type.value === 'favorites') {
2022-06-10 10:47:46 +00:00
await favoriteStore.like(songs)
2022-04-15 17:00:08 +00:00
} else if (type.value === 'playlist') {
2022-07-04 10:39:02 +00:00
await playlistStore.addSongs(playlist.value, songs)
toaster.value.success(`Added ${pluralize(songs.length, 'song')} into "${playlist.value.name}."`)
2022-04-15 17:00:08 +00:00
}
return false
}
const openContextMenu = async (event: MouseEvent) => {
if (hasContextMenu.value) {
await nextTick()
router.go(`/playlist/${playlist.value.id}`)
contextMenu.value?.open(event.pageY, event.pageX, { playlist })
2022-04-15 17:00:08 +00:00
}
}
const cancelEditing = () => (editing.value = false)
const onPlaylistNameUpdated = (name: string) => {
playlist.value.name = name
2022-04-15 17:00:08 +00:00
editing.value = false
}
eventBus.on('LOAD_MAIN_CONTENT', (view: MainViewName, _playlist: Playlist): void => {
switch (view) {
case 'Favorites':
active.value = type.value === 'favorites'
break
case 'RecentlyPlayed':
active.value = type.value === 'recently-played'
break
case 'Playlist':
active.value = playlist.value === _playlist
break
default:
active.value = false
break
2022-04-15 14:24:30 +00:00
}
})
</script>
<style lang="scss" scoped>
.playlist {
user-select: none;
overflow: hidden;
2022-04-15 14:24:30 +00:00
a {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
2022-07-16 16:37:17 +00:00
display: block;
2022-04-15 14:24:30 +00:00
span {
pointer-events: none;
}
}
input {
width: calc(100% - 32px);
margin: 5px 16px;
}
&.editing {
a {
display: none !important;
}
}
}
</style>