player use icons
This commit is contained in:
@@ -35,6 +35,21 @@
|
|||||||
} from "$lib/player/player.svelte";
|
} from "$lib/player/player.svelte";
|
||||||
import { createMediaSessionBindings } from "$lib/player/media-session";
|
import { createMediaSessionBindings } from "$lib/player/media-session";
|
||||||
|
|
||||||
|
import {
|
||||||
|
ChevronsUpDown,
|
||||||
|
ListX,
|
||||||
|
Pause as PauseIcon,
|
||||||
|
PanelRightClose,
|
||||||
|
PanelRightOpen,
|
||||||
|
Play as PlayIcon,
|
||||||
|
Repeat,
|
||||||
|
Shuffle,
|
||||||
|
SkipBack,
|
||||||
|
SkipForward,
|
||||||
|
Volume2,
|
||||||
|
X,
|
||||||
|
} from "@lucide/svelte";
|
||||||
|
|
||||||
let audioEl: HTMLAudioElement | null = null;
|
let audioEl: HTMLAudioElement | null = null;
|
||||||
|
|
||||||
// Best-effort preload of the upcoming track's URL
|
// Best-effort preload of the upcoming track's URL
|
||||||
@@ -439,12 +454,17 @@
|
|||||||
>
|
>
|
||||||
<div class="mx-auto flex max-w-4xl items-center gap-2 px-3 py-2">
|
<div class="mx-auto flex max-w-4xl items-center gap-2 px-3 py-2">
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => toggleUiOpen()}
|
onclick={() => toggleUiOpen()}
|
||||||
aria-label={snap.uiOpen ? "Close player" : "Open player"}
|
aria-label={snap.uiOpen ? "Close player" : "Open player"}
|
||||||
|
title={snap.uiOpen ? "Close player" : "Open player"}
|
||||||
>
|
>
|
||||||
{snap.uiOpen ? "Close" : "Player"}
|
{#if snap.uiOpen}
|
||||||
|
<X class="h-4 w-4" />
|
||||||
|
{:else}
|
||||||
|
<ChevronsUpDown class="h-4 w-4" />
|
||||||
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -502,20 +522,24 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!canPrev}
|
disabled={!canPrev}
|
||||||
|
aria-label="Previous"
|
||||||
|
title="Previous"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
prev(currentTime);
|
prev(currentTime);
|
||||||
void syncAndAutoplay();
|
void syncAndAutoplay();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Prev
|
<SkipBack class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
|
aria-label={isPlaying ? "Pause" : "Play"}
|
||||||
|
title={isPlaying ? "Pause" : "Play"}
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (!audioEl) return;
|
if (!audioEl) return;
|
||||||
if (audioEl.paused) void audioEl.play();
|
if (audioEl.paused) void audioEl.play();
|
||||||
@@ -523,19 +547,25 @@
|
|||||||
}}
|
}}
|
||||||
disabled={!snap.currentTrack}
|
disabled={!snap.currentTrack}
|
||||||
>
|
>
|
||||||
{isPlaying ? "Pause" : "Play"}
|
{#if isPlaying}
|
||||||
|
<PauseIcon class="h-4 w-4" />
|
||||||
|
{:else}
|
||||||
|
<PlayIcon class="h-4 w-4" />
|
||||||
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!canNext}
|
disabled={!canNext}
|
||||||
|
aria-label="Next"
|
||||||
|
title="Next"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
next();
|
next();
|
||||||
void syncAndAutoplay();
|
void syncAndAutoplay();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Next
|
<SkipForward class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -544,22 +574,29 @@
|
|||||||
<div class="mx-auto max-w-4xl space-y-3">
|
<div class="mx-auto max-w-4xl space-y-3">
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => toggleShuffle()}
|
onclick={() => toggleShuffle()}
|
||||||
|
aria-label={snap.shuffleEnabled ? "Shuffle on" : "Shuffle off"}
|
||||||
|
title={snap.shuffleEnabled ? "Shuffle on" : "Shuffle off"}
|
||||||
>
|
>
|
||||||
Shuffle: {snap.shuffleEnabled ? "On" : "Off"}
|
<Shuffle class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => toggleWrap()}
|
onclick={() => toggleWrap()}
|
||||||
|
aria-label={snap.wrapEnabled ? "Wrap on" : "Wrap off"}
|
||||||
|
title={snap.wrapEnabled ? "Wrap on" : "Wrap off"}
|
||||||
>
|
>
|
||||||
Wrap: {snap.wrapEnabled ? "On" : "Off"}
|
<Repeat class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<label class="ml-auto flex items-center gap-2 text-sm">
|
<label class="ml-auto flex items-center gap-2 text-sm">
|
||||||
<span class="text-muted-foreground">Vol</span>
|
<span class="text-muted-foreground" aria-hidden="true">
|
||||||
|
<Volume2 class="h-4 w-4" />
|
||||||
|
</span>
|
||||||
|
<span class="sr-only">Volume</span>
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="0"
|
min="0"
|
||||||
@@ -623,11 +660,13 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-xs"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => removeTrack(item.track.id)}
|
onclick={() => removeTrack(item.track.id)}
|
||||||
|
aria-label="Remove from queue"
|
||||||
|
title="Remove from queue"
|
||||||
>
|
>
|
||||||
Remove
|
<ListX class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -679,12 +718,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => toggleUiOpen()}
|
onclick={() => toggleUiOpen()}
|
||||||
aria-label={snap.uiOpen ? "Hide player sidebar" : "Show player sidebar"}
|
aria-label={snap.uiOpen ? "Hide player sidebar" : "Show player sidebar"}
|
||||||
|
title={snap.uiOpen ? "Hide player sidebar" : "Show player sidebar"}
|
||||||
>
|
>
|
||||||
{snap.uiOpen ? "Hide" : "Show"}
|
{#if snap.uiOpen}
|
||||||
|
<PanelRightClose class="h-4 w-4" />
|
||||||
|
{:else}
|
||||||
|
<PanelRightOpen class="h-4 w-4" />
|
||||||
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -697,38 +741,46 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => toggleShuffle()}
|
onclick={() => toggleShuffle()}
|
||||||
|
aria-label={snap.shuffleEnabled ? "Shuffle on" : "Shuffle off"}
|
||||||
|
title={snap.shuffleEnabled ? "Shuffle on" : "Shuffle off"}
|
||||||
>
|
>
|
||||||
Shuffle: {snap.shuffleEnabled ? "On" : "Off"}
|
<Shuffle class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => toggleWrap()}
|
onclick={() => toggleWrap()}
|
||||||
|
aria-label={snap.wrapEnabled ? "Wrap on" : "Wrap off"}
|
||||||
|
title={snap.wrapEnabled ? "Wrap on" : "Wrap off"}
|
||||||
>
|
>
|
||||||
Wrap: {snap.wrapEnabled ? "On" : "Off"}
|
<Repeat class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!canPrev}
|
disabled={!canPrev}
|
||||||
|
aria-label="Previous"
|
||||||
|
title="Previous"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
prev(currentTime);
|
prev(currentTime);
|
||||||
void syncAndAutoplay();
|
void syncAndAutoplay();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Prev
|
<SkipBack class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
|
aria-label={isPlaying ? "Pause" : "Play"}
|
||||||
|
title={isPlaying ? "Pause" : "Play"}
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (!audioEl) return;
|
if (!audioEl) return;
|
||||||
if (audioEl.paused) void audioEl.play();
|
if (audioEl.paused) void audioEl.play();
|
||||||
@@ -736,23 +788,32 @@
|
|||||||
}}
|
}}
|
||||||
disabled={!snap.currentTrack}
|
disabled={!snap.currentTrack}
|
||||||
>
|
>
|
||||||
{isPlaying ? "Pause" : "Play"}
|
{#if isPlaying}
|
||||||
|
<PauseIcon class="h-4 w-4" />
|
||||||
|
{:else}
|
||||||
|
<PlayIcon class="h-4 w-4" />
|
||||||
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!canNext}
|
disabled={!canNext}
|
||||||
|
aria-label="Next"
|
||||||
|
title="Next"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
next();
|
next();
|
||||||
void syncAndAutoplay();
|
void syncAndAutoplay();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Next
|
<SkipForward class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<label class="ml-auto flex items-center gap-2 text-sm">
|
<label class="ml-auto flex items-center gap-2 text-sm">
|
||||||
<span class="text-muted-foreground">Vol</span>
|
<span class="text-muted-foreground" aria-hidden="true">
|
||||||
|
<Volume2 class="h-4 w-4" />
|
||||||
|
</span>
|
||||||
|
<span class="sr-only">Volume</span>
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="0"
|
min="0"
|
||||||
@@ -819,11 +880,13 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="rounded border px-2 py-1 text-xs"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => removeTrack(item.track.id)}
|
onclick={() => removeTrack(item.track.id)}
|
||||||
|
aria-label="Remove from queue"
|
||||||
|
title="Remove from queue"
|
||||||
>
|
>
|
||||||
Remove
|
<ListX class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
ListPlus,
|
||||||
|
Play as PlayIcon,
|
||||||
|
SkipForward,
|
||||||
|
Trash2,
|
||||||
|
} from "@lucide/svelte";
|
||||||
import {
|
import {
|
||||||
addToQueue,
|
addToQueue,
|
||||||
hasTrack,
|
hasTrack,
|
||||||
@@ -77,49 +83,57 @@
|
|||||||
<div class="mt-2 flex flex-wrap items-center gap-2">
|
<div class="mt-2 flex flex-wrap items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
disabled={!track}
|
disabled={!track}
|
||||||
|
title="Play"
|
||||||
|
aria-label="Play"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (!track) return;
|
if (!track) return;
|
||||||
play(track);
|
play(track);
|
||||||
requestGlobalAutoplay();
|
requestGlobalAutoplay();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Play
|
<PlayIcon class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
disabled={!track}
|
disabled={!track}
|
||||||
|
title="Play next"
|
||||||
|
aria-label="Play next"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (!track) return;
|
if (!track) return;
|
||||||
playNext(track);
|
playNext(track);
|
||||||
requestGlobalAutoplay();
|
requestGlobalAutoplay();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Play next
|
<SkipForward class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
disabled={!track}
|
disabled={!track}
|
||||||
|
title="Add to queue"
|
||||||
|
aria-label="Add to queue"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (!track) return;
|
if (!track) return;
|
||||||
addToQueue(track);
|
addToQueue(track);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add to queue
|
<ListPlus class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{#if isQueued}
|
{#if isQueued}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded border px-2 py-1 text-sm"
|
class="inline-flex h-8 w-8 items-center justify-center rounded border"
|
||||||
|
title="Remove from queue"
|
||||||
|
aria-label="Remove from queue"
|
||||||
onclick={() => removeTrack(annSongId)}
|
onclick={() => removeTrack(annSongId)}
|
||||||
>
|
>
|
||||||
Remove
|
<Trash2 class="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<span class="text-xs text-muted-foreground">Queued</span>
|
<span class="text-xs text-muted-foreground">Queued</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user