49 lines
4.1 KiB
Plaintext
49 lines
4.1 KiB
Plaintext
component DragAndDrop {
|
|
outputs {
|
|
dragStart(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
|
dragEnd(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
|
dragEnter(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
|
dragLeave(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
|
drop(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
|
change(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
|
|
}
|
|
|
|
props {
|
|
size: string = "default"
|
|
color: string = "primary"
|
|
title: string = "Drag And Drop"
|
|
description: string = ""
|
|
items: unknown[] = []
|
|
variant: string = "default"
|
|
class: string = ""
|
|
}
|
|
state orderedItems = items
|
|
state draggedIndex: number = -1
|
|
functions {
|
|
client function startDrag(index, sourceEvent) { draggedIndex = index; sourceEvent.dataTransfer.setData("text/plain", String(index)); output.dragStart({ index: index }) }
|
|
client function enter(sourceEvent) { sourceEvent.preventDefault(); output.dragEnter({ sourceEvent: sourceEvent }) }
|
|
client function leave(sourceEvent) { output.dragLeave({ sourceEvent: sourceEvent }) }
|
|
client function finish(sourceEvent) { draggedIndex = -1; output.dragEnd({ sourceEvent: sourceEvent }) }
|
|
client function move(from, to, sourceEvent, next, moved) {
|
|
if (from < 0 || to < 0 || to >= orderedItems.length || from === to) { return }
|
|
next = orderedItems.slice(); moved = next.splice(from, 1)[0]; next.splice(to, 0, moved); orderedItems = next
|
|
output.change({ value: orderedItems, sourceEvent: sourceEvent })
|
|
}
|
|
client function dropAt(index, sourceEvent) { sourceEvent.preventDefault(); move(draggedIndex, index, sourceEvent); draggedIndex = -1; output.drop({ index: index, items: orderedItems }) }
|
|
}
|
|
view {
|
|
<section {...attrs} class="wrn-sortable {class}" data-wrn-owner="DragAndDrop" data-wrn-part="root" data-wrn-component="DragAndDrop" data-wrn-axis-size="{size}" data-wrn-axis-color="{color}" data-wrn-axis-variant="{variant}">{#if title}<strong>{title}</strong>{/if}{#if description}<p>{description}</p>{/if}<ol>{#each orderedItems as item, index}<li draggable="true" @dragstart="startDrag(index, event)" @dragover="enter(event)" @dragleave="leave(event)" @dragend="finish(event)" @drop="dropAt(index, event)"><span class="icon-[lucide--grip-vertical]" aria-hidden="true"></span><span>{item.label}</span><button type="button" aria-label="Move {item.label} up" disabled="{index === 0}" @click="move(index, index - 1, event)" data-wrn-owner="DragAndDrop" data-wrn-part="button" data-wrn-axis-size="{size}" data-wrn-axis-color="{color}" data-wrn-axis-variant="{variant}">↑</button><button type="button" aria-label="Move {item.label} down" disabled="{index === orderedItems.length - 1}" @click="move(index, index + 1, event)" data-wrn-owner="DragAndDrop" data-wrn-part="button" data-wrn-axis-size="{size}" data-wrn-axis-color="{color}" data-wrn-axis-variant="{variant}">↓</button></li>{/each}</ol><slot /></section>
|
|
}
|
|
|
|
style {
|
|
.wrn-sortable { display:grid; gap:.75rem; color:var(--wrn-color-text); }
|
|
.wrn-sortable p { margin:0; color:var(--wrn-color-muted); font-size:.875rem; }
|
|
.wrn-sortable ol { display:grid; gap:.5rem; list-style:none; margin:0; padding:0; }
|
|
.wrn-sortable li { display:flex; align-items:center; gap:.6rem; padding:.65rem; background:var(--wrn-color-surface); border:1px solid var(--wrn-color-border); border-radius:var(--wrn-radius-sm); font-size:.875rem; cursor:grab; }
|
|
.wrn-sortable li > span:nth-child(2) { flex:1; }
|
|
.wrn-sortable li > span:first-child { width:1rem; height:1rem; color:var(--wrn-color-muted); }
|
|
.wrn-sortable button { border:1px solid var(--wrn-color-border); background:transparent; color:inherit; border-radius:4px; width:1.75rem; height:1.75rem; cursor:pointer; }
|
|
.wrn-sortable button:disabled { opacity:.3; }
|
|
}
|
|
}
|