1
0
Fork 0
muzika-gromche/Frontend/src/components/library/VolumeSlider.vue

94 lines
2.7 KiB
Vue

<script setup lang="ts">
import VolumeDown from '@material-design-icons/svg/outlined/volume_down.svg'
import VolumeMute from '@material-design-icons/svg/outlined/volume_mute.svg'
import VolumeOff from '@material-design-icons/svg/outlined/volume_off.svg'
import VolumeUp from '@material-design-icons/svg/outlined/volume_up.svg'
import { computed, useId } from 'vue'
import Slider from '@/components/library/Slider.vue'
import classes from './ToolBar.module.css'
const {
defaultVolume = 1,
enableBoost = true,
} = defineProps<{
defaultVolume?: number
// Boost increases volume range from 0..1 up to 0..1.5
enableBoost?: boolean
}>()
/** Muted flag. When muted, volume slider shows zero value, but remains interactive. */
const muted = defineModel<boolean>('muted', { required: true })
/** Volume in range 0..1 or 0..1.5 if boost is enabled. */
const volume = defineModel<number>('volume', { required: true })
const mutedId = useId()
const mutedTitle = computed(() => muted.value ? 'Unmute' : 'Mute')
function toggleMuted() {
muted.value = !muted.value
}
const volumeMax = computed(() => enableBoost ? 1.5 : 1.0)
const sliderSteps = computed(() => enableBoost ? 24 : 16)
function toSteps(volume: number): number {
return volume / volumeMax.value * sliderSteps.value
}
function fromSteps(steps: number): number {
return steps / sliderSteps.value * volumeMax.value
}
const volumeDisplay = computed<number>({
get() {
// displays zero volume when muted, despite actual unmuted volume is remembered
return muted.value ? 0 : toSteps(volume.value)
},
set(value: number) {
volume.value = fromSteps(value)
},
})
function reset() {
volume.value = defaultVolume
}
const defaultValue = computed(() => toSteps(defaultVolume))
</script>
<template>
<div class="tw:flex tw:flex-row tw:gap-2 tw:items-center">
<input
:id="mutedId"
type="checkbox"
class="tw:hidden"
:title="mutedTitle"
@click="toggleMuted"
>
<label
:for="mutedId"
:class="[classes.toolbarControl, classes.toolButton]"
:title="mutedTitle"
tabindex="0"
>
<VolumeOff v-if="muted" class="tw:text-[#e64b3d]" />
<!-- transforms are needed because icons are centered rather than aligned with each other -->
<VolumeMute v-else-if="volume < 0.33" style="transform: translateX(-8px);" />
<VolumeDown v-else-if="volume < 0.66" style="transform: translateX(-4px);" />
<VolumeUp v-else :class="{ 'tw:text-[#e8ba3d]': volume > 1.01 }" />
</label>
<Slider
v-model.number="volumeDisplay"
:min="0"
:max="sliderSteps"
:step="1"
:reset="reset"
:default-value
title="Volume"
/>
</div>
</template>
<style scoped></style>