11<template >
22 <v-slider
3+ ref =" sliderRef"
34 v-bind =" playerVolumeProps"
4- @touchstart =" isThumbHidden = false"
5- @touchend =" isThumbHidden = true"
6- @mouseenter =" isThumbHidden = false"
7- @mouseleave =" isThumbHidden = true"
5+ :model-value =" displayValue"
86 @wheel.prevent =" onWheel"
9- @end =" (value) => $emit('update:model-value', value)"
7+ @click.stop
8+ @start =" onDragStart"
9+ @update:model-value =" onUpdateModelValue"
10+ @end =" onDragEnd"
1011 >
1112 <!-- Dynamically inherit slots from parent -->
1213 <!-- @vue-ignore -->
1718</template >
1819
1920<script lang="ts">
20- import { computed , ref } from " vue" ;
21+ import { computed , ref , watch } from " vue" ;
2122
2223export default {
24+ inheritAttrs: false ,
2325 props: {
2426 // eslint-disable-next-line vue/require-default-prop
2527 width: String ,
@@ -31,34 +33,110 @@ export default {
3133 },
3234 emits: [" update:model-value" ],
3335 setup(props , ctx ) {
34- const isThumbHidden = ref (true );
36+ const sliderRef = ref (null );
37+ const startValue = ref (0 );
38+ const updateCount = ref (0 );
39+ const lastEnd = ref (0 );
40+ const displayValue = ref <number >(
41+ typeof ctx .attrs [" model-value" ] === " number"
42+ ? (ctx .attrs [" model-value" ] as number )
43+ : 0 ,
44+ );
3545
3646 const playerVolumeDefaults = computed (() => ({
3747 class: " player-volume" ,
3848 hideDetails: true ,
3949 trackSize: 2 ,
40- thumbSize: isThumbHidden . value ? 0 : 10 ,
50+ thumbSize: 12 ,
4151 step: 2 ,
4252 elevation: 0 ,
4353 style: ` width: ${props .width }; height:${props .height }; display: inline-grid; ${props .style } ` ,
4454 }));
4555
56+ const sliderAttrs = computed (() => {
57+ const attrs = ctx .attrs as Record <string , unknown >;
58+ const { " model-value" : _mv, modelValue : _mv2, ... rest } = attrs || {};
59+
60+ return rest ;
61+ });
62+
4663 const playerVolumeProps = computed (() => ({
4764 ... playerVolumeDefaults .value ,
48- ... ctx ,
65+ ... sliderAttrs . value ,
4966 }));
5067
5168 const onWheel = ({ deltaY }: WheelEvent ) => {
5269 if (! props .allowWheel ) return ;
5370 const step = playerVolumeProps .value .step ;
5471
55- const volumeValue = ctx .attrs [" model-value" ] as number ;
72+ const volumeValue =
73+ (ctx .attrs [" model-value" ] as number ) ?? displayValue .value ;
5674 const volumeDelta = deltaY < 0 ? step : - step ;
5775
58- ctx .emit (" update:model-value" , volumeValue + volumeDelta );
76+ const newValue = Math .max (0 , Math .min (100 , volumeValue + volumeDelta ));
77+ displayValue .value = newValue ;
78+ ctx .emit (" update:model-value" , newValue );
79+ };
80+
81+ const onDragStart = (value : number ) => {
82+ startValue .value = value ;
83+ updateCount .value = 0 ;
84+ displayValue .value = value ;
5985 };
6086
61- return { isThumbHidden , playerVolumeProps , onWheel };
87+ const onUpdateModelValue = (newValue : number ) => {
88+ updateCount .value ++ ;
89+
90+ if (updateCount .value > 2 ) {
91+ displayValue .value = newValue ;
92+ }
93+ };
94+
95+ const onDragEnd = (endValue : number ) => {
96+ // Cooldown to avoid duplicate emits only for moible click (otherwise it fires 2 be calls)
97+ const now = Date .now ();
98+ if (now - lastEnd .value < 250 ) {
99+ return ;
100+ }
101+ lastEnd .value = now ;
102+
103+ const step = playerVolumeProps .value .step ;
104+
105+ // If we had many updates, that means it was a drag so we emit only the final value
106+ if (updateCount .value > 3 ) {
107+ displayValue .value = endValue ;
108+ ctx .emit (" update:model-value" , endValue );
109+ } else {
110+ const volumeDelta = endValue > startValue .value ? step : - step ;
111+ const newVolume = Math .max (
112+ 0 ,
113+ Math .min (100 , startValue .value + volumeDelta ),
114+ );
115+ displayValue .value = newVolume ;
116+ ctx .emit (" update:model-value" , newVolume );
117+ }
118+
119+ updateCount .value = 0 ;
120+ };
121+
122+ watch (
123+ () => ctx .attrs [" model-value" ] as number ,
124+ (val ) => {
125+ if (typeof val === " number" && updateCount .value === 0 ) {
126+ displayValue .value = val ;
127+ }
128+ },
129+ );
130+
131+ return {
132+ playerVolumeProps ,
133+ onWheel ,
134+ sliderRef ,
135+ onDragStart ,
136+ onUpdateModelValue ,
137+ onDragEnd ,
138+ displayValue ,
139+ };
62140 },
63141};
64142 </script >
0 commit comments