fillSteps.tsx 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import React, { cloneElement, Children, useMemo, ReactElement, isValidElement } from 'react';
  2. import PropTypes from 'prop-types';
  3. import cls from 'classnames';
  4. import { stepsClasses as css } from '@douyinfe/semi-foundation/steps/constants';
  5. import { Row, Col } from '../grid';
  6. export type Status = 'wait' | 'process' | 'finish' | 'error' | 'warning';
  7. export type Direction = 'horizontal' | 'vertical';
  8. export interface FillStepsProps {
  9. prefixCls?: string;
  10. className?: string;
  11. current?: number;
  12. direction?: Direction;
  13. initial?: number;
  14. status?: Status;
  15. style?: React.CSSProperties;
  16. children?: React.ReactNode;
  17. onChange?: (current: number) => void;
  18. "aria-label"?: string;
  19. }
  20. const Steps = (props: FillStepsProps) => {
  21. const { current, status, children, prefixCls, initial, direction, className, style, onChange } = props;
  22. const inner = useMemo(() => {
  23. const filteredChildren = Children.toArray(children).filter(c => isValidElement(c)) as Array<ReactElement>;
  24. const colStyle = direction === 'vertical' ? null : { width: `${100 / filteredChildren.length }%` };
  25. const content = Children.map(filteredChildren, (child: ReactElement, index) => {
  26. if (!child) {
  27. return null;
  28. }
  29. const stepNumber = initial + index;
  30. const childProps = {
  31. stepNumber: `${stepNumber + 1}`,
  32. direction,
  33. ...child.props,
  34. };
  35. if (status === 'error' && index === current - 1) {
  36. childProps.className = `${prefixCls}-next-error`;
  37. }
  38. if (!child.props.status) {
  39. if (stepNumber === current) {
  40. childProps.status = status;
  41. } else if (stepNumber < current) {
  42. childProps.status = 'finish';
  43. } else {
  44. childProps.status = 'wait';
  45. }
  46. }
  47. childProps.onChange = () => {
  48. if (index !== current) {
  49. onChange(index + initial);
  50. }
  51. };
  52. return <Col style={colStyle}>{cloneElement(child, { ...childProps })}</Col>;
  53. });
  54. return content;
  55. }, [children, initial, prefixCls, direction, status, current, onChange]);
  56. const wrapperCls = cls(className, {
  57. [prefixCls]: true,
  58. [`${prefixCls}-${ direction}`]: true
  59. });
  60. return (
  61. <div
  62. className={wrapperCls}
  63. style={style}
  64. aria-label={props["aria-label"]}
  65. >
  66. <Row type="flex" justify="start">
  67. {inner}
  68. </Row>
  69. </div>
  70. );
  71. };
  72. Steps.propTypes = {
  73. prefixCls: PropTypes.string,
  74. className: PropTypes.string,
  75. style: PropTypes.object,
  76. current: PropTypes.number,
  77. initial: PropTypes.number,
  78. direction: PropTypes.oneOf(['horizontal', 'vertical']),
  79. status: PropTypes.oneOf(['wait', 'process', 'finish', 'error', 'warning'])
  80. };
  81. Steps.defaultProps = {
  82. prefixCls: css.PREFIX,
  83. current: 0,
  84. direction: 'horizontal',
  85. initial: 0,
  86. status: 'process',
  87. };
  88. export default Steps;