Skip to content

Commit

Permalink
Add mobile support
Browse files Browse the repository at this point in the history
Add mobile support for drag and drop functionality.

* **Draggable and Droppable Actions:**
  - Add touch event listeners (`touchstart`, `touchmove`, `touchend`) in `src/lib/actions/draggable.ts` and `src/lib/actions/droppable.ts`.
  - Update `handleDragStart` and `handleDragEnd` in `draggable.ts` to handle touch events.
  - Update `handleDragEnter`, `handleDragLeave`, `handleDragOver`, and `handleDrop` in `droppable.ts` to handle touch events.
  - Optimize touch event handling in `droppable.ts` by debouncing touch events.

* **Styles:**
  - Add visual feedback styles for touch interactions in `src/lib/styles/dnd.css`.
  - Add media queries for responsive design in `src/lib/styles/dnd.css`.
  - Update `src/app.css` to include mobile-specific styles and media queries.

* **Svelte Components:**
  - Update drag and drop logic to handle touch events in `src/routes/+page.svelte`, `src/routes/grid-sort/+page.svelte`, `src/routes/horizontal-scroll/+page.svelte`, `src/routes/nested/+page.svelte`, and `src/routes/simple-list/+page.svelte`.
  - Add visual feedback for touch interactions in the above Svelte components.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/thisuxhq/SvelteDnD?shareId=XXXX-XXXX-XXXX-XXXX).
  • Loading branch information
rezapex committed Nov 10, 2024
1 parent 43cd9fe commit 6e5bd5d
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 15 deletions.
12 changes: 12 additions & 0 deletions src/app.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

/* Mobile-specific styles and media queries for better responsiveness */
@media (max-width: 600px) {
/* Adjust layout and styling for smaller screens */
.draggable-item {
width: 100%;
}

.droppable-container {
padding: 10px;
}
}
20 changes: 17 additions & 3 deletions src/lib/actions/draggable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { dndState } from '$lib/stores/dnd.svelte.js';
import type { DragDropOptions } from '$lib/types/index.js';

