1 | import React, { PropTypes } from 'react';
|
2 | import ReactDOM from 'react-dom';
|
3 | import classNames from 'classnames';
|
4 |
|
5 | const 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 |
|
16 | const 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 |
|
26 | class 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 |
|
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 |
|
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 | }
|
114 | Steps.defaultProps = defaultProps;
|
115 | Steps.propTypes = propTypes;
|
116 |
|
117 | export default Steps;
|