Newer
Older
<script setup lang="ts">
import { useVModel } from '@vueuse/core';
import { computed } from 'vue';
import { convertThemeToColorBlackDefault, convertThemeToColorWhiteDefault } from '@/app/helpers';
interface Props {
value: string | number;
width?: number;
min?: number;
max?: number;
step?: number;
size?: 'small' | 'medium' | 'large' | 'extraLarge';
theme?: string;
backgroundColor?: string;
orientation?: 'horizontal' | 'vertical';
isSmooth?: any;
options?: {
label: string;
value: number;
}[];
}
const props = defineProps<Props>();
const emit = defineEmits(['update:value']);
const value = useVModel(props, 'value', emit);
const sliderButtonSize = computed(() => {
if (!props.size) return '40px';
switch (props.size) {
case 'small':
return '25px';
case 'medium':
return '40px';
case 'large':
return '70px';
case 'extraLarge':
return '100px';
}
});
const sliderHeight = computed(() => `${Math.floor(sliderButtonSize.value.slice(0, -2) / 3)}px`);
const sliderBorderRadius = props.isSmooth ? sliderHeight.value : '0%';
const sliderButtonBorderRadius = props.isSmooth ? '50%' : '0%';
const themeColor = computed(() => convertThemeToColorWhiteDefault(props.theme));
const themeBackground = computed(() => convertThemeToColorBlackDefault(props.backgroundColor));
</script>
<template>
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<div
:class="[
'slideContainer',
{
verticalSlider: orientation === 'vertical'
}
]"
:style="`width: ${width ?? 200}px`"
>
<input
v-model="value"
type="range"
class="slider"
:min="min ?? 0"
:max="max ?? 100"
:step="step ?? 1"
/>
<input type="range" list="values" class="opacity-0 size-0" />
<div v-if="options?.length">
<ul
class="marksList"
:style="`width: ${width ?? 200}px; margin-bottom: 5px; font-size: 10px`"
>
<li v-for="option of options">|</li>
</ul>
<datalist
id="values"
:class="[
'',
{
datalistVertical: orientation === 'vertical'
}
]"
>
<template v-for="option of options" :key="option.value">
<option :value="option.value" :label="option.label"></option>
</template>
</datalist>
</div>
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
</div>
</template>
<style scoped>
.slideContainer {
width: 100%;
}
.slider {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: v-bind(sliderHeight);
background: v-bind(themeBackground);
outline: none;
border-radius: v-bind(sliderBorderRadius);
filter: brightness(100%);
-webkit-transition: 0.2s;
transition: filter 0.2s;
cursor: pointer;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: v-bind(sliderButtonSize);
height: v-bind(sliderButtonSize);
border-radius: v-bind(sliderButtonBorderRadius);
background: v-bind(themeColor);
-webkit-transition: 0.2s;
transition: filter 0.2s;
}
.slider::-webkit-slider-thumb:hover {
filter: brightness(90%);
}
.slider::-moz-range-thumb {
width: v-bind(sliderButtonSize);
height: v-bind(sliderButtonSize);
background: v-bind(themeColor);
cursor: pointer;
}
.verticalSlider {
transform: rotate(270deg);
}
datalist {
display: flex;
justify-content: space-between;
width: v-bind(width);
}
.datalistVertical {
flex-direction: column;
writing-mode: vertical-lr;
}
option {
padding: 0;
}
.marksList {
display: flex;
justify-content: space-between;
}