PageRevisionTable.jsx 5.7 KB

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