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