You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
3.4 KiB

  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import classNames from 'classnames';
  4. import Icon from 'mastodon/components/icon';
  5. import AnimatedNumber from 'mastodon/components/animated_number';
  6. export default class IconButton extends React.PureComponent {
  7. static propTypes = {
  8. className: PropTypes.string,
  9. title: PropTypes.string.isRequired,
  10. icon: PropTypes.string.isRequired,
  11. onClick: PropTypes.func,
  12. onMouseDown: PropTypes.func,
  13. onKeyDown: PropTypes.func,
  14. onKeyPress: PropTypes.func,
  15. size: PropTypes.number,
  16. active: PropTypes.bool,
  17. pressed: PropTypes.bool,
  18. expanded: PropTypes.bool,
  19. style: PropTypes.object,
  20. activeStyle: PropTypes.object,
  21. disabled: PropTypes.bool,
  22. inverted: PropTypes.bool,
  23. animate: PropTypes.bool,
  24. overlay: PropTypes.bool,
  25. tabIndex: PropTypes.string,
  26. counter: PropTypes.number,
  27. obfuscateCount: PropTypes.bool,
  28. };
  29. static defaultProps = {
  30. size: 18,
  31. active: false,
  32. disabled: false,
  33. animate: false,
  34. overlay: false,
  35. tabIndex: '0',
  36. };
  37. state = {
  38. activate: false,
  39. deactivate: false,
  40. }
  41. componentWillReceiveProps (nextProps) {
  42. if (!nextProps.animate) return;
  43. if (this.props.active && !nextProps.active) {
  44. this.setState({ activate: false, deactivate: true });
  45. } else if (!this.props.active && nextProps.active) {
  46. this.setState({ activate: true, deactivate: false });
  47. }
  48. }
  49. handleClick = (e) => {
  50. e.preventDefault();
  51. if (!this.props.disabled) {
  52. this.props.onClick(e);
  53. }
  54. }
  55. handleKeyPress = (e) => {
  56. if (this.props.onKeyPress && !this.props.disabled) {
  57. this.props.onKeyPress(e);
  58. }
  59. }
  60. handleMouseDown = (e) => {
  61. if (!this.props.disabled && this.props.onMouseDown) {
  62. this.props.onMouseDown(e);
  63. }
  64. }
  65. handleKeyDown = (e) => {
  66. if (!this.props.disabled && this.props.onKeyDown) {
  67. this.props.onKeyDown(e);
  68. }
  69. }
  70. render () {
  71. const style = {
  72. fontSize: `${this.props.size}px`,
  73. width: `${this.props.size * 1.28571429}px`,
  74. height: `${this.props.size * 1.28571429}px`,
  75. lineHeight: `${this.props.size}px`,
  76. ...this.props.style,
  77. ...(this.props.active ? this.props.activeStyle : {}),
  78. };
  79. const {
  80. active,
  81. className,
  82. disabled,
  83. expanded,
  84. icon,
  85. inverted,
  86. overlay,
  87. pressed,
  88. tabIndex,
  89. title,
  90. counter,
  91. obfuscateCount,
  92. } = this.props;
  93. const {
  94. activate,
  95. deactivate,
  96. } = this.state;
  97. const classes = classNames(className, 'icon-button', {
  98. active,
  99. disabled,
  100. inverted,
  101. activate,
  102. deactivate,
  103. overlayed: overlay,
  104. });
  105. if (typeof counter !== 'undefined') {
  106. style.width = 'auto';
  107. }
  108. return (
  109. <button
  110. aria-label={title}
  111. aria-pressed={pressed}
  112. aria-expanded={expanded}
  113. title={title}
  114. className={classes}
  115. onClick={this.handleClick}
  116. onMouseDown={this.handleMouseDown}
  117. onKeyDown={this.handleKeyDown}
  118. onKeyPress={this.handleKeyPress}
  119. style={style}
  120. tabIndex={tabIndex}
  121. disabled={disabled}
  122. >
  123. <Icon id={icon} fixedWidth aria-hidden='true' /> {typeof counter !== 'undefined' && <span className='icon-button__counter'><AnimatedNumber value={counter} obfuscate={obfuscateCount} /></span>}
  124. </button>
  125. );
  126. }
  127. }