export function draggable(node: HTMLElement, options: DragDropOptions) {
function handleDragStart(event: DragEvent) {
function handleDragStart(event: DragEvent | TouchEvent) {
if (options.disabled) return;

// Update state using assignment (Svelte 5 style)
Expand All @@ -11,7 +11,7 @@ export function draggable(node: HTMLElement, options: DragDropOptions) {
dndState.sourceContainer = options.container;
dndState.targetContainer = null;

if (event.dataTransfer) {
if (event instanceof DragEvent && event.dataTransfer) {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', JSON.stringify(options.dragData));
}
Expand All @@ -20,7 +20,7 @@ export function draggable(node: HTMLElement, options: DragDropOptions) {
options.callbacks?.onDragStart?.(dndState);
}

function handleDragEnd() {
function handleDragEnd(event: DragEvent | TouchEvent) {
node.classList.remove('dragging');
options.callbacks?.onDragEnd?.(dndState);

Expand All @@ -31,9 +31,21 @@ export function draggable(node: HTMLElement, options: DragDropOptions) {
dndState.targetContainer = null;
}

function handleTouchStart(event: TouchEvent) {
event.preventDefault();
handleDragStart(event);
}

function handleTouchEnd(event: TouchEvent) {
event.preventDefault();
handleDragEnd(event);
}

node.draggable = !options.disabled;
node.addEventListener('dragstart', handleDragStart);
node.addEventListener('dragend', handleDragEnd);
node.addEventListener('touchstart', handleTouchStart);
node.addEventListener('touchend', handleTouchEnd);

return {
update(newOptions: DragDropOptions) {
Expand All @@ -44,6 +56,8 @@ export function draggable(node: HTMLElement, options: DragDropOptions) {
destroy() {
node.removeEventListener('dragstart', handleDragStart);
node.removeEventListener('dragend', handleDragEnd);
node.removeEventListener('touchstart', handleTouchStart);
node.removeEventListener('touchend', handleTouchEnd);
}
};
}
26 changes: 20 additions & 6 deletions src/lib/actions/droppable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { dndState } from '$lib/stores/dnd.svelte.js';
import type { DragDropOptions } from '$lib/types/index.js';

export function droppable(node: HTMLElement, options: DragDropOptions) {
function handleDragEnter(event: DragEvent) {
let touchTimeout: number | null = null;

function handleDragEnter(event: DragEvent | TouchEvent) {
if (options.disabled) return;
event.preventDefault();

Expand All @@ -11,7 +13,7 @@ export function droppable(node: HTMLElement, options: DragDropOptions) {
options.callbacks?.onDragEnter?.(dndState);
}

function handleDragLeave(event: DragEvent) {
function handleDragLeave(event: DragEvent | TouchEvent) {
if (options.disabled) return;

const target = event.target as HTMLElement;
Expand All @@ -22,25 +24,25 @@ export function droppable(node: HTMLElement, options: DragDropOptions) {
}
}

function handleDragOver(event: DragEvent) {
function handleDragOver(event: DragEvent | TouchEvent) {
if (options.disabled) return;
event.preventDefault();

if (event.dataTransfer) {
if (event instanceof DragEvent && event.dataTransfer) {
event.dataTransfer.dropEffect = 'move';
}

options.callbacks?.onDragOver?.(dndState);
}

async function handleDrop(event: DragEvent) {
async function handleDrop(event: DragEvent | TouchEvent) {
if (options.disabled) return;
event.preventDefault();

node.classList.remove('drag-over');

try {
if (event.dataTransfer) {
if (event instanceof DragEvent && event.dataTransfer) {
const dragData = JSON.parse(event.dataTransfer.getData('text/plain'));
dndState.draggedItem = dragData;
}
Expand All @@ -51,10 +53,21 @@ export function droppable(node: HTMLElement, options: DragDropOptions) {
}
}

function handleTouchMove(event: TouchEvent) {
if (touchTimeout) {
clearTimeout(touchTimeout);
}

touchTimeout = window.setTimeout(() => {
handleDragOver(event);
}, 100);
}

node.addEventListener('dragenter', handleDragEnter);
node.addEventListener('dragleave', handleDragLeave);
node.addEventListener('dragover', handleDragOver);
node.addEventListener('drop', handleDrop);
node.addEventListener('touchmove', handleTouchMove);

return {
update(newOptions: DragDropOptions) {
Expand All @@ -66,6 +79,7 @@ export function droppable(node: HTMLElement, options: DragDropOptions) {
node.removeEventListener('dragleave', handleDragLeave);
node.removeEventListener('dragover', handleDragOver);
node.removeEventListener('drop', handleDrop);
node.removeEventListener('touchmove', handleTouchMove);
}
};
}
18 changes: 18 additions & 0 deletions src/lib/styles/dnd.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,21 @@
opacity: 0.3;
border: 2px dashed #9e9e9e;
}

/* Visual feedback for touch interactions */
.svelte-dnd-touch-feedback {
opacity: 0.7;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-color: #2196f3;
}

/* Media queries for responsive design */
@media (max-width: 600px) {
.svelte-dnd-draggable {
width: 100%;
}

.svelte-dnd-droppable {
padding: 10px;
}
}
2 changes: 1 addition & 1 deletion src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
in:fade={{ duration: 150 }}
out:fade={{ duration: 150 }}
class="cursor-move rounded-lg bg-white p-3 shadow-sm ring-1 ring-gray-200
transition-all duration-200 hover:shadow-md hover:ring-2 hover:ring-blue-200"
transition-all duration-200 hover:shadow-md hover:ring-2 hover:ring-blue-200 svelte-dnd-touch-feedback"
>
<div class="mb-2 flex items-start justify-between gap-2">
<h3 class="font-medium text-gray-900">
Expand Down
2 changes: 1 addition & 1 deletion src/routes/grid-sort/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
container: index.toString(),
dragData: card
}}
class={`h-full w-full cursor-move rounded-lg bg-gradient-to-br ${card.color} shadow-lg transition-all duration-300 hover:scale-[1.02] hover:shadow-xl active:scale-95 active:brightness-110`}
class={`h-full w-full cursor-move rounded-lg bg-gradient-to-br ${card.color} shadow-lg transition-all duration-300 hover:scale-[1.02] hover:shadow-xl active:scale-95 active:brightness-110 svelte-dnd-touch-feedback`}
>
<div class="flex h-full items-center justify-center">
<span class="text-4xl">{card.icon}</span>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/horizontal-scroll/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
dragData: image
}}
class="group relative h-[300px] w-[200px] cursor-move overflow-hidden rounded-xl
transition-transform hover:scale-105"
transition-transform hover:scale-105 svelte-dnd-touch-feedback"
>
<img
src={image.url}
Expand Down
4 changes: 2 additions & 2 deletions src/routes/nested/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@
container: groupIndex.toString(),
dragData: group
}}
class="rounded-xl bg-white p-4 shadow-sm ring-1 ring-gray-200"
class="rounded-xl bg-white p-4 shadow-sm ring-1 ring-gray-200 svelte-dnd-touch-feedback"
in:fade={{ duration: 150 }}
out:fade={{ duration: 150 }}
>
Expand Down Expand Up @@ -160,7 +160,7 @@
dragData: item
}}
class="cursor-move rounded-lg bg-white p-3 shadow-sm ring-1 ring-gray-200
transition-all duration-200 hover:shadow-md hover:ring-2 hover:ring-blue-200"
transition-all duration-200 hover:shadow-md hover:ring-2 hover:ring-blue-200 svelte-dnd-touch-feedback"
>
<div class="mb-2 flex items-start justify-between gap-2">
<h3 class="font-medium text-gray-900">{item.title}</h3>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/simple-list/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
in:fade={{ duration: 150 }}
out:fade={{ duration: 150 }}
class="cursor-move rounded-lg bg-white p-3 shadow-sm ring-1 ring-gray-200
transition-all duration-200 hover:shadow-md hover:ring-2 hover:ring-blue-200"
transition-all duration-200 hover:shadow-md hover:ring-2 hover:ring-blue-200 svelte-dnd-touch-feedback"
>
<div class="mb-2 flex items-start justify-between gap-2">
<h3 class="font-medium text-gray-900">
Expand Down

0 comments on commit 6e5bd5d

Please sign in to comment.