Drag was already wired via @neodrag/svelte; this adds the missing keyboard path so reorder is accessible without a pointer device. - The drag handle becomes a real <button> (focusable). Disabled when canDrag is false so non-owners + unavailable tracks can't attempt a move. - ArrowUp / ArrowDown on the focused handle call onMove(row.position, row.position +/- 1). Bounds clamping already lives in the page-level handler (playlists/[id]/+page.svelte:39), so out-of-range targets resolve as no-ops. - Matches QueueTrackRow's pattern: aria-label "Reorder track …", aria-keyshortcuts, swallow Space/Enter to avoid scrolling. Test updates: drag-handle assertions retargeted to /reorder track/ + two new ArrowUp/ArrowDown handler tests.
This commit is contained in:
@@ -41,6 +41,21 @@
|
||||
onMove?.(row.position, row.position + delta);
|
||||
}
|
||||
|
||||
function handleHandleKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
onMove?.(row.position, row.position - 1);
|
||||
} else if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
onMove?.(row.position, row.position + 1);
|
||||
} else if (e.key === ' ' || e.key === 'Enter') {
|
||||
// Reorder activates via arrow keys, not Space/Enter. Prevent
|
||||
// default so Space doesn't scroll the list and Enter doesn't
|
||||
// fire the button's no-op click.
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function format(sec: number): string {
|
||||
const m = Math.floor(sec / 60);
|
||||
const s = sec % 60;
|
||||
@@ -74,12 +89,18 @@
|
||||
}}
|
||||
>
|
||||
{#if isOwner}
|
||||
<span
|
||||
class="flex-shrink-0 cursor-grab text-text-muted active:cursor-grabbing"
|
||||
aria-label="Drag handle"
|
||||
<button
|
||||
type="button"
|
||||
disabled={!canDrag}
|
||||
aria-label="Reorder track (drag or use arrow keys)"
|
||||
aria-keyshortcuts="ArrowUp ArrowDown"
|
||||
onkeydown={handleHandleKeydown}
|
||||
class="flex-shrink-0 cursor-grab text-text-muted hover:text-text-primary
|
||||
active:cursor-grabbing focus-visible:outline focus-visible:outline-2
|
||||
focus-visible:outline-accent disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
<GripVertical size={14} strokeWidth={1} />
|
||||
</span>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user