151 lines
4.6 KiB
Svelte
151 lines
4.6 KiB
Svelte
<script>
|
|
import { arrayMoveElement } from "../helpers";
|
|
import { flip } from "svelte/animate";
|
|
|
|
let {
|
|
sortableClass,
|
|
onUpdate,
|
|
items,
|
|
itemView,
|
|
itemCssClass,
|
|
itemKey = "id",
|
|
type = "list",
|
|
handleClass = null,
|
|
disabled = false,
|
|
} = $props();
|
|
// let sortableInstance = $state();
|
|
let sortableContainer = $state();
|
|
let draggedItem = $state();
|
|
|
|
function handleDragStart(event, item) {
|
|
if (disabled) {
|
|
return false;
|
|
}
|
|
draggedItem = item;
|
|
// Set data required for drag operation
|
|
event.dataTransfer.effectAllowed = "move";
|
|
event.dataTransfer.setData("text/plain", item.id);
|
|
// Set a ghost drag image
|
|
event.currentTarget.classList.add("dragging");
|
|
}
|
|
|
|
// Handle drag over another item
|
|
function handleDragOver(event) {
|
|
event.preventDefault();
|
|
event.target.closest(".draggable-item").classList.add("dragover");
|
|
event.dataTransfer.dropEffect = "move";
|
|
}
|
|
|
|
// Handle dropping the item
|
|
function handleDrop(event, targetItem) {
|
|
event.preventDefault();
|
|
|
|
if (draggedItem === targetItem) return;
|
|
// Find positions of dragged and target items
|
|
const draggedIndex = items.findIndex(
|
|
(item) => getItem(item, itemKey) === getItem(draggedItem, itemKey),
|
|
);
|
|
const targetIndex = items.findIndex(
|
|
(item) => getItem(item, itemKey) === getItem(targetItem, itemKey),
|
|
);
|
|
|
|
onUpdate(
|
|
arrayMoveElement([...items], draggedIndex, targetIndex),
|
|
draggedIndex,
|
|
targetIndex,
|
|
);
|
|
}
|
|
|
|
function getItem(item, key) {
|
|
if (key === null) {
|
|
return item;
|
|
}
|
|
if (key.includes(".")) {
|
|
return key.split(".").reduce((a, b) => a[b] ?? null, item);
|
|
}
|
|
return item[key];
|
|
}
|
|
|
|
function handleDragEnd(event) {
|
|
event.target.classList.remove("dragging");
|
|
sortableContainer
|
|
.querySelector(".dragover")
|
|
?.classList.remove("dragover");
|
|
draggedItem = null;
|
|
const draggableItem = event.target.closest(".draggable-item");
|
|
draggableItem.draggable = false;
|
|
}
|
|
|
|
function handleDragLeave(event) {
|
|
// event.target.classList.remove("dragover");
|
|
const draggableItem = event.target.closest(".draggable-item");
|
|
draggableItem.classList.remove("dragover");
|
|
draggableItem.draggable = false;
|
|
}
|
|
|
|
function handleMouseDown(e) {
|
|
const handleEl = e.target.closest("." + handleClass);
|
|
if (!handleEl) {
|
|
return true;
|
|
}
|
|
const draggableItem = e.target.closest(".draggable-item");
|
|
draggableItem.draggable = true;
|
|
}
|
|
|
|
function handleMouseUp(e) {
|
|
const handleEl = e.target.closest("." + handleClass);
|
|
if (!handleEl) {
|
|
return true;
|
|
}
|
|
const draggableItem = e.target.closest(".draggable-item");
|
|
draggableItem.draggable = false;
|
|
}
|
|
</script>
|
|
|
|
{#if type === "table"}
|
|
<tbody
|
|
class="sortable-container {sortableClass}"
|
|
bind:this={sortableContainer}
|
|
>
|
|
{#each items as item (getItem(item, itemKey))}
|
|
<tr
|
|
animate:flip={{ duration: 100 }}
|
|
class="draggable-item {itemCssClass}"
|
|
draggable="false"
|
|
onmousedown={handleMouseDown}
|
|
onmouseup={handleMouseUp}
|
|
ondragstart={(e) => handleDragStart(e, item)}
|
|
ondragover={handleDragOver}
|
|
ondragleave={handleDragLeave}
|
|
ondrop={(e) => handleDrop(e, item)}
|
|
ondragend={handleDragEnd}
|
|
>
|
|
{@render itemView(item)}
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
{:else}
|
|
<div
|
|
class="sortable-container {sortableClass}"
|
|
bind:this={sortableContainer}
|
|
>
|
|
{#each items as item (getItem(item, itemKey))}
|
|
<div
|
|
animate:flip={{ duration: 100 }}
|
|
role="listitem"
|
|
class="draggable-item {itemCssClass}"
|
|
draggable="false"
|
|
onmousedown={handleMouseDown}
|
|
onmouseup={handleMouseUp}
|
|
ondragstart={(e) => handleDragStart(e, item)}
|
|
ondragover={handleDragOver}
|
|
ondragleave={handleDragLeave}
|
|
ondrop={(e) => handleDrop(e, item)}
|
|
ondragend={handleDragEnd}
|
|
>
|
|
{@render itemView(item)}
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{/if}
|