Vue.jsでTeamsみたいなメンションをテキストエリアに実装する「vue-mention」

by kabanoki

HTML

<div id="app">
  <mentionable
    :keys="['@', '#']"
    :items="items"
    offset="6"
    @open="onOpen"
  >
    <textarea v-model="text" class="form-control" rows="8"></textarea>
    <template #no-result>
      <div class="dim">
        No result
      </div>
    </template>
    <template #item-@="{ item }">
      <div class="user">
        {{ item.value }}
        <span class="dim">
          ({{ item.firstName }})
        </span>
      </div>
    </template>
    <template #item-#="{ item }">
      <div class="issue">
        <span class="number">
          #{{ item.value }}
        </span>
        <span class="dim">
          {{ item.label }}
        </span>
      </div>
    </template>
  </mentionable>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<script src="https://unpkg.com/[email protected]/dist/v-tooltip.min.js"></script>
<script>
const vTooltip  = VTooltip;
Vue.use(VTooltip);
</script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-mention.min.js"></script>

CSS

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

.tooltip {
    display: block!important;
    z-index: 10000
}
.tooltip .tooltip-inner {
    background: #000;
    color: #fff;
    border-radius: 16px;
    padding: 5px 10px 4px
}
.tooltip .tooltip-arrow {
    width: 0;
    height: 0;
    border-style: solid;
    position: absolute;
    margin: 5px;
    border-color: #000;
    z-index: 1
}
.tooltip[x-placement^=top] {
    margin-bottom: 5px
}
.tooltip[x-placement^=top] .tooltip-arrow {
    border-width: 5px 5px 0;
    border-left-color: transparent!important;
    border-right-color: transparent!important;
    border-bottom-color: transparent!important;
    bottom: -5px;
    left: calc(50% - 5px);
    margin-top: 0;
    margin-bottom: 0
}
.tooltip[x-placement^=bottom] {
    margin-top: 5px
}
.tooltip[x-placement^=bottom] .tooltip-arrow {
    border-width: 0 5px 5px;
    border-left-color: transparent!important;
    border-right-color: transparent!important;
    border-top-color: transparent!important;
    top: -5px;
    left: calc(50% - 5px);
    margin-top: 0;
    margin-bottom: 0
}
.tooltip[x-placement^=right] {
    margin-left: 5px
}
.tooltip[x-placement^=right] .tooltip-arrow {
    border-width: 5px 5px 5px 0;
    border-left-color: transparent!important;
    border-top-color: transparent!important;
    border-bottom-color: transparent!important;
    left: -5px;
    top: calc(50% - 5px);
    margin-left: 0;
    margin-right: 0
}
.tooltip[x-placement^=left] {
    margin-right: 5px
}
.tooltip[x-placement^=left] .tooltip-arrow {
    border-width: 5px 0 5px 5px;
    border-top-color: transparent!important;
    border-right-color: transparent!important;
    border-bottom-color: transparent!important;
    right: -5px;
    top: calc(50% - 5px);
    margin-left: 0;
    margin-right: 0
}
.tooltip.popover .popover-inner {
    background: #f9f9f9;
   ...

Vue

const Mentionable  = window['VueMention'].Mentionable;

new Vue({
  el: '#app',
  components: {
    'mentionable': Mentionable,
  },
  data: {
    text: '',
    items: [],
    users: [
      {
        value: 'akryum',
        firstName: 'Guillaume',
      },
      {
        value: 'posva',
        firstName: 'Eduardo',
      },
      {
        value: 'atinux',
        firstName: 'Sébastien',
      },
    ],
    issues: [
      {
        value: 123,
        label: 'Error with foo bar',
        searchText: 'foo bar'
      },
      {
        value: 42,
        label: 'Cannot read line',
        searchText: 'foo bar line'
      },
      {
        value: 77,
        label: 'I have a feature suggestion',
        searchText: 'feature'
      }
    ]
  },
  methods: {
    onOpen:function (key) {
      this.items = key === '@' ? this.users : this.issues
    },
  },
});