<script> import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; import Vue from 'vue'; import { createAlert } from '~/flash'; import { __ } from '~/locale'; import SuggestionDiff from './suggestion_diff.vue'; export default { directives: { SafeHtml, }, props: { lineType: { type: String, required: false, default: '', }, suggestions: { type: Array, required: false, default: () => [], }, batchSuggestionsInfo: { type: Array, required: false, default: () => [], }, noteHtml: { type: String, required: true, }, disabled: { type: Boolean, required: false, default: false, }, helpPagePath: { type: String, required: true, }, defaultCommitMessage: { type: String, required: true, }, suggestionsCount: { type: Number, required: false, default: 0, }, failedToLoadMetadata: { type: Boolean, required: false, default: false, }, }, data() { return { isRendered: false, }; }, watch: { suggestions() { this.reset(); }, noteHtml() { this.reset(); }, failedToLoadMetadata() { this.reset(); }, }, mounted() { this.renderSuggestions(); }, beforeDestroy() { if (this.suggestionsWatch) { this.suggestionsWatch(); } if (this.defaultCommitMessageWatch) { this.defaultCommitMessageWatch(); } }, 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') { createAlert({ message: __('Unable to apply suggestions to a deleted line.'), parent: this.$el, }); } suggestionElements.forEach((suggestionEl, i) => { const suggestionParentEl = suggestionEl.parentElement; const diffComponent = this.generateDiff(i); diffComponent.$mount(suggestionParentEl); }); this.isRendered = true; }, generateDiff(suggestionIndex) { const { suggestions, disabled, batchSuggestionsInfo, helpPagePath, defaultCommitMessage, suggestionsCount, failedToLoadMetadata, } = this; const suggestion = suggestions && suggestions[suggestionIndex] ? suggestions[suggestionIndex] : {}; const SuggestionDiffComponent = Vue.extend(SuggestionDiff); const suggestionDiff = new SuggestionDiffComponent({ propsData: { disabled, suggestion, batchSuggestionsInfo, helpPagePath, defaultCommitMessage, suggestionsCount, failedToLoadMetadata, }, }); // We're using `$watch` as `suggestionsCount` updates do not // propagate to this component for some unknown reason while // using a traditional prop watcher. this.suggestionsWatch = this.$watch('suggestionsCount', () => { suggestionDiff.suggestionsCount = this.suggestionsCount; }); this.defaultCommitMessageWatch = this.$watch('defaultCommitMessage', () => { suggestionDiff.defaultCommitMessage = this.defaultCommitMessage; }); suggestionDiff.$on('apply', ({ suggestionId, callback, message }) => { this.$emit('apply', { suggestionId, callback, flashContainer: this.$el, message }); }); suggestionDiff.$on('applyBatch', (message) => { this.$emit('applyBatch', { message, flashContainer: this.$el }); }); suggestionDiff.$on('addToBatch', (suggestionId) => { this.$emit('addToBatch', suggestionId); }); suggestionDiff.$on('removeFromBatch', (suggestionId) => { this.$emit('removeFromBatch', suggestionId); }); return suggestionDiff; }, reset() { // resets the container HTML (replaces it with the updated noteHTML) // calls `renderSuggestions` once the updated noteHTML is added to the DOM // eslint-disable-next-line no-unsanitized/property this.$refs.container.innerHTML = this.noteHtml; this.isRendered = false; this.renderSuggestions(); this.$nextTick(() => this.renderSuggestions()); }, }, }; </script> <template> <div> <div class="flash-container js-suggestions-flash"></div> <div v-show="isRendered" ref="container" v-safe-html="noteHtml" class="md suggestions"></div> </div> </template>