Common
useDragReorder
Composable per il riordino col trascinamento, col puntatore (drag & drop HTML5) e da tastiera, con annunci per gli screen reader. Usato da OrderList e dalla colonna rowReorder del DataTable.
Composable comune — usato da
OrderList(draggable) e dalla colonnarowReorderdelDataTable. È esportato anche dal pacchetto, insieme amoveItem, per i componenti custom.
Firma TypeScript
export function useDragReorder(options: UseDragReorderOptions): {
dragIndex: Ref<number | null> // voce trascinata col puntatore
dropIndex: Ref<number | null> // voce sotto la linea di rilascio
dropPosition: Ref<'before' | 'after' | null>
grabbedIndex: Ref<number | null> // voce afferrata da tastiera
targetIndex: Ref<number | null> // posizione finale scelta da tastiera
announcement: Ref<string> // testo per una live region
onDragStart: (index: number, event: DragEvent, dragImage?: Element | null) => void
onDragOver: (index: number, event: DragEvent) => void
onDrop: (index: number, event: DragEvent) => void
onDragEnd: () => void
handleKeydown: (index: number, event: KeyboardEvent) => boolean
cancelGrab: () => void
}
export interface UseDragReorderOptions {
count: () => number
label: (index: number) => string
onReorder: (from: number, to: number, originalEvent: Event) => void
disabled?: () => boolean
grabKeys?: readonly string[] // default [' ', 'Enter']
}
/** Copia della lista con la voce `from` spostata all'indice `to`. */
export function moveItem<T>(list: readonly T[], from: number, to: number): T[]
Parametri
| Parametro | Tipo | Descrizione |
|---|---|---|
count | () => number | Numero di voci della lista |
label | (index) => string | Testo della voce, usato negli annunci |
onReorder | (from, to, originalEvent) => void | Commit dello spostamento: indice di partenza e indice finale, sempre diversi. La lista non viene toccata: decide il chiamante |
disabled | () => boolean (opzionale) | Disattiva puntatore e tastiera |
grabKeys | readonly string[] (opzionale) | Tasti che afferrano la voce. OrderList passa [' '] per lasciare Invio alla selezione. A voce afferrata rilasciano sempre Spazio e Invio |
Comportamento
- Puntatore:
dropIndex/dropPositionindicano dove cadrà la voce (metà superiore della voce = prima, inferiore = dopo). Il rilascio sulla voce stessa non riordina. - Tastiera: il tasto di presa afferra la voce; frecce, Home e Fine scelgono la posizione
(fermandosi ai bordi); Spazio o Invio rilasciano; Escape annulla e chiama
event.stopPropagation(), così un Dialog contenitore resta aperto; Tab annulla e lascia passare il focus.handleKeydownrestituiscetruese ha consumato il tasto. - Annunci (i18n
list.dragGrabbed,list.dragMoved,list.dragDropped,list.dragCancelled, con{item},{position},{total}) inannouncement, da rendere in una live region. Le istruzioni sono inlist.dragInstructions.
Esempio
import { moveItem, useDragReorder } from '@pzeta/vue-components'
const reorder = useDragReorder({
count: () => items.value.length,
label: (index) => items.value[index]?.nome ?? '',
onReorder: (from, to) => {
items.value = moveItem(items.value, from, to)
},
})
<ul role="listbox" tabindex="0" aria-describedby="istruzioni"
@keydown="reorder.handleKeydown(focused, $event)" @blur="reorder.cancelGrab()">
<li v-for="(item, i) in items" :key="item.id" draggable="true"
@dragstart="reorder.onDragStart(i, $event)" @dragover="reorder.onDragOver(i, $event)"
@drop="reorder.onDrop(i, $event)" @dragend="reorder.onDragEnd()">
{{ item.nome }}
</li>
</ul>
<span id="istruzioni" hidden>{{ t('list.dragInstructions') }}</span>
<div class="sr-only" aria-live="assertive">{{ reorder.announcement.value }}</div>