UNPKG

4.86 kBJavaScriptView Raw
1/**
2* This source code is quoted from rc-steps.
3* homepage: https://github.com/react-component/steps
4*/
5import React, { cloneElement, Children, Component } from 'react';
6import PropTypes from 'prop-types';
7import { findDOMNode } from 'react-dom';
8import classNames from 'classnames';
9import debounce from 'lodash.debounce';
10import { isFlexSupported } from './utils';
11
12const 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
33const 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
45class 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 // Just for IE9
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 // +1 for fit edge bug of digit width, like 35.4px
85 const lastStepOffsetWidth = (domNode.lastChild.offsetWidth || 0) + 1;
86 // Reduce shake bug
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 // fix tail color
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}
153Steps.defaultProps = defaultProps;
154Steps.propTypes = propTypes;
155
156export default Steps;