Skip to content

fix: rtl switch issue https://github.com/ant-design/ant-design/issues/40128 #167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions assets/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@
.segmented-item-selected();

position: absolute;
top: 0;
left: 0;
// top: 0;
// left: 0;
width: 0;
height: 100%;
padding: 4px 0;
Expand All @@ -93,4 +93,8 @@
width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
will-change: transform, width;
}

&-rtl {
direction: rtl;
}
}
42 changes: 42 additions & 0 deletions docs/demo/rtl.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="wrapper">
<button
onClick={() => {
setDirection('rtl');
}}
style={{
padding: '0 8px',
marginRight: 8,
}}
>
rtl
</button>
<button
onClick={() => {
setDirection('ltr');
}}
style={{
padding: '0 8px',
}}
>
ltr
</button>
Comment on lines +9 to +29
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

写成 toggleButtong 是不是更简洁一些

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

主要为了显示清楚一点,所以分成了两个 buttondemo 上看上去更直观一点,个人感觉。

<p
style={{
marginBottom: 8,
}}
/>
<Segmented
options={['iOS', 'Android', 'Web']}
onChange={(value) => console.log(value, typeof value)}
direction={direction}
/>
</div>
);
}
4 changes: 4 additions & 0 deletions docs/example.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ nav:
## refs

<code src="./demo/refs.tsx"></code>

## rtl

<code src="./demo/rtl.tsx"></code>
30 changes: 26 additions & 4 deletions src/MotionThumb.tsx
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -18,6 +19,7 @@ export interface MotionThumbInterface {
motionName: string;
onMotionStart: VoidFunction;
onMotionEnd: VoidFunction;
direction?: 'ltr' | 'rtl';
}

const calcThumbStyle = (
Expand All @@ -26,6 +28,10 @@ const calcThumbStyle = (
targetElement
? {
left: targetElement.offsetLeft,
right:
(targetElement.parentElement!.clientWidth as number) -
targetElement.clientWidth -
targetElement.offsetLeft,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感觉改多了,是不是把 left 换成 insetInlineStart 就够了?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好像不行,试了下 offsetLeft 偏移量是受 RTL 模式影响的,会导致计算结果不按预期。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里的 thumb 滑块使用 translateX 做的偏移,不是用 left/insetInlineStart 实现的

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

再跑下ci,之前的 test case 有问题 修复了一下。

width: targetElement.clientWidth,
}
: null;
Expand All @@ -42,6 +48,7 @@ export default function MotionThumb(props: MotionThumbInterface) {
motionName,
onMotionStart,
onMotionEnd,
direction,
} = props;

const thumbRef = React.useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>(
value={rawValue}
containerRef={containerRef}
motionName={`${prefixCls}-${motionName}`}
direction={direction}
getValueIndex={(val) =>
segmentedOptions.findIndex((n) => n.value === val)
}
Expand Down
34 changes: 33 additions & 1 deletion tests/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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(
<Segmented
direction="rtl"
options={['iOS', 'Android', 'Web']}
onChange={(value) => 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();
});
});