From 2a8e2edf40794cbb857568e0aa8bd722f1adb4e1 Mon Sep 17 00:00:00 2001 From: neverland Date: Sun, 22 Aug 2021 20:52:50 +0800 Subject: [PATCH] feat(Slider): add reverse prop (#9308) * feat(Slider): add reverse prop * chore: types * chore: upd --- .../test/__snapshots__/demo.spec.ts.snap | 4 +- src/form/test/__snapshots__/demo.spec.ts.snap | 2 +- src/slider/README.md | 1 + src/slider/README.zh-CN.md | 1 + src/slider/Slider.tsx | 72 ++++++++++++------ src/slider/index.less | 34 ++++----- .../test/__snapshots__/demo.spec.ts.snap | 26 +++---- .../test/__snapshots__/index.spec.ts.snap | 75 ++++++++++++++++++- src/slider/test/index.spec.ts | 60 +++++++++++++++ 9 files changed, 217 insertions(+), 58 deletions(-) diff --git a/src/config-provider/test/__snapshots__/demo.spec.ts.snap b/src/config-provider/test/__snapshots__/demo.spec.ts.snap index f88ff0310..15a22184f 100644 --- a/src/config-provider/test/__snapshots__/demo.spec.ts.snap +++ b/src/config-provider/test/__snapshots__/demo.spec.ts.snap @@ -85,7 +85,7 @@ exports[`should render demo and match snapshot 1`] = ` style="width: 50%; left: 0%;" >
+ const isRange = (val: unknown): val is NumberRange => props.range && Array.isArray(val); // 计算选中条的长度百分比 @@ -91,15 +94,27 @@ export default defineComponent({ return '0%'; }; - const barStyle = computed(() => { + const barStyle = computed(() => { const mainAxis = props.vertical ? 'height' : 'width'; - return { + const style: CSSProperties = { [mainAxis]: calcMainAxis(), - left: props.vertical ? undefined : calcOffset(), - top: props.vertical ? calcOffset() : undefined, background: props.activeColor, - transition: dragStatus.value ? 'none' : undefined, }; + + if (dragStatus.value) { + style.transition = 'none'; + } + + const getPositionKey = () => { + if (props.vertical) { + return props.reverse ? 'bottom' : 'top'; + } + return props.reverse ? 'right' : 'left'; + }; + + style[getPositionKey()] = calcOffset(); + + return style; }); const format = (value: number) => { @@ -116,7 +131,7 @@ export default defineComponent({ JSON.stringify(newValue) === JSON.stringify(oldValue); // 处理两个滑块重叠之后的情况 - const handleOverlap = (value: [number, number]) => { + const handleOverlap = (value: NumberRange) => { if (value[0] > value[1]) { return value.slice(0).reverse(); } @@ -125,7 +140,7 @@ export default defineComponent({ const updateValue = (value: SliderValue, end?: boolean) => { if (isRange(value)) { - value = handleOverlap(value).map(format) as [number, number]; + value = handleOverlap(value).map(format) as NumberRange; } else { value = format(value); } @@ -146,13 +161,24 @@ export default defineComponent({ return; } - const { min, vertical, modelValue } = props; + const { min, reverse, vertical, modelValue } = props; const rect = useRect(root); - const delta = vertical - ? event.clientY - rect.top - : event.clientX - rect.left; + + const getDelta = () => { + if (vertical) { + if (reverse) { + return rect.bottom - event.clientY; + } + return event.clientY - rect.top; + } + if (reverse) { + return rect.right - event.clientX; + } + return event.clientX - rect.left; + }; + const total = vertical ? rect.height : rect.width; - const value = Number(min) + (delta / total) * scope.value; + const value = Number(min) + (getDelta() / total) * scope.value; if (isRange(modelValue)) { const [left, right] = modelValue; @@ -177,7 +203,7 @@ export default defineComponent({ currentValue = props.modelValue; if (isRange(currentValue)) { - startValue = currentValue.map(format) as [number, number]; + startValue = currentValue.map(format) as NumberRange; } else { startValue = format(currentValue); } @@ -201,11 +227,15 @@ export default defineComponent({ const rect = useRect(root); const delta = props.vertical ? touch.deltaY.value : touch.deltaX.value; const total = props.vertical ? rect.height : rect.width; - const diff = (delta / total) * scope.value; + + let diff = (delta / total) * scope.value; + if (props.reverse) { + diff = -diff; + } if (isRange(startValue)) { - (currentValue as [number, number])[buttonIndex] = - startValue[buttonIndex] + diff; + const index = props.reverse ? 1 - buttonIndex : buttonIndex; + (currentValue as NumberRange)[index] = startValue[index] + diff; } else { currentValue = startValue + diff; } @@ -228,9 +258,9 @@ export default defineComponent({ const getButtonClassName = (index?: 0 | 1) => { if (typeof index === 'number') { const position = ['left', 'right']; - return bem(`button-wrapper-${position[index]}`); + return bem(`button-wrapper`, position[index]); } - return bem('button-wrapper'); + return bem('button-wrapper', props.reverse ? 'left' : 'right'); }; const renderButtonContent = (value: number, index?: 0 | 1) => { @@ -253,7 +283,7 @@ export default defineComponent({ const renderButton = (index?: 0 | 1) => { const currentValue = typeof index === 'number' - ? (props.modelValue as [number, number])[index] + ? (props.modelValue as NumberRange)[index] : (props.modelValue as number); return ( diff --git a/src/slider/index.less b/src/slider/index.less index edd3f7746..7c66aadcb 100644 --- a/src/slider/index.less +++ b/src/slider/index.less @@ -31,7 +31,7 @@ } &__bar { - position: relative; + position: absolute; width: 100%; height: 100%; background-color: var(--van-slider-active-background-color); @@ -46,21 +46,20 @@ border-radius: var(--van-slider-button-border-radius); box-shadow: var(--van-slider-button-box-shadow); - &-wrapper, - &-wrapper-right { + &-wrapper { position: absolute; - top: 50%; - right: 0; - transform: translate3d(50%, -50%, 0); cursor: grab; - } + top: 50%; - &-wrapper-left { - position: absolute; - top: 50%; - left: 0; - transform: translate3d(-50%, -50%, 0); - cursor: grab; + &--right { + right: 0; + transform: translate3d(50%, -50%, 0); + } + + &--left { + left: 0; + transform: translate3d(-50%, -50%, 0); + } } } @@ -68,9 +67,7 @@ cursor: not-allowed; opacity: var(--van-slider-disabled-opacity); - .van-slider__button-wrapper, - .van-slider__button-wrapper-left, - .van-slider__button-wrapper-right { + .van-slider__button-wrapper { cursor: not-allowed; } } @@ -80,15 +77,14 @@ width: var(--van-slider-bar-height); height: 100%; - .van-slider__button-wrapper, - .van-slider__button-wrapper-right { + .van-slider__button-wrapper--right { top: auto; right: 50%; bottom: 0; transform: translate3d(50%, 50%, 0); } - .van-slider__button-wrapper-left { + .van-slider__button-wrapper--left { top: 0; right: 50%; left: auto; diff --git a/src/slider/test/__snapshots__/demo.spec.ts.snap b/src/slider/test/__snapshots__/demo.spec.ts.snap index 96ddb8fd9..d139ef420 100644 --- a/src/slider/test/__snapshots__/demo.spec.ts.snap +++ b/src/slider/test/__snapshots__/demo.spec.ts.snap @@ -7,7 +7,7 @@ exports[`should render demo and match snapshot 1`] = ` style="width: 50%; left: 0%;" >
`; + +exports[`should render reversed range slider correctly 1`] = ` +
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`should render reversed slider correctly 1`] = ` +
+
+
+
+
+
+
+
+`; + +exports[`should render reversed vertical slider correctly 1`] = ` +
+
+
+
+
+
+
+
+`; diff --git a/src/slider/test/index.spec.ts b/src/slider/test/index.spec.ts index 3398b5fb0..303d3027f 100644 --- a/src/slider/test/index.spec.ts +++ b/src/slider/test/index.spec.ts @@ -219,3 +219,63 @@ test('should render left-button、right-button slot correctly', async () => { await later(); expect(wrapper.html()).toMatchSnapshot(); }); + +test('should render reversed slider correctly', () => { + const wrapper = mount(Slider, { + props: { + reverse: true, + modelValue: 25, + }, + }); + + expect(wrapper.html()).toMatchSnapshot(); +}); + +test('should render reversed vertical slider correctly', () => { + const wrapper = mount(Slider, { + props: { + reverse: true, + vertical: true, + modelValue: 25, + }, + }); + + expect(wrapper.html()).toMatchSnapshot(); +}); + +test('should render reversed range slider correctly', () => { + const wrapper = mount(Slider, { + props: { + range: true, + reverse: true, + modelValue: [25, 40], + }, + }); + + expect(wrapper.html()).toMatchSnapshot(); +}); + +test('should update modelValue correctly after clicking the reversed slider', () => { + const wrapper = mount(Slider, { + props: { + reverse: true, + modelValue: 50, + }, + }); + + trigger(wrapper, 'click', 100, 0); + expect(wrapper.emitted('update:modelValue')!.pop()).toEqual([0]); +}); + +test('should update modelValue correctly after clicking the reversed vertical slider', () => { + const wrapper = mount(Slider, { + props: { + reverse: true, + vertical: true, + modelValue: 50, + }, + }); + + trigger(wrapper, 'click', 0, 100); + expect(wrapper.emitted('update:modelValue')!.pop()).toEqual([0]); +});