worked on the miniplayer, volume slider is fucked
This commit is contained in:
@@ -2,39 +2,33 @@
|
|||||||
export let displayMode
|
export let displayMode
|
||||||
|
|
||||||
import IconButton from '$lib/components/utility/iconButton.svelte'
|
import IconButton from '$lib/components/utility/iconButton.svelte'
|
||||||
|
import VolumeSlider from '$lib/components/utility/volumeSlider.svelte'
|
||||||
import Slider from '$lib/components/utility/slider.svelte'
|
import Slider from '$lib/components/utility/slider.svelte'
|
||||||
import { currentlyPlaying } from '$lib/utils/stores.js'
|
import { currentlyPlaying } from '$lib/utils/stores.js'
|
||||||
import { slide } from 'svelte/transition'
|
import { slide } from 'svelte/transition'
|
||||||
import { getVolume, setVolume } from '$lib/utils/utils.js'
|
|
||||||
import { onMount } from 'svelte'
|
|
||||||
|
|
||||||
$: song = $currentlyPlaying
|
$: song = $currentlyPlaying
|
||||||
|
|
||||||
let songLiked = false
|
let songLiked = false
|
||||||
|
|
||||||
let volumeSlider
|
|
||||||
|
|
||||||
let volume
|
|
||||||
onMount(() => {
|
|
||||||
volume = getVolume()
|
|
||||||
})
|
|
||||||
$: console.log(volume)
|
|
||||||
|
|
||||||
const formatDuration = (timeMilliseconds) => {
|
const formatDuration = (timeMilliseconds) => {
|
||||||
const seconds = Math.floor((timeMilliseconds / 1000) % 60)
|
const seconds = Math.floor((timeMilliseconds / 1000) % 60)
|
||||||
const minutes = Math.floor((timeMilliseconds / 1000 / 60) % 60)
|
const minutes = Math.floor((timeMilliseconds / 1000 / 60) % 60)
|
||||||
|
|
||||||
return [minutes.toString(), seconds.toString.padStart(2, '0')].join(':')
|
return [minutes.toString(), seconds.toString().padStart(2, '0')].join(':')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let volume
|
||||||
|
$: console.log(volume)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if song}
|
{#if song}
|
||||||
<div id="player-wrapper" class="relative border-t-2 border-t-lazuli-primary bg-neutral-950" transition:slide={{ axis: 'y' }}>
|
<div id="player-wrapper" class="relative flex w-full justify-center" transition:slide={{ axis: 'y' }}>
|
||||||
{#if displayMode === 'vertical'}
|
{#if displayMode === 'vertical'}
|
||||||
<h1>Vertical Mode</h1>
|
<h1>Vertical Mode</h1>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="grid h-full grid-cols-3 grid-rows-1 p-3 text-sm">
|
<div class="grid h-full grid-cols-[1fr_auto_1fr] grid-rows-1 items-center gap-10 rounded-full bg-neutral-950 px-8 py-2.5 text-lg">
|
||||||
<section class="flex items-center gap-4">
|
<section class="flex h-full items-center justify-start gap-4 text-sm">
|
||||||
<img class="h-full rounded-lg object-contain" src={song.image} alt="{song.name} thumbnail" />
|
<img class="h-full rounded-lg object-contain" src={song.image} alt="{song.name} thumbnail" />
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<div>{song.name}</div>
|
<div>{song.name}</div>
|
||||||
@@ -48,44 +42,29 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
</section>
|
</section>
|
||||||
<section class="flex items-center justify-center gap-4 py-1 text-xl">
|
<section class="flex h-full flex-col justify-center gap-1">
|
||||||
<IconButton>
|
<div class="flex h-6 items-center justify-center gap-4">
|
||||||
|
<IconButton halo={false}>
|
||||||
<i slot="icon" class="fa-solid fa-backward-step" />
|
<i slot="icon" class="fa-solid fa-backward-step" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton>
|
<IconButton halo={false}>
|
||||||
<i slot="icon" class="fa-solid fa-play" />
|
<i slot="icon" class="fa-solid fa-play" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton on:click={() => ($currentlyPlaying = null)}>
|
<IconButton halo={false} on:click={() => ($currentlyPlaying = null)}>
|
||||||
<i slot="icon" class="fa-solid fa-stop" />
|
<i slot="icon" class="fa-solid fa-stop" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton>
|
<IconButton halo={false}>
|
||||||
<i slot="icon" class="fa-solid fa-forward-step" />
|
<i slot="icon" class="fa-solid fa-forward-step" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</section>
|
|
||||||
<section class="flex items-center justify-end gap-4 py-1 text-xl">
|
|
||||||
<div class="flex w-40 items-center gap-4">
|
|
||||||
<Slider
|
|
||||||
bind:this={volumeSlider}
|
|
||||||
initialValue={volume}
|
|
||||||
on:valuechange={(event) => {
|
|
||||||
volume = event.detail.value
|
|
||||||
setVolume(volume)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
class="transition-colors hover:text-lazuli-primary"
|
|
||||||
on:click={() => {
|
|
||||||
if (volume > 0) {
|
|
||||||
volume = 0
|
|
||||||
} else {
|
|
||||||
volume = getVolume()
|
|
||||||
}
|
|
||||||
volumeSlider.setValue(volume)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i class="fa-solid fa-volume-high" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex w-96 items-center gap-2 text-sm text-neutral-400">
|
||||||
|
<div class="whitespace-nowrap">0:00</div>
|
||||||
|
<Slider />
|
||||||
|
<div class="whitespace-nowrap">{formatDuration(song.duration)}</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="flex h-full items-center justify-end gap-4 py-3">
|
||||||
|
<VolumeSlider bind:volume />
|
||||||
<IconButton>
|
<IconButton>
|
||||||
<i slot="icon" class="fa-solid fa-ellipsis-vertical" />
|
<i slot="icon" class="fa-solid fa-ellipsis-vertical" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let halo = true
|
||||||
|
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
|
||||||
@@ -8,6 +9,7 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
class:disabled
|
class:disabled
|
||||||
|
class:halo
|
||||||
class="relative grid aspect-square h-full place-items-center transition-transform duration-75 active:scale-90 {disabled ? 'text-neutral-600' : ''}"
|
class="relative grid aspect-square h-full place-items-center transition-transform duration-75 active:scale-90 {disabled ? 'text-neutral-600' : ''}"
|
||||||
on:click|preventDefault={() => dispatch('click')}
|
on:click|preventDefault={() => dispatch('click')}
|
||||||
{disabled}
|
{disabled}
|
||||||
@@ -16,7 +18,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
button:not(.disabled)::before {
|
button:not(.disabled).halo::before {
|
||||||
content: '';
|
content: '';
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
@@ -26,7 +28,7 @@
|
|||||||
transition-duration: 200ms;
|
transition-duration: 200ms;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
button:not(.disabled):hover::before {
|
button:not(.disabled).halo:hover::before {
|
||||||
width: 130%;
|
width: 130%;
|
||||||
height: 130%;
|
height: 130%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
nav {
|
nav {
|
||||||
padding: 20px 2rem;
|
padding: 18px 2rem;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte'
|
export let value = 0
|
||||||
import { createEventDispatcher } from 'svelte'
|
|
||||||
|
|
||||||
export let initialValue = 0
|
|
||||||
onMount(() => (sliderValue = Number(initialValue)))
|
|
||||||
|
|
||||||
export const setValue = (value) => (sliderValue = value)
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
let sliderValue
|
|
||||||
let sliderTrail, sliderThumb
|
let sliderTrail, sliderThumb
|
||||||
|
|
||||||
const trackThumb = (sliderPos) => {
|
const trackThumb = (sliderPos) => {
|
||||||
@@ -17,17 +8,11 @@
|
|||||||
if (sliderTrail) sliderTrail.style.right = `${100 - sliderPos}%`
|
if (sliderTrail) sliderTrail.style.right = `${100 - sliderPos}%`
|
||||||
}
|
}
|
||||||
|
|
||||||
$: trackThumb(sliderValue)
|
$: trackThumb(value)
|
||||||
|
|
||||||
const handleKeyPress = (key) => {
|
const handleKeyPress = (key) => {
|
||||||
if ((key === 'ArrowRight' || key === 'ArrowUp') && sliderValue < 100) {
|
if ((key === 'ArrowRight' || key === 'ArrowUp') && value < 100) return (value += 1)
|
||||||
sliderValue += 1
|
if ((key === 'ArrowLeft' || key === 'ArrowDown') && value > 0) return (value -= 1)
|
||||||
return dispatch('valuechange', { value: sliderValue })
|
|
||||||
}
|
|
||||||
if ((key === 'ArrowLeft' || key === 'ArrowDown') && sliderValue > 0) {
|
|
||||||
sliderValue -= 1
|
|
||||||
return dispatch('valuechange', { value: sliderValue })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -36,25 +21,14 @@
|
|||||||
class="relative isolate h-1 w-full rounded-full bg-neutral-600"
|
class="relative isolate h-1 w-full rounded-full bg-neutral-600"
|
||||||
role="slider"
|
role="slider"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
aria-valuenow={sliderValue}
|
aria-valuenow={value}
|
||||||
aria-valuemin="0"
|
aria-valuemin="0"
|
||||||
aria-valuemax="100"
|
aria-valuemax="100"
|
||||||
on:keydown={(event) => handleKeyPress(event.key)}
|
on:keydown={(event) => handleKeyPress(event.key)}
|
||||||
>
|
>
|
||||||
<input
|
<input type="range" class="absolute z-10 h-1 w-full" step="any" min="0" max="100" bind:value tabindex="-1" aria-hidden="true" aria-disabled="true" />
|
||||||
type="range"
|
|
||||||
class="absolute z-10 h-1 w-full"
|
|
||||||
step="any"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
bind:value={sliderValue}
|
|
||||||
tabindex="-1"
|
|
||||||
aria-hidden="true"
|
|
||||||
aria-disabled="true"
|
|
||||||
on:mouseup={() => dispatch('valuechange', { value: sliderValue })}
|
|
||||||
/>
|
|
||||||
<span bind:this={sliderTrail} id="slider-trail" class="absolute left-0 h-1 rounded-full bg-white transition-colors" />
|
<span bind:this={sliderTrail} id="slider-trail" class="absolute left-0 h-1 rounded-full bg-white transition-colors" />
|
||||||
<span bind:this={sliderThumb} id="slider-thumb" class="absolute top-1/2 aspect-square h-4 -translate-x-1/2 -translate-y-1/2 rounded-full bg-white opacity-0 transition-opacity duration-300" />
|
<span bind:this={sliderThumb} id="slider-thumb" class="absolute top-1/2 aspect-square h-3 -translate-x-1/2 -translate-y-1/2 rounded-full bg-white opacity-0 transition-opacity duration-300" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
37
src/lib/components/utility/volumeSlider.svelte
Normal file
37
src/lib/components/utility/volumeSlider.svelte
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<script>
|
||||||
|
import Slider from '$lib/components/utility/slider.svelte'
|
||||||
|
import { getVolume, setVolume } from '$lib/utils/utils.js'
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
|
export let volume = 0
|
||||||
|
|
||||||
|
let muted = false
|
||||||
|
let storedVolume
|
||||||
|
|
||||||
|
onMount(() => (storedVolume = getVolume()))
|
||||||
|
|
||||||
|
$: changeVolume(storedVolume)
|
||||||
|
const changeVolume = (newVolume) => {
|
||||||
|
if (typeof newVolume === 'number' && !isNaN(newVolume)) setVolume(newVolume)
|
||||||
|
}
|
||||||
|
|
||||||
|
$: volume = muted ? 0 : storedVolume
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="volume-slider" class="flex h-full flex-row-reverse items-center gap-2">
|
||||||
|
<button id="volume-toggle" class="grid aspect-square h-full place-items-center transition-colors hover:text-lazuli-primary" on:click={() => (muted = !muted)}>
|
||||||
|
<i class="fa-solid {volume > 50 ? 'fa-volume-high' : volume > 0 ? 'fa-volume-low' : 'fa-volume-xmark'} w-full text-left text-base" />
|
||||||
|
</button>
|
||||||
|
<div id="slider-wrapper" class="w-0 transition-all duration-300">
|
||||||
|
<Slider bind:value={storedVolume} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#volume-slider:hover > #slider-wrapper {
|
||||||
|
width: 8rem;
|
||||||
|
}
|
||||||
|
#slider-wrapper:focus-within {
|
||||||
|
width: 8rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,7 +2,7 @@ import Joi from 'joi'
|
|||||||
|
|
||||||
export const getVolume = () => {
|
export const getVolume = () => {
|
||||||
const currentVolume = localStorage.getItem('volume')
|
const currentVolume = localStorage.getItem('volume')
|
||||||
if (currentVolume) return currentVolume
|
if (currentVolume) return Number(currentVolume)
|
||||||
|
|
||||||
const defaultVolume = 100
|
const defaultVolume = 100
|
||||||
localStorage.setItem('volume', defaultVolume)
|
localStorage.setItem('volume', defaultVolume)
|
||||||
|
|||||||
Reference in New Issue
Block a user