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.

137 lines
3.2 KiB

7 years ago
7 years ago
  1. import React from 'react';
  2. import Motion from 'flavours/glitch/util/optional_motion';
  3. import spring from 'react-motion/lib/spring';
  4. import PropTypes from 'prop-types';
  5. import classNames from 'classnames';
  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. size: PropTypes.number,
  13. active: PropTypes.bool,
  14. pressed: PropTypes.bool,
  15. expanded: PropTypes.bool,
  16. style: PropTypes.object,
  17. activeStyle: PropTypes.object,
  18. disabled: PropTypes.bool,
  19. inverted: PropTypes.bool,
  20. animate: PropTypes.bool,
  21. flip: PropTypes.bool,
  22. overlay: PropTypes.bool,
  23. tabIndex: PropTypes.string,
  24. label: PropTypes.string,
  25. };
  26. static defaultProps = {
  27. size: 18,
  28. active: false,
  29. disabled: false,
  30. animate: false,
  31. overlay: false,
  32. tabIndex: '0',
  33. };
  34. handleClick = (e) => {
  35. e.preventDefault();
  36. if (!this.props.disabled) {
  37. this.props.onClick(e);
  38. }
  39. }
  40. render () {
  41. let style = {
  42. fontSize: `${this.props.size}px`,
  43. height: `${this.props.size * 1.28571429}px`,
  44. lineHeight: `${this.props.size}px`,
  45. ...this.props.style,
  46. ...(this.props.active ? this.props.activeStyle : {}),
  47. };
  48. if (!this.props.label) {
  49. style.width = `${this.props.size * 1.28571429}px`;
  50. } else {
  51. style.textAlign = 'left';
  52. }
  53. const {
  54. active,
  55. animate,
  56. className,
  57. disabled,
  58. expanded,
  59. icon,
  60. inverted,
  61. flip,
  62. overlay,
  63. pressed,
  64. tabIndex,
  65. title,
  66. } = this.props;
  67. const classes = classNames(className, 'icon-button', {
  68. active,
  69. disabled,
  70. inverted,
  71. overlayed: overlay,
  72. });
  73. const flipDeg = flip ? -180 : -360;
  74. const rotateDeg = active ? flipDeg : 0;
  75. const motionDefaultStyle = {
  76. rotate: rotateDeg,
  77. };
  78. const springOpts = {
  79. stiffness: this.props.flip ? 60 : 120,
  80. damping: 7,
  81. };
  82. const motionStyle = {
  83. rotate: animate ? spring(rotateDeg, springOpts) : 0,
  84. };
  85. if (!animate) {
  86. // Perf optimization: avoid unnecessary <Motion> components unless
  87. // we actually need to animate.
  88. return (
  89. <button
  90. aria-label={title}
  91. aria-pressed={pressed}
  92. aria-expanded={expanded}
  93. title={title}
  94. className={classes}
  95. onClick={this.handleClick}
  96. style={style}
  97. tabIndex={tabIndex}
  98. >
  99. <i className={`fa fa-fw fa-${icon}`} aria-hidden='true' />
  100. </button>
  101. );
  102. }
  103. return (
  104. <Motion defaultStyle={motionDefaultStyle} style={motionStyle}>
  105. {({ rotate }) =>
  106. (<button
  107. aria-label={title}
  108. aria-pressed={pressed}
  109. aria-expanded={expanded}
  110. title={title}
  111. className={classes}
  112. onClick={this.handleClick}
  113. style={style}
  114. tabIndex={tabIndex}
  115. >
  116. <i style={{ transform: `rotate(${rotate}deg)` }} className={`fa fa-fw fa-${icon}`} aria-hidden='true' />
  117. {this.props.label}
  118. </button>)
  119. }
  120. </Motion>
  121. );
  122. }
  123. }