214 lines
5.2 KiB
Vue
214 lines
5.2 KiB
Vue
<script>
|
|
import $ from 'jquery';
|
|
import { s__ } from '~/locale';
|
|
import Flash from '../../../flash';
|
|
import GLForm from '../../../gl_form';
|
|
import markdownHeader from './header.vue';
|
|
import markdownToolbar from './toolbar.vue';
|
|
import icon from '../icon.vue';
|
|
|
|
export default {
|
|
components: {
|
|
markdownHeader,
|
|
markdownToolbar,
|
|
icon,
|
|
},
|
|
props: {
|
|
markdownPreviewPath: {
|
|
type: String,
|
|
required: false,
|
|
default: '',
|
|
},
|
|
markdownDocsPath: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
markdownVersion: {
|
|
type: Number,
|
|
required: false,
|
|
default: 0,
|
|
},
|
|
addSpacingClasses: {
|
|
type: Boolean,
|
|
required: false,
|
|
default: true,
|
|
},
|
|
quickActionsDocsPath: {
|
|
type: String,
|
|
required: false,
|
|
default: '',
|
|
},
|
|
canAttachFile: {
|
|
type: Boolean,
|
|
required: false,
|
|
default: true,
|
|
},
|
|
enableAutocomplete: {
|
|
type: Boolean,
|
|
required: false,
|
|
default: true,
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
markdownPreview: '',
|
|
referencedCommands: '',
|
|
referencedUsers: '',
|
|
markdownPreviewLoading: false,
|
|
previewMarkdown: false,
|
|
};
|
|
},
|
|
computed: {
|
|
shouldShowReferencedUsers() {
|
|
const referencedUsersThreshold = 10;
|
|
return this.referencedUsers.length >= referencedUsersThreshold;
|
|
},
|
|
},
|
|
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;
|
|
|
|
this.previewMarkdown = true;
|
|
|
|
/*
|
|
Can't use `$refs` as the component is technically in the parent component
|
|
so we access the VNode & then get the element
|
|
*/
|
|
const text = this.$slots.textarea[0].elm.value;
|
|
|
|
if (text) {
|
|
this.markdownPreviewLoading = true;
|
|
this.$http
|
|
.post(this.versionedPreviewPath(), { text })
|
|
.then(resp => resp.json())
|
|
.then(data => this.renderMarkdown(data))
|
|
.catch(() => new Flash(s__('Error loading markdown preview')));
|
|
} else {
|
|
this.renderMarkdown();
|
|
}
|
|
},
|
|
|
|
showWriteTab() {
|
|
this.markdownPreview = '';
|
|
this.previewMarkdown = false;
|
|
},
|
|
|
|
renderMarkdown(data = {}) {
|
|
this.markdownPreviewLoading = false;
|
|
this.markdownPreview = data.body || 'Nothing to preview.';
|
|
|
|
if (data.references) {
|
|
this.referencedCommands = data.references.commands;
|
|
this.referencedUsers = data.references.users;
|
|
}
|
|
|
|
this.$nextTick(() => {
|
|
$(this.$refs['markdown-preview']).renderGFM();
|
|
});
|
|
},
|
|
|
|
versionedPreviewPath() {
|
|
const { markdownPreviewPath, markdownVersion } = this;
|
|
return `${markdownPreviewPath}${
|
|
markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'
|
|
}markdown_version=${markdownVersion}`;
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
ref="gl-form"
|
|
:class="{ 'prepend-top-default append-bottom-default': addSpacingClasses }"
|
|
class="md-area js-vue-markdown-field">
|
|
<markdown-header
|
|
:preview-markdown="previewMarkdown"
|
|
@preview-markdown="showPreviewTab"
|
|
@write-markdown="showWriteTab"
|
|
/>
|
|
<div
|
|
v-show="!previewMarkdown"
|
|
class="md-write-holder"
|
|
>
|
|
<div class="zen-backdrop">
|
|
<slot name="textarea"></slot>
|
|
<a
|
|
class="zen-control zen-control-leave js-zen-leave"
|
|
href="#"
|
|
aria-label="Enter zen mode"
|
|
>
|
|
<icon
|
|
:size="32"
|
|
name="screen-normal"
|
|
/>
|
|
</a>
|
|
<markdown-toolbar
|
|
:markdown-docs-path="markdownDocsPath"
|
|
:quick-actions-docs-path="quickActionsDocsPath"
|
|
:can-attach-file="canAttachFile"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-show="previewMarkdown"
|
|
class="md md-preview-holder md-preview js-vue-md-preview"
|
|
>
|
|
<div
|
|
ref="markdown-preview"
|
|
v-html="markdownPreview"
|
|
>
|
|
</div>
|
|
<span v-if="markdownPreviewLoading">
|
|
Loading...
|
|
</span>
|
|
</div>
|
|
<template v-if="previewMarkdown && !markdownPreviewLoading">
|
|
<div
|
|
v-if="referencedCommands"
|
|
class="referenced-commands"
|
|
v-html="referencedCommands"
|
|
>
|
|
</div>
|
|
<div
|
|
v-if="shouldShowReferencedUsers"
|
|
class="referenced-users"
|
|
>
|
|
<span>
|
|
<i
|
|
class="fa fa-exclamation-triangle"
|
|
aria-hidden="true"
|
|
>
|
|
</i>
|
|
You are about to add
|
|
<strong>
|
|
<span class="js-referenced-users-count">
|
|
{{ referencedUsers.length }}
|
|
</span>
|
|
</strong> people to the discussion. Proceed with caution.
|
|
</span>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</template>
|