Skip to content

Commit

Permalink
Merge pull request #4 from Bxnq/main
Browse files Browse the repository at this point in the history
Added custom classes and resolved issue
  • Loading branch information
Spikeysanju authored Nov 12, 2024
2 parents 7ae42a7 + bbb2460 commit 299c1ec
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 16 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ vite.config.js.timestamp-*
vite.config.ts.timestamp-*

# .github
.github/copilot-instructions.md
.github/copilot-instructions.md

# pnpm

pnpm-lock.yaml
12 changes: 8 additions & 4 deletions src/lib/actions/draggable.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { dndState } from '$lib/stores/dnd.svelte.js';
import type { DragDropOptions, DragDropState } from '$lib/types/index.js';

const DEFAULT_DRAGGING_CLASS = 'dragging';

export function draggable<T>(node: HTMLElement, options: DragDropOptions<T>) {
const draggingClass = (options.attributes?.draggingClass || DEFAULT_DRAGGING_CLASS).split(' ');

function handleDragStart(event: DragEvent) {
if (options.disabled) return;

Expand All @@ -15,12 +19,12 @@ export function draggable<T>(node: HTMLElement, options: DragDropOptions<T>) {
event.dataTransfer.setData('text/plain', JSON.stringify(options.dragData));
}

node.classList.add('dragging');
node.classList.add(...draggingClass);
options.callbacks?.onDragStart?.(dndState as DragDropState<T>);
}

function handleDragEnd() {
node.classList.remove('dragging');
node.classList.remove(...draggingClass);
options.callbacks?.onDragEnd?.(dndState as DragDropState<T>);

// Reset state
Expand All @@ -39,7 +43,7 @@ export function draggable<T>(node: HTMLElement, options: DragDropOptions<T>) {
dndState.targetContainer = null;

node.setPointerCapture(event.pointerId);
node.classList.add('dragging');
node.classList.add(...draggingClass);
options.callbacks?.onDragStart?.(dndState as DragDropState<T>);
}

Expand All @@ -53,7 +57,7 @@ export function draggable<T>(node: HTMLElement, options: DragDropOptions<T>) {
if (!dndState.isDragging) return;

node.releasePointerCapture(event.pointerId);
node.classList.remove('dragging');
node.classList.remove(...draggingClass);
options.callbacks?.onDragEnd?.(dndState as DragDropState<T>);

// Reset state
Expand Down
33 changes: 23 additions & 10 deletions src/lib/actions/droppable.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,38 @@
import { dndState } from '$lib/stores/dnd.svelte.js';
import type { DragDropOptions, DragDropState } from '$lib/types/index.js';

const DEFAULT_DRAG_OVER_CLASS = 'drag-over';

export function droppable<T>(node: HTMLElement, options: DragDropOptions<T>) {
const dragOverClass = (options.attributes?.draggingClass || DEFAULT_DRAG_OVER_CLASS).split(' ');

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

const target = event.target as HTMLElement;

dndState.targetContainer = options.container;
node.classList.add('drag-over');
dndState.targetElement = target;

node.classList.add(...dragOverClass);
options.callbacks?.onDragEnter?.(dndState as DragDropState<T>);
}

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

const target = event.target as HTMLElement;
if (!node.contains(target)) {
dndState.targetContainer = null;
node.classList.remove('drag-over');
options.callbacks?.onDragLeave?.(dndState as DragDropState<T>);
}

// check if element is still being dragged over
if (!dndState.targetElement?.isSameNode(target)) return;

node.classList.remove(...dragOverClass);

options.callbacks?.onDragLeave?.(dndState as DragDropState<T>);

dndState.targetContainer = null;
dndState.targetElement = null;
}

function handleDragOver(event: DragEvent) {
Expand All @@ -37,7 +50,7 @@ export function droppable<T>(node: HTMLElement, options: DragDropOptions<T>) {
if (options.disabled) return;
event.preventDefault();

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

try {
if (event.dataTransfer) {
Expand All @@ -55,22 +68,22 @@ export function droppable<T>(node: HTMLElement, options: DragDropOptions<T>) {
if (options.disabled || !dndState.isDragging) return;

dndState.targetContainer = options.container;
node.classList.add('drag-over');
node.classList.add(...dragOverClass);
options.callbacks?.onDragEnter?.(dndState as DragDropState<T>);
}

function handlePointerOut(event: PointerEvent) {
if (options.disabled || !dndState.isDragging) return;

dndState.targetContainer = null;
node.classList.remove('drag-over');
node.classList.remove(...dragOverClass);
options.callbacks?.onDragLeave?.(dndState as DragDropState<T>);
}

function handlePointerUp(event: PointerEvent) {
if (options.disabled || !dndState.isDragging) return;

node.classList.remove('drag-over');
node.classList.remove(...dragOverClass);
options.callbacks?.onDrop?.(dndState as DragDropState<T>);
}

Expand Down
3 changes: 2 additions & 1 deletion src/lib/stores/dnd.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export const dndState = $state<DragDropState>({
isDragging: false,
draggedItem: null,
sourceContainer: '',
targetContainer: null
targetContainer: null,
targetElement: null
});
7 changes: 7 additions & 0 deletions src/lib/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export interface DragDropState<T = unknown> {
draggedItem: T;
sourceContainer: string;
targetContainer: string | null;
targetElement: HTMLElement | null;
}

export interface DragDropCallbacks<T = unknown> {
Expand All @@ -14,9 +15,15 @@ export interface DragDropCallbacks<T = unknown> {
onDragEnd?: (state: DragDropState<T>) => void;
}

export interface DragDropAttributes {
draggingClass?: string;
dragOverClass?: string;
}

export interface DragDropOptions<T = unknown> {
dragData?: T;
container: string;
disabled?: boolean;
callbacks?: DragDropCallbacks<T>;
attributes?: DragDropAttributes;
}
110 changes: 110 additions & 0 deletions src/routes/custom-classes/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<script lang="ts">
import { draggable, droppable, type DragDropState } from '$lib/index.js';
import { flip } from 'svelte/animate';
import { fade } from 'svelte/transition';
interface Item {
id: string;
title: string;
description: string;
priority: 'low' | 'medium' | 'high';
}
const items = $state<Item[]>([
{
id: '1',
title: 'Design System Updates',
description: 'Update color palette and component library',
priority: 'high'
},
{
id: '2',
title: 'User Research',
description: 'Conduct interviews with 5 key customers',
priority: 'medium'
},
{
id: '3',
title: 'API Documentation',
description: 'Document new endpoints and examples',
priority: 'low'
}
]);
function handleDrop(state: DragDropState<Item>) {
const { draggedItem, targetContainer } = state;
const dragIndex = items.findIndex((item: Item) => item.id === draggedItem.id);
const dropIndex = parseInt(targetContainer ?? '0');
if (dragIndex !== -1 && !isNaN(dropIndex)) {
const [item] = items.splice(dragIndex, 1);
items.splice(dropIndex, 0, item);
}
}
const getPriorityColor = (priority: Item['priority']) => {
return {
low: 'bg-blue-50 text-blue-700',
medium: 'bg-yellow-50 text-yellow-700',
high: 'bg-red-50 text-red-700'
}[priority];
};
</script>

<div class="min-h-screen bg-gray-50 p-8">
<div class="mb-8 flex flex-col gap-2">
<h1 class="text-2xl font-bold text-gray-900">Sortable List</h1>
<p class="text-gray-600">Drag and drop items to reorder them in the list.</p>
</div>

<div class="w-80">
<div class="rounded-xl bg-gray-100 p-4 shadow-sm ring-1 ring-gray-200">
<div class="space-y-3">
{#each items as item, index (item.id)}
<div
use:draggable={{ container: index.toString(), dragData: item }}
use:droppable={{
container: index.toString(),
callbacks: { onDrop: handleDrop },
attributes: {
draggingClass: 'border border-blue-500',
dragOverClass: 'border border-red-500'
}
}}
animate:flip={{ duration: 200 }}
in:fade={{ duration: 150 }}
out:fade={{ duration: 150 }}
class="svelte-dnd-touch-feedback cursor-move rounded-lg bg-white p-3 shadow-sm
ring-gray-200 transition-all duration-200 hover:shadow-md hover:ring-2 hover:ring-blue-200"
>
<div class="mb-2 flex items-start justify-between gap-2">
<h3 class="font-medium text-gray-900">
{item.title}
</h3>
<span
class={`rounded-full px-2 py-0.5 text-xs font-medium ${getPriorityColor(
item.priority
)}`}
>
{item.priority}
</span>
</div>
<p class="text-sm text-gray-500">
{item.description}
</p>
</div>
{/each}
</div>
</div>
</div>
</div>

<style>
:global(.dragging) {
@apply opacity-50 shadow-lg ring-2 ring-blue-400;
}
:global(.drag-over) {
@apply bg-blue-50;
}
</style>

0 comments on commit 299c1ec

Please sign in to comment.