audiobookshelf/client/components/controls/PlaybackSpeedControl.vue
Miró Allard f7f80e03e0 Use multiplication symbol instead of letter x
Use the proper multiplication symbol for the playback speed. The order
of the number and symbol is reversed so it is read "times 1.0" instead
of "1.0 times" (or previously "1.0 x"). Between the symbol and the
number, a narrow no-break space ( ) is added for proper spacing.
The classes of some spans are removed for better alignment.
2025-03-11 12:58:18 +01:00

124 lines
4.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div ref="wrapper" class="relative ml-4 sm:ml-8" v-click-outside="clickOutside">
<div class="flex items-center justify-center text-gray-300 cursor-pointer h-full" @mousedown.prevent @mouseup.prevent @click="setShowMenu(true)">
<span class="text-gray-200 text-sm sm:text-base">×&#8239;{{ playbackRateDisplay }}</span>
</div>
<div v-show="showMenu" class="absolute -top-[5.5rem] z-20 bg-bg border-black-200 border shadow-xl rounded-lg" :style="{ left: menuLeft + 'px' }">
<div class="absolute -bottom-1.5 right-0 w-full flex justify-center" :style="{ left: arrowLeft + 'px' }">
<div class="arrow-down" />
</div>
<div class="flex items-center h-9 relative overflow-hidden rounded-lg" style="width: 220px">
<template v-for="rate in rates">
<div :key="rate" class="h-full border-black-300 w-11 cursor-pointer border rounded-sm" :class="value === rate ? 'bg-black-100' : 'hover:bg-black hover:bg-opacity-10'" style="min-width: 44px; max-width: 44px" @click="set(rate)">
<div class="w-full h-full flex justify-center items-center">
<p class="text-xs text-center">×&#8239;{{ rate }}</p>
</div>
</div>
</template>
</div>
<div class="w-full py-1 px-1">
<div class="flex items-center justify-between">
<ui-icon-btn :disabled="!canDecrement" icon="remove" @click="decrement" />
<p class="px-2 text-2xl sm:text-3xl"><span class="text-2xl">×&#8239;</span>{{ playbackRateDisplay }}</p>
<ui-icon-btn :disabled="!canIncrement" icon="add" @click="increment" />
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: [String, Number],
default: 1
},
playbackRateIncrementDecrement: {
type: Number,
default: 0.1
}
},
data() {
return {
showMenu: false,
currentPlaybackRate: 0,
MIN_SPEED: 0.5,
MAX_SPEED: 10,
menuLeft: -96,
arrowLeft: 0
}
},
computed: {
playbackRate: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
},
rates() {
return [0.5, 1, 1.2, 1.5, 2]
},
canIncrement() {
return this.playbackRate + this.playbackRateIncrementDecrement <= this.MAX_SPEED
},
canDecrement() {
return this.playbackRate - this.playbackRateIncrementDecrement >= this.MIN_SPEED
},
playbackRateDisplay() {
if (this.playbackRateIncrementDecrement == 0.05) return this.playbackRate.toFixed(2)
// For 0.1 increment: Only show 2 decimal places if the playback rate is 2 decimals
const numDecimals = String(this.playbackRate).split('.')[1]?.length || 0
if (numDecimals <= 1) return this.playbackRate.toFixed(1)
return this.playbackRate.toFixed(2)
}
},
methods: {
clickOutside() {
this.setShowMenu(false)
},
set(rate) {
this.playbackRate = Number(rate)
this.$nextTick(() => this.setShowMenu(false))
},
increment() {
if (this.playbackRate + this.playbackRateIncrementDecrement > this.MAX_SPEED) return
var newPlaybackRate = this.playbackRate + this.playbackRateIncrementDecrement
this.playbackRate = Number(newPlaybackRate.toFixed(2))
},
decrement() {
if (this.playbackRate - this.playbackRateIncrementDecrement < this.MIN_SPEED) return
var newPlaybackRate = this.playbackRate - this.playbackRateIncrementDecrement
this.playbackRate = Number(newPlaybackRate.toFixed(2))
},
updateMenuPositions() {
if (!this.$refs.wrapper) return
const boundingBox = this.$refs.wrapper.getBoundingClientRect()
if (boundingBox.left + 110 > window.innerWidth - 10) {
this.menuLeft = window.innerWidth - 230 - boundingBox.left
this.arrowLeft = Math.abs(this.menuLeft) - 96
} else {
this.menuLeft = -96
this.arrowLeft = 0
}
},
setShowMenu(val) {
if (val) {
this.updateMenuPositions()
this.currentPlaybackRate = this.playbackRate
} else if (this.currentPlaybackRate !== this.playbackRate) {
this.$emit('change', this.playbackRate)
}
this.showMenu = val
}
},
mounted() {
this.currentPlaybackRate = this.playbackRate
}
}
</script>