basicStep.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import React from 'react';
  2. import { isFunction } from 'lodash';
  3. import PropTypes from 'prop-types';
  4. import classnames from 'classnames';
  5. import { stepsClasses as css } from '@douyinfe/semi-foundation/steps/constants';
  6. import { IconTickCircle, IconAlertCircle, IconAlertTriangle } from '@douyinfe/semi-icons';
  7. export type Status = 'wait' | 'process' | 'finish' | 'error' | 'warning';
  8. export type Size = 'default' | 'small';
  9. export interface BasicStepProps {
  10. description?: React.ReactNode;
  11. icon?: React.ReactNode;
  12. status?: Status;
  13. title?: React.ReactNode;
  14. className?: string;
  15. style?: React.CSSProperties;
  16. active?: boolean;
  17. prefixCls?: string;
  18. stepNumber?: string;
  19. size?: Size;
  20. done?: boolean;
  21. onChange?: () => void;
  22. onClick?: React.MouseEventHandler<HTMLDivElement>;
  23. onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
  24. "role"?: React.AriaRole;
  25. "aria-label"?: React.AriaAttributes["aria-label"];
  26. // 以下参数用于 C2D, 用户请勿使用
  27. // The following parameters are used for C2D, users should not use them
  28. usedInC2D?: string;
  29. direction?: string
  30. }
  31. export enum stepSizeMapIconSize {
  32. small = 'large',
  33. default = 'extra-large'
  34. }
  35. const BasicStep = (props: BasicStepProps) => {
  36. const {
  37. prefixCls,
  38. className,
  39. size,
  40. title,
  41. description,
  42. status,
  43. style,
  44. active,
  45. done,
  46. icon,
  47. stepNumber,
  48. onClick,
  49. onChange,
  50. onKeyDown,
  51. usedInC2D,
  52. direction: direction,
  53. } = props;
  54. const renderIcon = () => {
  55. let inner, progress;
  56. if ('icon' in props) {
  57. if (React.isValidElement(icon)) {
  58. inner = icon;
  59. }
  60. } else if ('status' in props) {
  61. switch (status) {
  62. case 'error':
  63. inner = <IconAlertCircle size={stepSizeMapIconSize[size]} />;
  64. break;
  65. case 'wait':
  66. inner = <span className={`${prefixCls}-number-icon`}>{stepNumber}</span>;
  67. break;
  68. case 'process':
  69. inner = <span className={`${prefixCls}-number-icon`}>{stepNumber}</span>;
  70. progress = true;
  71. break;
  72. case 'finish':
  73. inner = <IconTickCircle size={stepSizeMapIconSize[size]} />;
  74. break;
  75. case 'warning':
  76. inner = <IconAlertTriangle size={stepSizeMapIconSize[size]} />;
  77. break;
  78. default:
  79. inner = null;
  80. break;
  81. }
  82. }
  83. const cls = classnames({
  84. [`${prefixCls}-icon`]: true,
  85. [`${prefixCls}-custom-icon`]: 'icon' in props,
  86. [`${prefixCls}-icon-process`]: progress,
  87. });
  88. return inner ? <span className={cls}>{inner}</span> : null;
  89. };
  90. const classString = classnames(prefixCls, `${prefixCls}-${status}`, {
  91. [`${prefixCls}-active`]: active,
  92. [`${prefixCls}-done`]: done,
  93. [`${prefixCls}-basic`]: usedInC2D,
  94. [`${prefixCls}-${direction}`]: direction,
  95. [`${prefixCls}-small`]: size === 'small',
  96. }, className);
  97. const handleClick = (e: React.MouseEvent) => {
  98. if (isFunction(onClick)) {
  99. onClick(e);
  100. }
  101. onChange();
  102. };
  103. const handleKeyDown = (e) => {
  104. if (e.key === 'Enter') {
  105. if (isFunction(onKeyDown)) {
  106. onKeyDown(e);
  107. }
  108. onChange();
  109. }
  110. };
  111. return (
  112. <div role={props["role"]} aria-label={props["aria-label"]} tabIndex={0} aria-current="step" className={classString} style={style} onClick={e => handleClick(e)} onKeyDown={handleKeyDown}>
  113. <div className={`${prefixCls}-container`}>
  114. <div className={`${prefixCls}-left`}>{renderIcon()}</div>
  115. <div className={`${prefixCls}-content`}>
  116. <div className={`${prefixCls}-title`}>
  117. <div className={`${prefixCls}-title-text`}>{title}</div>
  118. </div>
  119. {description && <div className={`${prefixCls}-description`}>{description}</div>}
  120. </div>
  121. </div>
  122. </div>
  123. );
  124. };
  125. BasicStep.propTypes = {
  126. prefixCls: PropTypes.string,
  127. description: PropTypes.node,
  128. icon: PropTypes.node,
  129. status: PropTypes.oneOf(['wait', 'process', 'finish', 'error', 'warning']),
  130. title: PropTypes.node,
  131. className: PropTypes.string,
  132. style: PropTypes.object,
  133. onClick: PropTypes.func,
  134. active: PropTypes.bool,
  135. done: PropTypes.bool,
  136. };
  137. BasicStep.defaultProps = {
  138. prefixCls: css.ITEM,
  139. active: false,
  140. done: false,
  141. status: 'wait',
  142. className: '',
  143. };
  144. BasicStep.elementType = 'Steps.BasicStep';
  145. export default BasicStep;