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.

157 lines
5.2 KiB

  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import classNames from 'classnames';
  4. import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
  5. const messages = defineMessages({
  6. show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
  7. hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
  8. moveLeft: { id: 'column_header.moveLeft_settings', defaultMessage: 'Move column to the left' },
  9. moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
  10. });
  11. @injectIntl
  12. export default class ColumnHeader extends React.PureComponent {
  13. static contextTypes = {
  14. router: PropTypes.object,
  15. };
  16. static propTypes = {
  17. intl: PropTypes.object.isRequired,
  18. title: PropTypes.node.isRequired,
  19. icon: PropTypes.string.isRequired,
  20. active: PropTypes.bool,
  21. multiColumn: PropTypes.bool,
  22. focusable: PropTypes.bool,
  23. showBackButton: PropTypes.bool,
  24. children: PropTypes.node,
  25. pinned: PropTypes.bool,
  26. onPin: PropTypes.func,
  27. onMove: PropTypes.func,
  28. onClick: PropTypes.func,
  29. };
  30. static defaultProps = {
  31. focusable: true,
  32. }
  33. state = {
  34. collapsed: true,
  35. animating: false,
  36. };
  37. handleToggleClick = (e) => {
  38. e.stopPropagation();
  39. this.setState({ collapsed: !this.state.collapsed, animating: true });
  40. }
  41. handleTitleClick = () => {
  42. this.props.onClick();
  43. }
  44. handleMoveLeft = () => {
  45. this.props.onMove(-1);
  46. }
  47. handleMoveRight = () => {
  48. this.props.onMove(1);
  49. }
  50. handleBackClick = () => {
  51. if (window.history && window.history.length === 1) this.context.router.history.push('/');
  52. else this.context.router.history.goBack();
  53. }
  54. handleTransitionEnd = () => {
  55. this.setState({ animating: false });
  56. }
  57. render () {
  58. const { title, icon, active, children, pinned, onPin, multiColumn, focusable, showBackButton, intl: { formatMessage } } = this.props;
  59. const { collapsed, animating } = this.state;
  60. const wrapperClassName = classNames('column-header__wrapper', {
  61. 'active': active,
  62. });
  63. const buttonClassName = classNames('column-header', {
  64. 'active': active,
  65. });
  66. const collapsibleClassName = classNames('column-header__collapsible', {
  67. 'collapsed': collapsed,
  68. 'animating': animating,
  69. });
  70. const collapsibleButtonClassName = classNames('column-header__button', {
  71. 'active': !collapsed,
  72. });
  73. let extraContent, pinButton, moveButtons, backButton, collapseButton;
  74. if (children) {
  75. extraContent = (
  76. <div key='extra-content' className='column-header__collapsible__extra'>
  77. {children}
  78. </div>
  79. );
  80. }
  81. if (multiColumn && pinned) {
  82. pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={onPin}><i className='fa fa fa-times' /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>;
  83. moveButtons = (
  84. <div key='move-buttons' className='column-header__setting-arrows'>
  85. <button title={formatMessage(messages.moveLeft)} aria-label={formatMessage(messages.moveLeft)} className='text-btn column-header__setting-btn' onClick={this.handleMoveLeft}><i className='fa fa-chevron-left' /></button>
  86. <button title={formatMessage(messages.moveRight)} aria-label={formatMessage(messages.moveRight)} className='text-btn column-header__setting-btn' onClick={this.handleMoveRight}><i className='fa fa-chevron-right' /></button>
  87. </div>
  88. );
  89. } else if (multiColumn) {
  90. pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={onPin}><i className='fa fa fa-plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
  91. }
  92. if (!pinned && (multiColumn || showBackButton)) {
  93. backButton = (
  94. <button onClick={this.handleBackClick} className='column-header__back-button'>
  95. <i className='fa fa-fw fa-chevron-left column-back-button__icon' />
  96. <FormattedMessage id='column_back_button.label' defaultMessage='Back' />
  97. </button>
  98. );
  99. }
  100. const collapsedContent = [
  101. extraContent,
  102. ];
  103. if (multiColumn) {
  104. collapsedContent.push(moveButtons);
  105. collapsedContent.push(pinButton);
  106. }
  107. if (children || multiColumn) {
  108. collapseButton = <button className={collapsibleButtonClassName} aria-label={formatMessage(collapsed ? messages.show : messages.hide)} aria-pressed={collapsed ? 'false' : 'true'} onClick={this.handleToggleClick}><i className='fa fa-sliders' /></button>;
  109. }
  110. return (
  111. <div className={wrapperClassName}>
  112. <h1 tabIndex={focusable ? 0 : null} role='button' className={buttonClassName} aria-label={title} onClick={this.handleTitleClick}>
  113. <i className={`fa fa-fw fa-${icon} column-header__icon`} />
  114. {title}
  115. <div className='column-header__buttons'>
  116. {backButton}
  117. {collapseButton}
  118. </div>
  119. </h1>
  120. <div className={collapsibleClassName} tabIndex={collapsed ? -1 : null} onTransitionEnd={this.handleTransitionEnd}>
  121. <div className='column-header__collapsible-inner'>
  122. {(!collapsed || animating) && collapsedContent}
  123. </div>
  124. </div>
  125. </div>
  126. );
  127. }
  128. }