icon_button.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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. href: PropTypes.string,
  29. };
  30. static defaultProps = {
  31. size: 18,
  32. active: false,
  33. disabled: false,
  34. animate: false,
  35. overlay: false,
  36. tabIndex: '0',
  37. };
  38. state = {
  39. activate: false,
  40. deactivate: false,
  41. }
  42. componentWillReceiveProps (nextProps) {
  43. if (!nextProps.animate) return;
  44. if (this.props.active && !nextProps.active) {
  45. this.setState({ activate: false, deactivate: true });
  46. } else if (!this.props.active && nextProps.active) {
  47. this.setState({ activate: true, deactivate: false });
  48. }
  49. }
  50. handleClick = (e) => {
  51. e.preventDefault();
  52. if (!this.props.disabled) {
  53. this.props.onClick(e);
  54. }
  55. }
  56. handleKeyPress = (e) => {
  57. if (this.props.onKeyPress && !this.props.disabled) {
  58. this.props.onKeyPress(e);
  59. }
  60. }
  61. handleMouseDown = (e) => {
  62. if (!this.props.disabled && this.props.onMouseDown) {
  63. this.props.onMouseDown(e);
  64. }
  65. }
  66. handleKeyDown = (e) => {
  67. if (!this.props.disabled && this.props.onKeyDown) {
  68. this.props.onKeyDown(e);
  69. }
  70. }
  71. render () {
  72. const style = {
  73. fontSize: `${this.props.size}px`,
  74. width: `${this.props.size * 1.28571429}px`,
  75. height: `${this.props.size * 1.28571429}px`,
  76. lineHeight: `${this.props.size}px`,
  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. inverted,
  87. overlay,
  88. pressed,
  89. tabIndex,
  90. title,
  91. counter,
  92. obfuscateCount,
  93. href,
  94. } = this.props;
  95. const {
  96. activate,
  97. deactivate,
  98. } = this.state;
  99. const classes = classNames(className, 'icon-button', {
  100. active,
  101. disabled,
  102. inverted,
  103. activate,
  104. deactivate,
  105. overlayed: overlay,
  106. 'icon-button--with-counter': typeof counter !== 'undefined',
  107. });
  108. if (typeof counter !== 'undefined') {
  109. style.width = 'auto';
  110. }
  111. let contents = (
  112. <React.Fragment>
  113. <Icon id={icon} fixedWidth aria-hidden='true' /> {typeof counter !== 'undefined' && <span className='icon-button__counter'><AnimatedNumber value={counter} obfuscate={obfuscateCount} /></span>}
  114. </React.Fragment>
  115. );
  116. if (href && !this.prop) {
  117. contents = (
  118. <a href={href} target='_blank' rel='noopener noreferrer'>
  119. {contents}
  120. </a>
  121. );
  122. }
  123. return (
  124. <button
  125. type='button'
  126. aria-label={title}
  127. aria-pressed={pressed}
  128. aria-expanded={expanded}
  129. title={title}
  130. className={classes}
  131. onClick={this.handleClick}
  132. onMouseDown={this.handleMouseDown}
  133. onKeyDown={this.handleKeyDown}
  134. onKeyPress={this.handleKeyPress}
  135. style={style}
  136. tabIndex={tabIndex}
  137. disabled={disabled}
  138. >
  139. {contents}
  140. </button>
  141. );
  142. }
  143. }