Skip to content

Commit

Permalink
fix: rtl switch issue ant-design/ant-design#40128 (#167)
Browse files Browse the repository at this point in the history
  • Loading branch information
RedJue committed Feb 6, 2023
1 parent cb02dd6 commit 56975dc
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 7 deletions.
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>
<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,
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();
});
});

0 comments on commit 56975dc

Please sign in to comment.