persistent volume control
This commit is contained in:
parent
4c8674d165
commit
e9b48cf01d
8 changed files with 86 additions and 47 deletions
src/lib
|
@ -2,12 +2,12 @@
|
|||
import { mdiChevronDown, mdiChevronUp } from "@mdi/js";
|
||||
|
||||
import Icon from "$lib/components/ui/Icon.svelte";
|
||||
import { layout, toggleCurrentCoverLarge } from "$lib/stores/layout";
|
||||
import { clientSettings, toggleCurrentCoverLarge } from "$lib/stores/clientSettings";
|
||||
|
||||
export let isLarge = false;
|
||||
</script>
|
||||
|
||||
{#if $layout.currentCoverLarge === isLarge}
|
||||
{#if $clientSettings.currentCoverLarge === isLarge}
|
||||
<div class="relative">
|
||||
<a href="/#">
|
||||
<div class:small={!isLarge}>
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
mdiHeart,
|
||||
mdiHeartOutline,
|
||||
mdiFullscreen,
|
||||
mdiVolumeHigh,
|
||||
mdiCardTextOutline,
|
||||
mdiPlaylistMusic,
|
||||
mdiPlay,
|
||||
mdiSkipPrevious,
|
||||
mdiSkipNext,
|
||||
mdiShuffle,
|
||||
mdiRepeat,
|
||||
mdiMicrophoneMessage,
|
||||
} from "@mdi/js";
|
||||
|
||||
import Icon from "$lib/components/ui/Icon.svelte";
|
||||
|
@ -19,11 +18,11 @@
|
|||
import TrackName from "./TrackName.svelte";
|
||||
import VolumeControl from "./VolumeControl.svelte";
|
||||
|
||||
import { layout } from "$lib/stores/layout";
|
||||
import { clientSettings } from "$lib/stores/clientSettings";
|
||||
</script>
|
||||
|
||||
<div class="playerbar">
|
||||
<div class="pr-4" class:pl-4={!$layout.currentCoverLarge}>
|
||||
<div class="pr-4" class:pl-4={!$clientSettings.currentCoverLarge}>
|
||||
<!-- current track -->
|
||||
<div class="playerbar-track">
|
||||
<div class="now-playing" aria-label="Now playing: Across the Sea by Leaves' Eyes">
|
||||
|
@ -68,7 +67,7 @@
|
|||
<div class="playerbar-options">
|
||||
<div>
|
||||
<button class="btn btn-ghost btn-circle btn-sm">
|
||||
<Icon path={mdiCardTextOutline} size={1.5} />
|
||||
<Icon path={mdiMicrophoneMessage} size={1.5} />
|
||||
</button>
|
||||
<button class="btn btn-ghost btn-circle btn-sm">
|
||||
<Icon path={mdiPlaylistMusic} size={1.5} />
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
|
||||
import Icon from "$lib/components/ui/Icon.svelte";
|
||||
import Slider from "$lib/components/ui/Slider.svelte";
|
||||
import { clientSettings, setVolume, toggleMute } from "$lib/stores/clientSettings";
|
||||
|
||||
let mute = false;
|
||||
let volume = 100;
|
||||
let mute: boolean;
|
||||
let volume = $clientSettings.volume || 100;
|
||||
let icon: string;
|
||||
|
||||
$: mute = Boolean($clientSettings.mute);
|
||||
|
||||
$: icon = mute
|
||||
? mdiVolumeMute
|
||||
: volume > 50
|
||||
|
@ -15,11 +18,31 @@
|
|||
: volume > 10
|
||||
? mdiVolumeMedium
|
||||
: mdiVolumeLow;
|
||||
|
||||
const SCROLL_STEP = 5;
|
||||
|
||||
function onScroll(e: WheelEvent) {
|
||||
if (e.deltaY < 0) {
|
||||
volume = Math.min(volume + SCROLL_STEP, 100);
|
||||
updateVolume();
|
||||
} else if (e.deltaY > 0) {
|
||||
volume = Math.max(volume - SCROLL_STEP, 0);
|
||||
updateVolume();
|
||||
}
|
||||
}
|
||||
|
||||
function updateVolume() {
|
||||
setVolume(volume);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button class="btn btn-ghost btn-circle btn-sm" on:click={() => (mute = !mute)}>
|
||||
<button
|
||||
class="btn btn-ghost btn-circle btn-sm"
|
||||
on:click={toggleMute}
|
||||
on:wheel={onScroll}
|
||||
>
|
||||
<Icon path={icon} size={1.5} />
|
||||
</button>
|
||||
<div class="inline-block w-20">
|
||||
<Slider min={0} max={100} bind:value={volume} />
|
||||
<div class="inline-block w-20" on:wheel={onScroll}>
|
||||
<Slider min={0} max={100} bind:value={volume} on:change={updateVolume} liveUpdate />
|
||||
</div>
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
}
|
||||
|
||||
function reset() {
|
||||
position = 0;
|
||||
if (timer !== null) {
|
||||
window.clearTimeout(timer);
|
||||
timer = null;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
export let min = 0;
|
||||
export let max = 100;
|
||||
export let value = 0;
|
||||
// Update value in real time while dragging
|
||||
export let liveUpdate = false;
|
||||
|
||||
// Node bindings
|
||||
let element: HTMLElement;
|
||||
|
@ -27,12 +29,9 @@
|
|||
}
|
||||
|
||||
// Allows both bind:value and on:change for parent value retrieval
|
||||
function setValue(newValue: number) {
|
||||
if (value !== newValue) {
|
||||
value = newValue;
|
||||
valueTmp = newValue;
|
||||
dispatch("change", { value });
|
||||
}
|
||||
function setValue(newValue: number, dispatchEvent = true) {
|
||||
value = newValue;
|
||||
if (dispatchEvent) dispatch("change", { value });
|
||||
}
|
||||
|
||||
function onTrackEvent(e: Event) {
|
||||
|
@ -41,21 +40,15 @@
|
|||
onDragStart();
|
||||
}
|
||||
|
||||
function onScroll(e: WheelEvent) {
|
||||
if (e.deltaY > 0) {
|
||||
setValue(Math.min(value + 5, max));
|
||||
} else if (e.deltaY < 0) {
|
||||
setValue(Math.max(value - 5, min));
|
||||
}
|
||||
}
|
||||
|
||||
function onDragStart() {
|
||||
holding = true;
|
||||
}
|
||||
|
||||
function onDragEnd() {
|
||||
holding = false;
|
||||
setValue(valueTmp);
|
||||
if (holding) {
|
||||
setValue(valueTmp);
|
||||
holding = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Accessible keypress handling
|
||||
|
@ -74,7 +67,7 @@
|
|||
}
|
||||
if (e.key === "ArrowDown" || e.key === "ArrowLeft") {
|
||||
if (value - throttled < min || value <= min) {
|
||||
valueTmp = min;
|
||||
setValue(min);
|
||||
} else {
|
||||
setValue(value - throttled);
|
||||
}
|
||||
|
@ -100,6 +93,10 @@
|
|||
|
||||
// Limit value min -> max
|
||||
valueTmp = (percent * (max - min)) / 100 + min;
|
||||
|
||||
if (liveUpdate) {
|
||||
setValue(valueTmp, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Handles both dragging of touch/mouse as well as simple one-off click/touches
|
||||
|
@ -116,7 +113,7 @@
|
|||
? (e as TouchEvent).touches[0]
|
||||
: (e as MouseEvent);
|
||||
|
||||
if (elementRect !== null) {
|
||||
if (elementRect !== null && !liveUpdate) {
|
||||
const dist = Math.abs(elementRect.y - coords.clientY);
|
||||
if (dist > window.innerHeight * 0.4) {
|
||||
valueTmp = value;
|
||||
|
@ -130,7 +127,9 @@
|
|||
// React to left position of element relative to window
|
||||
$: if (element) elementRect = element.getBoundingClientRect();
|
||||
|
||||
$: percent = ((Math.min(Math.max(valueTmp, min), max) - min) * 100) / (max - min);
|
||||
$: percent =
|
||||
((Math.min(Math.max(holding ? valueTmp : value, min), max) - min) * 100) /
|
||||
(max - min);
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
|
@ -154,7 +153,6 @@
|
|||
aria-valuenow={value}
|
||||
on:mousedown={onTrackEvent}
|
||||
on:touchstart={onTrackEvent}
|
||||
on:wheel={onScroll}
|
||||
style={`--slider-transform:${percent}%;`}
|
||||
>
|
||||
<div class="slider-bg slider-shape">
|
||||
|
|
30
src/lib/stores/clientSettings.ts
Normal file
30
src/lib/stores/clientSettings.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import type { ClientSettings } from "$lib/util/types";
|
||||
import { persisted } from "svelte-local-storage-store";
|
||||
import type { Writable } from "svelte/store";
|
||||
|
||||
export const clientSettings: Writable<ClientSettings> = persisted("settings", {
|
||||
currentCoverLarge: false,
|
||||
mute: false,
|
||||
volume: 100,
|
||||
});
|
||||
|
||||
export function toggleCurrentCoverLarge() {
|
||||
clientSettings.update((v: ClientSettings) => {
|
||||
v.currentCoverLarge = !v.currentCoverLarge;
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleMute() {
|
||||
clientSettings.update((v: ClientSettings) => {
|
||||
v.mute = !v.mute;
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
export function setVolume(volume: number) {
|
||||
clientSettings.update((v: ClientSettings) => {
|
||||
v.volume = volume;
|
||||
return v;
|
||||
});
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import { persisted } from "svelte-local-storage-store";
|
||||
import type { SettingsLayout } from "$lib/util/types";
|
||||
import type { Writable } from "svelte/store";
|
||||
|
||||
export const layout: Writable<SettingsLayout> = persisted("settings_layout", {
|
||||
currentCoverLarge: false,
|
||||
});
|
||||
|
||||
export function toggleCurrentCoverLarge() {
|
||||
layout.update((v: SettingsLayout) => {
|
||||
v.currentCoverLarge = !v.currentCoverLarge;
|
||||
return v;
|
||||
});
|
||||
}
|
|
@ -7,6 +7,8 @@ export type Link = {
|
|||
title: string;
|
||||
};
|
||||
|
||||
export type SettingsLayout = {
|
||||
export type ClientSettings = {
|
||||
currentCoverLarge: boolean;
|
||||
volume: number;
|
||||
mute: boolean;
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue