diff --git a/assets/index.less b/assets/index.less
index 0f93795..801039c 100644
--- a/assets/index.less
+++ b/assets/index.less
@@ -79,8 +79,8 @@
.segmented-item-selected();
position: absolute;
- top: 0;
- left: 0;
+ // top: 0;
+ // left: 0;
width: 0;
height: 100%;
padding: 4px 0;
@@ -93,4 +93,8 @@
width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
will-change: transform, width;
}
+
+ &-rtl {
+ direction: rtl;
+ }
}
diff --git a/docs/demo/rtl.tsx b/docs/demo/rtl.tsx
new file mode 100644
index 0000000..91c3761
--- /dev/null
+++ b/docs/demo/rtl.tsx
@@ -0,0 +1,42 @@
+import '../../assets/style.less';
+import React, { useState } from 'react';
+import Segmented from 'rc-segmented';
+
+export default function App() {
+ const [direction, setDirection] = useState<'rtl' | 'ltr'>('rtl');
+ return (
+
+
+
+
+
console.log(value, typeof value)}
+ direction={direction}
+ />
+
+ );
+}
diff --git a/docs/example.md b/docs/example.md
index adcb7f2..222034d 100644
--- a/docs/example.md
+++ b/docs/example.md
@@ -28,3 +28,7 @@ nav:
## refs
+
+## rtl
+
+
diff --git a/src/MotionThumb.tsx b/src/MotionThumb.tsx
index 6853c50..384aeb8 100644
--- a/src/MotionThumb.tsx
+++ b/src/MotionThumb.tsx
@@ -1,12 +1,13 @@
-import * as React from 'react';
-import CSSMotion from 'rc-motion';
import classNames from 'classnames';
+import CSSMotion from 'rc-motion';
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
import { composeRef } from 'rc-util/lib/ref';
+import * as React from 'react';
import type { SegmentedValue } from '.';
type ThumbReact = {
left: number;
+ right: number;
width: number;
} | null;
@@ -18,6 +19,7 @@ export interface MotionThumbInterface {
motionName: string;
onMotionStart: VoidFunction;
onMotionEnd: VoidFunction;
+ direction?: 'ltr' | 'rtl';
}
const calcThumbStyle = (
@@ -26,6 +28,10 @@ const calcThumbStyle = (
targetElement
? {
left: targetElement.offsetLeft,
+ right:
+ (targetElement.parentElement!.clientWidth as number) -
+ targetElement.clientWidth -
+ targetElement.offsetLeft,
width: targetElement.clientWidth,
}
: null;
@@ -42,6 +48,7 @@ export default function MotionThumb(props: MotionThumbInterface) {
motionName,
onMotionStart,
onMotionEnd,
+ direction,
} = props;
const thumbRef = React.useRef(null);
@@ -81,6 +88,21 @@ export default function MotionThumb(props: MotionThumbInterface) {
}
}, [value]);
+ const thumbStart = React.useMemo(
+ () =>
+ direction === 'rtl'
+ ? toPX(-(prevStyle?.right as number))
+ : toPX(prevStyle?.left as number),
+ [direction, prevStyle],
+ );
+ const thumbActive = React.useMemo(
+ () =>
+ direction === 'rtl'
+ ? toPX(-(nextStyle?.right as number))
+ : toPX(nextStyle?.left as number),
+ [direction, nextStyle],
+ );
+
// =========================== Motion ===========================
const onAppearStart = () => {
return {
@@ -118,9 +140,9 @@ export default function MotionThumb(props: MotionThumbInterface) {
{({ className: motionClassName, style: motionStyle }, ref) => {
const mergedStyle = {
...motionStyle,
- '--thumb-start-left': toPX(prevStyle?.left),
+ '--thumb-start-left': thumbStart,
'--thumb-start-width': toPX(prevStyle?.width),
- '--thumb-active-left': toPX(nextStyle?.left),
+ '--thumb-active-left': thumbActive,
'--thumb-active-width': toPX(nextStyle?.width),
} as React.CSSProperties;
diff --git a/src/index.tsx b/src/index.tsx
index 117e43e..37933eb 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -184,6 +184,7 @@ const Segmented = React.forwardRef(
value={rawValue}
containerRef={containerRef}
motionName={`${prefixCls}-${motionName}`}
+ direction={direction}
getValueIndex={(val) =>
segmentedOptions.findIndex((n) => n.value === val)
}
diff --git a/tests/index.test.tsx b/tests/index.test.tsx
index fc8a6eb..59fb1b1 100644
--- a/tests/index.test.tsx
+++ b/tests/index.test.tsx
@@ -27,7 +27,7 @@ describe('rc-segmented', () => {
const styleText = container
.querySelector('.rc-segmented-thumb')
?.getAttribute('data-test-style');
- const style = JSON.parse(styleText!) || {};
+ const style = styleText ? JSON.parse(styleText!) : {};
expect(style).toMatchObject(matchStyle);
}
@@ -523,4 +523,36 @@ describe('rc-segmented', () => {
expectMatchChecked(container, [true, false, false]);
});
+
+ it('click can work as expected with rtl', () => {
+ const offsetParentSpy = jest
+ .spyOn(HTMLElement.prototype, 'offsetParent', 'get')
+ .mockImplementation(() => {
+ return container;
+ });
+ const handleValueChange = jest.fn();
+ const { container } = render(
+ handleValueChange(value)}
+ />,
+ );
+
+ fireEvent.click(container.querySelectorAll('.rc-segmented-item-input')[1]);
+ expectMatchChecked(container, [false, true, false]);
+ expect(handleValueChange).toBeCalledWith('Android');
+
+ // Motion to active
+ act(() => {
+ jest.runAllTimers();
+ });
+
+ exceptThumbHaveStyle(container, {
+ '--thumb-active-left': '-22px',
+ '--thumb-active-width': '118px',
+ });
+
+ offsetParentSpy.mockRestore();
+ });
});