diff --git a/src/client/components/ui/range.vue b/src/client/components/ui/range.vue new file mode 100644 index 0000000000..ab59bf516c --- /dev/null +++ b/src/client/components/ui/range.vue @@ -0,0 +1,138 @@ +<template> +<div class="timctyfi" :class="{ focused, disabled }"> + <div class="icon"><slot name="icon"></slot></div> + <span class="title"><slot name="title"></slot></span> + <input + type="range" + ref="input" + v-model="v" + :disabled="disabled" + :min="min" + :max="max" + :step="step" + :autofocus="autofocus" + @focus="focused = true" + @blur="focused = false" + @input="$emit('input', $event.target.value)" + /> +</div> +</template> + +<script lang="ts"> +import Vue from "vue"; +export default Vue.extend({ + props: { + value: { + type: Number, + required: false, + default: 0 + }, + disabled: { + type: Boolean, + required: false, + default: false + }, + min: { + type: String, + required: false, + default: "0" + }, + max: { + type: String, + required: false, + default: "100" + }, + step: { + type: String, + required: false, + default: "1" + }, + autofocus: { + type: Boolean, + required: false + } + }, + data() { + return { + v: this.value, + focused: false + }; + }, + watch: { + value(v) { + this.v = parseFloat(v); + } + }, + mounted() { + if (this.autofocus) { + this.$nextTick(() => { + this.$refs.input.focus(); + }); + } + } +}); +</script> + +<style lang="scss" scoped> +.timctyfi { + position: relative; + margin: 8px; + + > .icon { + display: inline-block; + width: 24px; + text-align: center; + } + + > .title { + pointer-events: none; + font-size: 16px; + color: var(--inputLabel); + overflow: hidden; + } + + > input { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: var(--xxubwiul); + height: 7px; + margin: 0 8px; + outline: 0; + border: 0; + border-radius: 7px; + + &.disabled { + opacity: 0.6; + cursor: not-allowed; + } + + &::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + cursor: pointer; + width: 20px; + height: 20px; + display: block; + border-radius: 50%; + border: none; + background: var(--accent); + box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); + box-sizing: content-box; + } + + &::-moz-range-thumb { + -moz-appearance: none; + appearance: none; + cursor: pointer; + width: 20px; + height: 20px; + display: block; + border-radius: 50%; + border: none; + background: var(--accent); + box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); + } + } +} +</style> diff --git a/src/client/pages/preferences/index.vue b/src/client/pages/preferences/index.vue index eddf69653d..3bc305a229 100644 --- a/src/client/pages/preferences/index.vue +++ b/src/client/pages/preferences/index.vue @@ -8,8 +8,10 @@ <section class="_card"> <div class="_title"><fa :icon="faMusic"/> {{ $t('sounds') }}</div> <div class="_content"> - {{ $t('volume') }} - <input type="range" v-model="sfxVolume" min="0" max="1" step="0.1"/> + <mk-range v-model="sfxVolume" min="0" max="1" step="0.1"> + <fa slot="icon" :icon="volumeIcon"/> + <span slot="title">{{ $t('volume') }}</span> + </mk-range> </div> <div class="_content"> <mk-select v-model="sfxNote"> @@ -85,12 +87,13 @@ <script lang="ts"> import Vue from 'vue'; -import { faImage, faCog, faMusic, faPlay } from '@fortawesome/free-solid-svg-icons'; +import { faImage, faCog, faMusic, faPlay, faVolumeUp, faVolumeMute } from '@fortawesome/free-solid-svg-icons'; import MkInput from '../../components/ui/input.vue'; import MkButton from '../../components/ui/button.vue'; import MkSwitch from '../../components/ui/switch.vue'; import MkSelect from '../../components/ui/select.vue'; import MkRadio from '../../components/ui/radio.vue'; +import MkRange from '../../components/ui/range.vue'; import XTheme from './theme.vue'; import i18n from '../../i18n'; import { langs } from '../../config'; @@ -128,6 +131,7 @@ export default Vue.extend({ MkSwitch, MkSelect, MkRadio, + MkRange }, data() { @@ -136,7 +140,7 @@ export default Vue.extend({ lang: localStorage.getItem('lang'), fontSize: localStorage.getItem('fontSize'), sounds, - faImage, faCog, faMusic, faPlay + faImage, faCog, faMusic, faPlay, faVolumeUp, faVolumeMute } }, @@ -210,6 +214,12 @@ export default Vue.extend({ get() { return this.$store.state.device.sfxAntenna; }, set(value) { this.$store.commit('device/set', { key: 'sfxAntenna', value }); } }, + + volumeIcon: { + get() { + return this.sfxVolume === 0 ? faVolumeMute : faVolumeUp; + } + } }, watch: {