chore(Slider): use tsx

This commit is contained in:
chenjiahan 2020-10-11 19:27:55 +08:00
parent 6341967482
commit f879b6d77a

View File

@ -1,13 +1,13 @@
import { ref, computed } from 'vue'; import { ref, computed, PropType, CSSProperties } from 'vue';
// Utils // Utils
import { import {
addUnit, addUnit,
getSizeStyle, getSizeStyle,
preventDefault, preventDefault,
stopPropagation,
createNamespace, createNamespace,
} from '../utils'; } from '../utils';
import { deepClone } from '../utils/deep-clone';
// Composition // Composition
import { useRect } from '@vant/use'; import { useRect } from '@vant/use';
@ -16,6 +16,8 @@ import { useLinkField } from '../composition/use-link-field';
const [createComponent, bem] = createNamespace('slider'); const [createComponent, bem] = createNamespace('slider');
type SliderValue = number | number[];
export default createComponent({ export default createComponent({
props: { props: {
range: Boolean, range: Boolean,
@ -38,7 +40,7 @@ export default createComponent({
default: 1, default: 1,
}, },
modelValue: { modelValue: {
type: [Number, Array], type: [Number, Array] as PropType<SliderValue>,
default: 0, default: 0,
}, },
}, },
@ -46,15 +48,15 @@ export default createComponent({
emits: ['change', 'drag-end', 'drag-start', 'update:modelValue'], emits: ['change', 'drag-end', 'drag-start', 'update:modelValue'],
setup(props, { emit, slots }) { setup(props, { emit, slots }) {
let startValue; let buttonIndex: number;
let buttonIndex; let startValue: SliderValue;
let currentValue; let currentValue: SliderValue;
const root = ref(); const root = ref<HTMLElement>();
const dragStatus = ref(); const dragStatus = ref<'start' | 'draging' | ''>();
const touch = useTouch(); const touch = useTouch();
const scope = computed(() => props.max - props.min); const scope = computed(() => Number(props.max) - Number(props.min));
const wrapperStyle = computed(() => { const wrapperStyle = computed(() => {
const crossAxis = props.vertical ? 'width' : 'height'; const crossAxis = props.vertical ? 'width' : 'height';
@ -64,56 +66,57 @@ export default createComponent({
}; };
}); });
const isRange = (val: unknown): val is number[] =>
!!props.range && Array.isArray(val);
// 计算选中条的长度百分比 // 计算选中条的长度百分比
const calcMainAxis = () => { const calcMainAxis = () => {
const { modelValue, min, range } = props; const { modelValue, min } = props;
if (range) { if (isRange(modelValue)) {
return `${((modelValue[1] - modelValue[0]) * 100) / scope.value}%`; return `${((modelValue[1] - modelValue[0]) * 100) / scope.value}%`;
} }
return `${((modelValue - min) * 100) / scope.value}%`; return `${((modelValue - Number(min)) * 100) / scope.value}%`;
}; };
// 计算选中条的开始位置的偏移量 // 计算选中条的开始位置的偏移量
const calcOffset = () => { const calcOffset = () => {
const { modelValue, min, range } = props; const { modelValue, min } = props;
if (range) { if (isRange(modelValue)) {
return `${((modelValue[0] - min) * 100) / scope.value}%`; return `${((modelValue[0] - Number(min)) * 100) / scope.value}%`;
} }
return `0%`; return `0%`;
}; };
const barStyle = computed(() => { const barStyle = computed<CSSProperties>(() => {
const mainAxis = props.vertical ? 'height' : 'width'; const mainAxis = props.vertical ? 'height' : 'width';
return { return {
[mainAxis]: calcMainAxis(), [mainAxis]: calcMainAxis(),
left: props.vertical ? null : calcOffset(), left: props.vertical ? undefined : calcOffset(),
top: props.vertical ? calcOffset() : null, top: props.vertical ? calcOffset() : undefined,
background: props.activeColor, background: props.activeColor,
transition: dragStatus.value ? 'none' : null, transition: dragStatus.value ? 'none' : undefined,
}; };
}); });
const format = (value) => { const format = (value: number) => {
const { min, max, step } = props; const { min, max, step } = props;
value = Math.max(min, Math.min(value, max)); value = Math.max(+min, Math.min(value, +max));
return Math.round(value / step) * step; return Math.round(value / +step) * +step;
}; };
const isSameValue = (newValue, oldValue) => { const isSameValue = (newValue: SliderValue, oldValue: SliderValue) =>
return JSON.stringify(newValue) === JSON.stringify(oldValue); JSON.stringify(newValue) === JSON.stringify(oldValue);
};
// 处理两个滑块重叠之后的情况 // 处理两个滑块重叠之后的情况
const handleOverlap = (value) => { const handleOverlap = (value: number[]) => {
if (value[0] > value[1]) { if (value[0] > value[1]) {
value = deepClone(value); return value.slice(0).reverse();
return value.reverse();
} }
return value; return value;
}; };
const updateValue = (value, end) => { const updateValue = (value: SliderValue, end?: boolean) => {
if (props.range) { if (isRange(value)) {
value = handleOverlap(value).map(format); value = handleOverlap(value).map(format);
} else { } else {
value = format(value); value = format(value);
@ -128,52 +131,53 @@ export default createComponent({
} }
}; };
const onClick = (event) => { const onClick = (event: MouseEvent) => {
event.stopPropagation(); event.stopPropagation();
if (props.disabled) { if (props.disabled) {
return; return;
} }
const { min, vertical, modelValue, range } = props; const { min, vertical, modelValue } = props;
const rect = useRect(root); const rect = useRect(root);
const delta = vertical const delta = vertical
? event.clientY - rect.top ? event.clientY - rect.top
: event.clientX - rect.left; : event.clientX - rect.left;
const total = vertical ? rect.height : rect.width; const total = vertical ? rect.height : rect.width;
let value = +min + (delta / total) * scope.value; const value = Number(min) + (delta / total) * scope.value;
if (range) { if (isRange(modelValue)) {
let left = modelValue[0]; const [left, right] = modelValue;
let right = modelValue[1];
const middle = (left + right) / 2; const middle = (left + right) / 2;
if (value <= middle) {
left = value;
} else {
right = value;
}
value = [left, right];
}
if (value <= middle) {
updateValue([value, right], true);
} else {
updateValue([left, value], true);
}
} else {
updateValue(value, true); updateValue(value, true);
}
}; };
const onTouchStart = (event) => { const onTouchStart = (event: TouchEvent) => {
if (props.disabled) { if (props.disabled) {
return; return;
} }
touch.start(event); touch.start(event);
currentValue = props.modelValue; currentValue = props.modelValue;
if (props.range) {
startValue = props.modelValue.map(format); if (isRange(currentValue)) {
startValue = currentValue.map(format);
} else { } else {
startValue = format(props.modelValue); startValue = format(currentValue);
} }
dragStatus.value = 'start'; dragStatus.value = 'start';
}; };
const onTouchMove = (event) => { const onTouchMove = (event: TouchEvent) => {
if (props.disabled) { if (props.disabled) {
return; return;
} }
@ -191,8 +195,9 @@ export default createComponent({
const total = props.vertical ? rect.height : rect.width; const total = props.vertical ? rect.height : rect.width;
const diff = (delta / total) * scope.value; const diff = (delta / total) * scope.value;
if (props.range) { if (isRange(startValue)) {
currentValue[buttonIndex] = startValue[buttonIndex] + diff; (currentValue as number[])[buttonIndex] =
startValue[buttonIndex] + diff;
} else { } else {
currentValue = startValue + diff; currentValue = startValue + diff;
} }
@ -212,7 +217,7 @@ export default createComponent({
dragStatus.value = ''; dragStatus.value = '';
}; };
const renderButton = (index) => { const renderButton = (index?: number) => {
const getClassName = () => { const getClassName = () => {
if (typeof index === 'number') { if (typeof index === 'number') {
const position = ['left', 'right']; const position = ['left', 'right'];
@ -221,18 +226,23 @@ export default createComponent({
return `button-wrapper`; return `button-wrapper`;
}; };
const currentValue =
typeof index === 'number'
? (props.modelValue as number[])[index]
: (props.modelValue as number);
return ( return (
<div <div
role="slider" role="slider"
class={bem(getClassName())} class={bem(getClassName())}
tabindex={props.disabled ? -1 : 0} tabindex={props.disabled ? -1 : 0}
aria-valuemin={props.min} aria-valuemin={+props.min}
aria-valuenow={props.modelValue} aria-valuenow={currentValue}
aria-valuemax={props.max} aria-valuemax={+props.max}
aria-orientation={props.vertical ? 'vertical' : 'horizontal'} aria-orientation={props.vertical ? 'vertical' : 'horizontal'}
onTouchstart={(e) => { onTouchstart={(e) => {
if (typeof index === 'number') { if (typeof index === 'number') {
// 保存当前按钮的索引 // save index of current button
buttonIndex = index; buttonIndex = index;
} }
onTouchStart(e); onTouchStart(e);
@ -240,7 +250,7 @@ export default createComponent({
onTouchmove={onTouchMove} onTouchmove={onTouchMove}
onTouchend={onTouchEnd} onTouchend={onTouchEnd}
onTouchcancel={onTouchEnd} onTouchcancel={onTouchEnd}
onClick={(e) => e.stopPropagation()} onClick={stopPropagation}
> >
{slots.button ? ( {slots.button ? (
slots.button() slots.button()