|
<script lang="ts"> |
|
import type { PicletInstance } from '$lib/db/schema'; |
|
import DraggablePicletCard from './DraggablePicletCard.svelte'; |
|
import EmptySlotCard from './EmptySlotCard.svelte'; |
|
|
|
interface Props { |
|
position: number; |
|
piclet?: PicletInstance; |
|
size?: number; |
|
onDrop?: (position: number, dragData: any) => void; |
|
onPicletClick?: (piclet: PicletInstance) => void; |
|
onEmptyClick?: (position: number) => void; |
|
onDragStart?: (instance: PicletInstance) => void; |
|
onDragEnd?: () => void; |
|
} |
|
|
|
let { |
|
position, |
|
piclet, |
|
size = 100, |
|
onDrop, |
|
onPicletClick, |
|
onEmptyClick, |
|
onDragStart, |
|
onDragEnd |
|
}: Props = $props(); |
|
|
|
let isDragOver = $state(false); |
|
let canAcceptDrop = $state(false); |
|
|
|
function handleDragOver(e: DragEvent) { |
|
e.preventDefault(); |
|
|
|
|
|
const data = e.dataTransfer?.getData('application/json'); |
|
if (data) { |
|
isDragOver = true; |
|
canAcceptDrop = true; |
|
e.dataTransfer!.dropEffect = 'move'; |
|
} |
|
} |
|
|
|
function handleDragLeave() { |
|
isDragOver = false; |
|
canAcceptDrop = false; |
|
} |
|
|
|
function handleDrop(e: DragEvent) { |
|
e.preventDefault(); |
|
isDragOver = false; |
|
canAcceptDrop = false; |
|
|
|
const data = e.dataTransfer?.getData('application/json'); |
|
if (data) { |
|
const dragData = JSON.parse(data); |
|
onDrop?.(position, dragData); |
|
} |
|
} |
|
|
|
function handleClick() { |
|
if (piclet) { |
|
onPicletClick?.(piclet); |
|
} else { |
|
onEmptyClick?.(position); |
|
} |
|
} |
|
</script> |
|
|
|
<div |
|
class="roster-slot" |
|
ondragover={handleDragOver} |
|
ondragleave={handleDragLeave} |
|
ondrop={handleDrop} |
|
class:drag-over={isDragOver} |
|
role="region" |
|
aria-label="Roster slot {position + 1}" |
|
> |
|
{#if piclet} |
|
<DraggablePicletCard |
|
instance={piclet} |
|
{size} |
|
onClick={handleClick} |
|
{onDragStart} |
|
{onDragEnd} |
|
/> |
|
{:else} |
|
<EmptySlotCard |
|
{size} |
|
isHighlighted={isDragOver && canAcceptDrop} |
|
onClick={handleClick} |
|
/> |
|
{/if} |
|
</div> |
|
|
|
<style> |
|
.roster-slot { |
|
position: relative; |
|
} |
|
|
|
.roster-slot.drag-over::after { |
|
content: ''; |
|
position: absolute; |
|
inset: -4px; |
|
border: 2px solid #007bff; |
|
border-radius: 16px; |
|
background: rgba(0, 123, 255, 0.1); |
|
pointer-events: none; |
|
} |
|
</style> |