diff options
Diffstat (limited to 'src/components/emoji_picker')
| -rw-r--r-- | src/components/emoji_picker/emoji_picker.js | 97 | ||||
| -rw-r--r-- | src/components/emoji_picker/emoji_picker.scss | 28 | ||||
| -rw-r--r-- | src/components/emoji_picker/emoji_picker.vue | 3 |
3 files changed, 107 insertions, 21 deletions
diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js index b1d70176..0f397b59 100644 --- a/src/components/emoji_picker/emoji_picker.js +++ b/src/components/emoji_picker/emoji_picker.js @@ -1,5 +1,12 @@ import Checkbox from '../checkbox/checkbox.vue' +// At widest, approximately 20 emoji are visible in a row, +// loading 3 rows, could be overkill for narrow picker +const LOAD_EMOJI_BY = 60 + +// When to start loading new batch emoji, in pixels +const LOAD_EMOJI_MARGIN = 64 + const filterByKeyword = (list, keyword = '') => { return list.filter(x => x.displayText.includes(keyword)) } @@ -18,7 +25,10 @@ const EmojiPicker = { activeGroup: 'custom', showingStickers: false, groupsScrolledClass: 'scrolled-top', - keepOpen: false + keepOpen: false, + customEmojiBufferSlice: LOAD_EMOJI_BY, + customEmojiTimeout: null, + customEmojiLoadAllConfirmed: false } }, components: { @@ -26,10 +36,22 @@ const EmojiPicker = { Checkbox }, methods: { + onStickerUploaded (e) { + this.$emit('sticker-uploaded', e) + }, + onStickerUploadFailed (e) { + this.$emit('sticker-upload-failed', e) + }, onEmoji (emoji) { const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen }) }, + onScroll (e) { + const target = (e && e.target) || this.$refs['emoji-groups'] + this.updateScrolledClass(target) + this.scrolledGroup(target) + this.triggerLoadMore(target) + }, highlight (key) { const ref = this.$refs['group-' + key] const top = ref[0].offsetTop @@ -39,9 +61,7 @@ const EmojiPicker = { this.$refs['emoji-groups'].scrollTop = top + 1 }) }, - scrolledGroup (e) { - const target = (e && e.target) || this.$refs['emoji-groups'] - const top = target.scrollTop + 5 + updateScrolledClass (target) { if (target.scrollTop <= 5) { this.groupsScrolledClass = 'scrolled-top' } else if (target.scrollTop >= target.scrollTopMax - 5) { @@ -49,6 +69,28 @@ const EmojiPicker = { } else { this.groupsScrolledClass = 'scrolled-middle' } + }, + triggerLoadMore (target) { + const ref = this.$refs['group-end-custom'][0] + if (!ref) return + const bottom = ref.offsetTop + ref.offsetHeight + + const scrollerBottom = target.scrollTop + target.clientHeight + const scrollerTop = target.scrollTop + const scrollerMax = target.scrollHeight + + // Loads more emoji when they come into view + const approachingBottom = bottom - scrollerBottom < LOAD_EMOJI_MARGIN + // Always load when at the very top in case there's no scroll space yet + const atTop = scrollerTop < 5 + // Don't load when looking at unicode category or at the very bottom + const bottomAboveViewport = bottom < scrollerTop || scrollerBottom === scrollerMax + if (!bottomAboveViewport && (approachingBottom || atTop)) { + this.loadEmoji() + } + }, + scrolledGroup (target) { + const top = target.scrollTop + 5 this.$nextTick(() => { this.emojisView.forEach(group => { const ref = this.$refs['group-' + group.id] @@ -58,22 +100,41 @@ const EmojiPicker = { }) }) }, + loadEmoji () { + const allLoaded = this.customEmojiBuffer.length === this.filteredEmoji.length + + if (allLoaded) { + return + } + + this.customEmojiBufferSlice += LOAD_EMOJI_BY + }, + startEmojiLoad (forceUpdate = false) { + if (!forceUpdate) { + this.keyword = '' + } + this.$nextTick(() => { + this.$refs['emoji-groups'].scrollTop = 0 + }) + const bufferSize = this.customEmojiBuffer.length + const bufferPrefilledAll = bufferSize === this.filteredEmoji.length + if (bufferPrefilledAll && !forceUpdate) { + return + } + this.customEmojiBufferSlice = LOAD_EMOJI_BY + }, toggleStickers () { this.showingStickers = !this.showingStickers }, setShowStickers (value) { this.showingStickers = value - }, - onStickerUploaded (e) { - this.$emit('sticker-uploaded', e) - }, - onStickerUploadFailed (e) { - this.$emit('sticker-upload-failed', e) } }, watch: { keyword () { - this.scrolledGroup() + this.customEmojiLoadAllConfirmed = false + this.onScroll() + this.startEmojiLoad(true) } }, computed: { @@ -86,15 +147,25 @@ const EmojiPicker = { } return 0 }, + filteredEmoji () { + return filterByKeyword( + this.$store.state.instance.customEmoji || [], + this.keyword + ) + }, + customEmojiBuffer () { + return this.filteredEmoji.slice(0, this.customEmojiBufferSlice) + }, emojis () { const standardEmojis = this.$store.state.instance.emoji || [] - const customEmojis = this.$store.state.instance.customEmoji || [] + const customEmojis = this.customEmojiBuffer + return [ { id: 'custom', text: this.$t('emoji.custom'), icon: 'icon-smile', - emojis: filterByKeyword(customEmojis, this.keyword) + emojis: customEmojis }, { id: 'standard', diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss index d99539b0..6608f393 100644 --- a/src/components/emoji_picker/emoji_picker.scss +++ b/src/components/emoji_picker/emoji_picker.scss @@ -6,15 +6,25 @@ position: absolute; right: 0; left: 0; - height: 320px; margin: 0 !important; z-index: 1; - .keep-open { + .keep-open, + .too-many-emoji { padding: 7px; line-height: normal; } + .too-many-emoji { + display: flex; + flex-direction: column; + } + + .keep-open-label { + padding: 0 7px; + display: flex; + } + .heading { display: flex; height: 32px; @@ -24,7 +34,7 @@ .content { display: flex; flex-direction: column; - flex: 1 1 0; + flex: 1 1 auto; min-height: 0px; } @@ -32,12 +42,16 @@ flex-grow: 1; } + .emoji-groups { + min-height: 200px; + } + .additional-tabs { border-left: 1px solid; border-left-color: $fallback--icon; border-left-color: var(--icon, $fallback--icon); padding-left: 7px; - flex: 0 0 0; + flex: 0 0 auto; } .additional-tabs, @@ -68,7 +82,7 @@ } .sticker-picker { - flex: 1 1 0 + flex: 1 1 auto } .stickers, @@ -76,7 +90,7 @@ &-content { display: flex; flex-direction: column; - flex: 1 1 0; + flex: 1 1 auto; min-height: 0; &.hidden { @@ -90,7 +104,7 @@ .emoji { &-search { padding: 5px; - flex: 0 0 0; + flex: 0 0 auto; input { width: 100%; diff --git a/src/components/emoji_picker/emoji_picker.vue b/src/components/emoji_picker/emoji_picker.vue index b974fce9..191b9fa1 100644 --- a/src/components/emoji_picker/emoji_picker.vue +++ b/src/components/emoji_picker/emoji_picker.vue @@ -47,7 +47,7 @@ ref="emoji-groups" class="emoji-groups" :class="groupsScrolledClass" - @scroll="scrolledGroup" + @scroll="onScroll" > <div v-for="group in emojisView" @@ -73,6 +73,7 @@ :src="emoji.imageUrl" > </span> + <span :ref="'group-end-' + group.id" /> </div> </div> <div class="keep-open"> |
