浏览代码

enh: drag ghost

Timothy J. Baek 6 月之前
父节点
当前提交
73a251fc49
共有 2 个文件被更改,包括 91 次插入2 次删除
  1. 29 0
      src/lib/components/common/DragGhost.svelte
  2. 62 2
      src/lib/components/layout/Sidebar/ChatItem.svelte

+ 29 - 0
src/lib/components/common/DragGhost.svelte

@@ -0,0 +1,29 @@
+<script lang="ts">
+	import { onDestroy, onMount } from 'svelte';
+
+	export let x;
+	export let y;
+
+	let popupElement = null;
+
+	onMount(() => {
+		document.body.appendChild(popupElement);
+		document.body.style.overflow = 'hidden';
+	});
+
+	onDestroy(() => {
+		document.body.removeChild(popupElement);
+		document.body.style.overflow = 'unset';
+	});
+</script>
+
+<!-- svelte-ignore a11y-click-events-have-key-events -->
+<!-- svelte-ignore a11y-no-static-element-interactions -->
+
+<div
+	bind:this={popupElement}
+	class=" absolute text-white z-[99999]"
+	style="top: {y}px; left: {x}px;"
+>
+	<slot></slot>
+</div>

+ 62 - 2
src/lib/components/layout/Sidebar/ChatItem.svelte

@@ -1,7 +1,7 @@
 <script lang="ts">
 <script lang="ts">
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 	import { goto, invalidate, invalidateAll } from '$app/navigation';
 	import { goto, invalidate, invalidateAll } from '$app/navigation';
-	import { onMount, getContext, createEventDispatcher, tick } from 'svelte';
+	import { onMount, getContext, createEventDispatcher, tick, onDestroy } from 'svelte';
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
@@ -32,6 +32,7 @@
 	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
 	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
 	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
+	import DragGhost from '$lib/components/common/DragGhost.svelte';
 
 
 	export let chat;
 	export let chat;
 	export let selected = false;
 	export let selected = false;
@@ -89,11 +90,70 @@
 	const focusEdit = async (node: HTMLInputElement) => {
 	const focusEdit = async (node: HTMLInputElement) => {
 		node.focus();
 		node.focus();
 	};
 	};
+
+	let itemElement;
+
+	let drag = false;
+	let x = 0;
+	let y = 0;
+
+	const dragImage = new Image();
+	dragImage.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=';
+
+	const onDragStart = (event) => {
+		console.log('Dragging started:', event.target);
+		event.dataTransfer.setDragImage(dragImage, 0, 0);
+
+		drag = true;
+		itemElement.style.opacity = '0.5'; // Optional: Visual cue to show it's being dragged
+	};
+
+	const onDrag = (event) => {
+		x = event.clientX;
+		y = event.clientY;
+	};
+
+	const onDragEnd = (event) => {
+		console.log('Dragging ended:', event.target);
+		drag = false;
+		itemElement.style.opacity = '1'; // Reset visual cue after drag
+	};
+
+	onMount(() => {
+		if (itemElement) {
+			// Event listener for when dragging starts
+			itemElement.addEventListener('dragstart', onDragStart);
+			// Event listener for when dragging occurs (optional)
+			itemElement.addEventListener('drag', onDrag);
+			// Event listener for when dragging ends
+			itemElement.addEventListener('dragend', onDragEnd);
+		}
+	});
+
+	onDestroy(() => {
+		if (itemElement) {
+			itemElement.removeEventListener('dragstart', onDragStart);
+			itemElement.removeEventListener('drag', onDrag);
+			itemElement.removeEventListener('dragend', onDragEnd);
+		}
+	});
 </script>
 </script>
 
 
 <ShareChatModal bind:show={showShareChatModal} chatId={chat.id} />
 <ShareChatModal bind:show={showShareChatModal} chatId={chat.id} />
 
 
-<div class=" w-full pr-2 relative group" draggable="true">
+{#if drag && x && y}
+	<DragGhost {x} {y}>
+		<div class=" bg-black/50 backdrop-blur-2xl px-2 py-1 rounded-lg">
+			<div>
+				<div class=" text-xs text-gray-500">
+					{chat.title}
+				</div>
+			</div>
+		</div>
+	</DragGhost>
+{/if}
+
+<div bind:this={itemElement} class=" w-full pr-2 relative group" draggable="true">
 	{#if confirmEdit}
 	{#if confirmEdit}
 		<div
 		<div
 			class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId || confirmEdit
 			class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId || confirmEdit