diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/emoji-input/emoji-input.js | 3 | ||||
| -rw-r--r-- | src/components/emoji-selector/emoji-selector.js | 72 | ||||
| -rw-r--r-- | src/components/emoji-selector/emoji-selector.vue | 139 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.js | 2 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.vue | 7 |
5 files changed, 223 insertions, 0 deletions
diff --git a/src/components/emoji-input/emoji-input.js b/src/components/emoji-input/emoji-input.js index fab64a69..c3c5c88b 100644 --- a/src/components/emoji-input/emoji-input.js +++ b/src/components/emoji-input/emoji-input.js @@ -63,6 +63,9 @@ const EmojiInput = { blurTimeout: null } }, + components: { + EmojiSelector + }, computed: { suggestions () { const firstchar = this.textAtCaret.charAt(0) diff --git a/src/components/emoji-selector/emoji-selector.js b/src/components/emoji-selector/emoji-selector.js new file mode 100644 index 00000000..969b880b --- /dev/null +++ b/src/components/emoji-selector/emoji-selector.js @@ -0,0 +1,72 @@ +const filterByKeyword = (list, keyword = '') => { + return list.filter(x => x.shortcode.indexOf(keyword) !== -1) +} + +const EmojiSelector = { + mounted () { + document.body.addEventListener('click', this.outsideClicked) + }, + destroyed () { + document.body.removeEventListener('click', this.outsideClicked) + }, + data () { + return { + open: false, + keyword: '', + activeGroup: 'standard' + } + }, + methods: { + togglePanel () { + this.open = !this.open + }, + insideClicked (e) { + e.stopPropagation() + }, + outsideClicked () { + this.open = false + }, + onEmoji (emoji) { + const value = emoji.image_url ? `:${emoji.shortcode}:` : emoji.utf + this.$emit('emoji', ` ${value} `) + this.open = false + }, + highlight (key) { + const ref = this.$refs['group-' + key] + const top = ref[0].offsetTop + this.$refs['emoji-groups'].scrollTop = top + 1 + this.activeGroup = key + }, + scrolledGroup (e) { + const top = e.target.scrollTop + Object.keys(this.emojis).forEach(key => { + if (this.$refs['group-' + key][0].offsetTop < top) { + this.activeGroup = key + } + }) + } + }, + computed: { + emojis () { + const standardEmojis = this.$store.state.instance.emoji || [] + const customEmojis = this.$store.state.instance.customEmoji || [] + return { + standard: { + text: 'Standard', + icon: 'icon-star', + emojis: filterByKeyword(standardEmojis, this.keyword) + }, + custom: { + text: 'Custom', + icon: 'icon-picture', + emojis: filterByKeyword(customEmojis, this.keyword) + } + } + }, + serverUrl () { + return this.$store.state.instance.server + } + } +} + +export default EmojiSelector diff --git a/src/components/emoji-selector/emoji-selector.vue b/src/components/emoji-selector/emoji-selector.vue new file mode 100644 index 00000000..0630772c --- /dev/null +++ b/src/components/emoji-selector/emoji-selector.vue @@ -0,0 +1,139 @@ +<template> + <div class="emoji-dropdown" @click.prevent="insideClicked"> + <span class="emoji-dropdown-toggle" @click.prevent="togglePanel"> + <i class="icon-smile"></i> + </span> + <div class="emoji-dropdown-menu panel panel-default" v-if="open"> + <div class="panel-heading emoji-tabs"> + <span class="emoji-tabs-item" :class="{'active': activeGroup === key}" v-for="(value, key) in emojis" :key="key" :title="value.text" @click.prevent="highlight(key)"> + <i :class="value.icon"></i> + </span> + </div> + <div class="panel-body emoji-dropdown-menu-content"> + <div class="emoji-search"> + <input type="text" class="form-control" v-model="keyword" /> + </div> + <div class="emoji-groups" ref="emoji-groups" @scroll="scrolledGroup"> + <div v-for="(value, key) in emojis" :key="key" class="emoji-group"> + <h6 class="emoji-group-title" :ref="'group-' + key">{{value.text}}</h6> + <span + v-for="emoji in value.emojis" + :key="key + emoji.shortcode" + :title="emoji.shortcode" + class="emoji-item" + @click="onEmoji(emoji)" + > + <span v-if="!emoji.image_url">{{emoji.utf}}</span> + <img :src="serverUrl + emoji.image_url" v-else> + </span> + </div> + </div> + </div> + </div> + </div> +</template> + +<script src="./emoji-selector.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.emoji { + &-dropdown { + position: absolute; + right: 0; + top: 28px; + z-index: 1; + + &-toggle { + cursor: pointer; + position: absolute; + top: -23px; + right: 2px; + line-height: 1; + + i { + font-size: 18px; + } + } + + &-menu { + position: absolute; + z-index: 1; + right: 0; + width: 300px; + height: 300px; + display: flex; + flex-direction: column; + margin: 0 !important; + + &-content { + flex: 1 1 1px; + display: flex; + flex-direction: column; + } + } + } + + &-tabs { + &-item { + padding: 0 5px; + + &.active { + border-bottom: 4px solid; + + i { + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); + } + } + } + } + + &-search { + padding: 5px; + + input { + width: 100%; + } + } + + &-groups { + flex: 1 1 1px; + overflow: auto; + position: relative; + } + + &-group { + display: flex; + align-items: center; + flex-wrap: wrap; + width: 100%; + + &-title { + font-size: 12px; + width: 100%; + margin: 0; + padding: 5px; + } + } + + &-item { + width: 34px; + height: 34px; + box-sizing: border-box; + display: flex; + font-size: 16px; + align-items: center; + justify-content: center; + padding: 5px; + cursor: pointer; + + img { + max-width: 100%; + max-height: 100%; + } + } + +} +</style> diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 40bbf6d4..5aaac8e6 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -2,6 +2,7 @@ import statusPoster from '../../services/status_poster/status_poster.service.js' import MediaUpload from '../media_upload/media_upload.vue' import ScopeSelector from '../scope_selector/scope_selector.vue' import EmojiInput from '../emoji-input/emoji-input.vue' +import EmojiSelector from '../emoji-selector/emoji-selector.vue' import PollForm from '../poll/poll_form.vue' import StickerPicker from '../sticker_picker/sticker_picker.vue' import fileTypeService from '../../services/file_type/file_type.service.js' @@ -36,6 +37,7 @@ const PostStatusForm = { EmojiInput, PollForm, StickerPicker, + EmojiSelector, ScopeSelector }, mounted () { diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index d29d47e4..d15c75ad 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -369,6 +369,13 @@ } } + .status-input-wrapper { + display: flex; + position: relative; + width: 100%; + flex-direction: column; + } + .attachments { padding: 0 0.5em; |
