Explorar el Código

Merge pull request #4759 from weseek/feat/79579-78640-color-diff-lines

feat: show color diff lines
yuto-o hace 4 años
padre
commit
57df054e5b
Se han modificado 1 ficheros con 115 adiciones y 109 borrados
  1. 115 109
      packages/app/src/components/PageEditor/ConflictDiffModal.tsx

+ 115 - 109
packages/app/src/components/PageEditor/ConflictDiffModal.tsx

@@ -1,12 +1,15 @@
-import React, { useState, useRef, FC } from 'react';
+import React, {
+  useState, useRef, useEffect, FC,
+} from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { UserPicture } from '@growi/ui';
 import { UserPicture } from '@growi/ui';
 import {
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 } from 'reactstrap';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
-import { UnControlled as CodeMirror } from 'react-codemirror2';
 import { format } from 'date-fns';
 import { format } from 'date-fns';
+import CodeMirror from 'codemirror/lib/codemirror';
+
 import PageContainer from '../../client/services/PageContainer';
 import PageContainer from '../../client/services/PageContainer';
 import EditorContainer from '../../client/services/EditorContainer';
 import EditorContainer from '../../client/services/EditorContainer';
 import AppContainer from '../../client/services/AppContainer';
 import AppContainer from '../../client/services/AppContainer';
@@ -14,7 +17,9 @@ import AppContainer from '../../client/services/AppContainer';
 import { IRevisionOnConflict } from '../../interfaces/revision';
 import { IRevisionOnConflict } from '../../interfaces/revision';
 import { UncontrolledCodeMirror } from '../UncontrolledCodeMirror';
 import { UncontrolledCodeMirror } from '../UncontrolledCodeMirror';
 
 
-require('codemirror/mode/htmlmixed/htmlmixed');
+require('codemirror/lib/codemirror.css');
+require('codemirror/addon/merge/merge');
+require('codemirror/addon/merge/merge.css');
 const DMP = require('diff_match_patch');
 const DMP = require('diff_match_patch');
 
 
 Object.keys(DMP).forEach((key) => { window[key] = DMP[key] });
 Object.keys(DMP).forEach((key) => { window[key] = DMP[key] });
@@ -36,6 +41,7 @@ export const ConflictDiffModal: FC<ConflictDiffModalProps> = (props) => {
   const { t } = useTranslation('');
   const { t } = useTranslation('');
   const resolvedRevision = useRef<string>('');
   const resolvedRevision = useRef<string>('');
   const [isRevisionselected, setIsRevisionSelected] = useState<boolean>(false);
   const [isRevisionselected, setIsRevisionSelected] = useState<boolean>(false);
+  const [codeMirrorRef, setCodeMirrorRef] = useState<HTMLDivElement | null>(null);
 
 
   const { pageContainer, editorContainer, appContainer } = props;
   const { pageContainer, editorContainer, appContainer } = props;
 
 
@@ -60,13 +66,22 @@ export const ConflictDiffModal: FC<ConflictDiffModalProps> = (props) => {
     user: pageContainer.state.lastUpdateUser,
     user: pageContainer.state.lastUpdateUser,
   };
   };
 
 
-  const codeMirrorRevisionOption = {
-    mode: 'htmlmixed',
-    lineNumbers: true,
-    tabSize: 2,
-    indentUnit: 2,
-    readOnly: true,
-  };
+  useEffect(() => {
+    if (codeMirrorRef != null) {
+      CodeMirror.MergeView(codeMirrorRef, {
+        value: origin.revisionBody,
+        origLeft: request.revisionBody,
+        origRight: latest.revisionBody,
+        lineNumbers: true,
+        collapseIdentical: true,
+        showDifferences: true,
+        highlightDifferences: true,
+        connect: 'connect',
+        readOnly: true,
+        revertButtons: false,
+      });
+    }
+  }, [codeMirrorRef, origin.revisionBody, request.revisionBody, latest.revisionBody]);
 
 
   const onCancel = () => {
   const onCancel = () => {
     if (props.onCancel != null) {
     if (props.onCancel != null) {
@@ -97,118 +112,109 @@ export const ConflictDiffModal: FC<ConflictDiffModalProps> = (props) => {
         <i className="icon-fw icon-exclamation" />{t('modal_resolve_conflict.resolve_conflict')}
         <i className="icon-fw icon-exclamation" />{t('modal_resolve_conflict.resolve_conflict')}
       </ModalHeader>
       </ModalHeader>
       <ModalBody>
       <ModalBody>
-        {
-          pageContainer.state.isConflictDiffModalOpen
-          && (
-            <div className="row mx-2">
-              <div className="col-12 text-center mt-2 mb-4">
-                <h2 className="font-weight-bold">{t('modal_resolve_conflict.resolve_conflict_message')}</h2>
-              </div>
-              <div className="col-12 col-md-4 border border-dark">
-                <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.requested_revision')}</h3>
-                <div className="d-flex align-items-center my-3">
-                  <div>
-                    <UserPicture user={request.user} size="lg" noLink noTooltip />
-                  </div>
-                  <div className="ml-3 text-muted">
-                    <p className="my-0">updated by {request.user.username}</p>
-                    <p className="my-0">{request.createdAt}</p>
-                  </div>
+        { pageContainer.state.isConflictDiffModalOpen
+        && (
+          <div className="row">
+            <div className="col-12 text-center mt-2 mb-4">
+              <h2 className="font-weight-bold">{t('modal_resolve_conflict.resolve_conflict_message')}</h2>
+            </div>
+            <div className="col-4">
+              <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.requested_revision')}</h3>
+              <div className="d-flex align-items-center my-3">
+                <div>
+                  <UserPicture user={request.user} size="lg" noLink noTooltip />
                 </div>
                 </div>
-                <CodeMirror
-                  value={request.revisionBody}
-                  options={codeMirrorRevisionOption}
-                />
-                <div className="text-center my-4">
-                  <button
-                    type="button"
-                    className="btn btn-primary"
-                    onClick={() => {
-                      setIsRevisionSelected(true);
-                      resolvedRevision.current = request.revisionBody;
-                    }}
-                  >
-                    <i className="icon-fw icon-arrow-down-circle"></i>
-                    {t('modal_resolve_conflict.select_revision', { revision: 'request' })}
-                  </button>
+                <div className="ml-3 text-muted">
+                  <p className="my-0">updated by {request.user.username}</p>
+                  <p className="my-0">{request.createdAt}</p>
                 </div>
                 </div>
               </div>
               </div>
-              <div className="col-12 col-md-4 border border-dark">
-                <h3 className="font-weight-bold my-2">{t('origin_revision')}</h3>
-                <div className="d-flex align-items-center my-3">
-                  <div>
-                    <UserPicture user={origin.user} size="lg" noLink noTooltip />
-                  </div>
-                  <div className="ml-3 text-muted">
-                    <p className="my-0">updated by {origin.user.username}</p>
-                    <p className="my-0">{origin.createdAt}</p>
-                  </div>
+            </div>
+            <div className="col-4">
+              <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.origin_revision')}</h3>
+              <div className="d-flex align-items-center my-3">
+                <div>
+                  <UserPicture user={origin.user} size="lg" noLink noTooltip />
                 </div>
                 </div>
-                <CodeMirror
-                  value={origin.revisionBody}
-                  options={codeMirrorRevisionOption}
-                />
-                <div className="text-center my-4">
-                  <button
-                    type="button"
-                    className="btn btn-primary"
-                    onClick={() => {
-                      setIsRevisionSelected(true);
-                      if (resolvedRevision != null) {
-                        resolvedRevision.current = origin.revisionBody;
-                      }
-                    }}
-                  >
-                    <i className="icon-fw icon-arrow-down-circle"></i>
-                    {t('modal_resolve_conflict.select_revision', { revision: 'origin' })}
-                  </button>
+                <div className="ml-3 text-muted">
+                  <p className="my-0">updated by {origin.user.username}</p>
+                  <p className="my-0">{origin.createdAt}</p>
                 </div>
                 </div>
               </div>
               </div>
-              <div className="col-12 col-md-4 border border-dark">
-                <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.latest_revision')}</h3>
-                <div className="d-flex align-items-center my-3">
-                  <div>
-                    <UserPicture user={latest.user} size="lg" noLink noTooltip />
-                  </div>
-                  <div className="ml-3 text-muted">
-                    <p className="my-0">updated by {latest.user.username}</p>
-                    <p className="my-0">{latest.createdAt}</p>
-                  </div>
+            </div>
+            <div className="col-4">
+              <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.latest_revision')}</h3>
+              <div className="d-flex align-items-center my-3">
+                <div>
+                  <UserPicture user={latest.user} size="lg" noLink noTooltip />
                 </div>
                 </div>
-                <CodeMirror
-                  value={latest.revisionBody}
-                  options={codeMirrorRevisionOption}
-                />
-                <div className="text-center my-4">
-                  <button
-                    type="button"
-                    className="btn btn-primary"
-                    onClick={() => {
-                      setIsRevisionSelected(true);
-                      resolvedRevision.current = latest.revisionBody;
-                    }}
-                  >
-                    <i className="icon-fw icon-arrow-down-circle"></i>
-                    {t('modal_resolve_conflict.select_revision', { revision: 'latest' })}
-                  </button>
+                <div className="ml-3 text-muted">
+                  <p className="my-0">updated by {latest.user.username}</p>
+                  <p className="my-0">{latest.createdAt}</p>
                 </div>
                 </div>
               </div>
               </div>
-              <div className="col-12 border border-dark">
-                <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.selected_editable_revision')}</h3>
-                <UncontrolledCodeMirror
-                  value={resolvedRevision.current}
-                  options={{
-                    placeholder: t('modal_resolve_conflict.resolve_conflict_message'),
+            </div>
+            <div className="col-12" ref={(el) => { setCodeMirrorRef(el) }}></div>
+            <div className="col-4">
+              <div className="text-center my-4">
+                <button
+                  type="button"
+                  className="btn btn-primary"
+                  onClick={() => {
+                    setIsRevisionSelected(true);
+                    resolvedRevision.current = request.revisionBody;
                   }}
                   }}
-                  onChange={(editor, data, pageBody) => {
-                    if (pageBody === '') setIsRevisionSelected(false);
-                    resolvedRevision.current = pageBody;
+                >
+                  <i className="icon-fw icon-arrow-down-circle"></i>
+                  {t('modal_resolve_conflict.select_revision', { revision: 'request' })}
+                </button>
+              </div>
+            </div>
+            <div className="col-4">
+              <div className="text-center my-4">
+                <button
+                  type="button"
+                  className="btn btn-primary"
+                  onClick={() => {
+                    setIsRevisionSelected(true);
+                    resolvedRevision.current = origin.revisionBody;
                   }}
                   }}
-                />
+                >
+                  <i className="icon-fw icon-arrow-down-circle"></i>
+                  {t('modal_resolve_conflict.select_revision', { revision: 'origin' })}
+                </button>
               </div>
               </div>
             </div>
             </div>
-          )
-        }
+            <div className="col-4">
+              <div className="text-center my-4">
+                <button
+                  type="button"
+                  className="btn btn-primary"
+                  onClick={() => {
+                    setIsRevisionSelected(true);
+                    resolvedRevision.current = latest.revisionBody;
+                  }}
+                >
+                  <i className="icon-fw icon-arrow-down-circle"></i>
+                  {t('modal_resolve_conflict.select_revision', { revision: 'latest' })}
+                </button>
+              </div>
+            </div>
+            <div className="col-12 border border-dark">
+              <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.selected_editable_revision')}</h3>
+              <UncontrolledCodeMirror
+                value={resolvedRevision.current}
+                options={{
+                  placeholder: t('modal_resolve_conflict.resolve_conflict_message'),
+                }}
+                onChange={(editor, data, pageBody) => {
+                  if (pageBody === '') setIsRevisionSelected(false);
+                  resolvedRevision.current = pageBody;
+                }}
+              />
+            </div>
+          </div>
+        )}
       </ModalBody>
       </ModalBody>
       <ModalFooter>
       <ModalFooter>
         <button
         <button