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">
|
<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 * as AlertDialog from "$lib/components/ui/alert-dialog";
|
||||||
import { Button } from "$lib/components/ui/button";
|
import { Button } from "$lib/components/ui/button";
|
||||||
import VirtualList from "$lib/components/ui/VirtualList.svelte";
|
import VirtualList from "$lib/components/ui/VirtualList.svelte";
|
||||||
@@ -7,6 +7,16 @@
|
|||||||
import type { Track } from "$lib/player/types";
|
import type { Track } from "$lib/player/types";
|
||||||
import { songTypeNumberLabel } from "$lib/utils/amq";
|
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;
|
const ITEM_HEIGHT = 64;
|
||||||
|
|
||||||
function onRemove(id: number) {
|
function onRemove(id: number) {
|
||||||
@@ -45,21 +55,31 @@
|
|||||||
<div
|
<div
|
||||||
class="flex flex-col h-full w-full bg-background/50 backdrop-blur rounded-lg border overflow-hidden"
|
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">
|
<div
|
||||||
<h3 class="font-semibold text-sm">
|
class="px-4 py-3 border-b flex text-sm items-center justify-between bg-muted/20"
|
||||||
Up Next
|
>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<h3 class="font-semibold">Up Next</h3>
|
||||||
{#if player.displayQueue.length > 0}
|
{#if player.displayQueue.length > 0}
|
||||||
<span class="text-muted-foreground font-normal ml-1"
|
<span class="text-muted-foreground font-normal ml-1"
|
||||||
>({player.displayQueue.length})</span
|
>({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}
|
{/if}
|
||||||
</h3>
|
</div>
|
||||||
<AlertDialog.Root>
|
<AlertDialog.Root>
|
||||||
<AlertDialog.Trigger>
|
<AlertDialog.Trigger>
|
||||||
{#snippet child({ props })}
|
{#snippet child({ props })}
|
||||||
<Button variant="ghost" size="sm" class="h-6 w-6 p-0" {...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" aria-label="Clear" />
|
||||||
<X class="h-3 w-3" />
|
|
||||||
</Button>
|
</Button>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</AlertDialog.Trigger>
|
</AlertDialog.Trigger>
|
||||||
@@ -81,6 +101,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<VirtualList
|
<VirtualList
|
||||||
|
bind:this={virtualList}
|
||||||
items={player.displayQueue}
|
items={player.displayQueue}
|
||||||
itemHeight={ITEM_HEIGHT}
|
itemHeight={ITEM_HEIGHT}
|
||||||
overscan={5}
|
overscan={5}
|
||||||
|
|||||||
@@ -56,6 +56,14 @@
|
|||||||
scrollTop = (e.target as HTMLDivElement).scrollTop;
|
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(() => {
|
$effect(() => {
|
||||||
if (!containerEl) return;
|
if (!containerEl) return;
|
||||||
const ro = new ResizeObserver((entries) => {
|
const ro = new ResizeObserver((entries) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user