1- import React , { FC , useContext , useMemo } from 'react' ;
2- import classnames from 'classnames' ;
3- import { Icon } from 'tdesign-icons-react' ;
1+ import React , { FC , useContext , useEffect , useMemo , useRef , MouseEvent } from 'react' ;
2+ import classNames from 'classnames' ;
3+ import { CheckIcon , CloseIcon } from 'tdesign-icons-react' ;
44import withNativeProps , { NativeProps } from '../_util/withNativeProps' ;
5+ import parseTNode from '../_util/parseTNode' ;
6+ import useDefaultProps from '../hooks/useDefaultProps' ;
7+ import { usePrefixClass } from '../hooks/useClass' ;
58import { TdStepItemProps } from './type' ;
6- import useConfig from '../_util/useConfig' ;
79import { stepItemDefaultProps } from './defaultProps' ;
8- import StepsContext from './StepsContext' ;
10+ import { StepsContext } from './StepsContext' ;
911
1012export enum StepItemStatusEnum {
1113 DEFAULT = 'default' ,
@@ -19,106 +21,164 @@ export enum StepThemeEnum {
1921 DOT = 'dot' ,
2022}
2123
22- export enum StepLayoutEnum {
23- VERTICAL = 'vertical' ,
24- HORIZONTAL = 'horizontal' ,
25- }
26-
27- export interface StepItemProps extends TdStepItemProps , NativeProps {
28- index : number ;
29- }
24+ export interface StepItemProps extends TdStepItemProps , NativeProps { }
3025
3126const StepItem : FC < StepItemProps > = ( props ) => {
32- const { title, content, icon, status, index, children } = props ;
33-
34- const { value, readonly, theme, layout, onChange } = useContext ( StepsContext ) ;
27+ const { title, content, icon, status, children, titleRight, extra } = useDefaultProps ( props , stepItemDefaultProps ) ;
28+
29+ const stepItemClass = usePrefixClass ( 'step-item' ) ;
30+ const stepItemRef = useRef < HTMLDivElement > ( null ) ;
31+
32+ const {
33+ childrenNodes,
34+ current,
35+ relation,
36+ removeRelation,
37+ onClickItem,
38+ currentStatus : stepsStatus ,
39+ layout,
40+ readonly,
41+ theme,
42+ sequence,
43+ } = useContext ( StepsContext ) ;
44+
45+ useEffect ( ( ) => {
46+ relation ( stepItemRef . current ) ;
47+ const stepItemCur = stepItemRef . current ;
48+ return ( ) => {
49+ removeRelation ( stepItemCur ) ;
50+ } ;
51+ // eslint-disable-next-line react-hooks/exhaustive-deps
52+ } , [ ] ) ;
53+
54+ const index = useMemo ( ( ) => childrenNodes . indexOf ( stepItemRef . current ) , [ childrenNodes ] ) ;
55+ const isLastChild = useMemo (
56+ ( ) => index === ( sequence === 'positive' ? childrenNodes . length - 1 : 0 ) ,
57+ [ index , sequence , childrenNodes ] ,
58+ ) ;
3559
36- const { classPrefix } = useConfig ( ) ;
60+ const dot = useMemo ( ( ) => theme === StepThemeEnum . DOT , [ theme ] ) ;
61+ const currentStatus = useMemo ( ( ) => {
62+ if ( status !== 'default' ) return status ;
63+ if ( index === current ) return stepsStatus ;
64+ if ( index < + current ) return 'finish' ;
65+ return status ;
66+ } , [ index , current , stepsStatus , status ] ) ;
67+
68+ const rootClassName = useMemo (
69+ ( ) =>
70+ classNames ( stepItemClass , `${ stepItemClass } --${ layout } ` , {
71+ [ `${ stepItemClass } --default` ] : readonly ,
72+ [ `${ stepItemClass } --${ currentStatus } ` ] : currentStatus ,
73+ } ) ,
74+ [ stepItemClass , layout , readonly , currentStatus ] ,
75+ ) ;
3776
38- const name = `${ classPrefix } -step` ;
77+ const iconWrapperClassName = useMemo (
78+ ( ) => classNames ( `${ stepItemClass } __anchor` , `${ stepItemClass } __anchor--${ layout } ` ) ,
79+ [ stepItemClass , layout ] ,
80+ ) ;
81+ const dotClassName = useMemo (
82+ ( ) => classNames ( `${ stepItemClass } __dot` , `${ stepItemClass } __dot--${ currentStatus } ` ) ,
83+ [ stepItemClass , currentStatus ] ,
84+ ) ;
85+ const iconClassName = useMemo (
86+ ( ) =>
87+ classNames ( {
88+ [ `${ stepItemClass } __icon` ] : icon ,
89+ [ `${ stepItemClass } __icon--${ currentStatus } ` ] : icon ,
90+ [ `${ stepItemClass } __circle` ] : ! icon ,
91+ [ `${ stepItemClass } __circle--${ currentStatus } ` ] : ! icon ,
92+ } ) ,
93+ [ stepItemClass , currentStatus , icon ] ,
94+ ) ;
95+ const contentClassName = useMemo (
96+ ( ) =>
97+ classNames ( `${ stepItemClass } __content` , `${ stepItemClass } __content--${ layout } ` , {
98+ [ `${ stepItemClass } __content--last` ] : isLastChild ,
99+ } ) ,
100+ [ stepItemClass , layout , isLastChild ] ,
101+ ) ;
39102
40- const dot = useMemo ( ( ) => theme === StepThemeEnum . DOT && layout === StepLayoutEnum . VERTICAL , [ theme , layout ] ) ;
103+ const titleClassName = useMemo (
104+ ( ) =>
105+ classNames (
106+ `${ stepItemClass } __title` ,
107+ `${ stepItemClass } __title--${ currentStatus } ` ,
108+ `${ stepItemClass } __title--${ layout } ` ,
109+ ) ,
110+ [ stepItemClass , currentStatus , layout ] ,
111+ ) ;
112+ const descriptionClassName = useMemo (
113+ ( ) =>
114+ classNames (
115+ `${ stepItemClass } __description` ,
116+ `${ stepItemClass } __description--${ currentStatus } ` ,
117+ `${ stepItemClass } __description--${ layout } ` ,
118+ ) ,
119+ [ stepItemClass , currentStatus , layout ] ,
120+ ) ;
121+ const extraClassName = useMemo (
122+ ( ) =>
123+ classNames (
124+ `${ stepItemClass } __extra` ,
125+ `${ stepItemClass } __extra--${ currentStatus } ` ,
126+ `${ stepItemClass } __extra--${ layout } ` ,
127+ ) ,
128+ [ stepItemClass , currentStatus , layout ] ,
129+ ) ;
130+ const separatorClassName = useMemo (
131+ ( ) =>
132+ classNames (
133+ `${ stepItemClass } __line` ,
134+ `${ stepItemClass } __line--${ currentStatus } ` ,
135+ `${ stepItemClass } __line--${ sequence } ` ,
136+ `${ stepItemClass } __line--${ layout } ` ,
137+ `${ stepItemClass } __line--${ theme } ` ,
138+ ) ,
139+ [ stepItemClass , currentStatus , layout , sequence , theme ] ,
140+ ) ;
41141
42- const onStepClick = ( e ) => {
43- if ( readonly || dot ) return ;
44- const currentValue = index ;
45- onChange ( currentValue , value , { e } ) ;
142+ const onStepClick = ( e : MouseEvent < HTMLDivElement > ) => {
143+ if ( readonly ) return ;
144+ onClickItem ( index , current , { e } ) ;
46145 } ;
47146
48- const innerClassName = useMemo ( ( ) => {
49- if ( typeof icon === 'boolean' ) {
50- return ` ${ name } __inner` ;
147+ const renderIconContent = ( ) => {
148+ if ( icon ) {
149+ return parseTNode ( icon ) ;
51150 }
52- return classnames ( `${ name } __inner` , `${ name } __inner__icon` ) ;
53- } , [ name , icon ] ) ;
54151
55- const iconContent = useMemo ( ( ) => {
56- if ( dot ) {
57- return '' ;
152+ if ( currentStatus === StepItemStatusEnum . ERROR ) {
153+ return < CloseIcon /> ;
58154 }
59155
60- if ( status === StepItemStatusEnum . ERROR ) {
61- return < Icon name = "close" /> ;
156+ if ( currentStatus === StepItemStatusEnum . FINISH ) {
157+ return < CheckIcon /> ;
62158 }
63159
64- if ( index < value && readonly ) {
65- return < Icon name = "check" /> ;
66- }
67-
68- if ( typeof icon === 'boolean' ) {
69- return index + 1 ;
70- }
71-
72- if ( typeof icon === 'string' ) {
73- return < Icon name = { icon } size = "24" /> ;
74- }
75-
76- return icon ;
77- } , [ status , index , value , readonly , icon , dot ] ) ;
78-
79- const currentStatus = useMemo ( ( ) => {
80- if ( status !== StepItemStatusEnum . DEFAULT ) {
81- return status ;
82- }
83- if ( + value === index ) {
84- return StepItemStatusEnum . PROCESS ;
85- }
86- if ( + value > index ) {
87- return StepItemStatusEnum . FINISH ;
88- }
89- return '' ;
90- } , [ value , index , status ] ) ;
160+ return index + 1 ;
161+ } ;
91162
92163 return withNativeProps (
93164 props ,
94- < div
95- className = { classnames ( `${ name } ` , {
96- [ `${ name } --default` ] : ! readonly ,
97- [ `${ name } --${ currentStatus } ` ] : currentStatus ,
98- } ) }
99- >
100- < div className = { innerClassName } >
101- < div className = { `${ name } -icon` } >
102- < div
103- className = { classnames ( `${ name } -icon__number` , {
104- [ `${ name } -icon__dot` ] : dot ,
105- } ) }
106- onClick = { onStepClick }
107- >
108- { iconContent }
109- </ div >
110- </ div >
111- < div className = { `${ name } -content` } >
112- < div className = { `${ name } -title` } > { title } </ div >
113- < div className = { `${ name } -description` } > { content || children } </ div >
114- < div className = { `${ name } -extra` } />
165+ < div ref = { stepItemRef } className = { rootClassName } onClick = { onStepClick } >
166+ < div className = { iconWrapperClassName } >
167+ { dot ? < div className = { dotClassName } > </ div > : < div className = { iconClassName } > { renderIconContent ( ) } </ div > }
168+ </ div >
169+ < div className = { contentClassName } >
170+ < div className = { titleClassName } >
171+ { parseTNode ( title ) }
172+ { layout === 'vertical' && parseTNode ( titleRight ) }
115173 </ div >
174+ < div className = { descriptionClassName } > { parseTNode ( content ) || parseTNode ( children ) } </ div >
175+ < div className = { extraClassName } > { parseTNode ( extra ) } </ div >
116176 </ div >
177+ { ! isLastChild && < div className = { separatorClassName } > </ div > }
117178 </ div > ,
118179 ) ;
119180} ;
120181
121182StepItem . displayName = 'StepItem' ;
122- StepItem . defaultProps = stepItemDefaultProps ;
123183
124184export default StepItem ;
0 commit comments