Skip to content

Commit aebfe79

Browse files
committed
feat: refactor Transaction component
1 parent 6f9fe9a commit aebfe79

12 files changed

Lines changed: 322 additions & 72 deletions

File tree

packages/react/package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
"@tiny-design/icons": "workspace:*",
6767
"@tiny-design/tokens": "workspace:*",
6868
"classnames": "^2.3.1",
69-
"react-transition-group": "^4.4.2",
7069
"tslib": "^2.3.1"
7170
},
7271
"devDependencies": {
@@ -75,7 +74,6 @@
7574
"@types/jest": "^29.0.0",
7675
"@types/react": "^18.2.0",
7776
"@types/react-dom": "^18.2.0",
78-
"@types/react-transition-group": "^4.4.4",
7977
"jest": "^29.0.0",
8078
"jest-environment-jsdom": "^29.0.0",
8179
"autoprefixer": "^10.4.4",

packages/react/src/alert/__tests__/__snapshots__/alert.test.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
exports[`<Alert /> should match the snapshot 1`] = `
44
<DocumentFragment>
55
<div
6-
class="ty-alert ty-alert_info ty-undefined-appear ty-undefined-appear-active"
6+
class="ty-alert ty-alert_info ty-undefined-enter"
77
role="alert"
88
>
99
<div>

packages/react/src/drawer/drawer.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useContext, useEffect, useId, useRef, useState } from 'react';
22
import classNames from 'classnames';
3-
import { CSSTransition } from 'react-transition-group';
3+
import Transition from '../transition';
44
import Overlay from '../overlay';
55
import { ConfigContext } from '../config-provider/config-context';
66
import { getPrefixCls } from '../_utils/general';
@@ -91,11 +91,12 @@ const Drawer = React.forwardRef<HTMLDivElement, DrawerProps>((props, ref) => {
9191
}}
9292
style={maskStyle}>
9393
<div ref={ref} className={cls} style={{ ...style, ...sty }}>
94-
<CSSTransition
94+
<Transition
9595
appear={true}
9696
nodeRef={nodeRef}
9797
in={drawerVisible}
9898
timeout={0}
99+
unmountOnExit={false}
99100
classNames={`${prefixCls}__content_move`}>
100101
<div
101102
ref={nodeRef}
@@ -113,7 +114,7 @@ const Drawer = React.forwardRef<HTMLDivElement, DrawerProps>((props, ref) => {
113114
<div className={`${prefixCls}__body`}>{children}</div>
114115
{footer && <div className={`${prefixCls}__footer`}>{footer}</div>}
115116
</div>
116-
</CSSTransition>
117+
</Transition>
117118
</div>
118119
</Overlay>
119120
);

packages/react/src/message/__tests__/__snapshots__/message.test.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
exports[`<Message /> should match the snapshot 1`] = `
44
<DocumentFragment>
55
<div
6-
class="ty-message ty-message_fade-slide-appear ty-message_fade-slide-appear-active"
6+
class="ty-message ty-message_fade-slide-enter"
77
role="alert"
88
>
99
<svg

packages/react/src/message/message.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useEffect, useRef, useState, useContext } from 'react';
22
import classNames from 'classnames';
3-
import { CSSTransition } from 'react-transition-group';
3+
import Transition from '../transition';
44
import { ConfigContext } from '../config-provider/config-context';
55
import { getPrefixCls } from '../_utils/general';
66
import {
@@ -72,13 +72,13 @@ const Message = (props: MessageProps): JSX.Element => {
7272
}, [duration, willUnmount]);
7373

7474
return (
75-
<CSSTransition nodeRef={ref} in={visible} appear={true} timeout={0} classNames={`${prefixCls}_fade-slide`}>
75+
<Transition nodeRef={ref} in={visible} appear={true} timeout={0} unmountOnExit={false} classNames={`${prefixCls}_fade-slide`}>
7676
<div role="alert" className={cls} style={style} ref={ref}>
7777
{renderIcon()}
7878
<span className={`${prefixCls}__content`}>{content}</span>
7979
{extra && <div className={`${prefixCls}__extra`}>{extra}</div>}
8080
</div>
81-
</CSSTransition>
81+
</Transition>
8282
);
8383
};
8484

