element-plus/packages/components/slider/src/useSlide.ts
spx 7ee4f44e22
fix(components): trigger update:modelValue by itself (#3660)
trigger update:modelValue by component itself

fix #2282
2021-09-27 11:09:59 +08:00

169 lines
4.2 KiB
TypeScript

import { computed, inject, nextTick, ref, shallowRef } from 'vue'
import {
CHANGE_EVENT,
UPDATE_MODEL_EVENT,
INPUT_EVENT,
} from '@element-plus/utils/constants'
import { elFormKey, elFormItemKey } from '@element-plus/tokens'
import type { CSSProperties } from 'vue'
import type { ButtonRefs, ISliderInitData, ISliderProps } from './slider.type'
import type { ElFormContext, ElFormItemContext } from '@element-plus/tokens'
import type { Nullable } from '@element-plus/utils/types'
export const useSlide = (
props: ISliderProps,
initData: ISliderInitData,
emit
) => {
const elForm = inject(elFormKey, {} as ElFormContext)
const elFormItem = inject(elFormItemKey, {} as ElFormItemContext)
const slider = shallowRef<Nullable<HTMLElement>>(null)
const firstButton = ref(null)
const secondButton = ref(null)
const buttonRefs: ButtonRefs = {
firstButton,
secondButton,
}
const sliderDisabled = computed(() => {
return props.disabled || elForm.disabled || false
})
const minValue = computed(() => {
return Math.min(initData.firstValue, initData.secondValue)
})
const maxValue = computed(() => {
return Math.max(initData.firstValue, initData.secondValue)
})
const barSize = computed(() => {
return props.range
? `${
(100 * (maxValue.value - minValue.value)) / (props.max - props.min)
}%`
: `${
(100 * (initData.firstValue - props.min)) / (props.max - props.min)
}%`
})
const barStart = computed(() => {
return props.range
? `${(100 * (minValue.value - props.min)) / (props.max - props.min)}%`
: '0%'
})
const runwayStyle = computed<CSSProperties>(() => {
return props.vertical ? { height: props.height } : {}
})
const barStyle = computed<CSSProperties>(() => {
return props.vertical
? {
height: barSize.value,
bottom: barStart.value,
}
: {
width: barSize.value,
left: barStart.value,
}
})
const resetSize = () => {
if (slider.value) {
initData.sliderSize =
slider.value[`client${props.vertical ? 'Height' : 'Width'}`]
}
}
const setPosition = (percent: number) => {
const targetValue = props.min + (percent * (props.max - props.min)) / 100
if (!props.range) {
firstButton.value.setPosition(percent)
return
}
let buttonRefName: string
if (
Math.abs(minValue.value - targetValue) <
Math.abs(maxValue.value - targetValue)
) {
buttonRefName =
initData.firstValue < initData.secondValue
? 'firstButton'
: 'secondButton'
} else {
buttonRefName =
initData.firstValue > initData.secondValue
? 'firstButton'
: 'secondButton'
}
buttonRefs[buttonRefName].value.setPosition(percent)
}
const setFirstValue = (firstValue: number) => {
initData.firstValue = firstValue
_emit(props.range ? [minValue.value, maxValue.value] : firstValue)
}
const setSecondValue = (secondValue: number) => {
initData.secondValue = secondValue
if (props.range) {
_emit([minValue.value, maxValue.value])
}
}
const _emit = (val: number | number[]) => {
emit(UPDATE_MODEL_EVENT, val)
emit(INPUT_EVENT, val)
}
const emitChange = async () => {
await nextTick()
emit(
CHANGE_EVENT,
props.range ? [minValue.value, maxValue.value] : props.modelValue
)
}
const onSliderClick = (event: MouseEvent) => {
if (sliderDisabled.value || initData.dragging) return
resetSize()
if (props.vertical) {
const sliderOffsetBottom = slider.value.getBoundingClientRect().bottom
setPosition(
((sliderOffsetBottom - event.clientY) / initData.sliderSize) * 100
)
} else {
const sliderOffsetLeft = slider.value.getBoundingClientRect().left
setPosition(
((event.clientX - sliderOffsetLeft) / initData.sliderSize) * 100
)
}
emitChange()
}
return {
elFormItem,
slider,
firstButton,
secondButton,
sliderDisabled,
minValue,
maxValue,
runwayStyle,
barStyle,
resetSize,
setPosition,
emitChange,
onSliderClick,
setFirstValue,
setSecondValue,
}
}