Timeline.jsx 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. {props.loggedUser.idRoleUserWorkspace >= 2 &&
  95. <form className={classnames(`${props.customClass}__texteditor`, 'timeline__body__texteditor')}>
  96. <div className={classnames(`${props.customClass}__texteditor__textinput`, 'timeline__body__texteditor__textinput')}>
  97. <textarea
  98. id='wysiwygTimelineComment'
  99. placeholder='Votre message ...'
  100. value={props.newComment}
  101. onChange={props.onChangeNewComment}
  102. disabled={props.disableComment}
  103. />
  104. </div>
  105. <div className={classnames(`${props.customClass}__texteditor__wrapper`, 'timeline__body__texteditor__wrapper')}>
  106. <div className={classnames(`${props.customClass}__texteditor__advancedtext`, 'timeline__body__texteditor__advancedtext')}>
  107. <button
  108. type='button'
  109. className={classnames(
  110. `${props.customClass}__texteditor__advancedtext__btn timeline__body__texteditor__advancedtext__btn btn`
  111. )}
  112. onClick={props.onClickWysiwygBtn}
  113. disabled={props.disableComment}
  114. style={{
  115. backgroundColor: 'transparent',
  116. color: '#333',
  117. borderColor: props.customColor,
  118. ':hover': {
  119. backgroundColor: props.customColor,
  120. color: '#fdfdfd'
  121. }
  122. }}
  123. key={'timeline__comment__advancedtext'}
  124. >
  125. {props.wysiwyg ? 'Texte simple' : 'Texte riche'}
  126. </button>
  127. </div>
  128. <div className={classnames(`${props.customClass}__texteditor__submit`, 'timeline__body__texteditor__submit')}>
  129. <button
  130. type='button'
  131. className={classnames(`${props.customClass}__texteditor__submit__btn`, 'timeline__body__texteditor__submit__btn btn')}
  132. onClick={props.onClickValidateNewCommentBtn}
  133. disabled={props.disableComment}
  134. style={{
  135. backgroundColor: props.customColor,
  136. color: '#fdfdfd',
  137. ':hover': {
  138. backgroundColor: color(props.customColor).darken(0.15).hexString()
  139. }
  140. }}
  141. key={'timeline__comment__send'}
  142. >
  143. Envoyer
  144. <div
  145. className={classnames(`${props.customClass}__texteditor__submit__btn__icon`, 'timeline__body__texteditor__submit__btn__icon')}>
  146. <i className='fa fa-paper-plane-o' />
  147. </div>
  148. </button>
  149. </div>
  150. </div>
  151. </form>
  152. }
  153. </div>
  154. </div>
  155. )
  156. }
  157. }
  158. export default Radium(translate()(Timeline))
  159. Timeline.propTypes = {
  160. timelineData: PropTypes.array.isRequired,
  161. newComment: PropTypes.string.isRequired,
  162. onChangeNewComment: PropTypes.func.isRequired,
  163. onClickValidateNewCommentBtn: PropTypes.func.isRequired,
  164. disableComment: PropTypes.bool,
  165. customClass: PropTypes.string,
  166. customColor: PropTypes.string,
  167. loggedUser: PropTypes.object,
  168. wysiwyg: PropTypes.bool,
  169. onClickWysiwygBtn: PropTypes.func,
  170. onClickRevisionBtn: PropTypes.func,
  171. shouldScrollToBottom: PropTypes.bool,
  172. showHeader: PropTypes.bool,
  173. rightPartOpen: PropTypes.bool, // irrelevant if showHeader is false
  174. isArchived: PropTypes.bool,
  175. onClickRestoreArchived: PropTypes.func,
  176. isDeleted: PropTypes.bool,
  177. onClickRestoreDeleted: PropTypes.func
  178. }
  179. Timeline.defaultProps = {
  180. disableComment: false,
  181. customClass: '',
  182. customColor: '',
  183. loggedUser: {
  184. id: '',
  185. name: '',
  186. avatar: '',
  187. idRoleUserWorkspace: 1
  188. },
  189. timelineData: [],
  190. wysiwyg: false,
  191. onClickWysiwygBtn: () => {},
  192. shouldScrollToBottom: true,
  193. showHeader: true,
  194. rightPartOpen: false,
  195. isArchived: false,
  196. isDeleted: false
  197. }