Browse Source

refactor: Started implementing custom draggable on import album

Owen Diffey 2 years ago
parent
commit
79f3730c12

+ 17 - 12
frontend/src/components/Draggable.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { PropType, watch, onMounted, ref } from "vue";
+import { PropType, Slot as SlotType, watch, onMounted, ref } from "vue";
 
 const props = defineProps({
 	itemKey: { type: String, default: "" },
@@ -26,7 +26,7 @@ const listUuid = ref(
 	})
 );
 const mounted = ref(false);
-const data = ref([]);
+const data = ref([] as any[]);
 
 watch(
 	() => props.list,
@@ -42,9 +42,10 @@ onMounted(() => {
 
 const emit = defineEmits(["update:list", "start", "end", "update"]);
 
-const itemOnMove = index => {
+const itemOnMove = (index: number) => {
 	// Deletes the remove function for the dragging element
-	delete window.draggingItem.itemOnMove;
+	if (window.draggingItem && window.draggingItem.itemOnMove)
+		delete window.draggingItem.itemOnMove;
 	// Remove the item from the current list and return it
 	const listItem = data.value.splice(index, 1)[0];
 	emit("update:list", data.value);
@@ -55,7 +56,7 @@ const itemOnMove = index => {
 const onDragStart = (itemIndex: number, event: DragEvent) => {
 	const { draggable } = event.target as HTMLElement;
 
-	if (props.disabled === true || !draggable) {
+	if (props.disabled === true || !draggable || !event.dataTransfer) {
 		event.preventDefault();
 		return;
 	}
@@ -79,14 +80,14 @@ const onDragStart = (itemIndex: number, event: DragEvent) => {
 
 // When a dragging element hovers over another draggable element, this gets triggered, usually many times in a second
 const onDragOver = (itemIndex: number, event: DragEvent) => {
-	const getDraggableElement = element =>
+	const getDraggableElement = (element: any): any =>
 		element.classList.contains("draggable-item")
 			? element
 			: getDraggableElement(element.parentElement);
 	const draggableElement = getDraggableElement(event.target);
 	const { draggable } = draggableElement;
 
-	if (props.disabled === true || !draggable) return;
+	if (props.disabled === true || !draggable || !window.draggingItem) return;
 
 	// The index and list uuid of the item that is being dragged, stored in window since it can come from another list as well
 	const fromIndex = window.draggingItem.itemIndex;
@@ -113,7 +114,7 @@ const onDragOver = (itemIndex: number, event: DragEvent) => {
 	window.draggingItem.itemListUuid = listUuid.value;
 
 	// If the item comes from another list
-	if (toList !== fromList) {
+	if (toList !== fromList && window.draggingItem.itemOnMove) {
 		// Call the remove function from the dragging element, which removes the item from the previous list and returns it
 		const item = window.draggingItem.itemOnMove(fromIndex);
 		// Define a new remove function for the dragging element
@@ -138,6 +139,7 @@ const onDragEnd = () => {
 // Gets called when an element is dropped on another element
 const onDrop = () => {
 	// Emits the update event to parent component, indicating that the order is now done and ordering/moving is done
+	if (!window.draggingItem) return;
 	const { itemIndex, itemListUuid, initialItemIndex, initialItemListUuid } =
 		window.draggingItem;
 	if (itemListUuid === initialItemListUuid)
@@ -161,7 +163,7 @@ const convertAttributes = (item: any) =>
 		])
 	);
 
-const hasSlotContent = (slot, slotProps = {}) => {
+const hasSlotContent = (slot: SlotType | undefined, slotProps = {}) => {
 	if (!slot) return false;
 
 	return slot(slotProps).some(vnode => {
@@ -186,7 +188,7 @@ const hasSlotContent = (slot, slotProps = {}) => {
 <template>
 	<template v-for="(item, itemIndex) in data" :key="item[itemKey]">
 		<component
-			v-if="hasSlotContent($slots.item, { element: item })"
+			v-if="$slots.item && hasSlotContent($slots.item, { element: item })"
 			:is="tag"
 			:draggable="
 				typeof disabled === 'function' ? !disabled(item) : !disabled
@@ -206,8 +208,11 @@ const hasSlotContent = (slot, slotProps = {}) => {
 	</template>
 </template>
 
-<style>
-.draggable-item .is-draggable {
+<style scoped>
+.draggable-item[draggable="true"] {
 	cursor: move;
 }
+.draggable-item:not(:last-of-type) {
+	margin-bottom: 10px;
+}
 </style>

+ 2 - 6
frontend/src/components/PlaylistTabBase.vue

@@ -820,10 +820,7 @@ onMounted(() => {
 						@update="savePlaylistOrder"
 					>
 						<template #item="{ element }">
-							<playlist-item
-								class="is-draggable"
-								:playlist="element"
-							>
+							<playlist-item :playlist="element">
 								<template #item-icon>
 									<i
 										class="material-icons blacklisted-icon"
@@ -1028,8 +1025,7 @@ onMounted(() => {
 		.tab {
 			padding: 15px 0;
 			border-radius: 0;
-			.playlist-item:not(:last-of-type),
-			:deep(.draggable-item:not(:last-of-type)) {
+			.playlist-item:not(:last-of-type) {
 				margin-bottom: 10px;
 			}
 			.load-more-button {

+ 0 - 4
frontend/src/components/Queue.vue

@@ -219,10 +219,6 @@ onUpdated(() => {
 		max-height: 100%;
 	}
 
-	:deep(.draggable-item:not(:last-of-type)) {
-		margin-bottom: 10px;
-	}
-
 	#queue-locked {
 		display: flex;
 		justify-content: center;

+ 0 - 8
frontend/src/components/modals/EditPlaylist/index.vue

@@ -671,13 +671,5 @@ onBeforeUnmount(() => {
 			}
 		}
 	}
-
-	.right-section {
-		#rearrange-songs-section {
-			:deep(.draggable-item:not(:last-of-type)) {
-				margin-bottom: 10px;
-			}
-		}
-	}
 }
 </style>

+ 57 - 73
frontend/src/components/modals/ImportAlbum.vue

@@ -1,5 +1,4 @@
 <script setup lang="ts">
-// TODO - Fix sortable
 import {
 	defineAsyncComponent,
 	ref,
@@ -8,7 +7,6 @@ import {
 	onBeforeUnmount
 } from "vue";
 import Toast from "toasters";
-import { Sortable } from "sortablejs-vue3";
 import { storeToRefs } from "pinia";
 import { useWebsocketsStore } from "@/stores/websockets";
 import { useModalsStore } from "@/stores/modals";
@@ -19,6 +17,9 @@ const Modal = defineAsyncComponent(() => import("@/components/Modal.vue"));
 const SongItem = defineAsyncComponent(
 	() => import("@/components/SongItem.vue")
 );
+const Draggable = defineAsyncComponent(
+	() => import("@/components/Draggable.vue")
+);
 
 const props = defineProps({
 	modalUuid: { type: String, default: "" }
@@ -56,7 +57,6 @@ const discogs = ref({
 	disableLoadMore: false
 });
 const discogsTabs = ref([]);
-const sortableUpdateNumber = ref(0);
 
 // TODO might not not be needed anymore, might be able to directly edit prefillDiscogs
 const localPrefillDiscogs = computed({
@@ -333,60 +333,52 @@ const updateTrackSong = updatedSong => {
 	});
 };
 
-const updatePlaylistSongPosition = ({ oldIndex, newIndex }) => {
-	if (oldIndex === newIndex) return;
-	const oldSongs = playlistSongs.value;
-	oldSongs.splice(newIndex, 0, oldSongs.splice(oldIndex, 1)[0]);
-	playlistSongs.value = oldSongs;
-};
-
-const updateTrackSongPosition = (trackIndex, { oldIndex, newIndex }) => {
-	if (oldIndex === newIndex) return;
-	const oldSongs = trackSongs.value[trackIndex];
-	oldSongs.splice(newIndex, 0, oldSongs.splice(oldIndex, 1)[0]);
-	trackSongs.value[trackIndex] = oldSongs;
-};
-
-const playlistSongAdded = event => {
-	const fromTrack = event.from;
-	const fromTrackIndex = Number(fromTrack.dataset.trackIndex);
-	const song = trackSongs.value[fromTrackIndex][event.oldIndex];
-	const newPlaylistSongs = JSON.parse(JSON.stringify(playlistSongs.value));
-	newPlaylistSongs.splice(event.newIndex, 0, song);
-	playlistSongs.value = newPlaylistSongs;
-
-	sortableUpdateNumber.value += 1;
-};
-
-const playlistSongRemoved = event => {
-	playlistSongs.value.splice(event.oldIndex, 1);
-
-	sortableUpdateNumber.value += 1;
-};
-
-const trackSongAdded = (trackIndex, event) => {
-	const fromElement = event.from;
-	let song = null;
-	if (fromElement.dataset.trackIndex) {
-		const fromTrackIndex = Number(fromElement.dataset.trackIndex);
-		song = trackSongs.value[fromTrackIndex][event.oldIndex];
-	} else {
-		song = playlistSongs.value[event.oldIndex];
-	}
-	const newTrackSongs = JSON.parse(
-		JSON.stringify(trackSongs.value[trackIndex])
-	);
-	newTrackSongs.splice(event.newIndex, 0, song);
-	trackSongs.value[trackIndex] = newTrackSongs;
-
-	sortableUpdateNumber.value += 1;
-};
-
-const trackSongRemoved = (trackIndex, event) => {
-	trackSongs.value[trackIndex].splice(event.oldIndex, 1);
-
-	sortableUpdateNumber.value += 1;
-};
+// const updatePlaylistSongPosition = ({ oldIndex, newIndex }) => {
+// 	if (oldIndex === newIndex) return;
+// 	const oldSongs = playlistSongs.value;
+// 	oldSongs.splice(newIndex, 0, oldSongs.splice(oldIndex, 1)[0]);
+// 	playlistSongs.value = oldSongs;
+// };
+
+// const updateTrackSongPosition = (trackIndex, { oldIndex, newIndex }) => {
+// 	if (oldIndex === newIndex) return;
+// 	const oldSongs = trackSongs.value[trackIndex];
+// 	oldSongs.splice(newIndex, 0, oldSongs.splice(oldIndex, 1)[0]);
+// 	trackSongs.value[trackIndex] = oldSongs;
+// };
+
+// const playlistSongAdded = event => {
+// 	const fromTrack = event.from;
+// 	const fromTrackIndex = Number(fromTrack.dataset.trackIndex);
+// 	const song = trackSongs.value[fromTrackIndex][event.oldIndex];
+// 	const newPlaylistSongs = JSON.parse(JSON.stringify(playlistSongs.value));
+// 	newPlaylistSongs.splice(event.newIndex, 0, song);
+// 	playlistSongs.value = newPlaylistSongs;
+// };
+
+// const playlistSongRemoved = event => {
+// 	playlistSongs.value.splice(event.oldIndex, 1);
+// };
+
+// const trackSongAdded = (trackIndex, event) => {
+// 	const fromElement = event.from;
+// 	let song = null;
+// 	if (fromElement.dataset.trackIndex) {
+// 		const fromTrackIndex = Number(fromElement.dataset.trackIndex);
+// 		song = trackSongs.value[fromTrackIndex][event.oldIndex];
+// 	} else {
+// 		song = playlistSongs.value[event.oldIndex];
+// 	}
+// 	const newTrackSongs = JSON.parse(
+// 		JSON.stringify(trackSongs.value[trackIndex])
+// 	);
+// 	newTrackSongs.splice(event.newIndex, 0, song);
+// 	trackSongs.value[trackIndex] = newTrackSongs;
+// };
+
+// const trackSongRemoved = (trackIndex, event) => {
+// 	trackSongs.value[trackIndex].splice(event.oldIndex, 1);
+// };
 
 onMounted(() => {
 	ws.onConnect(init);
@@ -657,15 +649,11 @@ onBeforeUnmount(() => {
 					>
 						Reset
 					</button>
-					<sortable
-						:key="`${sortableUpdateNumber}-playlistSongs`"
+					<draggable
 						v-if="playlistSongs.length > 0"
-						:list="playlistSongs"
+						v-model:list="playlistSongs"
 						item-key="_id"
-						:options="{ group: 'songs' }"
-						@update="updatePlaylistSongPosition"
-						@add="playlistSongAdded"
-						@remove="playlistSongRemoved"
+						:group="`import-album-${modalUuid}-songs`"
 					>
 						<template #item="{ element }">
 							<song-item
@@ -674,7 +662,7 @@ onBeforeUnmount(() => {
 							>
 							</song-item>
 						</template>
-					</sortable>
+					</draggable>
 				</div>
 				<div
 					class="track-boxes"
@@ -689,16 +677,12 @@ onBeforeUnmount(() => {
 							<span>{{ track.position }}.</span>
 							<p>{{ track.title }}</p>
 						</div>
-						<sortable
-							:key="`${sortableUpdateNumber}-${index}-playlistSongs`"
+						<draggable
 							class="track-box-songs-drag-area"
-							:list="trackSongs[index]"
+							v-model:list="trackSongs[index]"
 							:data-track-index="index"
 							item-key="_id"
-							:options="{ group: 'songs' }"
-							@update="updateTrackSongPosition(index, $event)"
-							@add="trackSongAdded(index, $event)"
-							@remove="trackSongRemoved(index, $event)"
+							:group="`import-album-${modalUuid}-songs`"
 						>
 							<template #item="{ element }">
 								<song-item
@@ -707,7 +691,7 @@ onBeforeUnmount(() => {
 								>
 								</song-item>
 							</template>
-						</sortable>
+						</draggable>
 					</div>
 				</div>
 			</template>

+ 0 - 3
frontend/src/pages/Profile/Tabs/Playlists.vue

@@ -56,9 +56,6 @@ onMounted(() => {
 								element.createdBy === userId)
 						"
 						:playlist="element"
-						:class="{
-							'is-draggable': isCurrentUser
-						}"
 					>
 						<template #actions>
 							<i

+ 1 - 1
frontend/src/types/global.d.ts

@@ -16,7 +16,7 @@ declare global {
 				itemIndex: number;
 				itemListUuid: string;
 				itemGroup: string;
-				itemOnMove: (index: number) => any;
+				itemOnMove?: (index: number) => any;
 				initialItemIndex: number;
 				initialItemListUuid: string;
 		  };