resources/assets/js/components/ui/Volume.vue

Summary

Maintainability
Test Coverage
<template>
  <span id="volume" class="volume" :class="level">
    <span
      v-show="level === 'muted'"
      v-koel-tooltip.top
      role="button"
      tabindex="0"
      title="Unmute"
      @click="unmute"
    >
      <Icon :icon="faVolumeMute" fixed-width />
    </span>

    <span
      v-show="level !== 'muted'"
      v-koel-tooltip.top
      role="button"
      tabindex="0"
      title="Mute"
      @click="mute"
    >
      <Icon :icon="level === 'discreet' ? faVolumeLow : faVolumeHigh" fixed-width />
    </span>

    <input
      id="volumeInput"
      class="plyr__volume"
      max="10"
      role="slider"
      step="0.1"
      title="Volume"
      type="range"
      @change="broadcastVolume"
      @input="setVolume"
    >
  </span>
</template>

<script lang="ts" setup>
import { faVolumeHigh, faVolumeLow, faVolumeMute } from '@fortawesome/free-solid-svg-icons'
import { computed } from 'vue'
import { socketService, volumeManager } from '@/services'

const volume = volumeManager.volume

const level = computed(() => {
  if (volume.value === 0) return 'muted'
  if (volume.value < 3) return 'discreet'
  return 'loud'
})

const mute = () => volumeManager.mute()
const unmute = () => volumeManager.unmute()
const setVolume = (e: Event) => volumeManager.set(parseFloat((e.target as HTMLInputElement).value))

/**
 * Broadcast the volume changed event to remote controller.
 */
const broadcastVolume = (e: Event) => {
  socketService.broadcast('SOCKET_VOLUME_CHANGED', parseFloat((e.target as HTMLInputElement).value))
}
</script>

<style lang="scss">
#volume {
  position: relative;
  display: flex;
  align-items: center;

  [type=range] {
    margin: 0 0 0 8px;
    width: 120px;
    height: 4px;
    border-radius: 4px;
    position: relative;

    // increase click area
    &::before {
      position: absolute;
      content: ' ';
      left: 0;
      right: 0;
      top: -12px;
      bottom: -12px;
    }

    &::-webkit-slider-thumb {
      background: var(--color-text-secondary);
    }

    &:hover {
      &::-webkit-slider-thumb {
        background: var(--color-text-primary);
      }
    }
  }

  &.muted {
    [type=range] {
      &::-webkit-slider-thumb {
        background: transparent;
        box-shadow: none;
      }
    }
  }

  @media only screen and (max-width: 768px) {
    display: none !important;
  }
}
</style>