debian-mirror-gitlab/app/assets/javascripts/vue_shared/components/markdown/field.vue

254 lines
6.7 KiB
Vue
Raw Normal View History

2017-09-10 17:25:29 +05:30
<script>
2018-12-13 13:39:08 +05:30
import $ from 'jquery';
2019-02-15 15:39:39 +05:30
import _ from 'underscore';
import { __ } from '~/locale';
import { stripHtml } from '~/lib/utils/text_utility';
2018-12-13 13:39:08 +05:30
import Flash from '../../../flash';
import GLForm from '../../../gl_form';
import markdownHeader from './header.vue';
import markdownToolbar from './toolbar.vue';
import icon from '../icon.vue';
2019-02-15 15:39:39 +05:30
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
2017-09-10 17:25:29 +05:30
2018-12-13 13:39:08 +05:30
export default {
components: {
markdownHeader,
markdownToolbar,
icon,
2019-02-15 15:39:39 +05:30
Suggestions,
2018-12-13 13:39:08 +05:30
},
props: {
markdownPreviewPath: {
type: String,
required: false,
default: '',
2018-03-17 18:26:18 +05:30
},
2018-12-13 13:39:08 +05:30
markdownDocsPath: {
type: String,
required: true,
2017-09-10 17:25:29 +05:30
},
2018-12-13 13:39:08 +05:30
addSpacingClasses: {
type: Boolean,
required: false,
default: true,
2017-09-10 17:25:29 +05:30
},
2018-12-13 13:39:08 +05:30
quickActionsDocsPath: {
type: String,
required: false,
default: '',
2017-09-10 17:25:29 +05:30
},
2018-12-13 13:39:08 +05:30
canAttachFile: {
type: Boolean,
required: false,
default: true,
},
enableAutocomplete: {
type: Boolean,
required: false,
default: true,
2017-09-10 17:25:29 +05:30
},
2019-02-15 15:39:39 +05:30
line: {
type: Object,
required: false,
default: null,
},
note: {
type: Object,
required: false,
default: () => ({}),
},
canSuggest: {
type: Boolean,
required: false,
default: false,
},
helpPagePath: {
type: String,
required: false,
default: '',
},
2018-12-13 13:39:08 +05:30
},
data() {
return {
markdownPreview: '',
referencedCommands: '',
referencedUsers: '',
2019-02-15 15:39:39 +05:30
hasSuggestion: false,
2018-12-13 13:39:08 +05:30
markdownPreviewLoading: false,
previewMarkdown: false,
};
},
computed: {
shouldShowReferencedUsers() {
const referencedUsersThreshold = 10;
return this.referencedUsers.length >= referencedUsersThreshold;
},
2019-02-15 15:39:39 +05:30
lineContent() {
const [firstSuggestion] = this.suggestions;
if (firstSuggestion) {
return firstSuggestion.from_content;
}
if (this.line) {
const { rich_text: richText, text } = this.line;
if (text) {
return text;
}
return _.unescape(stripHtml(richText).replace(/\n/g, ''));
}
return '';
},
lineNumber() {
let lineNumber;
if (this.line) {
const { new_line: newLine, old_line: oldLine } = this.line;
lineNumber = newLine || oldLine;
}
return lineNumber;
},
suggestions() {
return this.note.suggestions || [];
},
lineType() {
return this.line ? this.line.type : '';
},
2018-12-13 13:39:08 +05:30
},
mounted() {
/*
GLForm class handles all the toolbar buttons
*/
return new GLForm($(this.$refs['gl-form']), {
emojis: this.enableAutocomplete,
members: this.enableAutocomplete,
issues: this.enableAutocomplete,
mergeRequests: this.enableAutocomplete,
epics: this.enableAutocomplete,
milestones: this.enableAutocomplete,
labels: this.enableAutocomplete,
snippets: this.enableAutocomplete,
});
},
beforeDestroy() {
const glForm = $(this.$refs['gl-form']).data('glForm');
if (glForm) {
glForm.destroy();
}
},
methods: {
showPreviewTab() {
if (this.previewMarkdown) return;
2018-03-17 18:26:18 +05:30
2018-12-13 13:39:08 +05:30
this.previewMarkdown = true;
2018-03-17 18:26:18 +05:30
2018-12-13 13:39:08 +05:30
/*
2018-03-17 18:26:18 +05:30
Can't use `$refs` as the component is technically in the parent component
so we access the VNode & then get the element
*/
2018-12-13 13:39:08 +05:30
const text = this.$slots.textarea[0].elm.value;
2018-03-17 18:26:18 +05:30
2018-12-13 13:39:08 +05:30
if (text) {
this.markdownPreviewLoading = true;
2019-02-15 15:39:39 +05:30
this.markdownPreview = __('Loading…');
2018-12-13 13:39:08 +05:30
this.$http
2019-03-02 22:35:43 +05:30
.post(this.markdownPreviewPath, { text })
2018-12-13 13:39:08 +05:30
.then(resp => resp.json())
.then(data => this.renderMarkdown(data))
2019-02-15 15:39:39 +05:30
.catch(() => new Flash(__('Error loading markdown preview')));
2018-12-13 13:39:08 +05:30
} else {
this.renderMarkdown();
}
},
2018-03-17 18:26:18 +05:30
2018-12-13 13:39:08 +05:30
showWriteTab() {
this.markdownPreview = '';
this.previewMarkdown = false;
},
2018-03-17 18:26:18 +05:30
2018-12-13 13:39:08 +05:30
renderMarkdown(data = {}) {
this.markdownPreviewLoading = false;
this.markdownPreview = data.body || 'Nothing to preview.';
2018-03-17 18:26:18 +05:30
2018-12-13 13:39:08 +05:30
if (data.references) {
this.referencedCommands = data.references.commands;
this.referencedUsers = data.references.users;
2019-02-15 15:39:39 +05:30
this.hasSuggestion = data.references.suggestions && data.references.suggestions.length;
2018-12-13 13:39:08 +05:30
}
2018-03-17 18:26:18 +05:30
2019-03-02 22:35:43 +05:30
this.$nextTick()
.then(() => $(this.$refs['markdown-preview']).renderGFM())
.catch(() => new Flash(__('Error rendering markdown preview')));
2018-03-17 18:26:18 +05:30
},
2018-12-13 13:39:08 +05:30
},
};
2017-09-10 17:25:29 +05:30
</script>
<template>
<div
2018-11-08 19:23:39 +05:30
ref="gl-form"
2018-03-17 18:26:18 +05:30
:class="{ 'prepend-top-default append-bottom-default': addSpacingClasses }"
2019-02-15 15:39:39 +05:30
class="md-area js-vue-markdown-field"
>
2017-09-10 17:25:29 +05:30
<markdown-header
:preview-markdown="previewMarkdown"
2019-02-15 15:39:39 +05:30
:line-content="lineContent"
:can-suggest="canSuggest"
2018-03-17 18:26:18 +05:30
@preview-markdown="showPreviewTab"
@write-markdown="showWriteTab"
/>
2019-02-15 15:39:39 +05:30
<div v-show="!previewMarkdown" class="md-write-holder">
2017-09-10 17:25:29 +05:30
<div class="zen-backdrop">
<slot name="textarea"></slot>
2019-02-15 15:39:39 +05:30
<a class="zen-control zen-control-leave js-zen-leave" href="#" aria-label="Enter zen mode">
<icon :size="32" name="screen-normal" />
2017-09-10 17:25:29 +05:30
</a>
<markdown-toolbar
2018-03-17 18:26:18 +05:30
:markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath"
:can-attach-file="canAttachFile"
/>
2017-09-10 17:25:29 +05:30
</div>
</div>
2019-02-15 15:39:39 +05:30
<template v-if="hasSuggestion">
2017-09-10 17:25:29 +05:30
<div
2019-02-15 15:39:39 +05:30
v-show="previewMarkdown"
2017-09-10 17:25:29 +05:30
ref="markdown-preview"
2019-02-15 15:39:39 +05:30
class="md-preview js-vue-md-preview md md-preview-holder"
2019-01-03 12:48:30 +05:30
>
2019-02-15 15:39:39 +05:30
<suggestions
v-if="hasSuggestion"
:note-html="markdownPreview"
:from-line="lineNumber"
:from-content="lineContent"
:line-type="lineType"
:disabled="true"
:suggestions="suggestions"
:help-page-path="helpPagePath"
/>
2019-01-03 12:48:30 +05:30
</div>
2019-02-15 15:39:39 +05:30
</template>
<template v-else>
2019-01-03 12:48:30 +05:30
<div
2019-02-15 15:39:39 +05:30
v-show="previewMarkdown"
ref="markdown-preview"
class="md-preview js-vue-md-preview md md-preview-holder"
v-html="markdownPreview"
></div>
</template>
<template v-if="previewMarkdown && !markdownPreviewLoading">
<div v-if="referencedCommands" class="referenced-commands" v-html="referencedCommands"></div>
<div v-if="shouldShowReferencedUsers" class="referenced-users">
2018-03-17 18:26:18 +05:30
<span>
2019-02-15 15:39:39 +05:30
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> You are about to add
2018-03-17 18:26:18 +05:30
<strong>
2019-02-15 15:39:39 +05:30
<span class="js-referenced-users-count">{{ referencedUsers.length }}</span>
</strong>
people to the discussion. Proceed with caution.
2018-03-17 18:26:18 +05:30
</span>
</div>
</template>
2017-09-10 17:25:29 +05:30
</div>
</template>