1 | import React, { cloneElement, Children, Component } from 'react';
|
2 | import PropTypes from 'prop-types';
|
3 | import { findDOMNode } from 'react-dom';
|
4 | import classNames from 'classnames';
|
5 | import debounce from 'lodash.debounce';
|
6 | import { isFlexSupported } from './utils';
|
7 |
|
8 | const propTypes = {prefixCls: PropTypes.string,
|
9 | className: PropTypes.string,
|
10 | iconPrefix: PropTypes.string,
|
11 | direction: PropTypes.string,
|
12 | labelPlacement: PropTypes.string,
|
13 | children: PropTypes.any,
|
14 | status: PropTypes.string,
|
15 | size: PropTypes.string,
|
16 | progressDot: PropTypes.oneOfType([
|
17 | PropTypes.bool,
|
18 | PropTypes.func,
|
19 | ]),
|
20 | style: PropTypes.object,
|
21 | initial: PropTypes.number,
|
22 | current: PropTypes.number,
|
23 | icons: PropTypes.shape({
|
24 | finish: PropTypes.node,
|
25 | error: PropTypes.node,
|
26 | }),
|
27 | };
|
28 |
|
29 | const defaultProps = {
|
30 | prefixCls: 'u-steps',
|
31 | iconPrefix: 'u',
|
32 | direction: 'horizontal',
|
33 | labelPlacement: 'horizontal',
|
34 | current: 0,
|
35 | initial: 0,
|
36 | status: 'process',
|
37 | size: 'default',
|
38 | progressDot: false,
|
39 | };
|
40 |
|
41 | class Steps extends React.Component {
|
42 | constructor(props) {
|
43 | super(props);
|
44 | this.state = {
|
45 | flexSupported: true,
|
46 | lastStepOffsetWidth: 0,
|
47 | };
|
48 | this.calcStepOffsetWidth = debounce(this.calcStepOffsetWidth, 150);
|
49 | }
|
50 | componentDidMount() {
|
51 | this.calcStepOffsetWidth();
|
52 | if (!isFlexSupported()) {
|
53 | this.setState({
|
54 | flexSupported: false,
|
55 | });
|
56 | }
|
57 | }
|
58 | componentDidUpdate() {
|
59 | this.calcStepOffsetWidth();
|
60 | }
|
61 | componentWillUnmount() {
|
62 | if (this.calcTimeout) {
|
63 | clearTimeout(this.calcTimeout);
|
64 | }
|
65 | if (this.calcStepOffsetWidth && this.calcStepOffsetWidth.cancel) {
|
66 | this.calcStepOffsetWidth.cancel();
|
67 | }
|
68 | }
|
69 | calcStepOffsetWidth = () => {
|
70 | if (isFlexSupported()) {
|
71 | return;
|
72 | }
|
73 |
|
74 | const domNode = findDOMNode(this);
|
75 | if (domNode.children.length > 0) {
|
76 | if (this.calcTimeout) {
|
77 | clearTimeout(this.calcTimeout);
|
78 | }
|
79 | this.calcTimeout = setTimeout(() => {
|
80 |
|
81 | const lastStepOffsetWidth = (domNode.lastChild.offsetWidth || 0) + 1;
|
82 |
|
83 | if (this.state.lastStepOffsetWidth === lastStepOffsetWidth ||
|
84 | Math.abs(this.state.lastStepOffsetWidth - lastStepOffsetWidth) <= 3) {
|
85 | return;
|
86 | }
|
87 | this.setState({ lastStepOffsetWidth });
|
88 | });
|
89 | }
|
90 | }
|
91 | render() {
|
92 | const {
|
93 | prefixCls, style = {}, className, children, direction,
|
94 | labelPlacement, iconPrefix, status, size, current, progressDot, initial,
|
95 | icons,
|
96 | ...restProps,
|
97 | } = this.props;
|
98 | const { lastStepOffsetWidth, flexSupported } = this.state;
|
99 | const filteredChildren = React.Children.toArray(children).filter(c => !!c);
|
100 | const lastIndex = filteredChildren.length - 1;
|
101 | const adjustedlabelPlacement = !!progressDot ? 'vertical' : labelPlacement;
|
102 | const classString = classNames(prefixCls, `${prefixCls}-${direction}`, className, {
|
103 | [`${prefixCls}-${size}`]: size,
|
104 | [`${prefixCls}-label-${adjustedlabelPlacement}`]: direction === 'horizontal',
|
105 | [`${prefixCls}-dot`]: !!progressDot,
|
106 | });
|
107 |
|
108 | return (
|
109 | <div className={classString} style={style} {...restProps}>
|
110 | {
|
111 | Children.map(filteredChildren, (child, index) => {
|
112 | if (!child) {
|
113 | return null;
|
114 | }
|
115 | const stepNumber = initial + index;
|
116 | const childProps = {
|
117 | stepNumber: `${stepNumber + 1}`,
|
118 | prefixCls,
|
119 | iconPrefix,
|
120 | wrapperStyle: style,
|
121 | progressDot,
|
122 | icons,
|
123 | ...child.props,
|
124 | };
|
125 | if (!flexSupported && direction !== 'vertical' && index !== lastIndex) {
|
126 | childProps.itemWidth = `${100 / lastIndex}%`;
|
127 | childProps.adjustMarginRight = -Math.round(lastStepOffsetWidth / lastIndex + 1);
|
128 | }
|
129 |
|
130 | if (status === 'error' && index === current - 1) {
|
131 | childProps.className = `${prefixCls}-next-error`;
|
132 | }
|
133 | if (!child.props.status) {
|
134 | if (stepNumber === current) {
|
135 | childProps.status = status;
|
136 | } else if (stepNumber < current) {
|
137 | childProps.status = 'finish';
|
138 | } else {
|
139 | childProps.status = 'wait';
|
140 | }
|
141 | }
|
142 | return cloneElement(child, childProps);
|
143 | })
|
144 | }
|
145 | </div>
|
146 | );
|
147 | }
|
148 | }
|
149 | Steps.defaultProps = defaultProps;
|
150 | Steps.propTypes = propTypes;
|
151 |
|
152 | export default Steps;
|