field.vue 4.72 KB
Newer Older
1
<script>
2
  import $ from 'jquery';
Phil Hughes's avatar
Phil Hughes committed
3
  import Flash from '../../../flash';
4
  import GLForm from '../../../gl_form';
5 6
  import markdownHeader from './header.vue';
  import markdownToolbar from './toolbar.vue';
Tim Zallmann's avatar
Tim Zallmann committed
7
  import icon from '../icon.vue';
8 9

  export default {
Filipa Lacerda's avatar
Filipa Lacerda committed
10 11 12 13 14
    components: {
      markdownHeader,
      markdownToolbar,
      icon,
    },
15
    props: {
16
      markdownPreviewPath: {
17 18 19 20
        type: String,
        required: false,
        default: '',
      },
21
      markdownDocsPath: {
Phil Hughes's avatar
Phil Hughes committed
22 23 24
        type: String,
        required: true,
      },
25 26 27 28 29
      addSpacingClasses: {
        type: Boolean,
        required: false,
        default: true,
      },
30
      quickActionsDocsPath: {
31 32
        type: String,
        required: false,
Filipa Lacerda's avatar
Filipa Lacerda committed
33
        default: '',
34
      },
35 36 37 38 39
      canAttachFile: {
        type: Boolean,
        required: false,
        default: true,
      },
Clement Ho's avatar
Clement Ho committed
40 41 42 43 44
      enableAutocomplete: {
        type: Boolean,
        required: false,
        default: true,
      },
45 46 47 48
    },
    data() {
      return {
        markdownPreview: '',
49 50
        referencedCommands: '',
        referencedUsers: '',
51 52 53 54
        markdownPreviewLoading: false,
        previewMarkdown: false,
      };
    },
55 56
    computed: {
      shouldShowReferencedUsers() {
Filipa Lacerda's avatar
Filipa Lacerda committed
57 58
        const referencedUsersThreshold = 10;
        return this.referencedUsers.length >= referencedUsersThreshold;
59 60
      },
    },
Filipa Lacerda's avatar
Filipa Lacerda committed
61 62 63 64 65 66 67
    mounted() {
      /*
        GLForm class handles all the toolbar buttons
      */
      return new GLForm($(this.$refs['gl-form']), this.enableAutocomplete);
    },
    beforeDestroy() {
Jacob Schatz's avatar
Jacob Schatz committed
68
      const glForm = $(this.$refs['gl-form']).data('glForm');
Filipa Lacerda's avatar
Filipa Lacerda committed
69 70 71 72
      if (glForm) {
        glForm.destroy();
      }
    },
73
    methods: {
74 75
      showPreviewTab() {
        if (this.previewMarkdown) return;
76

77
        this.previewMarkdown = true;
78

79 80 81 82 83 84
        /*
          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;

85
        if (text) {
86 87 88
          this.markdownPreviewLoading = true;
          this.$http.post(this.markdownPreviewPath, { text })
            .then(resp => resp.json())
89
            .then(data => this.renderMarkdown(data))
90
            .catch(() => new Flash('Error loading markdown preview'));
91
        } else {
92
          this.renderMarkdown();
93 94
        }
      },
95 96 97 98 99 100

      showWriteTab() {
        this.markdownPreview = '';
        this.previewMarkdown = false;
      },

101 102 103
      renderMarkdown(data = {}) {
        this.markdownPreviewLoading = false;
        this.markdownPreview = data.body || 'Nothing to preview.';
104

105 106 107
        if (data.references) {
          this.referencedCommands = data.references.commands;
          this.referencedUsers = data.references.users;
108
        }
109 110 111 112

        this.$nextTick(() => {
          $(this.$refs['markdown-preview']).renderGFM();
        });
113 114 115 116 117 118 119
      },
    },
  };
</script>

<template>
  <div
120 121
    class="md-area js-vue-markdown-field"
    :class="{ 'prepend-top-default append-bottom-default': addSpacingClasses }"
122 123 124
    ref="gl-form">
    <markdown-header
      :preview-markdown="previewMarkdown"
125
      @preview-markdown="showPreviewTab"
Filipa Lacerda's avatar
Filipa Lacerda committed
126 127
      @write-markdown="showWriteTab"
    />
128 129
    <div
      class="md-write-holder"
Filipa Lacerda's avatar
Filipa Lacerda committed
130 131
      v-show="!previewMarkdown"
    >
132 133 134 135 136
      <div class="zen-backdrop">
        <slot name="textarea"></slot>
        <a
          class="zen-control zen-control-leave js-zen-leave"
          href="#"
Filipa Lacerda's avatar
Filipa Lacerda committed
137 138
          aria-label="Enter zen mode"
        >
Tim Zallmann's avatar
Tim Zallmann committed
139 140
          <icon
            name="screen-normal"
Filipa Lacerda's avatar
Filipa Lacerda committed
141 142
            :size="32"
          />
143
        </a>
Phil Hughes's avatar
Phil Hughes committed
144
        <markdown-toolbar
145 146
          :markdown-docs-path="markdownDocsPath"
          :quick-actions-docs-path="quickActionsDocsPath"
147
          :can-attach-file="canAttachFile"
Filipa Lacerda's avatar
Filipa Lacerda committed
148
        />
149 150 151 152
      </div>
    </div>
    <div
      class="md md-preview-holder md-preview"
Filipa Lacerda's avatar
Filipa Lacerda committed
153 154
      v-show="previewMarkdown"
    >
155 156
      <div
        ref="markdown-preview"
Filipa Lacerda's avatar
Filipa Lacerda committed
157 158
        v-html="markdownPreview"
      >
159 160 161 162 163
      </div>
      <span v-if="markdownPreviewLoading">
        Loading...
      </span>
    </div>
164 165 166 167
    <template v-if="previewMarkdown && !markdownPreviewLoading">
      <div
        v-if="referencedCommands"
        v-html="referencedCommands"
Filipa Lacerda's avatar
Filipa Lacerda committed
168 169 170
        class="referenced-commands"
      >
      </div>
171 172
      <div
        v-if="shouldShowReferencedUsers"
Filipa Lacerda's avatar
Filipa Lacerda committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
        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>
189
    </template>
190 191
  </div>
</template>