UNPKG

3.53 kBJavaScriptView Raw
1import React, { PropTypes } from 'react';
2import ReactDOM from 'react-dom';
3import classNames from 'classnames';
4
5const propTypes = {
6 prefixCls: PropTypes.string,
7 iconPrefix: PropTypes.string,
8 labelPlacement: PropTypes.string,
9 children: PropTypes.any,
10 current: PropTypes.number,
11 status: PropTypes.oneOf(['wait', 'process', 'finish', 'error']),
12 direction: PropTypes.oneOf(['horizontal', 'vertical']),
13 size: PropTypes.oneOf(['default', 'small'])
14};
15
16const defaultProps = {
17 prefixCls: 'u-steps',
18 iconPrefix: 'u',
19 direction: 'horizontal',
20 labelPlacement: 'horizontal',
21 current: 0,
22 status: 'process',
23 size: 'default',
24};
25
26class Steps extends React.Component {
27 constructor(props) {
28 super(props);
29 this.state = {
30 lastStepOffsetWidth: 0,
31 };
32 }
33 componentDidMount() {
34 this.calcLastStepOffsetWidth();
35 }
36 componentDidUpdate() {
37 this.calcLastStepOffsetWidth();
38 }
39 componentWillUnmount() {
40 if (this.calcTimeout) {
41 clearTimeout(this.calcTimeout);
42 }
43 }
44 calcLastStepOffsetWidth = () => {
45 const domNode = ReactDOM.findDOMNode(this);
46 if (domNode.children.length > 0) {
47 if (this.calcTimeout) {
48 clearTimeout(this.calcTimeout);
49 }
50 this.calcTimeout = setTimeout(() => {
51 // +1 for fit edge bug of digit width, like 35.4px
52 const lastStepOffsetWidth = (domNode.lastChild.offsetWidth || 0) + 1;
53 if (this.state.lastStepOffsetWidth === lastStepOffsetWidth) {
54 return;
55 }
56 this.setState({ lastStepOffsetWidth });
57 });
58 }
59 }
60 render() {
61 const props = this.props;
62 const { prefixCls, style = {}, className, children, direction,
63 labelPlacement, iconPrefix, status, size, current, ...restProps } = props;
64 const lastIndex = children.length - 1;
65 const reLayouted = this.state.lastStepOffsetWidth > 0;
66 const classString = classNames({
67 [prefixCls]: true,
68 [`${prefixCls}-${size}`]: size,
69 [`${prefixCls}-${direction}`]: true,
70 [`${prefixCls}-label-${labelPlacement}`]: direction === 'horizontal',
71 [`${prefixCls}-hidden`]: !reLayouted,
72 [className]: className,
73 });
74
75 return (
76 <div className={classString} style={style} {...restProps}>
77 {
78 React.Children.map(children, (ele, idx) => {
79 const tailWidth = (direction === 'vertical' || idx === lastIndex || !reLayouted)
80 ? null : `${100 / lastIndex}%`;
81 const adjustMarginRight = (direction === 'vertical' || idx === lastIndex)
82 ? null : -Math.round(this.state.lastStepOffsetWidth / lastIndex + 1);
83 const np = {
84 stepNumber: (idx + 1).toString(),
85 stepLast: idx === lastIndex,
86 tailWidth,
87 adjustMarginRight,
88 prefixCls,
89 iconPrefix,
90 wrapperStyle: style,
91 };
92
93 // fix tail color
94 if (status === 'error' && idx === current - 1) {
95 np.className = `${props.prefixCls}-next-error`;
96 }
97
98 if (!ele.props.status) {
99 if (idx === current) {
100 np.status = status;
101 } else if (idx < current) {
102 np.status = 'finish';
103 } else {
104 np.status = 'wait';
105 }
106 }
107 return React.cloneElement(ele, np);
108 }, this)
109 }
110 </div>
111 );
112 }
113}
114Steps.defaultProps = defaultProps;
115Steps.propTypes = propTypes;
116
117export default Steps;