Button.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /* eslint-disable react/destructuring-assignment */
  2. import React, { PureComponent, ReactNode } from 'react';
  3. import classNames from 'classnames';
  4. import PropTypes from 'prop-types';
  5. import { cssClasses, strings } from '@douyinfe/semi-foundation/button/constants';
  6. import '@douyinfe/semi-foundation/button/button.scss';
  7. import { noop } from '@douyinfe/semi-foundation/utils/function';
  8. const btnSizes = strings.sizes;
  9. const { htmlTypes, btnTypes } = strings;
  10. export type HtmlType = 'button' | 'reset' | 'submit';
  11. export type Size = 'default' | 'small' | 'large';
  12. export type Theme = 'solid' | 'borderless' | 'light';
  13. export type Type = 'primary' | 'secondary' | 'tertiary' | 'warning' | 'danger';
  14. export interface ButtonProps {
  15. id?: string;
  16. block?: boolean;
  17. circle?: boolean;
  18. children?: ReactNode | undefined;
  19. disabled?: boolean;
  20. className?: string;
  21. icon?: React.ReactNode;
  22. iconPosition?: 'left' | 'right';
  23. loading?: boolean;
  24. htmlType?: HtmlType;
  25. size?: Size;
  26. style?: React.CSSProperties;
  27. theme?: Theme;
  28. type?: Type;
  29. prefixCls?: string;
  30. onClick?: React.MouseEventHandler<HTMLButtonElement>;
  31. onMouseDown?: React.MouseEventHandler<HTMLButtonElement>;
  32. onMouseEnter?: React.MouseEventHandler<HTMLButtonElement>;
  33. onMouseLeave?: React.MouseEventHandler<HTMLButtonElement>;
  34. 'aria-label'?: React.AriaAttributes['aria-label'];
  35. }
  36. // TODO: icon configuration
  37. export default class Button extends PureComponent<ButtonProps> {
  38. static defaultProps = {
  39. disabled: false,
  40. size: 'default',
  41. type: 'primary',
  42. theme: 'light',
  43. block: false,
  44. htmlType: 'button',
  45. onMouseDown: noop,
  46. onClick: noop,
  47. onMouseEnter: noop,
  48. onMouseLeave: noop,
  49. prefixCls: cssClasses.PREFIX,
  50. };
  51. static propTypes = {
  52. children: PropTypes.node,
  53. disabled: PropTypes.bool,
  54. prefixCls: PropTypes.string,
  55. style: PropTypes.object,
  56. size: PropTypes.oneOf(btnSizes),
  57. type: PropTypes.oneOf(btnTypes),
  58. block: PropTypes.bool,
  59. onClick: PropTypes.func,
  60. onMouseDown: PropTypes.func,
  61. circle: PropTypes.bool,
  62. loading: PropTypes.bool,
  63. htmlType: PropTypes.oneOf(htmlTypes),
  64. theme: PropTypes.oneOf(strings.themes),
  65. className: PropTypes.string,
  66. onMouseEnter: PropTypes.func,
  67. onMouseLeave: PropTypes.func,
  68. 'aria-label': PropTypes.string,
  69. };
  70. buttonRef: React.RefObject<HTMLButtonElement>;
  71. constructor(props = {}) {
  72. super(props);
  73. this.buttonRef = React.createRef();
  74. }
  75. focus() {
  76. this.buttonRef.current.focus();
  77. }
  78. blur() {
  79. this.buttonRef.current.blur();
  80. }
  81. render() {
  82. const {
  83. children,
  84. block,
  85. htmlType,
  86. loading,
  87. circle,
  88. className,
  89. style,
  90. disabled,
  91. size,
  92. theme,
  93. type,
  94. prefixCls,
  95. iconPosition,
  96. ...attr
  97. } = this.props;
  98. const baseProps = {
  99. disabled,
  100. ...attr,
  101. className: classNames(
  102. prefixCls,
  103. {
  104. [`${prefixCls}-${type}`]: !disabled && type,
  105. [`${prefixCls}-disabled`]: disabled,
  106. [`${prefixCls}-size-large`]: size === 'large',
  107. [`${prefixCls}-size-small`]: size === 'small',
  108. // [`${prefixCls}-loading`]: loading,
  109. [`${prefixCls}-light`]: theme === 'light',
  110. [`${prefixCls}-block`]: block,
  111. [`${prefixCls}-circle`]: circle,
  112. [`${prefixCls}-borderless`]: theme === 'borderless',
  113. },
  114. className
  115. ),
  116. type: htmlType,
  117. 'aria-disabled': disabled,
  118. };
  119. return (
  120. // eslint-disable-next-line react/button-has-type
  121. <button
  122. {...baseProps}
  123. onClick={this.props.onClick}
  124. onMouseDown={this.props.onMouseDown}
  125. style={style}
  126. ref={this.buttonRef}
  127. >
  128. {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
  129. <span className={`${prefixCls}-content`} onClick={e => disabled && e.stopPropagation()}>
  130. {children}
  131. </span>
  132. </button>
  133. );
  134. }
  135. }