diff --git a/src/components/swiper/demos/demo8.tsx b/src/components/swiper/demos/demo8.tsx new file mode 100644 index 0000000000..c475b8f93f --- /dev/null +++ b/src/components/swiper/demos/demo8.tsx @@ -0,0 +1,73 @@ +import React, { useState } from 'react' +import { Space, Swiper, Button } from 'antd-mobile' +import { DemoBlock, DemoDescription } from 'demos' + +import styles from './demo1.less' + +const colors = ['#ace0ff', '#bcffbd', '#e4fabd', '#ffcfac'] + +const items = colors.map((color, index) => ( + +
+ {index + 1} +
+
+)) + +export default () => { + return ( + <> + + {items} + + + { + console.log(i, 'onIndexChange1') + }} + docDirection={'rtl'} + > + {items} + + + + + {items} + + + + + + {items} + + + + + + {items} + + + + ) +} diff --git a/src/components/swiper/index.en.md b/src/components/swiper/index.en.md index 4d9287e340..e840526dea 100644 --- a/src/components/swiper/index.en.md +++ b/src/components/swiper/index.en.md @@ -36,6 +36,8 @@ When you use a vertical Swiper, be sure to set its height through the `--height` + + ## Swiper ### Props @@ -56,6 +58,7 @@ When you use a vertical Swiper, be sure to set its height through the `--height` | stuckAtBoundary | Whether to stuck at boundary in order to prevent white spaces. Only available when `loop` is `false` and `slideWidth` < 100. | `boolean` | `true` | | trackOffset | The track offset in percentage | `number` | `0` | | stopPropagation | Stop the propagation of some events. | `PropagationEvent[]` | `[]` | 5.28.0 | +| docDirection | The document layout direction,only effective when the direction is set to `horizontal`. | `'ltr' \| 'rtl'` | `'ltr'` | ```ts type PropagationEvent = 'mouseup' | 'mousemove' | 'mousedown' diff --git a/src/components/swiper/index.zh.md b/src/components/swiper/index.zh.md index 20d7d97ef2..9ccf0e1362 100644 --- a/src/components/swiper/index.zh.md +++ b/src/components/swiper/index.zh.md @@ -34,6 +34,8 @@ + + ## Swiper ### 属性 @@ -54,6 +56,7 @@ | stuckAtBoundary | 是否在边界两边卡住,避免出现空白,仅在非 `loop` 模式且 `slideSize` < 100 时生效 | `boolean` | `true` | | trackOffset | 滑块轨道整体的偏移量百分比 | `number` | `0` | | stopPropagation | 阻止某些事件的冒泡 | `PropagationEvent[]` | `[]` | 5.28.0 | +| docDirection | 文档排版方向,仅在 direction 为`horizontal`是生效 | `'ltr' \| 'rtl'` | `'ltr'` | ```ts type PropagationEvent = 'mouseup' | 'mousemove' | 'mousedown' diff --git a/src/components/swiper/swiper.tsx b/src/components/swiper/swiper.tsx index f1c7797641..6eb83cb0f3 100644 --- a/src/components/swiper/swiper.tsx +++ b/src/components/swiper/swiper.tsx @@ -57,6 +57,7 @@ export type SwiperProps = { rubberband?: boolean stopPropagation?: PropagationEvent[] children?: ReactElement | ReactElement[] + docDirection?: 'ltr' | 'rtl' } & NativeProps<'--height' | '--width' | '--border-radius' | '--track-padding'> const defaultProps = { @@ -71,6 +72,7 @@ const defaultProps = { stuckAtBoundary: true, rubberband: true, stopPropagation: [] as PropagationEvent[], + docDirection: 'ltr', } let currentUid: undefined | {} @@ -85,6 +87,9 @@ export const Swiper = forwardRef( const slideRatio = props.slideSize / 100 const offsetRatio = props.trackOffset / 100 + const isRtl = + props.direction === 'horizontal' && props.docDirection === 'rtl' + const { validChildren, count } = useMemo(() => { let count = 0 const validChildren = React.Children.map(props.children, child => { @@ -140,7 +145,9 @@ export const Swiper = forwardRef( const [{ position }, api] = useSpring( () => ({ - position: boundIndex(current) * 100, + position: isRtl + ? -boundIndex(current) * 100 + : boundIndex(current) * 100, config: { tension: 200, friction: 30 }, onRest: () => { if (draggingRef.current) return @@ -210,8 +217,12 @@ export const Swiper = forwardRef( bounds: () => { if (loop) return {} const slidePixels = getSlidePixels() - const lowerBound = boundIndex(0) * slidePixels - const upperBound = boundIndex(count - 1) * slidePixels + const lowerBound = isRtl + ? -boundIndex(count - 1) * slidePixels + : boundIndex(0) * slidePixels + const upperBound = isRtl + ? -boundIndex(0) * slidePixels + : boundIndex(count - 1) * slidePixels return isVertical ? { top: lowerBound, @@ -232,7 +243,7 @@ export const Swiper = forwardRef( ) function swipeTo(index: number, immediate = false) { - const roundedIndex = Math.round(index) + const roundedIndex = isRtl ? -Math.round(index) : Math.round(index) const targetIndex = loop ? modulus(roundedIndex, count) : bound(roundedIndex, 0, count - 1) @@ -244,21 +255,29 @@ export const Swiper = forwardRef( setCurrent(targetIndex) api.start({ - position: (loop ? roundedIndex : boundIndex(roundedIndex)) * 100, + position: isRtl + ? -(loop ? roundedIndex : boundIndex(roundedIndex)) * 100 + : (loop ? roundedIndex : boundIndex(roundedIndex)) * 100, immediate, }) } function swipeNext() { - swipeTo(Math.round(position.get() / 100) + 1) + isRtl + ? swipeTo(Math.round(position.get() / 100) - 1) + : swipeTo(Math.round(position.get() / 100) + 1) } function swipePrev() { - swipeTo(Math.round(position.get() / 100) - 1) + isRtl + ? swipeTo(Math.round(position.get() / 100) + 1) + : swipeTo(Math.round(position.get() / 100) - 1) } useImperativeHandle(ref, () => ({ - swipeTo, + swipeTo: index => { + swipeTo(isRtl ? -index : index) + }, swipeNext, swipePrev, })) @@ -300,7 +319,9 @@ export const Swiper = forwardRef( })} style={{ [isVertical ? 'y' : 'x']: position.to(position => { - let finalPosition = -position + index * 100 + let finalPosition = isRtl + ? -position - index * 100 + : -position + index * 100 const totalWidth = count * 100 const flagWidth = totalWidth / 2 finalPosition = @@ -308,7 +329,9 @@ export const Swiper = forwardRef( flagWidth return `${finalPosition}%` }), - [isVertical ? 'top' : 'left']: `-${index * 100}%`, + [isVertical ? 'top' : `${isRtl ? 'right' : 'left'}`]: `-${ + index * 100 + }%`, }} > {child} @@ -346,7 +369,7 @@ export const Swiper = forwardRef( const style: CSSProperties & Record<'--slide-size' | '--track-offset', string> = { '--slide-size': `${props.slideSize}%`, - '--track-offset': `${props.trackOffset}%`, + '--track-offset': `${isRtl ? -props.trackOffset : props.trackOffset}%`, } const dragProps = { ...(props.allowTouchMove ? bind() : {}) } @@ -369,7 +392,7 @@ export const Swiper = forwardRef( classPrefix, `${classPrefix}-${props.direction}` )} - style={style} + style={{ ...style, direction: props.docDirection }} >
`; + +exports[`Swiper rtl mode 1`] = ` +
+
+
+
+
+
+
+ 1 +
+
+
+
+
+
+ 2 +
+
+
+
+
+
+ 3 +
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`Swiper rtl mode 2`] = ` +
+
+
+
+
+
+
+ 1 +
+
+
+
+
+
+ 2 +
+
+
+
+
+
+ 3 +
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; diff --git a/src/components/swiper/tests/swiper.test.tsx b/src/components/swiper/tests/swiper.test.tsx index ded1fa445b..444fcd7934 100644 --- a/src/components/swiper/tests/swiper.test.tsx +++ b/src/components/swiper/tests/swiper.test.tsx @@ -353,4 +353,29 @@ describe('Swiper', () => { expect(onMouseMove).not.toBeCalled() expect(onMouseUp).not.toBeCalled() }) + + test('rtl mode', async () => { + const { container } = render({items}) + + console.log(expect(container)) + + expect(container).toMatchSnapshot() + + const el = $$(`.${classPrefix}-track`)[0] + mockDrag(el, [ + { clientX: 0, clientY: 0 }, + { + clientX: 200, + clientY: 25, + }, + { + clientX: 300, + clientY: 30, + }, + ]) + + console.log(expect(container)) + + expect(container).toMatchSnapshot() + }) })