icon_button.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { PureComponent, createRef } from 'react';
  2. import classNames from 'classnames';
  3. import { AnimatedNumber } from './animated_number';
  4. import type { IconProp } from './icon';
  5. import { Icon } from './icon';
  6. interface Props {
  7. className?: string;
  8. title: string;
  9. icon: string;
  10. iconComponent: IconProp;
  11. onClick?: React.MouseEventHandler<HTMLButtonElement>;
  12. onMouseDown?: React.MouseEventHandler<HTMLButtonElement>;
  13. onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
  14. onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>;
  15. active: boolean;
  16. expanded?: boolean;
  17. style?: React.CSSProperties;
  18. activeStyle?: React.CSSProperties;
  19. disabled: boolean;
  20. inverted?: boolean;
  21. animate: boolean;
  22. overlay: boolean;
  23. tabIndex: number;
  24. counter?: number;
  25. href?: string;
  26. ariaHidden: boolean;
  27. }
  28. interface States {
  29. activate: boolean;
  30. deactivate: boolean;
  31. }
  32. export class IconButton extends PureComponent<Props, States> {
  33. buttonRef = createRef<HTMLButtonElement>();
  34. static defaultProps = {
  35. active: false,
  36. disabled: false,
  37. animate: false,
  38. overlay: false,
  39. tabIndex: 0,
  40. ariaHidden: false,
  41. };
  42. state = {
  43. activate: false,
  44. deactivate: false,
  45. };
  46. UNSAFE_componentWillReceiveProps(nextProps: Props) {
  47. if (!nextProps.animate) return;
  48. if (this.props.active && !nextProps.active) {
  49. this.setState({ activate: false, deactivate: true });
  50. } else if (!this.props.active && nextProps.active) {
  51. this.setState({ activate: true, deactivate: false });
  52. }
  53. }
  54. handleClick: React.MouseEventHandler<HTMLButtonElement> = (e) => {
  55. e.preventDefault();
  56. if (!this.props.disabled && this.props.onClick != null) {
  57. this.props.onClick(e);
  58. }
  59. };
  60. handleKeyPress: React.KeyboardEventHandler<HTMLButtonElement> = (e) => {
  61. if (this.props.onKeyPress && !this.props.disabled) {
  62. this.props.onKeyPress(e);
  63. }
  64. };
  65. handleMouseDown: React.MouseEventHandler<HTMLButtonElement> = (e) => {
  66. if (!this.props.disabled && this.props.onMouseDown) {
  67. this.props.onMouseDown(e);
  68. }
  69. };
  70. handleKeyDown: React.KeyboardEventHandler<HTMLButtonElement> = (e) => {
  71. if (!this.props.disabled && this.props.onKeyDown) {
  72. this.props.onKeyDown(e);
  73. }
  74. };
  75. render() {
  76. const style = {
  77. ...this.props.style,
  78. ...(this.props.active ? this.props.activeStyle : {}),
  79. };
  80. const {
  81. active,
  82. className,
  83. disabled,
  84. expanded,
  85. icon,
  86. iconComponent,
  87. inverted,
  88. overlay,
  89. tabIndex,
  90. title,
  91. counter,
  92. href,
  93. ariaHidden,
  94. } = this.props;
  95. const { activate, deactivate } = this.state;
  96. const classes = classNames(className, 'icon-button', {
  97. active,
  98. disabled,
  99. inverted,
  100. activate,
  101. deactivate,
  102. overlayed: overlay,
  103. 'icon-button--with-counter': typeof counter !== 'undefined',
  104. });
  105. let contents = (
  106. <>
  107. <Icon id={icon} icon={iconComponent} aria-hidden='true' />{' '}
  108. {typeof counter !== 'undefined' && (
  109. <span className='icon-button__counter'>
  110. <AnimatedNumber value={counter} />
  111. </span>
  112. )}
  113. </>
  114. );
  115. if (href != null) {
  116. contents = (
  117. <a href={href} target='_blank' rel='noopener noreferrer'>
  118. {contents}
  119. </a>
  120. );
  121. }
  122. return (
  123. <button
  124. type='button'
  125. aria-label={title}
  126. aria-expanded={expanded}
  127. aria-hidden={ariaHidden}
  128. title={title}
  129. className={classes}
  130. onClick={this.handleClick}
  131. onMouseDown={this.handleMouseDown}
  132. onKeyDown={this.handleKeyDown}
  133. onKeyPress={this.handleKeyPress}
  134. style={style}
  135. tabIndex={tabIndex}
  136. disabled={disabled}
  137. ref={this.buttonRef}
  138. >
  139. {contents}
  140. </button>
  141. );
  142. }
  143. }