element-plus/packages/components/slider/src/slider.vue

253 lines
6.2 KiB
Vue
Raw Normal View History

<template>
<div
:id="range ? inputId : undefined"
ref="sliderWrapper"
:class="sliderKls"
:role="range ? 'group' : undefined"
:aria-label="range && !isLabeledByFormItem ? groupLabel : undefined"
:aria-labelledby="
range && isLabeledByFormItem ? elFormItem?.labelId : undefined
"
@touchstart="onSliderWrapperPrevent"
@touchmove="onSliderWrapperPrevent"
>
<div
ref="slider"
:class="[
ns.e('runway'),
{ 'show-input': showInput && !range },
ns.is('disabled', sliderDisabled),
]"
:style="runwayStyle"
@mousedown="onSliderDown"
@touchstart="onSliderDown"
>
<div :class="ns.e('bar')" :style="barStyle" />
<slider-button
:id="!range ? inputId : undefined"
ref="firstButton"
:model-value="firstValue"
:vertical="vertical"
:tooltip-class="tooltipClass"
role="slider"
:aria-label="
range || !isLabeledByFormItem ? firstButtonLabel : undefined
"
:aria-labelledby="
!range && isLabeledByFormItem ? elFormItem?.labelId : undefined
"
:aria-valuemin="min"
:aria-valuemax="range ? secondValue : max"
:aria-valuenow="firstValue"
:aria-valuetext="firstValueText"
:aria-orientation="vertical ? 'vertical' : 'horizontal'"
:aria-disabled="sliderDisabled"
@update:model-value="setFirstValue"
/>
<slider-button
v-if="range"
ref="secondButton"
:model-value="secondValue"
:vertical="vertical"
:tooltip-class="tooltipClass"
role="slider"
:aria-label="secondButtonLabel"
:aria-valuemin="firstValue"
:aria-valuemax="max"
:aria-valuenow="secondValue"
:aria-valuetext="secondValueText"
:aria-orientation="vertical ? 'vertical' : 'horizontal'"
:aria-disabled="sliderDisabled"
@update:model-value="setSecondValue"
/>
<div v-if="showStops">
<div
v-for="(item, key) in stops"
:key="key"
:class="ns.e('stop')"
:style="getStopStyle(item)"
/>
</div>
<template v-if="markList.length > 0">
<div>
<div
v-for="(item, key) in markList"
:key="key"
:style="getStopStyle(item.position)"
:class="[ns.e('stop'), ns.e('marks-stop')]"
/>
</div>
<div :class="ns.e('marks')">
<slider-marker
v-for="(item, key) in markList"
:key="key"
:mark="item.mark"
:style="getStopStyle(item.position)"
/>
</div>
</template>
</div>
<el-input-number
v-if="showInput && !range"
ref="input"
:model-value="firstValue"
:class="ns.e('input')"
:step="step"
:disabled="sliderDisabled"
:controls="showInputControls"
:min="min"
:max="max"
:debounce="debounce"
:size="sliderInputSize"
@update:model-value="setFirstValue"
@change="emitChange"
/>
</div>
</template>
<script lang="ts" setup>
import { computed, provide, reactive, toRefs } from 'vue'
import ElInputNumber from '@element-plus/components/input-number'
import {
useFormItemInputId,
useLocale,
useNamespace,
useSize,
} from '@element-plus/hooks'
import { sliderContextKey } from '@element-plus/tokens'
import { sliderEmits, sliderProps } from './slider'
import SliderButton from './button.vue'
import SliderMarker from './marker'
import {
useLifecycle,
useMarks,
useSlide,
useStops,
useWatch,
} from './composables'
import type { SliderInitData } from './slider'
defineOptions({
name: 'ElSlider',
})
const props = defineProps(sliderProps)
const emit = defineEmits(sliderEmits)
const ns = useNamespace('slider')
const { t } = useLocale()
const initData = reactive<SliderInitData>({
firstValue: 0,
secondValue: 0,
oldValue: 0,
dragging: false,
sliderSize: 1,
})
const {
elFormItem,
slider,
firstButton,
secondButton,
sliderDisabled,
minValue,
maxValue,
runwayStyle,
barStyle,
resetSize,
emitChange,
onSliderWrapperPrevent,
onSliderClick,
onSliderDown,
setFirstValue,
setSecondValue,
} = useSlide(props, initData, emit)
const { stops, getStopStyle } = useStops(props, initData, minValue, maxValue)
const { inputId, isLabeledByFormItem } = useFormItemInputId(props, {
formItemContext: elFormItem,
})
const sliderWrapperSize = useSize()
const sliderInputSize = computed(
() => props.inputSize || sliderWrapperSize.value
)
const groupLabel = computed<string>(() => {
return (
props.label ||
t('el.slider.defaultLabel', {
min: props.min,
max: props.max,
})
)
})
const firstButtonLabel = computed<string>(() => {
if (props.range) {
return props.rangeStartLabel || t('el.slider.defaultRangeStartLabel')
} else {
return groupLabel.value
}
})
const firstValueText = computed<string>(() => {
return props.formatValueText
? props.formatValueText(firstValue.value)
: `${firstValue.value}`
})
const secondButtonLabel = computed<string>(() => {
return props.rangeEndLabel || t('el.slider.defaultRangeEndLabel')
})
const secondValueText = computed<string>(() => {
return props.formatValueText
? props.formatValueText(secondValue.value)
: `${secondValue.value}`
})
const sliderKls = computed(() => [
ns.b(),
ns.m(sliderWrapperSize.value),
ns.is('vertical', props.vertical),
{ [ns.m('with-input')]: props.showInput },
])
const markList = useMarks(props)
useWatch(props, initData, minValue, maxValue, emit, elFormItem!)
const precision = computed(() => {
const precisions = [props.min, props.max, props.step].map((item) => {
const decimal = `${item}`.split('.')[1]
return decimal ? decimal.length : 0
})
return Math.max.apply(null, precisions)
})
const { sliderWrapper } = useLifecycle(props, initData, resetSize)
const { firstValue, secondValue, sliderSize } = toRefs(initData)
const updateDragging = (val: boolean) => {
initData.dragging = val
}
provide(sliderContextKey, {
...toRefs(props),
sliderSize,
disabled: sliderDisabled,
precision,
emitChange,
resetSize,
updateDragging,
})
defineExpose({
onSliderClick,
})
</script>