packages/react/src/modal/modal.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useContext, useEffect, useId, useRef, useState } from 'react';
22
import classNames from 'classnames';
3-
import { CSSTransition } from 'react-transition-group';
3+
import Transition from '../transition';
44
import Overlay from '../overlay';
55
import Button from '../button/button';
66
import Flex from '../flex/flex';
@@ -132,11 +132,12 @@ const Modal = React.forwardRef<HTMLDivElement, ModalProps>((props, ref) => {
132132
style={maskStyle}>
133133
<div ref={ref} className={cls} style={{ top }}>
134134
<div style={{ width, ...style }}>
135-
<CSSTransition
135+
<Transition
136136
appear={true}
137137
nodeRef={nodeRef}
138138
in={modalVisible}
139139
classNames={`${prefixCls}__content_${animation}`}
140+
unmountOnExit={false}
140141
timeout={0}>
141142
<div
142143
ref={nodeRef}
@@ -160,7 +161,7 @@ const Modal = React.forwardRef<HTMLDivElement, ModalProps>((props, ref) => {
160161
</div>
161162
{renderFooter()}
162163
</div>
163-
</CSSTransition>
164+
</Transition>
164165
</div>
165166
</div>
166167
</Overlay>

packages/react/src/overlay/overlay.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useContext, useEffect, useRef } from 'react';
22
import classNames from 'classnames';
33
import Portal from '../portal';
4-
import { CSSTransition } from 'react-transition-group';
4+
import Transition from '../transition';
55
import { ConfigContext } from '../config-provider/config-context';
66
import { getPrefixCls } from '../_utils/general';
77
import { OverlayProps } from './types';
@@ -47,7 +47,7 @@ const Overlay = (props: OverlayProps): JSX.Element => {
4747

4848
return (
4949
<Portal>
50-
<CSSTransition
50+
<Transition
5151
appear={true}
5252
nodeRef={nodeRef}
5353
onEnter={onEnter}
@@ -62,7 +62,7 @@ const Overlay = (props: OverlayProps): JSX.Element => {
6262
<div ref={nodeRef} tabIndex={-1} className={cls} onClick={clickCallback} style={{ zIndex, ...style }}>
6363
{children}
6464
</div>
65-
</CSSTransition>
65+
</Transition>
6666
</Portal>
6767
);
6868
};

packages/react/src/transition/__tests__/__snapshots__/transition.test.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
exports[`<Transition /> should match the snapshot 1`] = `
44
<DocumentFragment>
55
<div
6-
class="ty-zoom-top-appear ty-zoom-top-appear-active"
6+
class="ty-zoom-top-enter"
77
>
88
Content
99
</div>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import Transition from './transition';
22

33
export type { AnimationName, TransitionProps } from './transition';
4+
export { default as useTransition } from './use-transition';
5+
export type { TransitionState, UseTransitionOptions, UseTransitionResult } from './use-transition';
46
export default Transition;

packages/react/src/transition/transition.tsx

Lines changed: 86 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React from 'react';
2-
import { CSSTransition } from 'react-transition-group';
3-
import { CSSTransitionProps } from 'react-transition-group/CSSTransition';
2+
import useTransition, { TransitionState } from './use-transition';
43

54
export type AnimationName =
65
| 'zoom-center-top'
@@ -23,42 +22,112 @@ export type AnimationName =
2322
| 'slide-down';
2423

2524
export type TransitionProps = {
25+
in?: boolean;
26+
timeout?: number | { enter: number; exit: number };
27+
appear?: boolean;
28+
unmountOnExit?: boolean;
29+
mountOnEnter?: boolean;
30+
2631
/** Animation prefix */
2732
prefix?: string;
2833

2934
/** Preset animation name */
3035
animation?: AnimationName;
3136

37+
/** Custom class name base (overrides prefix + animation) */
38+
classNames?: string;
39+
3240
/** Prevent the transition conflict with the inner component */
3341
wrapper?: boolean;
42+
43+
nodeRef?: React.RefObject<HTMLElement | null>;
44+
45+
onEnter?: () => void;
46+
onEntering?: () => void;
47+
onEntered?: () => void;
48+
onExit?: () => void;
49+
onExiting?: () => void;
50+
onExited?: () => void;
51+
3452
children?: React.ReactNode;
35-
} & Partial<CSSTransitionProps<HTMLElement>>;
53+
};
54+
55+
function getTransitionClasses(base: string, state: TransitionState): string {
56+
switch (state) {
57+
case 'enter':
58+
return `${base}-enter`;
59+
case 'entering':
60+
return `${base}-enter ${base}-enter-active`;
61+
case 'entered':
62+
return `${base}-enter-done`;
63+
case 'exit':
64+
return `${base}-exit`;
65+
case 'exiting':
66+
return `${base}-exit ${base}-exit-active`;
67+
case 'exited':
68+
return `${base}-exit-done`;
69+
default:
70+
return '';
71+
}
72+
}
3673

37-
const Transition = (props: TransitionProps): React.ReactElement => {
74+
const Transition = (props: TransitionProps): React.ReactElement | null => {
3875
const {
76+
in: inProp = false,
3977
timeout = 300,
4078
unmountOnExit = true,
79+
mountOnEnter,
4180
appear = true,
4281
prefix = 'ty',
4382
animation,
44-
classNames,
83+
classNames: classNamesProp,
4584
nodeRef,
4685
children,
4786
wrapper,
48-
...otherProps
87+
onEnter,
88+
onEntering,
89+
onEntered,
90+
onExit,
91+
onExiting,
92+
onExited,
4993
} = props;
5094

51-
return (
52-
<CSSTransition
53-
{...(otherProps as CSSTransitionProps<HTMLElement>)}
54-
timeout={timeout}
55-
appear={appear}
56-
unmountOnExit={unmountOnExit}
57-
nodeRef={nodeRef}
58-
classNames={classNames ? classNames : `${prefix}-${animation}`}>
59-
{wrapper ? <div>{children}</div> : (children as React.ReactElement)}
60-
</CSSTransition>
61-
);
95+
const { state, shouldMount } = useTransition({
96+
in: inProp,
97+
timeout,
98+
appear,
99+
unmountOnExit,
100+
mountOnEnter,
101+
onEnter,
102+
onEntering,
103+
onEntered,
104+
onExit,
105+
onExiting,
106+
onExited,
107+
nodeRef,
108+
});
109+
110+
if (!shouldMount) {
111+
return null;
112+
}
113+
114+
const base = classNamesProp ? classNamesProp : `${prefix}-${animation}`;
115+
const transitionClasses = getTransitionClasses(base, state);
116+
117+
const child = wrapper ? <div>{children}</div> : children;
118+
119+
if (React.isValidElement(child)) {
120+
const existingClassName = (child.props as { className?: string }).className || '';
121+
const mergedClassName = existingClassName
122+
? `${existingClassName} ${transitionClasses}`.trim()
123+
: transitionClasses;
124+
125+
return React.cloneElement(child as React.ReactElement<{ className?: string }>, {
126+
className: mergedClassName || undefined,
127+
});
128+
}
129+
130+
return child as React.ReactElement;
62131
};
63132

64133
Transition.displayName = 'Transition';

0 commit comments

Comments
 (0)