Timeline.jsx 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import React from 'react'
  2. import PropTypes from 'prop-types'
  3. import classnames from 'classnames'
  4. import Radium from 'radium'
  5. import color from 'color'
  6. import Comment from './Comment.jsx'
  7. import Revision from './Revision.jsx'
  8. import { translate } from 'react-i18next'
  9. require('./Timeline.styl')
  10. class Timeline extends React.Component {
  11. componentDidMount () {
  12. this.scrollToBottom()
  13. }
  14. componentDidUpdate () {
  15. this.props.shouldScrollToBottom && this.scrollToBottom()
  16. }
  17. scrollToBottom = () => this.timelineBottom.scrollIntoView({behavior: 'instant'})
  18. render () {
  19. const { props } = this
  20. if (!Array.isArray(props.timelineData)) {
  21. console.log('Error in Timeline.jsx, props.timelineData is not an array. timelineData: ', props.timelineData)
  22. return null
  23. }
  24. return (
  25. <div className={classnames('timeline')}>
  26. {props.showHeader &&
  27. <div
  28. className={classnames(`${props.customClass}__header`, 'timeline__header')}
  29. onClick={props.toggleRightPart}
  30. >
  31. <div className='timeline__header__icon mt-3 mb-auto'>
  32. <i className={classnames('fa fa-fw', {'fa-angle-double-right': props.rightPartOpen, 'fa-angle-double-left': !props.rightPartOpen})} />
  33. </div>
  34. <div className='timeline__header__title'>
  35. {this.props.t('Timeline')}
  36. </div>
  37. <div className='timeline__header__icon mb-3 mt-auto'>
  38. <i className={classnames('fa fa-fw', {'fa-angle-double-right': props.rightPartOpen, 'fa-angle-double-left': !props.rightPartOpen})} />
  39. </div>
  40. </div>
  41. }
  42. {props.isArchived &&
  43. <div className='timeline__info'>
  44. <div className='timeline__info__msg'>
  45. <i className='fa fa-fw fa-archive' />
  46. {this.props.t('This content is archived.')}
  47. </div>
  48. <button className='timeline__info__btnrestore btn' onClick={props.onClickRestoreArchived}>
  49. <i className='fa fa-fw fa-archive' />
  50. {this.props.t('Restore')}
  51. </button>
  52. </div>
  53. }
  54. {props.isDeleted &&
  55. <div className='timeline__info'>
  56. <div className='timeline__info__msg'>
  57. <i className='fa fa-fw fa-trash' />
  58. {this.props.t('This content is deleted.')}
  59. </div>
  60. <button className='timeline__info__btnrestore btn' onClick={props.onClickRestoreDeleted}>
  61. <i className='fa fa-fw fa-trash' />
  62. {this.props.t('Restore')}
  63. </button>
  64. </div>
  65. }
  66. <div className='timeline__body'>
  67. <ul className={classnames(`${props.customClass}__messagelist`, 'timeline__body__messagelist')}>
  68. {props.timelineData.map(content => {
  69. switch (content.timelineType) {
  70. case 'comment':
  71. return <Comment
  72. customClass={props.customClass}
  73. customColor={props.customColor}
  74. author={content.author.public_name}
  75. avatar={content.author.avatar_url}
  76. createdAt={content.created}
  77. text={content.raw_content}
  78. fromMe={props.loggedUser.user_id === content.author.user_id}
  79. key={`comment_${content.content_id}`}
  80. />
  81. case 'revision':
  82. return <Revision
  83. customClass={props.customClass}
  84. customColor={props.customColor}
  85. createdAt={content.created}
  86. number={content.number}
  87. key={`revision_${content.revision_id}`}
  88. onClickRevision={() => props.onClickRevisionBtn(content)}
  89. />
  90. }
  91. })}
  92. <li style={{visibility: 'hidden'}} ref={el => { this.timelineBottom = el }} />
  93. </ul>
  94. <form className={classnames(`${props.customClass}__texteditor`, 'timeline__body__texteditor')}>
  95. <div className={classnames(`${props.customClass}__texteditor__textinput`, 'timeline__body__texteditor__textinput')}>
  96. <textarea
  97. id='wysiwygTimelineComment'
  98. placeholder='Votre message ...'
  99. value={props.newComment}
  100. onChange={props.onChangeNewComment}
  101. disabled={props.disableComment}
  102. />
  103. </div>
  104. <div className={classnames(`${props.customClass}__texteditor__wrapper`, 'timeline__body__texteditor__wrapper')}>
  105. <div className={classnames(`${props.customClass}__texteditor__advancedtext`, 'timeline__body__texteditor__advancedtext')}>
  106. <button
  107. type='button'
  108. className={classnames(
  109. `${props.customClass}__texteditor__advancedtext__btn timeline__body__texteditor__advancedtext__btn btn`
  110. )}
  111. onClick={props.onClickWysiwygBtn}
  112. disabled={props.disableComment}
  113. style={{
  114. backgroundColor: 'transparent',
  115. color: '#333',
  116. borderColor: props.customColor,
  117. ':hover': {
  118. backgroundColor: props.customColor,
  119. color: '#fdfdfd'
  120. }
  121. }}
  122. key={'timeline__comment__advancedtext'}
  123. >
  124. {props.wysiwyg ? 'Texte simple' : 'Texte riche'}
  125. </button>
  126. </div>
  127. <div className={classnames(`${props.customClass}__texteditor__submit`, 'timeline__body__texteditor__submit')}>
  128. <button
  129. type='button'
  130. className={classnames(`${props.customClass}__texteditor__submit__btn`, 'timeline__body__texteditor__submit__btn btn')}
  131. onClick={props.onClickValidateNewCommentBtn}
  132. disabled={props.disableComment}
  133. style={{
  134. backgroundColor: props.customColor,
  135. color: '#fdfdfd',
  136. ':hover': {
  137. backgroundColor: color(props.customColor).darken(0.15).hexString()
  138. }
  139. }}
  140. key={'timeline__comment__send'}
  141. >
  142. Envoyer
  143. <div
  144. className={classnames(`${props.customClass}__texteditor__submit__btn__icon`, 'timeline__body__texteditor__submit__btn__icon')}>
  145. <i className='fa fa-paper-plane-o' />
  146. </div>
  147. </button>
  148. </div>
  149. </div>
  150. </form>
  151. </div>
  152. </div>
  153. )
  154. }
  155. }
  156. export default Radium(translate()(Timeline))
  157. Timeline.propTypes = {
  158. timelineData: PropTypes.array.isRequired,
  159. newComment: PropTypes.string.isRequired,
  160. onChangeNewComment: PropTypes.func.isRequired,
  161. onClickValidateNewCommentBtn: PropTypes.func.isRequired,
  162. disableComment: PropTypes.bool,
  163. customClass: PropTypes.string,
  164. customColor: PropTypes.string,
  165. loggedUser: PropTypes.object,
  166. wysiwyg: PropTypes.bool,
  167. onClickWysiwygBtn: PropTypes.func,
  168. onClickRevisionBtn: PropTypes.func,
  169. shouldScrollToBottom: PropTypes.bool,
  170. showHeader: PropTypes.bool,
  171. rightPartOpen: PropTypes.bool, // irrelevant if showHeader is false
  172. isArchived: PropTypes.bool,
  173. onClickRestoreArchived: PropTypes.func,
  174. isDeleted: PropTypes.bool,
  175. onClickRestoreDeleted: PropTypes.func
  176. }
  177. Timeline.defaultProps = {
  178. disableComment: false,
  179. customClass: '',
  180. customColor: '',
  181. loggedUser: {
  182. id: '',
  183. name: '',
  184. avatar: ''
  185. },
  186. timelineData: [],
  187. wysiwyg: false,
  188. onClickWysiwygBtn: () => {},
  189. shouldScrollToBottom: true,
  190. showHeader: true,
  191. rightPartOpen: false,
  192. isArchived: false,
  193. isDeleted: false
  194. }