koel/resources/assets/js/remote/components/VolumeControl.vue

109 lines
2.5 KiB
Vue
Raw Normal View History

2024-04-04 22:20:42 +00:00
<template>
<span class="volume">
2024-05-08 09:11:32 +00:00
<OnClickOutside @trigger="closeVolumeSlider">
<span v-show="showingVolumeSlider" id="volumeSlider" ref="volumeSlider" />
</OnClickOutside>
2024-04-04 22:20:42 +00:00
<span class="icon" @click.stop="toggleVolumeSlider">
<Icon :icon="muted ? faVolumeMute : faVolumeHigh" fixed-width />
</span>
</span>
</template>
2024-04-23 21:01:27 +00:00
<script lang="ts" setup>
2024-04-04 22:20:42 +00:00
import { faVolumeHigh, faVolumeMute } from '@fortawesome/free-solid-svg-icons'
2024-05-08 09:11:32 +00:00
import noUISlider from 'nouislider'
import { OnClickOutside } from '@vueuse/components'
2024-04-04 22:20:42 +00:00
import { inject, onMounted, ref, watch } from 'vue'
2024-05-08 09:11:32 +00:00
import { socketService } from '@/services'
import type { RemoteState } from '@/remote/types'
2024-04-04 22:20:42 +00:00
const DEFAULT_VOLUME = 7
const volumeSlider = ref<EqualizerBandElement>()
const muted = ref(false)
const showingVolumeSlider = ref(false)
const toggleVolumeSlider = () => (showingVolumeSlider.value = !showingVolumeSlider.value)
const closeVolumeSlider = () => (showingVolumeSlider.value = false)
const state = inject<RemoteState>('state')
watch(() => state?.volume, volume => volumeSlider.value?.noUiSlider?.set(volume || DEFAULT_VOLUME))
2024-04-04 22:20:42 +00:00
onMounted(() => {
noUISlider.create(volumeSlider.value!, {
orientation: 'vertical',
connect: [true, false],
start: state?.volume || DEFAULT_VOLUME,
range: { min: 0, max: 10 },
direction: 'rtl',
2024-04-04 22:20:42 +00:00
})
if (!volumeSlider.value?.noUiSlider) {
throw new Error('Failed to initialize noUISlider on element #volumeSlider')
}
volumeSlider.value!.noUiSlider.on('change', (values: string[], handle: number) => {
const volume = values[handle]
muted.value = !volume
socketService.broadcast('SOCKET_SET_VOLUME', volume)
})
})
</script>
<style lang="postcss">
.volume {
position: relative;
.icon {
width: 20px;
display: inline-block;
text-align: center;
}
2024-04-18 21:51:06 +00:00
.noUi-target {
background: var(--color-text-primary);
border-radius: 4px;
border: 0;
box-shadow: none;
left: 7px;
}
2024-04-04 22:20:42 +00:00
2024-04-18 21:51:06 +00:00
.noUi-base {
height: calc(100% - 16px);
border-radius: 4px;
}
2024-04-04 22:20:42 +00:00
2024-04-18 21:51:06 +00:00
.noUi-vertical {
width: 8px;
}
2024-04-04 22:20:42 +00:00
2024-04-18 21:51:06 +00:00
.noUi-vertical .noUi-handle {
width: 16px;
height: 16px;
border-radius: 50%;
border: 0;
left: -12px;
top: 0;
background: var(--color-highlight);
box-shadow: none;
&::after,
&::before {
2024-04-18 21:51:06 +00:00
display: none;
}
}
2024-04-04 22:20:42 +00:00
2024-04-18 21:51:06 +00:00
.noUi-connect {
background: transparent;
box-shadow: none;
2024-04-04 22:20:42 +00:00
}
}
2024-04-18 21:51:06 +00:00
#volumeSlider {
height: 80px;
position: absolute;
bottom: calc(50% + 26px);
2024-04-04 22:20:42 +00:00
}
</style>