diff options
author | Oswaldo Ferreira <oswaldo@gitlab.com> | 2018-12-13 22:17:19 +0300 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2018-12-13 22:17:19 +0300 |
commit | ed3034bbb71d43b12944a9da29b5264cb3ff3312 (patch) | |
tree | 3110713f4455a4b3a830e177422663e082fc0eb9 /app/assets/javascripts/vue_shared/components/markdown/suggestions.vue | |
parent | eb81c1239ef86561b4304339609be32318419dbb (diff) |
Allow suggesting single line changes in diffs
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/markdown/suggestions.vue')
-rw-r--r-- | app/assets/javascripts/vue_shared/components/markdown/suggestions.vue | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue new file mode 100644 index 00000000000..7c6dbee3e19 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue @@ -0,0 +1,136 @@ +<script> +import Vue from 'vue'; +import SuggestionDiff from './suggestion_diff.vue'; +import Flash from '~/flash'; + +export default { + components: { SuggestionDiff }, + props: { + fromLine: { + type: Number, + required: false, + default: 0, + }, + fromContent: { + type: String, + required: false, + default: '', + }, + lineType: { + type: String, + required: false, + default: '', + }, + suggestions: { + type: Array, + required: false, + default: () => [], + }, + noteHtml: { + type: String, + required: true, + }, + disabled: { + type: Boolean, + required: false, + default: false, + }, + helpPagePath: { + type: String, + required: true, + }, + }, + data() { + return { + isRendered: false, + }; + }, + watch: { + suggestions() { + this.reset(); + }, + noteHtml() { + this.reset(); + }, + }, + mounted() { + this.renderSuggestions(); + }, + methods: { + renderSuggestions() { + // swaps out suggestion(s) markdown with rich diff components + // (while still keeping non-suggestion markdown in place) + + if (!this.noteHtml) return; + const { container } = this.$refs; + const suggestionElements = container.querySelectorAll('.js-render-suggestion'); + + if (this.lineType === 'old') { + Flash('Unable to apply suggestions to a deleted line.', 'alert', this.$el); + } + + suggestionElements.forEach((suggestionEl, i) => { + const suggestionParentEl = suggestionEl.parentElement; + const newLines = this.extractNewLines(suggestionParentEl); + const diffComponent = this.generateDiff(newLines, i); + diffComponent.$mount(suggestionParentEl); + }); + + this.isRendered = true; + }, + extractNewLines(suggestionEl) { + // extracts the suggested lines from the markdown + // calculates a line number for each line + + const FIRST_CHAR_REGEX = /^(\+|-)/; + const newLines = suggestionEl.querySelectorAll('.line'); + const fromLine = this.suggestions.length ? this.suggestions[0].from_line : this.fromLine; + const lines = []; + + newLines.forEach((line, i) => { + const content = `${line.innerText.replace(FIRST_CHAR_REGEX, '')}\n`; + const lineNumber = fromLine + i; + lines.push({ content, lineNumber }); + }); + + return lines; + }, + generateDiff(newLines, suggestionIndex) { + // generates the diff <suggestion-diff /> component + // all `suggestion` markdown will be swapped out by this component + + const { suggestions, disabled, helpPagePath } = this; + const suggestion = + suggestions && suggestions[suggestionIndex] ? suggestions[suggestionIndex] : {}; + const fromContent = suggestion.from_content || this.fromContent; + const fromLine = suggestion.from_line || this.fromLine; + const SuggestionDiffComponent = Vue.extend(SuggestionDiff); + const suggestionDiff = new SuggestionDiffComponent({ + propsData: { newLines, fromLine, fromContent, disabled, suggestion, helpPagePath }, + }); + + suggestionDiff.$on('apply', ({ suggestionId, callback }) => { + this.$emit('apply', { suggestionId, callback, flashContainer: this.$el }); + }); + + return suggestionDiff; + }, + reset() { + // resets the container HTML (replaces it with the updated noteHTML) + // calls `renderSuggestions` once the updated noteHTML is added to the DOM + + this.$refs.container.innerHTML = this.noteHtml; + this.isRendered = false; + this.renderSuggestions(); + this.$nextTick(() => this.renderSuggestions()); + }, + }, +}; +</script> + +<template> + <div> + <div class="flash-container mt-3"></div> + <div v-show="isRendered" ref="container" v-html="noteHtml"></div> + </div> +</template> |