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

  export default {
    props: {
10
      markdownPreviewPath: {
11 12 13 14
        type: String,
        required: false,
        default: '',
      },
15
      markdownDocsPath: {
Phil Hughes's avatar
Phil Hughes committed
16 17 18
        type: String,
        required: true,
      },
19 20 21 22 23
      addSpacingClasses: {
        type: Boolean,
        required: false,
        default: true,
      },
24
      quickActionsDocsPath: {
25 26 27
        type: String,
        required: false,
      },
28 29 30 31
    },
    data() {
      return {
        markdownPreview: '',
32 33
        referencedCommands: '',
        referencedUsers: '',
34 35 36 37 38 39 40
        markdownPreviewLoading: false,
        previewMarkdown: false,
      };
    },
    components: {
      markdownHeader,
      markdownToolbar,
Tim Zallmann's avatar
Tim Zallmann committed
41
      icon,
42
    },
43 44
    computed: {
      shouldShowReferencedUsers() {
Filipa Lacerda's avatar
Filipa Lacerda committed
45 46
        const referencedUsersThreshold = 10;
        return this.referencedUsers.length >= referencedUsersThreshold;
47 48
      },
    },
49
    methods: {
50 51
      showPreviewTab() {
        if (this.previewMarkdown) return;
52

53
        this.previewMarkdown = true;
54

55 56 57 58 59 60
        /*
          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;

61
        if (text) {
62 63 64
          this.markdownPreviewLoading = true;
          this.$http.post(this.markdownPreviewPath, { text })
            .then(resp => resp.json())
65
            .then(data => this.renderMarkdown(data))
66
            .catch(() => new Flash('Error loading markdown preview'));
67
        } else {
68
          this.renderMarkdown();
69 70
        }
      },
71 72 73 74 75 76

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

77 78 79
      renderMarkdown(data = {}) {
        this.markdownPreviewLoading = false;
        this.markdownPreview = data.body || 'Nothing to preview.';
80

81 82 83
        if (data.references) {
          this.referencedCommands = data.references.commands;
          this.referencedUsers = data.references.users;
84
        }
85 86 87 88

        this.$nextTick(() => {
          $(this.$refs['markdown-preview']).renderGFM();
        });
89 90 91 92
      },
    },
    mounted() {
      /*
Phil Hughes's avatar
Phil Hughes committed
93
        GLForm class handles all the toolbar buttons
94
      */
95
      return new GLForm($(this.$refs['gl-form']), true);
96
    },
97 98 99 100 101 102
    beforeDestroy() {
      const glForm = $(this.$refs['gl-form']).data('gl-form');
      if (glForm) {
        glForm.destroy();
      }
    },
103 104 105 106 107
  };
</script>

<template>
  <div
108 109
    class="md-area js-vue-markdown-field"
    :class="{ 'prepend-top-default append-bottom-default': addSpacingClasses }"
110 111 112
    ref="gl-form">
    <markdown-header
      :preview-markdown="previewMarkdown"
113 114
      @preview-markdown="showPreviewTab"
      @write-markdown="showWriteTab" />
115 116 117 118 119 120 121 122 123
    <div
      class="md-write-holder"
      v-show="!previewMarkdown">
      <div class="zen-backdrop">
        <slot name="textarea"></slot>
        <a
          class="zen-control zen-control-leave js-zen-leave"
          href="#"
          aria-label="Enter zen mode">
Tim Zallmann's avatar
Tim Zallmann committed
124 125 126 127
          <icon
            name="screen-normal"
            :size="32">
          </icon>
128
        </a>
Phil Hughes's avatar
Phil Hughes committed
129
        <markdown-toolbar
130 131
          :markdown-docs-path="markdownDocsPath"
          :quick-actions-docs-path="quickActionsDocsPath"
132
          />
133 134 135 136 137 138 139 140 141 142 143 144 145
      </div>
    </div>
    <div
      class="md md-preview-holder md-preview"
      v-show="previewMarkdown">
      <div
        ref="markdown-preview"
        v-html="markdownPreview">
      </div>
      <span v-if="markdownPreviewLoading">
        Loading...
      </span>
    </div>
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
    <template v-if="previewMarkdown && !markdownPreviewLoading">
      <div
        v-if="referencedCommands"
        v-html="referencedCommands"
        class="referenced-commands"></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>
168 169
  </div>
</template>