icon_button.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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. expanded: PropTypes.bool,
  18. style: PropTypes.object,
  19. activeStyle: PropTypes.object,
  20. disabled: PropTypes.bool,
  21. inverted: PropTypes.bool,
  22. animate: PropTypes.bool,
  23. overlay: PropTypes.bool,
  24. tabIndex: PropTypes.string,
  25. counter: PropTypes.number,
  26. obfuscateCount: PropTypes.bool,
  27. href: PropTypes.string,
  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. tabIndex,
  88. title,
  89. counter,
  90. obfuscateCount,
  91. href,
  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. 'icon-button--with-counter': typeof counter !== 'undefined',
  105. });
  106. if (typeof counter !== 'undefined') {
  107. style.width = 'auto';
  108. }
  109. let contents = (
  110. <React.Fragment>
  111. <Icon id={icon} fixedWidth aria-hidden='true' /> {typeof counter !== 'undefined' && <span className='icon-button__counter'><AnimatedNumber value={counter} obfuscate={obfuscateCount} /></span>}
  112. </React.Fragment>
  113. );
  114. if (href && !this.prop) {
  115. contents = (
  116. <a href={href} target='_blank' rel='noopener noreferrer'>
  117. {contents}
  118. </a>
  119. );
  120. }
  121. return (
  122. <button
  123. type='button'
  124. aria-label={title}
  125. aria-expanded={expanded}
  126. title={title}
  127. className={classes}
  128. onClick={this.handleClick}
  129. onMouseDown={this.handleMouseDown}
  130. onKeyDown={this.handleKeyDown}
  131. onKeyPress={this.handleKeyPress}
  132. style={style}
  133. tabIndex={tabIndex}
  134. disabled={disabled}
  135. >
  136. {contents}
  137. </button>
  138. );
  139. }
  140. }