diff options
| author | Henry Jameson <me@hjkos.com> | 2019-07-28 16:07:01 +0300 |
|---|---|---|
| committer | Henry Jameson <me@hjkos.com> | 2019-07-28 16:07:01 +0300 |
| commit | 03c2f29b0aa31eb502db29e5a809da8bc1c1af28 (patch) | |
| tree | 28d4dd9b6635ab77545cbca5f71d8fdf958d5950 /src | |
| parent | 4c78fdb3934745ccbc87c10daf56552f0bfc0edc (diff) | |
cleanup and appropriation for new emoji-input component API, styles updates
Diffstat (limited to 'src')
| -rw-r--r-- | src/boot/after_store.js | 12 | ||||
| -rw-r--r-- | src/components/emoji-input/emoji-input.js | 30 | ||||
| -rw-r--r-- | src/components/emoji-input/emoji-input.vue | 54 | ||||
| -rw-r--r-- | src/components/emoji-picker/emoji-picker.js | 33 | ||||
| -rw-r--r-- | src/components/emoji-picker/emoji-picker.vue | 57 | ||||
| -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 | 2 |
7 files changed, 122 insertions, 68 deletions
diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 3799359f..0e59358b 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -190,7 +190,7 @@ const getStaticEmoji = async ({ store }) => { imageUrl: false, replacement: values[key] } - }) + }).sort((a, b) => a.displayText - b.displayText) store.dispatch('setInstanceOption', { name: 'emoji', value: emoji }) } else { throw (res) @@ -209,14 +209,16 @@ const getCustomEmoji = async ({ store }) => { if (res.ok) { const result = await res.json() const values = Array.isArray(result) ? Object.assign({}, ...result) : result - const emoji = Object.keys(values).map((key) => { - const imageUrl = values[key].image_url + const emoji = Object.entries(values).map(([key, value]) => { + const imageUrl = value.image_url return { displayText: key, - imageUrl: imageUrl ? store.state.instance.server + imageUrl : values[key], + imageUrl: imageUrl ? store.state.instance.server + imageUrl : value, + tags: imageUrl ? value.tags.sort((a, b) => a > b ? 1 : 0) : ['utf'], replacement: `:${key}: ` } - }) + // Technically could use tags but those are kinda useless right now, should have been "pack" field, that would be more useful + }).sort((a, b) => a.displayText.toLowerCase() > b.displayText.toLowerCase() ? 1 : 0) store.dispatch('setInstanceOption', { name: 'customEmoji', value: emoji }) store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: true }) } else { diff --git a/src/components/emoji-input/emoji-input.js b/src/components/emoji-input/emoji-input.js index 62c58446..1c49c710 100644 --- a/src/components/emoji-input/emoji-input.js +++ b/src/components/emoji-input/emoji-input.js @@ -53,6 +53,11 @@ const EmojiInput = { */ required: true, type: String + }, + emojiPicker: { + required: false, + type: Boolean, + default: false } }, data () { @@ -61,7 +66,8 @@ const EmojiInput = { highlighted: 0, caret: 0, focused: false, - blurTimeout: null + blurTimeout: null, + showPicker: false, } }, components: { @@ -83,12 +89,15 @@ const EmojiInput = { highlighted: index === this.highlighted })) }, - showPopup () { + showSuggestions () { return this.focused && this.suggestions && this.suggestions.length > 0 }, textAtCaret () { return (this.wordAtCaret || {}).word || '' }, + pickerIconBottom () { + return this.input && this.input.tag === 'textarea' + }, wordAtCaret () { if (this.value && this.caret) { const word = Completion.wordAtPosition(this.value, this.caret - 1) || {} @@ -124,11 +133,22 @@ const EmojiInput = { } }, methods: { + togglePicker () { + this.showPicker = !this.showPicker + }, replace (replacement) { const newValue = Completion.replaceWord(this.value, this.wordAtCaret, replacement) this.$emit('input', newValue) this.caret = 0 }, + insert (insertion) { + const newValue = [ + this.value.substring(0, this.caret), + insertion, + this.value.substring(this.caret) + ].join('') + this.$emit('input', newValue) + }, replaceText (e, suggestion) { const len = this.suggestions.length || 0 if (this.textAtCaret.length === 1) { return } @@ -195,6 +215,7 @@ const EmojiInput = { this.blurTimeout = null } + this.showPicker = false this.focused = true this.setCaret(e) this.resize() @@ -231,6 +252,7 @@ const EmojiInput = { } }, onInput (e) { + this.showPicker = false this.setCaret(e) this.$emit('input', e.target.value) }, @@ -239,6 +261,9 @@ const EmojiInput = { this.resize() this.$emit('input', e.target.value) }, + onClickOutside () { + this.showPicker = false + }, setCaret ({ target: { selectionStart } }) { this.caret = selectionStart }, @@ -247,6 +272,7 @@ const EmojiInput = { if (!panel) return const { offsetHeight, offsetTop } = this.input.elm this.$refs.panel.style.top = (offsetTop + offsetHeight) + 'px' + this.$refs.picker.$el.style.top = (offsetTop + offsetHeight) + 'px' } } } diff --git a/src/components/emoji-input/emoji-input.vue b/src/components/emoji-input/emoji-input.vue index 48739ec8..605882e8 100644 --- a/src/components/emoji-input/emoji-input.vue +++ b/src/components/emoji-input/emoji-input.vue @@ -1,10 +1,29 @@ <template> - <div class="emoji-input"> +<div + class="emoji-input" + v-click-outside="onClickOutside" + > <slot /> + <template v-if="emojiPicker"> + <div + @click.prevent="togglePicker" + class="emoji-picker-icon" + :class="pickerIconBottom ? 'picker-icon-bottom': 'picker-icon-right'" + > + <i class="icon-smile"></i> + </div> + <EmojiPicker + v-if="emojiPicker" + :class="{ hide: !showPicker }" + ref="picker" + class="emoji-picker-panel" + @emoji="insert" + /> + </template> <div ref="panel" class="autocomplete-panel" - :class="{ hide: !showPopup }" + :class="{ hide: !showSuggestions }" > <div class="autocomplete-panel-body"> <div @@ -39,6 +58,37 @@ .emoji-input { display: flex; flex-direction: column; + position: relative; + + .emoji-picker-icon { + position: absolute; + margin: 0 .25em; + font-size: 16px; + cursor: pointer; + + &:hover i { + color: $fallback--text; + color: var(--text, $fallback--text); + } + + &.picker-icon-bottom { + bottom: 0; + left: 0; + } + &.picker-icon-right { + top: 0; + right: 0; + } + } + .emoji-picker-panel { + position: absolute; + z-index: 9; + margin-top: 2px; + + &.hide { + display: none + } + } .autocomplete { &-panel { diff --git a/src/components/emoji-picker/emoji-picker.js b/src/components/emoji-picker/emoji-picker.js index 9d2595aa..92d517b7 100644 --- a/src/components/emoji-picker/emoji-picker.js +++ b/src/components/emoji-picker/emoji-picker.js @@ -1,33 +1,17 @@ const filterByKeyword = (list, keyword = '') => { - return list.filter(x => x.shortcode.indexOf(keyword) !== -1) + return list.filter(x => x.displayText.includes(keyword)) } const EmojiPicker = { - 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 + const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement this.$emit('emoji', ` ${value} `) this.open = false }, @@ -51,20 +35,17 @@ const EmojiPicker = { 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) + }, + standard: { + text: 'Standard', + icon: 'icon-star', + emojis: filterByKeyword(standardEmojis, this.keyword) } } - }, - serverUrl () { - return this.$store.state.instance.server } } } diff --git a/src/components/emoji-picker/emoji-picker.vue b/src/components/emoji-picker/emoji-picker.vue index 663d9e2e..ea93e6fc 100644 --- a/src/components/emoji-picker/emoji-picker.vue +++ b/src/components/emoji-picker/emoji-picker.vue @@ -1,36 +1,31 @@ <template> - <div class="emoji-dropdown" @click.prevent="insideClicked"> - <span class="emoji-dropdown-toggle" @click.prevent="togglePanel"> - <i class="icon-smile"></i> +<div class="emoji-dropdown-menu panel panel-default"> + <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 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> + </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.displayText" + :title="emoji.displayText" + class="emoji-item" + @click="onEmoji(emoji)" + > + <span v-if="!emoji.imageUrl">{{emoji.replacement}}</span> + <img :src="emoji.imageUrl" v-else> </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> +</div> </template> <script src="./emoji-picker.js"></script> @@ -119,14 +114,14 @@ } &-item { - width: 34px; - height: 34px; + width: 32px; + height: 32px; box-sizing: border-box; display: flex; - font-size: 16px; + font-size: 32px; align-items: center; justify-content: center; - padding: 5px; + margin: 2px; cursor: pointer; img { diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 3e1b83b5..40bbf6d4 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -2,7 +2,6 @@ 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 EmojiPicker from '../emoji-picker/emoji-picker.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' @@ -37,7 +36,6 @@ 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 d15c75ad..0b3db30d 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -61,6 +61,7 @@ <EmojiInput v-if="newStatus.spoilerText || alwaysShowSubject" v-model="newStatus.spoilerText" + emojiPicker :suggest="emojiSuggestor" class="form-control" > @@ -75,6 +76,7 @@ <EmojiInput v-model="newStatus.status" :suggest="emojiUserSuggestor" + emojiPicker class="form-control main-input" > <textarea |
