Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a1f5a8c

Browse files
authored
fix: transform logic (#38)
1 parent 921d58f commit a1f5a8c

File tree

8 files changed

+296
-210
lines changed

8 files changed

+296
-210
lines changed

‎assets/index.less‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
}
8383

8484
// transition effect when `enter-active`
85+
&-thumb-motion-appear-active,
8586
&-thumb-motion-enter-active {
8687
transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
8788
width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);

‎docs/examples/controlled.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default class Demo extends React.Component<
1313

1414
render() {
1515
return (
16-
<>
16+
<React.StrictMode>
1717
<Segmented
1818
options={['iOS', 'Android', 'Web3']}
1919
value={this.state.value}
@@ -33,7 +33,7 @@ export default class Demo extends React.Component<
3333
})
3434
}
3535
/>
36-
</>
36+
</React.StrictMode>
3737
);
3838
}
3939
}

‎package.json‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@
4040
"lint": "eslint src/ --ext .ts,.tsx,.jsx,.js,.md",
4141
"prettier": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
4242
"pretty-quick": "pretty-quick",
43-
"test": "father test",
44-
"coverage": "father test --coverage"
43+
"test": "umi-test",
44+
"coverage": "umi-test --coverage"
4545
},
4646
"dependencies": {
4747
"@babel/runtime": "^7.11.1",
@@ -57,9 +57,11 @@
5757
"@types/react": "^17.0.13",
5858
"@types/react-dom": "^16.9.0",
5959
"@umijs/fabric": "^2.0.8",
60+
"@umijs/test": "^3.5.23",
6061
"coveralls": "^3.0.6",
6162
"cross-env": "^7.0.2",
62-
"dumi": "^1.1.0",
63+
"cssstyle": "^2.3.0",
64+
"dumi": "^1.1.41-rc.0",
6365
"eslint": "^7.0.0",
6466
"father": "^2.13.4",
6567
"father-build": "^1.18.6",

‎src/MotionThumb.tsx‎

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import * as React from 'react';
2+
import CSSMotion from 'rc-motion';
3+
import classNames from 'classnames';
4+
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
5+
import { composeRef } from 'rc-util/lib/ref';
6+
import type { SegmentedValue } from '.';
7+
8+
type ThumbReact = {
9+
left: number;
10+
width: number;
11+
} | null;
12+
13+
export interface MotionThumbInterface {
14+
containerRef: React.RefObject<HTMLDivElement>;
15+
value: SegmentedValue;
16+
getValueIndex: (value: SegmentedValue) => number;
17+
prefixCls: string;
18+
motionName: string;
19+
onMotionStart: VoidFunction;
20+
onMotionEnd: VoidFunction;
21+
}
22+
23+
const calcThumbStyle = (
24+
targetElement: HTMLElement | null | undefined,
25+
): ThumbReact =>
26+
targetElement
27+
? {
28+
left: targetElement.offsetLeft,
29+
width: targetElement.clientWidth,
30+
}
31+
: null;
32+
33+
const toPX = (value: number) =>
34+
value !== undefined ? `${value}px` : undefined;
35+
36+
export default function MotionThumb(props: MotionThumbInterface) {
37+
const {
38+
prefixCls,
39+
containerRef,
40+
value,
41+
getValueIndex,
42+
motionName,
43+
onMotionStart,
44+
onMotionEnd,
45+
} = props;
46+
47+
const thumbRef = React.useRef<HTMLDivElement>(null);
48+
const [prevValue, setPrevValue] = React.useState(value);
49+
50+
// =========================== Effect ===========================
51+
const findValueElement = (val: SegmentedValue) => {
52+
const index = getValueIndex(val);
53+
54+
const ele = containerRef.current?.querySelectorAll<HTMLDivElement>(
55+
`.${prefixCls}-item`,
56+
)[index];
57+
58+
return ele;
59+
};
60+
61+
const [prevStyle, setPrevStyle] = React.useState<ThumbReact>(null);
62+
const [nextStyle, setNextStyle] = React.useState<ThumbReact>(null);
63+
64+
useLayoutEffect(() => {
65+
if (prevValue !== value) {
66+
const prev = findValueElement(prevValue);
67+
const next = findValueElement(value);
68+
69+
const calcPrevStyle = calcThumbStyle(prev);
70+
const calcNextStyle = calcThumbStyle(next);
71+
72+
setPrevValue(value);
73+
setPrevStyle(calcPrevStyle);
74+
setNextStyle(calcNextStyle);
75+
76+
if (prev && next) {
77+
onMotionStart();
78+
} else {
79+
onMotionEnd();
80+
}
81+
}
82+
}, [value]);
83+
84+
// =========================== Motion ===========================
85+
const onAppearStart = () => {
86+
return {
87+
transform: `translateX(var(--thumb-start-left))`,
88+
width: `var(--thumb-start-width)`,
89+
};
90+
};
91+
const onAppearActive = () => {
92+
return {
93+
transform: `translateX(var(--thumb-active-left))`,
94+
width: `var(--thumb-active-width)`,
95+
};
96+
};
97+
const onAppearEnd = () => {
98+
setPrevStyle(null);
99+
setNextStyle(null);
100+
onMotionEnd();
101+
};
102+
103+
// =========================== Render ===========================
104+
// No need motion when nothing exist in queue
105+
if (!prevStyle || !nextStyle) {
106+
return null;
107+
}
108+
109+
return (
110+
<CSSMotion
111+
visible
112+
motionName={motionName}
113+
motionAppear
114+
onAppearStart={onAppearStart}
115+
onAppearActive={onAppearActive}
116+
onAppearEnd={onAppearEnd}
117+
>
118+
{({ className: motionClassName, style: motionStyle }, ref) => {
119+
const mergedStyle = {
120+
...motionStyle,
121+
'--thumb-start-left': toPX(prevStyle?.left),
122+
'--thumb-start-width': toPX(prevStyle?.width),
123+
'--thumb-active-left': toPX(nextStyle?.left),
124+
'--thumb-active-width': toPX(nextStyle?.width),
125+
} as React.CSSProperties;
126+
127+
// It's little ugly which should be refactor when @umi/test update to latest jsdom
128+
const motionProps = {
129+
ref: composeRef(thumbRef, ref),
130+
style: mergedStyle,
131+
className: classNames(`${prefixCls}-thumb`, motionClassName),
132+
};
133+
134+
if (process.env.NODE_ENV === 'test') {
135+
(motionProps as any)['data-test-style'] = JSON.stringify(mergedStyle);
136+
}
137+
138+
return <div {...motionProps} />;
139+
}}
140+
</CSSMotion>
141+
);
142+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /