Commit 95a4f817 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '321090-migrate-merge-conflicts-diff-components-to-vue' into 'master'

Migrate merge conflict views from HAML to Vue SFC

See merge request gitlab-org/gitlab!54698
parents 083e60a8 4d710ce4
...@@ -216,8 +216,6 @@ linters: ...@@ -216,8 +216,6 @@ linters:
- 'app/views/projects/merge_requests/conflicts/_commit_stats.html.haml' - 'app/views/projects/merge_requests/conflicts/_commit_stats.html.haml'
- 'app/views/projects/merge_requests/conflicts/_file_actions.html.haml' - 'app/views/projects/merge_requests/conflicts/_file_actions.html.haml'
- 'app/views/projects/merge_requests/conflicts/_submit_form.html.haml' - 'app/views/projects/merge_requests/conflicts/_submit_form.html.haml'
- 'app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml'
- 'app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml'
- 'app/views/projects/merge_requests/conflicts/show.html.haml' - 'app/views/projects/merge_requests/conflicts/show.html.haml'
- 'app/views/projects/merge_requests/creations/_diffs.html.haml' - 'app/views/projects/merge_requests/creations/_diffs.html.haml'
- 'app/views/projects/merge_requests/creations/_new_compare.html.haml' - 'app/views/projects/merge_requests/creations/_new_compare.html.haml'
......
// This is a true violation of @gitlab/no-runtime-template-compiler, as it relies on
// app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml
// for its template.
/* eslint-disable no-param-reassign, @gitlab/no-runtime-template-compiler */
import { debounce } from 'lodash';
import Vue from 'vue';
import { deprecatedCreateFlash as flash } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
global.mergeConflicts.diffFileEditor = Vue.extend({
props: {
file: {
type: Object,
required: true,
},
onCancelDiscardConfirmation: {
type: Function,
required: true,
},
onAcceptDiscardConfirmation: {
type: Function,
required: true,
},
},
data() {
return {
saved: false,
fileLoaded: false,
originalContent: '',
};
},
computed: {
classObject() {
return {
saved: this.saved,
};
},
},
watch: {
'file.showEditor': function showEditorWatcher(val) {
this.resetEditorContent();
if (!val || this.fileLoaded) {
return;
}
this.loadEditor();
},
},
mounted() {
if (this.file.loadEditor) {
this.loadEditor();
}
},
methods: {
loadEditor() {
const EditorPromise = import(/* webpackChunkName: 'EditorLite' */ '~/editor/editor_lite');
const DataPromise = axios.get(this.file.content_path);
Promise.all([EditorPromise, DataPromise])
.then(
([
{ default: EditorLite },
{
data: { content, new_path: path },
},
]) => {
const contentEl = this.$el.querySelector('.editor');
this.originalContent = content;
this.fileLoaded = true;
this.editor = new EditorLite().createInstance({
el: contentEl,
blobPath: path,
blobContent: content,
});
this.editor.onDidChangeModelContent(
debounce(this.saveDiffResolution.bind(this), 250),
);
},
)
.catch(() => {
flash(__('An error occurred while loading the file'));
});
},
saveDiffResolution() {
this.saved = true;
// This probably be better placed in the data provider
/* eslint-disable vue/no-mutating-props */
this.file.content = this.editor.getValue();
this.file.resolveEditChanged = this.file.content !== this.originalContent;
this.file.promptDiscardConfirmation = false;
/* eslint-enable vue/no-mutating-props */
},
resetEditorContent() {
if (this.fileLoaded) {
this.editor.setValue(this.originalContent);
}
},
cancelDiscardConfirmation(file) {
this.onCancelDiscardConfirmation(file);
},
acceptDiscardConfirmation(file) {
this.onAcceptDiscardConfirmation(file);
},
},
});
})(window.gl || (window.gl = {}));
<script>
import { debounce } from 'lodash';
import { deprecatedCreateFlash as flash } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
export default {
props: {
file: {
type: Object,
required: true,
},
onCancelDiscardConfirmation: {
type: Function,
required: true,
},
onAcceptDiscardConfirmation: {
type: Function,
required: true,
},
},
data() {
return {
saved: false,
fileLoaded: false,
originalContent: '',
};
},
computed: {
classObject() {
return {
saved: this.saved,
};
},
},
watch: {
'file.showEditor': function showEditorWatcher(val) {
this.resetEditorContent();
if (!val || this.fileLoaded) {
return;
}
this.loadEditor();
},
},
mounted() {
if (this.file.loadEditor) {
this.loadEditor();
}
},
methods: {
loadEditor() {
const EditorPromise = import(/* webpackChunkName: 'EditorLite' */ '~/editor/editor_lite');
const DataPromise = axios.get(this.file.content_path);
Promise.all([EditorPromise, DataPromise])
.then(
([
{ default: EditorLite },
{
data: { content, new_path: path },
},
]) => {
const contentEl = this.$el.querySelector('.editor');
this.originalContent = content;
this.fileLoaded = true;
this.editor = new EditorLite().createInstance({
el: contentEl,
blobPath: path,
blobContent: content,
});
this.editor.onDidChangeModelContent(debounce(this.saveDiffResolution.bind(this), 250));
},
)
.catch(() => {
flash(__('An error occurred while loading the file'));
});
},
saveDiffResolution() {
this.saved = true;
// This probably be better placed in the data provider
/* eslint-disable vue/no-mutating-props */
this.file.content = this.editor.getValue();
this.file.resolveEditChanged = this.file.content !== this.originalContent;
this.file.promptDiscardConfirmation = false;
/* eslint-enable vue/no-mutating-props */
},
resetEditorContent() {
if (this.fileLoaded) {
this.editor.setValue(this.originalContent);
}
},
cancelDiscardConfirmation(file) {
this.onCancelDiscardConfirmation(file);
},
acceptDiscardConfirmation(file) {
this.onAcceptDiscardConfirmation(file);
},
},
};
</script>
<template>
<div v-show="file.showEditor" class="diff-editor-wrap">
<div v-if="file.promptDiscardConfirmation" class="discard-changes-alert-wrap">
<div class="discard-changes-alert">
{{ __('Are you sure you want to discard your changes?') }}
<div class="discard-actions">
<button
class="btn btn-sm btn-danger-secondary gl-button"
@click="acceptDiscardConfirmation(file)"
>
{{ __('Discard changes') }}
</button>
<button class="btn btn-default btn-sm gl-button" @click="cancelDiscardConfirmation(file)">
{{ __('Cancel') }}
</button>
</div>
</div>
</div>
<div :class="classObject" class="editor-wrap">
<div class="editor" style="height: 350px" data-editor-loading="true"></div>
</div>
</div>
</template>
// This is a true violation of @gitlab/no-runtime-template-compiler, as it relies on
// app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml
// for its template.
/* eslint-disable no-param-reassign, @gitlab/no-runtime-template-compiler */
import Vue from 'vue';
import actionsMixin from '../mixins/line_conflict_actions';
import utilsMixin from '../mixins/line_conflict_utils';
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
global.mergeConflicts.inlineConflictLines = Vue.extend({
mixins: [utilsMixin, actionsMixin],
props: {
file: {
type: Object,
required: true,
},
},
});
})(window.gl || (window.gl = {}));
<script>
import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import actionsMixin from '../mixins/line_conflict_actions';
import utilsMixin from '../mixins/line_conflict_utils';
export default {
directives: {
SafeHtml,
},
mixins: [utilsMixin, actionsMixin],
props: {
file: {
type: Object,
required: true,
},
},
};
</script>
<template>
<table class="diff-wrap-lines code code-commit js-syntax-highlight">
<tr
v-for="line in file.inlineLines"
:key="(line.isHeader ? line.id : line.new_line) + line.richText"
class="line_holder diff-inline"
>
<template v-if="line.isHeader">
<td :class="lineCssClass(line)" class="diff-line-num header"></td>
<td :class="lineCssClass(line)" class="diff-line-num header"></td>
<td :class="lineCssClass(line)" class="line_content header">
<strong>{{ line.richText }}</strong>
<button class="btn" @click="handleSelected(file, line.id, line.section)">
{{ line.buttonTitle }}
</button>
</td>
</template>
<template v-else>
<td :class="lineCssClass(line)" class="diff-line-num new_line">
<a>{{ line.new_line }}</a>
</td>
<td :class="lineCssClass(line)" class="diff-line-num old_line">
<a>{{ line.old_line }}</a>
</td>
<td v-safe-html="line.richText" :class="lineCssClass(line)" class="line_content"></td>
</template>
</tr>
</table>
</template>
/* eslint-disable no-param-reassign */
import Vue from 'vue';
import actionsMixin from '../mixins/line_conflict_actions';
import utilsMixin from '../mixins/line_conflict_utils';
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
global.mergeConflicts.parallelConflictLines = Vue.extend({
mixins: [utilsMixin, actionsMixin],
props: {
file: {
type: Object,
required: true,
},
},
// This is a true violation of @gitlab/no-runtime-template-compiler, as it
// has a template string.
// eslint-disable-next-line @gitlab/no-runtime-template-compiler
template: `
<table class="diff-wrap-lines code js-syntax-highlight">
<tr class="line_holder parallel" v-for="section in file.parallelLines">
<template v-for="line in section">
<td class="diff-line-num header" :class="lineCssClass(line)" v-if="line.isHeader"></td>
<td class="line_content header" :class="lineCssClass(line)" v-if="line.isHeader">
<strong>{{line.richText}}</strong>
<button class="btn" @click="handleSelected(file, line.id, line.section)">{{line.buttonTitle}}</button>
</td>
<td class="diff-line-num old_line" :class="lineCssClass(line)" v-if="!line.isHeader">{{line.lineNumber}}</td>
<td class="line_content parallel" :class="lineCssClass(line)" v-if="!line.isHeader" v-html="line.richText"></td>
</template>
</tr>
</table>
`,
});
})(window.gl || (window.gl = {}));
<script>
import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import actionsMixin from '../mixins/line_conflict_actions';
import utilsMixin from '../mixins/line_conflict_utils';
export default {
directives: {
SafeHtml,
},
mixins: [utilsMixin, actionsMixin],
props: {
file: {
type: Object,
required: true,
},
},
};
</script>
<template>
<!-- Unfortunately there isn't a good key for these sections -->
<!-- eslint-disable vue/require-v-for-key -->
<table class="diff-wrap-lines code js-syntax-highlight">
<tr v-for="section in file.parallelLines" class="line_holder parallel">
<template v-for="line in section">
<template v-if="line.isHeader">
<td class="diff-line-num header" :class="lineCssClass(line)"></td>
<td class="line_content header" :class="lineCssClass(line)">
<strong>{{ line.richText }}</strong>
<button class="btn" @click="handleSelected(file, line.id, line.section)">
{{ line.buttonTitle }}
</button>
</td>
</template>
<template v-else>
<td class="diff-line-num old_line" :class="lineCssClass(line)">
{{ line.lineNumber }}
</td>
<td
v-safe-html="line.richText"
class="line_content parallel"
:class="lineCssClass(line)"
></td>
</template>
</template>
</tr>
</table>
</template>
...@@ -10,10 +10,10 @@ import { deprecatedCreateFlash as createFlash } from '../flash'; ...@@ -10,10 +10,10 @@ import { deprecatedCreateFlash as createFlash } from '../flash';
import initIssuableSidebar from '../init_issuable_sidebar'; import initIssuableSidebar from '../init_issuable_sidebar';
import './merge_conflict_store'; import './merge_conflict_store';
import syntaxHighlight from '../syntax_highlight'; import syntaxHighlight from '../syntax_highlight';
import DiffFileEditor from './components/diff_file_editor.vue';
import InlineConflictLines from './components/inline_conflict_lines.vue';
import ParallelConflictLines from './components/parallel_conflict_lines.vue';
import MergeConflictsService from './merge_conflict_service'; import MergeConflictsService from './merge_conflict_service';
import './components/diff_file_editor';
import './components/inline_conflict_lines';
import './components/parallel_conflict_lines';
export default function initMergeConflicts() { export default function initMergeConflicts() {
const INTERACTIVE_RESOLVE_MODE = 'interactive'; const INTERACTIVE_RESOLVE_MODE = 'interactive';
...@@ -30,9 +30,9 @@ export default function initMergeConflicts() { ...@@ -30,9 +30,9 @@ export default function initMergeConflicts() {
el: '#conflicts', el: '#conflicts',
components: { components: {
FileIcon, FileIcon,
'diff-file-editor': gl.mergeConflicts.diffFileEditor, DiffFileEditor,
'inline-conflict-lines': gl.mergeConflicts.inlineConflictLines, InlineConflictLines,
'parallel-conflict-lines': gl.mergeConflicts.parallelConflictLines, ParallelConflictLines,
}, },
data: mergeConflictsStore.state, data: mergeConflictsStore.state,
computed: { computed: {
......
%diff-file-editor{ "inline-template" => "true", ":file" => "file", ":on-cancel-discard-confirmation" => "cancelDiscardConfirmation", ":on-accept-discard-confirmation" => "acceptDiscardConfirmation" }
.diff-editor-wrap{ "v-show" => "file.showEditor" }
.discard-changes-alert-wrap{ "v-if" => "file.promptDiscardConfirmation" }
.discard-changes-alert
Are you sure you want to discard your changes?
.discard-actions
%button.btn.btn-sm.btn-danger-secondary.gl-button{ "@click" => "acceptDiscardConfirmation(file)" } Discard changes
%button.btn.btn-default.btn-sm.gl-button{ "@click" => "cancelDiscardConfirmation(file)" } Cancel
.editor-wrap{ ":class" => "classObject" }
.editor{ "style" => "height: 350px", data: { 'editor-loading': true } }
%inline-conflict-lines{ "inline-template" => "true", ":file" => "file" }
%table.diff-wrap-lines.code.code-commit.js-syntax-highlight
%tr.line_holder.diff-inline{ "v-for" => "line in file.inlineLines" }
%td.diff-line-num.new_line{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader" }
%a {{line.new_line}}
%td.diff-line-num.old_line{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader" }
%a {{line.old_line}}
%td.line_content{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader", "v-html" => "line.richText" }
%td.diff-line-num.header{ ":class" => "lineCssClass(line)", "v-if" => "line.isHeader" }
%td.diff-line-num.header{ ":class" => "lineCssClass(line)", "v-if" => "line.isHeader" }
%td.line_content.header{ ":class" => "lineCssClass(line)", "v-if" => "line.isHeader" }
%strong{ "v-html" => "line.richText" }
%button.btn{ "@click" => "handleSelected(file, line.id, line.section)" }
{{line.buttonTitle}}
...@@ -27,10 +27,10 @@ ...@@ -27,10 +27,10 @@
= render partial: 'projects/merge_requests/conflicts/file_actions' = render partial: 'projects/merge_requests/conflicts/file_actions'
.diff-content.diff-wrap-lines .diff-content.diff-wrap-lines
.file-content{ "v-show" => "!isParallel && file.resolveMode === 'interactive' && file.type === 'text'" } .file-content{ "v-show" => "!isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
= render partial: "projects/merge_requests/conflicts/components/inline_conflict_lines" %inline-conflict-lines{ ":file" => "file" }
.file-content{ "v-show" => "isParallel && file.resolveMode === 'interactive' && file.type === 'text'" } .file-content{ "v-show" => "isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
%parallel-conflict-lines{ ":file" => "file" } %parallel-conflict-lines{ ":file" => "file" }
%div{ "v-show" => "file.resolveMode === 'edit' || file.type === 'text-editor'" } %div{ "v-show" => "file.resolveMode === 'edit' || file.type === 'text-editor'" }
= render partial: "projects/merge_requests/conflicts/components/diff_file_editor" %diff-file-editor{ ":file" => "file", ":on-cancel-discard-confirmation" => "cancelDiscardConfirmation", ":on-accept-discard-confirmation" => "acceptDiscardConfirmation" }
= render partial: "projects/merge_requests/conflicts/submit_form" = render partial: "projects/merge_requests/conflicts/submit_form"
...@@ -3960,6 +3960,9 @@ msgstr "" ...@@ -3960,6 +3960,9 @@ msgstr ""
msgid "Are you sure you want to discard this comment?" msgid "Are you sure you want to discard this comment?"
msgstr "" msgstr ""
msgid "Are you sure you want to discard your changes?"
msgstr ""
msgid "Are you sure you want to erase this build?" msgid "Are you sure you want to erase this build?"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment