Files
WRNexusJS/packages/ui/components/DragAndDrop.wrn
T

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; }
}
}