PageRevisionTable.jsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import { withTranslation } from 'react-i18next';
  4. import RevisionComparerContainer from '~/client/services/RevisionComparerContainer';
  5. import Revision from './Revision';
  6. class PageRevisionTable extends React.Component {
  7. /**
  8. * render a row (Revision component and RevisionDiff component)
  9. * @param {Revison} revision
  10. * @param {Revision} previousRevision
  11. * @param {boolean} hasDiff whether revision has difference to previousRevision
  12. * @param {boolean} isContiguousNodiff true if the current 'hasDiff' and one of previous row is both false
  13. */
  14. renderRow(revisions, index, previousRevision, hasDiff, isContiguousNodiff) {
  15. const { revisionComparerContainer, t } = this.props;
  16. const revision = revisions[index];
  17. const latestRevision = revisions[0];
  18. const oldestRevision = revisions[revision.length - 1];
  19. const revisionId = revision._id;
  20. const { sourceRevision, targetRevision } = revisionComparerContainer.state;
  21. const handleCompareLatestRevisionButton = () => {
  22. revisionComparerContainer.setState({ sourceRevision: revision });
  23. revisionComparerContainer.setState({ targetRevision: latestRevision });
  24. };
  25. const handleComparePreviousRevisionButton = () => {
  26. revisionComparerContainer.setState({ sourceRevision: previousRevision });
  27. revisionComparerContainer.setState({ targetRevision: revision });
  28. };
  29. return (
  30. <tr className="d-flex" key={`revision-history-${revisionId}`}>
  31. <td className="col" key={`revision-history-top-${revisionId}`}>
  32. <div className="d-lg-flex">
  33. <Revision
  34. t={this.props.t}
  35. revision={revision}
  36. isLatestRevision={revision === latestRevision}
  37. hasDiff={hasDiff}
  38. key={`revision-history-rev-${revisionId}`}
  39. />
  40. {hasDiff && (
  41. <div className="ml-md-3 mt-auto">
  42. <div className="btn-group">
  43. <button
  44. type="button"
  45. className="btn btn-outline-secondary btn-sm"
  46. onClick={handleCompareLatestRevisionButton}
  47. >
  48. {t('page_history.compare_latest')}
  49. </button>
  50. <button
  51. type="button"
  52. className="btn btn-outline-secondary btn-sm"
  53. onClick={handleComparePreviousRevisionButton}
  54. disabled={revision === oldestRevision}
  55. >
  56. {t('page_history.compare_previous')}
  57. </button>
  58. </div>
  59. </div>
  60. )}
  61. </div>
  62. </td>
  63. <td className="col-1">
  64. {(hasDiff || revision._id === sourceRevision?._id) && (
  65. <div className="custom-control custom-radio custom-control-inline mr-0">
  66. <input
  67. type="radio"
  68. className="custom-control-input"
  69. id={`compareSource-${revision._id}`}
  70. name="compareSource"
  71. value={revision._id}
  72. checked={revision._id === sourceRevision?._id}
  73. onChange={() => revisionComparerContainer.setState({ sourceRevision: revision })}
  74. />
  75. <label className="custom-control-label" htmlFor={`compareSource-${revision._id}`} />
  76. </div>
  77. )}
  78. </td>
  79. <td className="col-2">
  80. {(hasDiff || revision._id === targetRevision?._id) && (
  81. <div className="custom-control custom-radio custom-control-inline mr-0">
  82. <input
  83. type="radio"
  84. className="custom-control-input"
  85. id={`compareTarget-${revision._id}`}
  86. name="compareTarget"
  87. value={revision._id}
  88. checked={revision._id === targetRevision?._id}
  89. onChange={() => revisionComparerContainer.setState({ targetRevision: revision })}
  90. />
  91. <label className="custom-control-label" htmlFor={`compareTarget-${revision._id}`} />
  92. </div>
  93. )}
  94. </td>
  95. </tr>
  96. );
  97. }
  98. render() {
  99. const { t, pagingLimit } = this.props;
  100. const revisions = this.props.revisions;
  101. const revisionCount = this.props.revisions.length;
  102. let hasDiffPrev;
  103. const revisionList = this.props.revisions.map((revision, idx) => {
  104. // Returns null because the last revision is for the bottom diff display
  105. if (idx === pagingLimit) {
  106. return null;
  107. }
  108. let previousRevision;
  109. if (idx + 1 < revisionCount) {
  110. previousRevision = revisions[idx + 1];
  111. }
  112. else {
  113. previousRevision = revision; // if it is the first revision, show full text as diff text
  114. }
  115. const hasDiff = revision.hasDiffToPrev !== false; // set 'true' if undefined for backward compatibility
  116. const isContiguousNodiff = !hasDiff && !hasDiffPrev;
  117. hasDiffPrev = hasDiff;
  118. return this.renderRow(revisions, idx, previousRevision, hasDiff, isContiguousNodiff);
  119. });
  120. return (
  121. <table className="table revision-history-table">
  122. <thead>
  123. <tr className="d-flex">
  124. <th className="col">{ t('page_history.revision') }</th>
  125. <th className="col-1">{ t('page_history.comparing_source') }</th>
  126. <th className="col-2">{ t('page_history.comparing_target') }</th>
  127. </tr>
  128. </thead>
  129. <tbody className="overflow-auto d-block">
  130. {revisionList}
  131. </tbody>
  132. </table>
  133. );
  134. }
  135. }
  136. PageRevisionTable.propTypes = {
  137. t: PropTypes.func.isRequired, // i18next
  138. revisionComparerContainer: PropTypes.instanceOf(RevisionComparerContainer).isRequired,
  139. revisions: PropTypes.array,
  140. pagingLimit: PropTypes.number,
  141. };
  142. export default withTranslation()(PageRevisionTable);