feat(queue): add scroll to currently playing button
Adds a scrollToIndex method to VirtualList and a locate button in the Queue header that scrolls to center the currently playing track.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { GripVertical, Play, X } from "@lucide/svelte";
|
||||
import { GripVertical, LocateFixed, Play, X } from "@lucide/svelte";
|
||||
import * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import VirtualList from "$lib/components/ui/VirtualList.svelte";
|
||||
@@ -7,6 +7,16 @@
|
||||
import type { Track } from "$lib/player/types";
|
||||
import { songTypeNumberLabel } from "$lib/utils/amq";
|
||||
|
||||
let virtualList: ReturnType<typeof VirtualList>;
|
||||
|
||||
function scrollToCurrentlyPlaying() {
|
||||
if (player.currentId == null) return;
|
||||
const index = player.displayQueue.findIndex(
|
||||
(t) => t.id === player.currentId,
|
||||
);
|
||||
if (index !== -1) virtualList?.scrollToIndex(index);
|
||||
}
|
||||
|
||||
const ITEM_HEIGHT = 64;
|
||||
|
||||
function onRemove(id: number) {
|
||||
@@ -45,21 +55,31 @@
|
||||
<div
|
||||
class="flex flex-col h-full w-full bg-background/50 backdrop-blur rounded-lg border overflow-hidden"
|
||||
>
|
||||
<div class="px-4 py-3 border-b flex justify-between items-center bg-muted/20">
|
||||
<h3 class="font-semibold text-sm">
|
||||
Up Next
|
||||
<div
|
||||
class="px-4 py-3 border-b flex text-sm items-center justify-between bg-muted/20"
|
||||
>
|
||||
<div class="flex items-center gap-1">
|
||||
<h3 class="font-semibold">Up Next</h3>
|
||||
{#if player.displayQueue.length > 0}
|
||||
<span class="text-muted-foreground font-normal ml-1"
|
||||
>({player.displayQueue.length})</span
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
class="h-6 w-6 p-0"
|
||||
aria-label="Scroll to currently playing"
|
||||
onclick={scrollToCurrentlyPlaying}
|
||||
>
|
||||
<LocateFixed class="h-3 w-3" />
|
||||
</Button>
|
||||
{/if}
|
||||
</h3>
|
||||
</div>
|
||||
<AlertDialog.Root>
|
||||
<AlertDialog.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<Button variant="ghost" size="sm" class="h-6 w-6 p-0" {...props}>
|
||||
<span class="sr-only">Clear</span>
|
||||
<X class="h-3 w-3" />
|
||||
<X class="h-3 w-3" aria-label="Clear" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</AlertDialog.Trigger>
|
||||
@@ -81,6 +101,7 @@
|
||||
</div>
|
||||
|
||||
<VirtualList
|
||||
bind:this={virtualList}
|
||||
items={player.displayQueue}
|
||||
itemHeight={ITEM_HEIGHT}
|
||||
overscan={5}
|
||||
|
||||
@@ -56,6 +56,14 @@
|
||||
scrollTop = (e.target as HTMLDivElement).scrollTop;
|
||||
}
|
||||
|
||||
export function scrollToIndex(index: number) {
|
||||
if (!containerEl) return;
|
||||
containerEl.scrollTop = Math.max(
|
||||
0,
|
||||
index * itemHeight - containerHeight / 2 + itemHeight / 2,
|
||||
);
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (!containerEl) return;
|
||||
const ro = new ResizeObserver((entries) => {
|
||||
|
||||
Reference in New Issue
Block a user