From 9c89eaf70cb0e30912c6c1d81460238ea97f1842 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sun, 14 Mar 2021 21:36:05 +0200 Subject: [PATCH 01/32] Add accessibility properties to emoji picker. It now works with VoiceOver, it is possible to loop through the picker elements and "click" them. It does not work with regular keyboard control (no Voice Over, just arrow keys). This is actually expected, and the parent project [change](https://github.com/missive/emoji-mart/pull/284) also states that: > should I now be able to type, then navigate results via arrow keys? Yes, in terms of improving the accessibility of emoji-mart, I didn't make it a full autocomplete pattern where you can press the arrow keys to navigate between results. You have to just use the regions to move around after typing into the search bar. Certainly not a perfect design a11y-wise, but it's at least usable with a keyboard, whereas before it wasn't. --- css/emoji-mart.css | 9 ++- src/components/Emoji.vue | 6 +- src/components/Picker.vue | 61 ++++++++++-------- src/components/anchors.vue | 51 ++++++++------- src/components/category.vue | 120 ++++++++++++++++++++---------------- src/components/search.vue | 8 ++- src/utils/emoji-data.js | 5 ++ src/utils/shared-props.js | 4 ++ 8 files changed, 161 insertions(+), 103 deletions(-) diff --git a/css/emoji-mart.css b/css/emoji-mart.css index ae32816c..52a72eae 100644 --- a/css/emoji-mart.css +++ b/css/emoji-mart.css @@ -22,6 +22,9 @@ position: relative; display: inline-block; font-size: 0; + border: none; + background: none; + box-shadow: none; } .emoji-mart-emoji span { @@ -99,6 +102,9 @@ padding: 12px 4px; overflow: hidden; transition: color .1s ease-out; + border: none; + background: none; + box-shadow: none; } .emoji-mart-anchor:hover, .emoji-mart-anchor-selected { @@ -192,8 +198,9 @@ /* position: -webkit-sticky; */ } -.emoji-mart-category-label span { +.emoji-mart-category-label h3 { display: block; + font-size: 16px; width: 100%; font-weight: 500; padding: 5px 6px; diff --git a/src/components/Emoji.vue b/src/components/Emoji.vue index fe26475e..7a2ce92f 100644 --- a/src/components/Emoji.vue +++ b/src/components/Emoji.vue @@ -1,7 +1,9 @@ diff --git a/src/components/category.vue b/src/components/category.vue index 9c5e26c2..18435e92 100644 --- a/src/components/category.vue +++ b/src/components/category.vue @@ -1,68 +1,83 @@ diff --git a/src/components/search.vue b/src/components/search.vue index 11eeefcb..17916fbd 100644 --- a/src/components/search.vue +++ b/src/components/search.vue @@ -1,6 +1,12 @@ diff --git a/src/utils/emoji-data.js b/src/utils/emoji-data.js index 71abc4a0..f84a658c 100644 --- a/src/utils/emoji-data.js +++ b/src/utils/emoji-data.js @@ -500,6 +500,10 @@ export class EmojiData { y = Math.round(multiply * this._data.sheet_y * 100) / 100 return `${x}% ${y}%` } + + ariaLabel() { + return [this.native].concat(this.short_names).filter(Boolean).join(', ') + } } export class EmojiView { @@ -523,6 +527,7 @@ export class EmojiView { this.cssStyle = this._cssStyle(emojiSize) this.content = this._content() this.title = emojiTooltip === true ? emoji.short_name : null + this.ariaLabel = emoji.ariaLabel() Object.freeze(this) } diff --git a/src/utils/shared-props.js b/src/utils/shared-props.js index 8fb7d5d9..b57b5336 100644 --- a/src/utils/shared-props.js +++ b/src/utils/shared-props.js @@ -26,6 +26,10 @@ const EmojiProps = { type: Number, default: null, }, + tag: { + type: String, + default: 'span', + }, } const PickerProps = { From 6213ef2a18f849772676372ad6c55f9c58712cd5 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sun, 14 Mar 2021 23:46:29 +0200 Subject: [PATCH 02/32] Implement arrow left/right to select emojis. It is incomplete yet and does not work when search is active (when something is in the search field, arrows move cursor inside the field). --- css/emoji-mart.css | 134 ++++++++++++++++++++++++------------ src/components/Picker.vue | 70 +++++++++++++++++-- src/components/category.vue | 26 +++++-- src/components/search.vue | 66 +++++++++++++----- src/utils/emoji-data.js | 5 +- 5 files changed, 228 insertions(+), 73 deletions(-) diff --git a/css/emoji-mart.css b/css/emoji-mart.css index 52a72eae..acfcf8e0 100644 --- a/css/emoji-mart.css +++ b/css/emoji-mart.css @@ -5,7 +5,7 @@ } .emoji-mart { - font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif; + font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif; font-size: 16px; /* display: inline-block; */ display: flex; @@ -38,7 +38,9 @@ } .emoji-type-native { - font-family: "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji", "Twemoji Mozilla", "Noto Color Emoji", "EmojiOne Color", "Android Emoji"; + font-family: 'Segoe UI Emoji', 'Segoe UI Symbol', 'Segoe UI', + 'Apple Color Emoji', 'Twemoji Mozilla', 'Noto Color Emoji', 'EmojiOne Color', + 'Android Emoji'; word-break: keep-all; } @@ -49,16 +51,16 @@ background-size: 5700%; } .emoji-type-image.emoji-set-apple { - background-image: url("https://unpkg.com/emoji-datasource-apple@5.0.1/img/apple/sheets-256/64.png"); + background-image: url('https://unpkg.com/emoji-datasource-apple@5.0.1/img/apple/sheets-256/64.png'); } .emoji-type-image.emoji-set-facebook { - background-image: url("https://unpkg.com/emoji-datasource-facebook@5.0.1/img/facebook/sheets-256/64.png"); + background-image: url('https://unpkg.com/emoji-datasource-facebook@5.0.1/img/facebook/sheets-256/64.png'); } .emoji-type-image.emoji-set-google { - background-image: url("https://unpkg.com/emoji-datasource-google@5.0.1/img/google/sheets-256/64.png"); + background-image: url('https://unpkg.com/emoji-datasource-google@5.0.1/img/google/sheets-256/64.png'); } .emoji-type-image.emoji-set-twitter { - background-image: url("https://unpkg.com/emoji-datasource-twitter@5.0.1/img/twitter/sheets-256/64.png"); + background-image: url('https://unpkg.com/emoji-datasource-twitter@5.0.1/img/twitter/sheets-256/64.png'); } .emoji-mart-bar { @@ -101,7 +103,7 @@ text-align: center; padding: 12px 4px; overflow: hidden; - transition: color .1s ease-out; + transition: color 0.1s ease-out; border: none; background: none; box-shadow: none; @@ -117,8 +119,10 @@ .emoji-mart-anchor-bar { position: absolute; - bottom: -3px; left: 0; - width: 100%; height: 3px; + bottom: -3px; + left: 0; + width: 100%; + height: 3px; background-color: #464646; } @@ -151,7 +155,7 @@ font-size: 16px; display: block; width: 100%; - padding: .2em .6em; + padding: 0.2em 0.6em; border-radius: 25px; border: 1px solid #d9d9d9; outline: 0; @@ -172,18 +176,21 @@ cursor: default; } -.emoji-mart-category .emoji-mart-emoji:hover:before { +.emoji-mart-category .emoji-mart-emoji:hover:before, +.emoji-mart-emoji-selected:before { z-index: 0; - content: ""; + content: ''; position: absolute; - top: 0; left: 0; - width: 100%; height: 100%; + top: 0; + left: 0; + width: 100%; + height: 100%; background-color: #f4f4f4; border-radius: 100%; opacity: 0; } - -.emoji-mart-category .emoji-mart-emoji:hover:before { +.emoji-mart-category .emoji-mart-emoji:hover:before, +.emoji-mart-emoji-selected:before { opacity: 1; } @@ -205,7 +212,7 @@ font-weight: 500; padding: 5px 6px; background-color: #fff; - background-color: rgba(255, 255, 255, .95); + background-color: rgba(255, 255, 255, 0.95); } .emoji-mart-emoji { @@ -224,7 +231,7 @@ display: none; } .emoji-mart-no-results .emoji-mart-no-results-label { - margin-top: .2em; + margin-top: 0.2em; } .emoji-mart-no-results .emoji-mart-emoji:hover:before { content: none; @@ -248,7 +255,8 @@ } .emoji-mart-preview-data { - left: 68px; right: 12px; + left: 68px; + right: 12px; word-break: break-all; } @@ -268,7 +276,7 @@ .emoji-mart-preview-shortname + .emoji-mart-preview-shortname, .emoji-mart-preview-shortname + .emoji-mart-preview-emoticon, .emoji-mart-preview-emoticon + .emoji-mart-preview-emoticon { - margin-left: .5em; + margin-left: 0.5em; } .emoji-mart-preview-emoticon { @@ -286,7 +294,7 @@ } .emoji-mart-title-label { - color: #999A9C; + color: #999a9c; font-size: 21px; font-weight: 300; } @@ -305,7 +313,7 @@ } .emoji-mart-skin-swatches-opened .emoji-mart-skin-swatch-selected:after { - opacity: .75; + opacity: 0.75; } .emoji-mart-skin-swatch { @@ -313,16 +321,28 @@ width: 0; vertical-align: middle; transition-property: width, padding; - transition-duration: .125s; + transition-duration: 0.125s; transition-timing-function: ease-out; } -.emoji-mart-skin-swatch:nth-child(1) { transition-delay: 0s } -.emoji-mart-skin-swatch:nth-child(2) { transition-delay: .03s } -.emoji-mart-skin-swatch:nth-child(3) { transition-delay: .06s } -.emoji-mart-skin-swatch:nth-child(4) { transition-delay: .09s } -.emoji-mart-skin-swatch:nth-child(5) { transition-delay: .12s } -.emoji-mart-skin-swatch:nth-child(6) { transition-delay: .15s } +.emoji-mart-skin-swatch:nth-child(1) { + transition-delay: 0s; +} +.emoji-mart-skin-swatch:nth-child(2) { + transition-delay: 0.03s; +} +.emoji-mart-skin-swatch:nth-child(3) { + transition-delay: 0.06s; +} +.emoji-mart-skin-swatch:nth-child(4) { + transition-delay: 0.09s; +} +.emoji-mart-skin-swatch:nth-child(5) { + transition-delay: 0.12s; +} +.emoji-mart-skin-swatch:nth-child(6) { + transition-delay: 0.15s; +} .emoji-mart-skin-swatch-selected { position: relative; @@ -330,32 +350,46 @@ padding: 0 2px; } .emoji-mart-skin-swatch-selected:after { - content: ""; + content: ''; position: absolute; - top: 50%; left: 50%; - width: 4px; height: 4px; + top: 50%; + left: 50%; + width: 4px; + height: 4px; margin: -2px 0 0 -2px; background-color: #fff; border-radius: 100%; pointer-events: none; opacity: 0; - transition: opacity .2s ease-out; + transition: opacity 0.2s ease-out; } .emoji-mart-skin { display: inline-block; - width: 100%; padding-top: 100%; + width: 100%; + padding-top: 100%; max-width: 12px; border-radius: 100%; } -.emoji-mart-skin-tone-1 { background-color: #ffc93a } -.emoji-mart-skin-tone-2 { background-color: #fadcbc } -.emoji-mart-skin-tone-3 { background-color: #e0bb95 } -.emoji-mart-skin-tone-4 { background-color: #bf8f68 } -.emoji-mart-skin-tone-5 { background-color: #9b643d } -.emoji-mart-skin-tone-6 { background-color: #594539 } - +.emoji-mart-skin-tone-1 { + background-color: #ffc93a; +} +.emoji-mart-skin-tone-2 { + background-color: #fadcbc; +} +.emoji-mart-skin-tone-3 { + background-color: #e0bb95; +} +.emoji-mart-skin-tone-4 { + background-color: #bf8f68; +} +.emoji-mart-skin-tone-5 { + background-color: #9b643d; +} +.emoji-mart-skin-tone-6 { + background-color: #594539; +} /* vue-virtual-scroller/dist/vue-virtual-scroller.css */ .emoji-mart .vue-recycle-scroller { @@ -385,17 +419,23 @@ left: 0; will-change: transform; } -.emoji-mart .vue-recycle-scroller.direction-vertical .vue-recycle-scroller__item-wrapper { +.emoji-mart + .vue-recycle-scroller.direction-vertical + .vue-recycle-scroller__item-wrapper { width: 100%; } -.emoji-mart .vue-recycle-scroller.direction-horizontal .vue-recycle-scroller__item-wrapper { +.emoji-mart + .vue-recycle-scroller.direction-horizontal + .vue-recycle-scroller__item-wrapper { height: 100%; } -.emoji-mart .vue-recycle-scroller.ready.direction-vertical +.emoji-mart + .vue-recycle-scroller.ready.direction-vertical .vue-recycle-scroller__item-view { width: 100%; } -.emoji-mart .vue-recycle-scroller.ready.direction-horizontal +.emoji-mart + .vue-recycle-scroller.ready.direction-horizontal .vue-recycle-scroller__item-view { height: 100%; } @@ -424,3 +464,7 @@ pointer-events: none; z-index: -1; } +.hidden { + display: none; + visibility: hidden; +} diff --git a/src/components/Picker.vue b/src/components/Picker.vue index e3d2a0f9..725f72b0 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -26,6 +26,8 @@ :auto-focus="autoFocus" :on-search="onSearch" @search="onSearch" + @arrowLeft="onArrowLeft" + @arrowRight="onArrowRight" /> @@ -35,10 +37,7 @@ ref="scroll" @scroll="onScroll" > -
+
= 0) { + return this.categories[this.previewEmojiCategoryIdx] + } + return null + }, }, created() { this.categories = [] @@ -252,10 +261,42 @@ export default { }, onEmojiEnter(emoji) { this.previewEmoji = emoji + this.previewEmojiIdx = -1 + this.previewEmojiCategoryIdx = -1 }, onEmojiLeave(emoji) { this.previewEmoji = null }, + onArrowLeft() { + if (this.previewEmojiIdx > 0) { + this.previewEmojiIdx -= 1 + } else { + this.previewEmojiCategoryIdx -= 1 + if (this.previewEmojiCategoryIdx < 0) { + this.previewEmojiCategoryIdx = 0 + } else { + this.previewEmojiIdx = + this.categories[this.previewEmojiCategoryIdx].emojis.length - 1 + } + } + this.updatePreviewEmoji() + }, + onArrowRight() { + if ( + this.previewEmojiIdx < + this.emojisLength(this.previewEmojiCategoryIdx) - 1 + ) { + this.previewEmojiIdx += 1 + } else { + this.previewEmojiCategoryIdx += 1 + if (this.previewEmojiCategoryIdx >= this.categories.length) { + this.previewEmojiCategoryIdx = this.categories.length - 1 + } else { + this.previewEmojiIdx = 0 + } + } + this.updatePreviewEmoji() + }, onEmojiClick(emoji) { this.$emit('select', emoji) frequently.add(emoji) @@ -275,6 +316,27 @@ export default { // Vue 3 does not support $refs as array. return component }, + updatePreviewEmoji() { + if (this.searchEmojis) { + this.previewEmoji = this.data.emoji( + this.searchEmojis[this.previewEmojiIdx], + ) + } else { + this.previewEmoji = this.categories[ + this.previewEmojiCategoryIdx + ].emojis[this.previewEmojiIdx] + } + }, + emojisLength(categoryIdx) { + if (this.searchEmojis) { + return this.searchEmojis.length + } else { + if (categoryIdx == -1) { + return 0 + } + return this.categories[categoryIdx].emojis.length + } + }, }, components: { Anchors, diff --git a/src/components/category.vue b/src/components/category.vue index 18435e92..529f7067 100644 --- a/src/components/category.vue +++ b/src/components/category.vue @@ -24,15 +24,14 @@ :key="emojiObject.id" :title="emojiView.title" class="emoji-mart-emoji" + :class="activeClass(emojiObject)" @mouseenter="emojiProps.onEnter(emojiView.getEmoji())" @mouseleave="emojiProps.onLeave(emojiView.getEmoji())" @click="emojiProps.onClick(emojiView.getEmoji())" > - {{ emojiView.content }} + {{ + emojiView.content + }} @@ -79,6 +78,23 @@ export default { required: true, }, }, + methods: { + activeClass: function(emojiObject) { + if (!this.emojiProps.selectedEmoji) { + return '' + } + if (!this.emojiProps.selectedEmojiCategory) { + return '' + } + if ( + this.emojiProps.selectedEmoji.id == emojiObject.id && + this.emojiProps.selectedEmojiCategory.id == this.id + ) { + return 'emoji-mart-emoji-selected' + } + return '' + }, + }, computed: { isVisible() { return !!this.emojis diff --git a/src/components/search.vue b/src/components/search.vue index 17916fbd..6b703fe5 100644 --- a/src/components/search.vue +++ b/src/components/search.vue @@ -1,55 +1,86 @@ diff --git a/src/utils/emoji-data.js b/src/utils/emoji-data.js index f84a658c..6f163b02 100644 --- a/src/utils/emoji-data.js +++ b/src/utils/emoji-data.js @@ -502,7 +502,10 @@ export class EmojiData { } ariaLabel() { - return [this.native].concat(this.short_names).filter(Boolean).join(', ') + return [this.native] + .concat(this.short_names) + .filter(Boolean) + .join(', ') } } From 8a006e6fe894c75ff7b7d73298d38445d06d8649 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sat, 20 Mar 2021 13:29:12 +0200 Subject: [PATCH 03/32] Add Enter handler --- src/components/Picker.vue | 5 +++++ src/components/search.vue | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index 725f72b0..55318353 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -28,6 +28,7 @@ @search="onSearch" @arrowLeft="onArrowLeft" @arrowRight="onArrowRight" + @enter="onEnter" /> @@ -297,6 +298,10 @@ export default { } this.updatePreviewEmoji() }, + onEnter(emoji) { + this.$emit('select', this.previewEmoji) + frequently.add(emoji) + }, onEmojiClick(emoji) { this.$emit('select', emoji) frequently.add(emoji) diff --git a/src/components/search.vue b/src/components/search.vue index 6b703fe5..8fb1952b 100644 --- a/src/components/search.vue +++ b/src/components/search.vue @@ -57,7 +57,7 @@ export default { type: Function, required: false, }, - onArrowEnter: { + onEnter: { type: Function, required: false, }, From 54fed884f282b97a53a076863d31e3676916beea Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sat, 20 Mar 2021 13:58:01 +0200 Subject: [PATCH 04/32] Add up and down arrow handlers --- src/components/Picker.vue | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index 55318353..63cc7a25 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -28,6 +28,8 @@ @search="onSearch" @arrowLeft="onArrowLeft" @arrowRight="onArrowRight" + @arrowDown="onArrowDown" + @arrowUp="onArrowUp" @enter="onEnter" /> @@ -298,6 +300,30 @@ export default { } this.updatePreviewEmoji() }, + onArrowDown() { + const categoryLength = this.categories[this.previewEmojiCategoryIdx] + .emojis.length + let diff = 10 + if (this.previewEmojiIdx + diff > categoryLength) { + diff = categoryLength - this.previewEmojiIdx + } + for (let i = 0; i < diff; i++) { + this.onArrowRight() + } + this.updatePreviewEmoji() + }, + onArrowUp() { + let diff = 10 + if (this.previewEmojiIdx - diff < 0) { + const prevCategoryLastRowLength = this.categories[this.previewEmojiCategoryIdx-1] + .emojis.length % 10 + diff = this.previewEmojiIdx + prevCategoryLastRowLength + } + for (let i = 0; i < diff; i++) { + this.onArrowLeft() + } + this.updatePreviewEmoji() + }, onEnter(emoji) { this.$emit('select', this.previewEmoji) frequently.add(emoji) From f69ddb05db6e46e1738d5451f14f838f3030b959 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sat, 20 Mar 2021 17:46:34 +0200 Subject: [PATCH 05/32] Handle edge cases --- src/components/Picker.vue | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index 63cc7a25..2bf5e1f4 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -301,6 +301,10 @@ export default { this.updatePreviewEmoji() }, onArrowDown() { + if (this.previewEmojiIdx == -1) { + return this.onArrowRight() + } + const categoryLength = this.categories[this.previewEmojiCategoryIdx] .emojis.length let diff = 10 @@ -315,9 +319,13 @@ export default { onArrowUp() { let diff = 10 if (this.previewEmojiIdx - diff < 0) { - const prevCategoryLastRowLength = this.categories[this.previewEmojiCategoryIdx-1] - .emojis.length % 10 - diff = this.previewEmojiIdx + prevCategoryLastRowLength + if (this.previewEmojiCategoryIdx > 0) { + const prevCategoryLastRowLength = + this.categories[this.previewEmojiCategoryIdx - 1].emojis.length % 10 + diff = this.previewEmojiIdx + prevCategoryLastRowLength + } else { + diff = 0 + } } for (let i = 0; i < diff; i++) { this.onArrowLeft() @@ -326,7 +334,7 @@ export default { }, onEnter(emoji) { this.$emit('select', this.previewEmoji) - frequently.add(emoji) + frequently.add(this.previewEmoji) }, onEmojiClick(emoji) { this.$emit('select', emoji) From e1d5760bfee52ac454740329728cb4e1c2bb6b62 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sun, 28 Mar 2021 18:26:01 +0300 Subject: [PATCH 06/32] Scroll to active emoji --- src/components/Picker.vue | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index 0274dd43..f0674336 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -365,6 +365,20 @@ export default { this.previewEmojiCategoryIdx ].emojis[this.previewEmojiIdx] } + + const emojiEl = document.querySelector('.emoji-mart-emoji-selected') + const scrollEl = document.querySelector('.emoji-mart-scroll') + const scrollHeight = scrollEl.offsetTop - scrollEl.offsetHeight + if ( + emojiEl && + emojiEl.offsetTop + emojiEl.offsetHeight > + scrollHeight + scrollEl.scrollTop + ) { + scrollEl.scrollTop += emojiEl.offsetHeight + } + if (emojiEl && emojiEl.offsetTop < scrollEl.scrollTop) { + scrollEl.scrollTop -= emojiEl.offsetHeight + } }, emojisLength(categoryIdx) { if (this.searchEmojis) { From fe33e0cf2790d6e553f6dcab222ecded7cbc6c2f Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sun, 28 Mar 2021 18:54:52 +0300 Subject: [PATCH 07/32] Keyboard navigation when search is active. Unify search and non-search categories. --- src/components/Picker.vue | 96 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index f0674336..b1c734c7 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -5,7 +5,7 @@ :data="data" :i18n="mergedI18n" :color="color" - :categories="categories" + :categories="_categories" :active-category="activeCategory" @click="onAnchorClick" /> @@ -41,21 +41,9 @@ @scroll="onScroll" >
- { + if (this.searchEmojis) { + return [ + { + id: 'search', + name: 'Search', + emojis: this.searchEmojis, + }, + ] + } + return this._categories.filter((category) => { let hasEmojis = category.emojis.length > 0 return hasEmojis }) @@ -196,21 +193,21 @@ export default { }, previewEmojiCategory() { if (this.previewEmojiCategoryIdx >= 0) { - return this.categories[this.previewEmojiCategoryIdx] + return this.filteredCategories[this.previewEmojiCategoryIdx] } return null }, }, created() { - this.categories = [] - this.categories.push(...this.data.categories()) - this.categories = this.categories.filter((category) => { + this._categories = [] + this._categories.push(...this.data.categories()) + this._categories = this._categories.filter((category) => { return category.emojis.length > 0 }) - this.categories[0].first = true - Object.freeze(this.categories) - this.activeCategory = this.categories[0] + this._categories[0].first = true + Object.freeze(this._categories) + this.activeCategory = this._categories[0] this.skipScrollUpdate = false }, methods: { @@ -261,6 +258,9 @@ export default { onSearch(value) { let emojis = this.data.search(value, this.maxSearchResults) this.searchEmojis = emojis + this.previewEmojiCategoryIdx = 0 + this.previewEmojiIdx = 0 + this.updatePreviewEmoji() }, onEmojiEnter(emoji) { this.previewEmoji = emoji @@ -279,7 +279,8 @@ export default { this.previewEmojiCategoryIdx = 0 } else { this.previewEmojiIdx = - this.categories[this.previewEmojiCategoryIdx].emojis.length - 1 + this.filteredCategories[this.previewEmojiCategoryIdx].emojis + .length - 1 } } this.updatePreviewEmoji() @@ -292,8 +293,8 @@ export default { this.previewEmojiIdx += 1 } else { this.previewEmojiCategoryIdx += 1 - if (this.previewEmojiCategoryIdx >= this.categories.length) { - this.previewEmojiCategoryIdx = this.categories.length - 1 + if (this.previewEmojiCategoryIdx >= this.filteredCategories.length) { + this.previewEmojiCategoryIdx = this.filteredCategories.length - 1 } else { this.previewEmojiIdx = 0 } @@ -305,8 +306,9 @@ export default { return this.onArrowRight() } - const categoryLength = this.categories[this.previewEmojiCategoryIdx] - .emojis.length + const categoryLength = this.filteredCategories[ + this.previewEmojiCategoryIdx + ].emojis.length let diff = 10 if (this.previewEmojiIdx + diff > categoryLength) { diff = categoryLength - this.previewEmojiIdx @@ -321,7 +323,8 @@ export default { if (this.previewEmojiIdx - diff < 0) { if (this.previewEmojiCategoryIdx > 0) { const prevCategoryLastRowLength = - this.categories[this.previewEmojiCategoryIdx - 1].emojis.length % 10 + this.filteredCategories[this.previewEmojiCategoryIdx - 1].emojis + .length % 10 diff = this.previewEmojiIdx + prevCategoryLastRowLength } else { diff = 0 @@ -356,15 +359,20 @@ export default { return component }, updatePreviewEmoji() { - if (this.searchEmojis) { - this.previewEmoji = this.data.emoji( - this.searchEmojis[this.previewEmojiIdx], - ) - } else { - this.previewEmoji = this.categories[ - this.previewEmojiCategoryIdx - ].emojis[this.previewEmojiIdx] - } + // if (this.searchEmojis) { + // this.previewEmoji = this.data.emoji( + // this.searchEmojis[this.previewEmojiIdx], + // ) + // } else { + this.previewEmoji = this.filteredCategories[ + this.previewEmojiCategoryIdx + ].emojis[this.previewEmojiIdx] + console.log( + this.previewEmojiCategoryIdx, + this.previewEmojiIdx, + this.previewEmoji.native, + ) + // } const emojiEl = document.querySelector('.emoji-mart-emoji-selected') const scrollEl = document.querySelector('.emoji-mart-scroll') @@ -381,14 +389,14 @@ export default { } }, emojisLength(categoryIdx) { - if (this.searchEmojis) { - return this.searchEmojis.length - } else { - if (categoryIdx == -1) { - return 0 - } - return this.categories[categoryIdx].emojis.length + // if (this.searchEmojis) { + // return this.searchEmojis.length + // } else { + if (categoryIdx == -1) { + return 0 } + return this.filteredCategories[categoryIdx].emojis.length + // } }, }, components: { From f5d1ce7de8badbc499fdcb5fb46219501a5dcd7a Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sun, 28 Mar 2021 18:58:19 +0300 Subject: [PATCH 08/32] Update aria description for input --- src/components/search.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/search.vue b/src/components/search.vue index 8fb1952b..724043df 100644 --- a/src/components/search.vue +++ b/src/components/search.vue @@ -16,7 +16,7 @@ v-model="value" />
From 4bcacad2353337910eaa033a4d475322fc0ee131 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sun, 28 Mar 2021 21:42:29 +0300 Subject: [PATCH 09/32] Prevent cursor movements --- src/components/Picker.vue | 22 +++++++++------------- src/components/search.vue | 4 ++-- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index b1c734c7..4c92c33c 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -270,7 +270,8 @@ export default { onEmojiLeave(emoji) { this.previewEmoji = null }, - onArrowLeft() { + onArrowLeft($event) { + const oldIdx = this.previewEmojiIdx if (this.previewEmojiIdx > 0) { this.previewEmojiIdx -= 1 } else { @@ -283,6 +284,10 @@ export default { .length - 1 } } + if ($event && this.previewEmojiIdx !== oldIdx) { + // Prevent cursor movement inside the input + $event.preventDefault() + } this.updatePreviewEmoji() }, onArrowRight() { @@ -318,7 +323,7 @@ export default { } this.updatePreviewEmoji() }, - onArrowUp() { + onArrowUp($event) { let diff = 10 if (this.previewEmojiIdx - diff < 0) { if (this.previewEmojiCategoryIdx > 0) { @@ -333,6 +338,8 @@ export default { for (let i = 0; i < diff; i++) { this.onArrowLeft() } + // Prevent cursor movement inside the input + $event.preventDefault() this.updatePreviewEmoji() }, onEnter(emoji) { @@ -359,20 +366,9 @@ export default { return component }, updatePreviewEmoji() { - // if (this.searchEmojis) { - // this.previewEmoji = this.data.emoji( - // this.searchEmojis[this.previewEmojiIdx], - // ) - // } else { this.previewEmoji = this.filteredCategories[ this.previewEmojiCategoryIdx ].emojis[this.previewEmojiIdx] - console.log( - this.previewEmojiCategoryIdx, - this.previewEmojiIdx, - this.previewEmoji.native, - ) - // } const emojiEl = document.querySelector('.emoji-mart-emoji-selected') const scrollEl = document.querySelector('.emoji-mart-scroll') diff --git a/src/components/search.vue b/src/components/search.vue index 724043df..f3a867a7 100644 --- a/src/components/search.vue +++ b/src/components/search.vue @@ -8,10 +8,10 @@ aria-owns="emoji-mart-list" aria-label="Search for an emoji" aria-describedby="emoji-mart-search-description" - @keydown.left="() => $emit('arrowLeft')" + @keydown.left="($event) => $emit('arrowLeft', $event)" @keydown.right="() => $emit('arrowRight')" @keydown.down="() => $emit('arrowDown')" - @keydown.up="() => $emit('arrowUp')" + @keydown.up="($event) => $emit('arrowUp', $event)" @keydown.enter="() => $emit('enter')" v-model="value" /> From 15fc50b676d196ca640f795a87335e7f36301307 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 21:33:16 +0300 Subject: [PATCH 10/32] Fix picker tests --- spec/__snapshots__/picker-spec.js.snap | 47681 ++++++++++++++--------- spec/picker-spec.js | 58 +- src/components/Picker.vue | 3 + 3 files changed, 29372 insertions(+), 18370 deletions(-) diff --git a/spec/__snapshots__/picker-spec.js.snap b/spec/__snapshots__/picker-spec.js.snap index f6bba39e..94ed2345 100644 --- a/spec/__snapshots__/picker-spec.js.snap +++ b/spec/__snapshots__/picker-spec.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Picker renders correctly 1`] = ` -
@@ -11,13 +11,19 @@ exports[`Picker renders correctly 1`] = `
- -
+
@@ -263,20145 +315,31095 @@ exports[`Picker renders correctly 1`] = ` class="emoji-mart-search" > + +
- -
-
- - Frequently Used - -
- - - - - - - - - - - - - + Frequently Used + +
+ + + + + + + + + + + + + + +
-
-
- - Smileys & Emotion - -
- - - + + + + + + + + +
- - - - - - + Smileys & Emotion + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-
-
- - People & Body - -
- - - + + + + + + + + + + + + + +
- - - - - - + People & Body + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-
-
- - Animals & Nature - -
- - + + + + + + +
- - - - - - + Animals & Nature + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-
-
- - Food & Drink - -
- - - + + + + + + + +
- - - - - - + Food & Drink + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- - - - - - + Activity + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
- - - - - - + Travel & Places + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
- - - - - - + Objects + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
- - - - - - + Symbols + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
- - - - - - + Flags + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -20415,6 +31417,7 @@ exports[`Picker renders correctly 1`] = ` class="emoji-mart-preview-emoji" > - + `; diff --git a/spec/picker-spec.js b/spec/picker-spec.js index 1ed4fc73..09f1e1d3 100644 --- a/spec/picker-spec.js +++ b/spec/picker-spec.js @@ -43,19 +43,19 @@ describe('Picker', () => { it('renders 10 categories', () => { let categories = picker.findAll(Category) - expect(categories.length).toBe(11) - // Hidden category with search results - expect(categories.at(0).vm.name).toBe('Search') - expect(categories.at(1).vm.name).toBe('Recent') - expect(categories.at(2).vm.name).toBe('Smileys & Emotion') - expect(categories.at(3).vm.name).toBe('People & Body') - expect(categories.at(4).vm.name).toBe('Animals & Nature') - expect(categories.at(5).vm.name).toBe('Food & Drink') - expect(categories.at(6).vm.name).toBe('Activities') - expect(categories.at(7).vm.name).toBe('Travel & Places') - expect(categories.at(8).vm.name).toBe('Objects') - expect(categories.at(9).vm.name).toBe('Symbols') - expect(categories.at(10).vm.name).toBe('Flags') + expect(categories.length).toBe(10) + // // Hidden category with search results + // expect(categories.at(0).vm.name).toBe('Search') + expect(categories.at(0).vm.name).toBe('Recent') + expect(categories.at(1).vm.name).toBe('Smileys & Emotion') + expect(categories.at(2).vm.name).toBe('People & Body') + expect(categories.at(3).vm.name).toBe('Animals & Nature') + expect(categories.at(4).vm.name).toBe('Food & Drink') + expect(categories.at(5).vm.name).toBe('Activities') + expect(categories.at(6).vm.name).toBe('Travel & Places') + expect(categories.at(7).vm.name).toBe('Objects') + expect(categories.at(8).vm.name).toBe('Symbols') + expect(categories.at(9).vm.name).toBe('Flags') }) it('no error when clicking anchor when search is active', (done) => { @@ -70,7 +70,7 @@ describe('Picker', () => { expect(searchCategory.vm.id).toBe('search') let anchors = picker.find(Anchors) - let anchorsCategories = anchors.findAll('span.emoji-mart-anchor') + let anchorsCategories = anchors.findAll('.emoji-mart-anchor') let symbols = anchorsCategories.at(8) expect(symbols.element.attributes['data-title'].value).toBe('Symbols') symbols.trigger('click') @@ -105,18 +105,15 @@ describe('categories', () => { it('will not show some based upon our filter', () => { let categories = picker.findAll(Category) - expect(categories.length).toBe(3) - // Hidden category with search results - expect(categories.at(0).vm.name).toBe('Search') - expect(categories.at(0).vm.id).toBe('search') + expect(categories.length).toBe(2) // Visible cateogires - Flags and Activity - expect(categories.at(1).vm.name).toBe('Activities') - expect(categories.at(1).vm.id).toBe('activity') + expect(categories.at(0).vm.name).toBe('Activities') + expect(categories.at(0).vm.id).toBe('activity') // only 1 emoji from Activities matches - expect(categories.at(1).vm.emojis.length).toBe(1) - expect(categories.at(2).vm.name).toBe('Flags') - expect(categories.at(2).vm.id).toBe('flags') - expect(categories.at(2).vm.emojis.length).toBe(250) + expect(categories.at(0).vm.emojis.length).toBe(1) + expect(categories.at(1).vm.name).toBe('Flags') + expect(categories.at(1).vm.id).toBe('flags') + expect(categories.at(1).vm.emojis.length).toBe(250) }) }) @@ -155,11 +152,10 @@ describe('categories include allows to select and order categories', () => { // Note: the error is printed into console. it('will not throw an error if default emoji is not available', () => { let categories = picker.findAll(Category) - expect(categories.length).toBe(4) - expect(categories.at(0).vm.name).toBe('Search') - expect(categories.at(1).vm.name).toBe('Recent') - expect(categories.at(2).vm.name).toBe('Animals & Nature') - expect(categories.at(3).vm.name).toBe('Smileys & Emotion') + expect(categories.length).toBe(3) + expect(categories.at(0).vm.name).toBe('Recent') + expect(categories.at(1).vm.name).toBe('Animals & Nature') + expect(categories.at(2).vm.name).toBe('Smileys & Emotion') }) }) @@ -183,7 +179,7 @@ describe('anchors', () => { it('contains all categories', () => { let anchors = picker.find(Anchors) - let categories = anchors.findAll('span.emoji-mart-anchor') + let categories = anchors.findAll('.emoji-mart-anchor') let names = [] for (let idx = 0; idx < categories.length; idx++) { names.push(categories.at(idx).element.attributes['data-title'].value) @@ -206,7 +202,7 @@ describe('anchors', () => { it('can be clicked to scroll to the category', async () => { let anchors = picker.find(Anchors) - let anchorsCategories = anchors.findAll('span.emoji-mart-anchor') + let anchorsCategories = anchors.findAll('.emoji-mart-anchor') let symbols = anchorsCategories.at(8) expect(symbols.element.attributes['data-title'].value).toBe('Symbols') diff --git a/src/components/Picker.vue b/src/components/Picker.vue index 4c92c33c..d66cf119 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -372,6 +372,9 @@ export default { const emojiEl = document.querySelector('.emoji-mart-emoji-selected') const scrollEl = document.querySelector('.emoji-mart-scroll') + + if (!scrollEl) return + const scrollHeight = scrollEl.offsetTop - scrollEl.offsetHeight if ( emojiEl && From 92cfdd6b172cc5c953e8a42f765f7fb7f76a1fd2 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 21:34:21 +0300 Subject: [PATCH 11/32] Update emoji test snapshots --- spec/__snapshots__/emoji-spec.js.snap | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/__snapshots__/emoji-spec.js.snap b/spec/__snapshots__/emoji-spec.js.snap index f02587f0..bd3d2a1b 100644 --- a/spec/__snapshots__/emoji-spec.js.snap +++ b/spec/__snapshots__/emoji-spec.js.snap @@ -2,6 +2,7 @@ exports[`Emoji native renders correctly native emoji 1`] = ` Date: Mon, 5 Apr 2021 21:37:08 +0300 Subject: [PATCH 12/32] Update virtual scroll picker tests --- .../picker-virtual-scroll-spec.js.snap | 11023 ++++++++++++---- spec/picker-virtual-scroll-spec.js | 4 +- 2 files changed, 8803 insertions(+), 2224 deletions(-) diff --git a/spec/__snapshots__/picker-virtual-scroll-spec.js.snap b/spec/__snapshots__/picker-virtual-scroll-spec.js.snap index 9c7965b0..bfb267dd 100644 --- a/spec/__snapshots__/picker-virtual-scroll-spec.js.snap +++ b/spec/__snapshots__/picker-virtual-scroll-spec.js.snap @@ -11,13 +11,18 @@ exports[`Picker renders correctly 1`] = `
- -
+
@@ -264,9 +316,22 @@ exports[`Picker renders correctly 1`] = ` class="emoji-mart-search" > + +
@@ -287,20 +352,29 @@ exports[`Picker renders correctly 1`] = `
-
- +

Frequently Used - +

- - - + -
+
-
- +

Smileys & Emotion - +

- - - + -
+
-
- +

People & Body - +

- - - + -
+
-
- +

Animals & Nature - +

- - - + -
+
-
- +

Food & Drink - +

- - - + -
+
-
- +

Activity - +

- - - + -
+
-
- +

Travel & Places - +

- - - + -
+
@@ -12371,6 +18949,7 @@ exports[`Picker renders correctly 1`] = ` class="emoji-mart-preview-emoji" > { it('contains all categories', () => { let anchors = picker.find(Anchors) - let categories = anchors.findAll('span.emoji-mart-anchor') + let categories = anchors.findAll('.emoji-mart-anchor') let names = [] for (let idx = 0; idx < categories.length; idx++) { names.push(categories.at(idx).element.attributes['data-title'].value) @@ -168,7 +168,7 @@ describe('anchors', () => { it('can be clicked to scroll to the category', () => { let anchors = picker.find(Anchors) - let anchorsCategories = anchors.findAll('span.emoji-mart-anchor') + let anchorsCategories = anchors.findAll('.emoji-mart-anchor') let symbols = anchorsCategories.at(8) expect(symbols.element.attributes['data-title'].value).toBe('Symbols') From ae6c43c27649b9fbdcca34c1db261e1943c53220 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 21:41:25 +0300 Subject: [PATCH 13/32] Fix frequently used emojis test. --- spec/frequently-spec.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/frequently-spec.js b/spec/frequently-spec.js index d45c551b..e2ef9857 100644 --- a/spec/frequently-spec.js +++ b/spec/frequently-spec.js @@ -14,8 +14,8 @@ describe('Picker frequnt category', () => { it('Has default emojis initially', () => { let categories = picker.findAll(Category) - expect(categories.at(1).vm.name).toBe('Recent') - expect(categories.at(1).vm.emojis.length).toBe(16) + expect(categories.at(0).vm.name).toBe('Recent') + expect(categories.at(0).vm.emojis.length).toBe(16) const DEFAULTS = [ '+1', @@ -36,8 +36,8 @@ describe('Picker frequnt category', () => { 'hankey', ] - for (let idx in categories.at(1).vm.emojis) { - let emoji = categories.at(1).vm.emojis[idx] + for (let idx in categories.at(0).vm.emojis) { + let emoji = categories.at(0).vm.emojis[idx] expect(emoji.id).toBe(DEFAULTS[idx]) } }) @@ -93,7 +93,7 @@ describe('Picker frequnt category', () => { // Wait for picker to be rendered. picker.vm.$nextTick(() => { let categories = newPicker.findAll(Category) - let recent = categories.at(1).vm + let recent = categories.at(0).vm expect(recent.emojis[0].id).toBe('nerd_face') expect(recent.emojis[1].id).toBe('space_invader') From 99d8f076cb4399ab88bceb43c734cff7f1e4ac5b Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 21:44:58 +0300 Subject: [PATCH 14/32] Fix search test --- spec/search-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/search-spec.js b/spec/search-spec.js index 5b5f3a29..b69a4404 100644 --- a/spec/search-spec.js +++ b/spec/search-spec.js @@ -60,7 +60,7 @@ describe('search', () => { expect(searchCategory.vm.id).toBe('search') let anchors = picker.find(Anchors) - let anchorsCategories = anchors.findAll('span.emoji-mart-anchor') + let anchorsCategories = anchors.findAll('.emoji-mart-anchor') let symbols = anchorsCategories.at(8) expect(symbols.element.attributes['data-title'].value).toBe('Symbols') symbols.trigger('click') From 8e554b4a059e26c95e32b19d4da3948d09018eb3 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 21:47:00 +0300 Subject: [PATCH 15/32] Add comment, remove commented out code --- src/components/Picker.vue | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index d66cf119..73acb8be 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -285,8 +285,8 @@ export default { } } if ($event && this.previewEmojiIdx !== oldIdx) { - // Prevent cursor movement inside the input - $event.preventDefault() + // Prevent cursor movement inside the input + $event.preventDefault() } this.updatePreviewEmoji() }, @@ -370,6 +370,7 @@ export default { this.previewEmojiCategoryIdx ].emojis[this.previewEmojiIdx] + // Scroll the view if the `previewEmoji` goes out of the visible area. const emojiEl = document.querySelector('.emoji-mart-emoji-selected') const scrollEl = document.querySelector('.emoji-mart-scroll') @@ -388,14 +389,10 @@ export default { } }, emojisLength(categoryIdx) { - // if (this.searchEmojis) { - // return this.searchEmojis.length - // } else { if (categoryIdx == -1) { return 0 } return this.filteredCategories[categoryIdx].emojis.length - // } }, }, components: { From 7abe9e600493c8198728128f96c268eeeb875016 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 22:27:41 +0300 Subject: [PATCH 16/32] Add tests for down and up arrows --- spec/keyboard-spec.js | 62 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 spec/keyboard-spec.js diff --git a/spec/keyboard-spec.js b/spec/keyboard-spec.js new file mode 100644 index 00000000..32891179 --- /dev/null +++ b/spec/keyboard-spec.js @@ -0,0 +1,62 @@ +import { mount } from '@vue/test-utils' + +import { Picker, Search } from '../src/components' +import data from '../data/all.json' +import { EmojiIndex } from '../src/utils/emoji-data' + +describe('Picker keyboard control', () => { + let index = new EmojiIndex(data) + let picker = null + + beforeEach(() => { + picker = mount(Picker, { + propsData: { + data: index, + }, + }) + }) + + it('Arrow down selects emoji below', () => { + expect(picker.vm.previewEmoji).toEqual(null) + + const search = picker.find(Search) + const input = search.find('input') + input.trigger('click') + + input.trigger('keydown.down') + expect(picker.vm.previewEmoji.native).toEqual('👍') + expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + + input.trigger('keydown.down') + expect(picker.vm.previewEmoji.native).toEqual('😒') + expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + + input.trigger('keydown.down') + expect(picker.vm.previewEmoji.native).toEqual('😀') + expect(picker.vm.previewEmojiCategory.id).toEqual('smileys') + }) + + it('Arrow down selects emoji above', () => { + expect(picker.vm.previewEmoji).toEqual(null) + + const search = picker.find(Search) + const input = search.find('input') + + input.trigger('click') + input.trigger('keydown.down') + input.trigger('keydown.down') + input.trigger('keydown.down') + + expect(picker.vm.previewEmoji.native).toEqual('😀') + expect(picker.vm.previewEmojiCategory.id).toEqual('smileys') + + input.trigger('keydown.up') + expect(picker.vm.previewEmoji.native).toEqual('😒') + expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + + input.trigger('keydown.up') + expect(picker.vm.previewEmoji.native).toEqual('👍') + expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + }) + +}) From a1414c0b529c252494b08152a6bc6063600dbcb1 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 22:31:09 +0300 Subject: [PATCH 17/32] Add test for Enter (select emoji) --- spec/keyboard-spec.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/spec/keyboard-spec.js b/spec/keyboard-spec.js index 32891179..37838bfa 100644 --- a/spec/keyboard-spec.js +++ b/spec/keyboard-spec.js @@ -59,4 +59,30 @@ describe('Picker keyboard control', () => { expect(picker.vm.previewEmojiCategory.id).toEqual('recent') }) + it('Enter selects the emoji', () => { + expect(picker.vm.previewEmoji).toEqual(null) + + const search = picker.find(Search) + const input = search.find('input') + input.trigger('click') + + input.trigger('keydown.down') + input.trigger('keydown.down') + input.trigger('keydown.down') + + expect(picker.vm.previewEmoji.native).toEqual('😀') + expect(picker.vm.previewEmojiCategory.id).toEqual('smileys') + + input.trigger('keydown.enter') + + let events = picker.emitted().select + expect(events.length).toBe(1) + let emojiData = events[0][0] + expect(emojiData).toBe(index.emoji('grinning')) + expect(emojiData.id).toBe('grinning') + expect(emojiData.name).toBe('Grinning Face') + expect(emojiData.colons).toBe(':grinning:') + expect(emojiData.native).toBe('😀') + }) + }) From 670094fa205ba49cfbd00e9623b8a296cccea0bd Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 22:35:01 +0300 Subject: [PATCH 18/32] Add test for left and right arrows --- spec/keyboard-spec.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/spec/keyboard-spec.js b/spec/keyboard-spec.js index 37838bfa..11649ff5 100644 --- a/spec/keyboard-spec.js +++ b/spec/keyboard-spec.js @@ -59,6 +59,49 @@ describe('Picker keyboard control', () => { expect(picker.vm.previewEmojiCategory.id).toEqual('recent') }) + it('Arrow right selects emoji on the right', () => { + expect(picker.vm.previewEmoji).toEqual(null) + + const search = picker.find(Search) + const input = search.find('input') + input.trigger('click') + + input.trigger('keydown.right') + expect(picker.vm.previewEmoji.native).toEqual('👍') + expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + + input.trigger('keydown.right') + expect(picker.vm.previewEmoji.native).toEqual('😀') + expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + + input.trigger('keydown.right') + expect(picker.vm.previewEmoji.native).toEqual('😘') + expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + }) + + it('Arrow left selects emoji on the left', () => { + expect(picker.vm.previewEmoji).toEqual(null) + + const search = picker.find(Search) + const input = search.find('input') + + input.trigger('click') + input.trigger('keydown.right') + input.trigger('keydown.right') + input.trigger('keydown.right') + + expect(picker.vm.previewEmoji.native).toEqual('😘') + expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + + input.trigger('keydown.left') + expect(picker.vm.previewEmoji.native).toEqual('😀') + expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + + input.trigger('keydown.left') + expect(picker.vm.previewEmoji.native).toEqual('👍') + expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + }) + it('Enter selects the emoji', () => { expect(picker.vm.previewEmoji).toEqual(null) From 1c42e2ffc2b397f1ce3b22aa3dac671ae23c80b3 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 23:20:46 +0300 Subject: [PATCH 19/32] Use $ref instead of the document.querySelector. --- src/components/Picker.vue | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index 73acb8be..52b66a6c 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -40,7 +40,12 @@ ref="scroll" @scroll="onScroll" > -
+
Date: Mon, 5 Apr 2021 23:21:32 +0300 Subject: [PATCH 20/32] Use `perLine` property for row length instead of the hard-coded --- src/components/Picker.vue | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index 52b66a6c..c77769f5 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -165,6 +165,11 @@ export default { calculateWidth() { return this.perLine * (this.emojiSize + 12) + 12 + 2 + measureScrollbar() }, + // emojisPerRow() { + // const listEl = this.$refs.scrollContent + // const emojiEl = listEl.querySelector('.emoji-mart-emoji') + // return Math.floor(listEl.offsetWidth / emojiEl.offsetWidth) + // }, filteredCategories() { if (this.searchEmojis) { return [ @@ -319,7 +324,8 @@ export default { const categoryLength = this.filteredCategories[ this.previewEmojiCategoryIdx ].emojis.length - let diff = 10 + + let diff = this.perLine if (this.previewEmojiIdx + diff > categoryLength) { diff = categoryLength - this.previewEmojiIdx } @@ -329,7 +335,7 @@ export default { this.updatePreviewEmoji() }, onArrowUp($event) { - let diff = 10 + let diff = this.perLine if (this.previewEmojiIdx - diff < 0) { if (this.previewEmojiCategoryIdx > 0) { const prevCategoryLastRowLength = From d2f568eb69e9b539252b1599d9b9b052247dfbac Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 23:25:26 +0300 Subject: [PATCH 21/32] Fix native emoji scaling. We had 10 native emoji per line while perLine parameter is set to 9. --- src/utils/emoji-data.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/emoji-data.js b/src/utils/emoji-data.js index 74fc444f..d6484670 100644 --- a/src/utils/emoji-data.js +++ b/src/utils/emoji-data.js @@ -568,9 +568,9 @@ export class EmojiView { // Set font-size for native emoji. cssStyle = Object.assign(cssStyle, { // font-size is used for native emoji which we need - // to scale with 0.8 factor to have them look approximately - // the same size as image-based emojl. - fontSize: Math.round(emojiSize * 0.8 * 10) / 10 + 'px', + // to scale with 0.95 factor to have them look approximately + // the same size as image-based emoji. + fontSize: Math.round(emojiSize * 0.95 * 10) / 10 + 'px', }) } else { // Set width/height for image emoji. From b547e675ebf3d8b11b9ddc0862b60f0268f4b9ba Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 23:26:09 +0300 Subject: [PATCH 22/32] Fix up/down position when moving between categories. --- src/components/Picker.vue | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index c77769f5..678d2f06 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -326,8 +326,22 @@ export default { ].emojis.length let diff = this.perLine + // If we jump to the next category and current category + // has the last row shorter than `perLine`, use the + // last row length as diff, so we go straight down, + // for example: + // + // 1 2 3 4 5 6 + // 7 8 9 + // A B C D E F + // + // If we go down from `8`, we need to move 3 emojis right + // to lend at `B` (and 3 is the length of the last row of + // this category). + // And if we used 6 instead (row length, `perLine`), we would + // lend up at `E`. if (this.previewEmojiIdx + diff > categoryLength) { - diff = categoryLength - this.previewEmojiIdx + diff = categoryLength % this.perLine } for (let i = 0; i < diff; i++) { this.onArrowRight() @@ -338,10 +352,22 @@ export default { let diff = this.perLine if (this.previewEmojiIdx - diff < 0) { if (this.previewEmojiCategoryIdx > 0) { + // If the previous category is shorter and does not extend to + // the end of row, use the last row length as diff, so + // we go straight up, for example: + // + // 1 2 3 4 5 + // 6 7 8 + // 9 A B C D + // + // If we go up from `A`, we need to move 3 emojis left to get + // to `7` (and 3 is the length of the last row of the previous + // category). const prevCategoryLastRowLength = this.filteredCategories[this.previewEmojiCategoryIdx - 1].emojis - .length % 10 - diff = this.previewEmojiIdx + prevCategoryLastRowLength + .length % this.perLine + // diff = this.previewEmojiIdx + prevCategoryLastRowLength + diff = prevCategoryLastRowLength } else { diff = 0 } From b8d26a5f8884ce1695e4ceb8e99863e44e29c838 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 23:29:16 +0300 Subject: [PATCH 23/32] Fix keyboard tests. --- spec/keyboard-spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/keyboard-spec.js b/spec/keyboard-spec.js index 11649ff5..79a6417a 100644 --- a/spec/keyboard-spec.js +++ b/spec/keyboard-spec.js @@ -28,7 +28,7 @@ describe('Picker keyboard control', () => { expect(picker.vm.previewEmojiCategory.id).toEqual('recent') input.trigger('keydown.down') - expect(picker.vm.previewEmoji.native).toEqual('😒') + expect(picker.vm.previewEmoji.native).toEqual('😞') expect(picker.vm.previewEmojiCategory.id).toEqual('recent') input.trigger('keydown.down') @@ -51,7 +51,7 @@ describe('Picker keyboard control', () => { expect(picker.vm.previewEmojiCategory.id).toEqual('smileys') input.trigger('keydown.up') - expect(picker.vm.previewEmoji.native).toEqual('😒') + expect(picker.vm.previewEmoji.native).toEqual('😞') expect(picker.vm.previewEmojiCategory.id).toEqual('recent') input.trigger('keydown.up') From cb35915cfd27fde8f9384c5f70e5a08448c937cb Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 23:42:25 +0300 Subject: [PATCH 24/32] Remove unnecessary check --- src/components/Picker.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index 678d2f06..ecf636a7 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -411,8 +411,6 @@ export default { const scrollEl = this.$refs.scroll const emojiEl = scrollEl.querySelector('.emoji-mart-emoji-selected') - if (!scrollEl) return - const scrollHeight = scrollEl.offsetTop - scrollEl.offsetHeight if ( emojiEl && From b0ebcc8669d51ece3997b0f737baf37cd636befe Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 23:42:46 +0300 Subject: [PATCH 25/32] Update font size checks for native emojis. --- spec/picker-spec.js | 8 ++++---- spec/picker-virtual-scroll-spec.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/picker-spec.js b/spec/picker-spec.js index 09f1e1d3..d7cf9aab 100644 --- a/spec/picker-spec.js +++ b/spec/picker-spec.js @@ -352,9 +352,9 @@ describe('emjoiSize', () => { let emoji = picker.find('[data-title="+1"]') // The inner span with applied inline style. let emojiSpan = emoji.element.childNodes[0] - // Font-size is 80% of width/height value. + // Font-size is 95% of width/height value. expect(emojiSpan.style.cssText).toBe( - 'background-position: 22.81% 49.12%; font-size: 19.2px;', + 'background-position: 22.81% 49.12%; font-size: 22.8px;', ) }) @@ -363,9 +363,9 @@ describe('emjoiSize', () => { let emoji = picker.find('[data-title="+1"]') // The inner span with applied inline style. let emojiSpan = emoji.element.childNodes[0] - // Font-size is 80% of width/height value. + // Font-size is 95% of width/height value. expect(emojiSpan.style.cssText).toBe( - 'background-position: 22.81% 49.12%; font-size: 16px;', + 'background-position: 22.81% 49.12%; font-size: 19px;', ) }) }) diff --git a/spec/picker-virtual-scroll-spec.js b/spec/picker-virtual-scroll-spec.js index 3478e646..c277b0e6 100644 --- a/spec/picker-virtual-scroll-spec.js +++ b/spec/picker-virtual-scroll-spec.js @@ -307,9 +307,9 @@ describe('emjoiSize', () => { let emoji = picker.find('[data-title="+1"]') // The inner span with applied inline style. let emojiSpan = emoji.element.childNodes[0] - // Font-size is 80% of width/height value. + // Font-size is 95% of width/height value. expect(emojiSpan.style.cssText).toBe( - 'background-position: 22.81% 49.12%; font-size: 19.2px;', + 'background-position: 22.81% 49.12%; font-size: 22.8px;', ) }) @@ -318,9 +318,9 @@ describe('emjoiSize', () => { let emoji = picker.find('[data-title="+1"]') // The inner span with applied inline style. let emojiSpan = emoji.element.childNodes[0] - // Font-size is 80% of width/height value. + // Font-size is 95% of width/height value. expect(emojiSpan.style.cssText).toBe( - 'background-position: 22.81% 49.12%; font-size: 16px;', + 'background-position: 22.81% 49.12%; font-size: 19px;', ) }) }) From c632c41c3b9b2d826d4cd9f567a4bf83f7570f13 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Mon, 5 Apr 2021 23:45:16 +0300 Subject: [PATCH 26/32] Formatting --- spec/keyboard-spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/keyboard-spec.js b/spec/keyboard-spec.js index 79a6417a..984186df 100644 --- a/spec/keyboard-spec.js +++ b/spec/keyboard-spec.js @@ -127,5 +127,4 @@ describe('Picker keyboard control', () => { expect(emojiData.colons).toBe(':grinning:') expect(emojiData.native).toBe('😀') }) - }) From f89c098835606515e97c8585f4a0dbc7241d1aca Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sun, 11 Apr 2021 19:01:50 +0300 Subject: [PATCH 27/32] Better scroll handling --- src/components/Picker.vue | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index ecf636a7..540731b9 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -407,21 +407,23 @@ export default { this.previewEmojiCategoryIdx ].emojis[this.previewEmojiIdx] - // Scroll the view if the `previewEmoji` goes out of the visible area. - const scrollEl = this.$refs.scroll - const emojiEl = scrollEl.querySelector('.emoji-mart-emoji-selected') + this.$nextTick(() => { + // Scroll the view if the `previewEmoji` goes out of the visible area. + const scrollEl = this.$refs.scroll + const emojiEl = scrollEl.querySelector('.emoji-mart-emoji-selected') - const scrollHeight = scrollEl.offsetTop - scrollEl.offsetHeight - if ( - emojiEl && - emojiEl.offsetTop + emojiEl.offsetHeight > - scrollHeight + scrollEl.scrollTop - ) { - scrollEl.scrollTop += emojiEl.offsetHeight - } - if (emojiEl && emojiEl.offsetTop < scrollEl.scrollTop) { - scrollEl.scrollTop -= emojiEl.offsetHeight - } + const scrollHeight = scrollEl.offsetTop - scrollEl.offsetHeight + if ( + emojiEl && + emojiEl.offsetTop + emojiEl.offsetHeight > + scrollHeight + scrollEl.scrollTop + ) { + scrollEl.scrollTop += emojiEl.offsetHeight + } + if (emojiEl && emojiEl.offsetTop < scrollEl.scrollTop) { + scrollEl.scrollTop -= emojiEl.offsetHeight + } + }) }, emojisLength(categoryIdx) { if (categoryIdx == -1) { From 3ea8d26d151f3feda5251a6dc7ca4c3adffdaaa5 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sun, 11 Apr 2021 22:57:12 +0300 Subject: [PATCH 28/32] Extract component logic --- src/components/Picker.vue | 226 ++++------------------------------- src/utils/picker.js | 242 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+), 201 deletions(-) create mode 100644 src/utils/picker.js diff --git a/src/components/Picker.vue b/src/components/Picker.vue index 540731b9..61f2be90 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -5,8 +5,8 @@ :data="data" :i18n="mergedI18n" :color="color" - :categories="_categories" - :active-category="activeCategory" + :categories="view.allCategories" + :active-category="view.activeCategory" @click="onAnchorClick" />
@@ -47,8 +47,8 @@ aria-expanded="true" > { - let hasEmojis = category.emojis.length > 0 - return hasEmojis - }) + return this.view.filteredCategories }, mergedI18n() { return Object.freeze(deepMerge(I18N, this.i18n)) @@ -201,24 +186,9 @@ export default { return this.data.firstEmoji() } }, - previewEmojiCategory() { - if (this.previewEmojiCategoryIdx >= 0) { - return this.filteredCategories[this.previewEmojiCategoryIdx] - } - return null - }, }, created() { - this._categories = [] - this._categories.push(...this.data.categories()) - this._categories = this._categories.filter((category) => { - return category.emojis.length > 0 - }) - - this._categories[0].first = true - Object.freeze(this._categories) - this.activeCategory = this._categories[0] - this.skipScrollUpdate = false + this.view = new PickerView(this) }, methods: { onScroll() { @@ -229,159 +199,42 @@ export default { }, onScrollPaint() { this.waitingForPaint = false - let scrollTop = this.$refs.scroll.scrollTop - let activeCategory = this.filteredCategories[0] - for (let i = 0, l = this.filteredCategories.length; i < l; i++) { - let category = this.filteredCategories[i] - let component = this.getCategoryComponent(i) - // The `-50` offset switches active category (selected in the - // anchors bar) a bit eariler, before it actually reaches the top. - if (component && component.$el.offsetTop - 50 > scrollTop) { - break - } - activeCategory = category - } - this.activeCategory = activeCategory + this.view.onScroll() }, onAnchorClick(category) { - if (this.searchEmojis) { - // No categories are shown when search is active. - return - } - let i = this.filteredCategories.indexOf(category) - let component = this.getCategoryComponent(i) - let scrollToComponent = () => { - if (component) { - let top = component.$el.offsetTop - if (category.first) { - top = 0 - } - this.$refs.scroll.scrollTop = top - } - } - if (this.infiniteScroll) { - scrollToComponent() - } else { - this.activeCategory = this.filteredCategories[i] - } + this.view.onAnchorClick(category) }, onSearch(value) { - let emojis = this.data.search(value, this.maxSearchResults) - this.searchEmojis = emojis - this.previewEmojiCategoryIdx = 0 - this.previewEmojiIdx = 0 - this.updatePreviewEmoji() + this.view.onSearch(value) }, onEmojiEnter(emoji) { - this.previewEmoji = emoji - this.previewEmojiIdx = -1 - this.previewEmojiCategoryIdx = -1 + this.view.onEmojiEnter(emoji) }, onEmojiLeave(emoji) { - this.previewEmoji = null + this.view.onEmojiLeave(emoji) }, onArrowLeft($event) { - const oldIdx = this.previewEmojiIdx - if (this.previewEmojiIdx > 0) { - this.previewEmojiIdx -= 1 - } else { - this.previewEmojiCategoryIdx -= 1 - if (this.previewEmojiCategoryIdx < 0) { - this.previewEmojiCategoryIdx = 0 - } else { - this.previewEmojiIdx = - this.filteredCategories[this.previewEmojiCategoryIdx].emojis - .length - 1 - } - } - if ($event && this.previewEmojiIdx !== oldIdx) { + const oldIdx = this.view.previewEmojiIdx + this.view.onArrowLeft() + if ($event && this.view.previewEmojiIdx !== oldIdx) { // Prevent cursor movement inside the input $event.preventDefault() } - this.updatePreviewEmoji() }, onArrowRight() { - if ( - this.previewEmojiIdx < - this.emojisLength(this.previewEmojiCategoryIdx) - 1 - ) { - this.previewEmojiIdx += 1 - } else { - this.previewEmojiCategoryIdx += 1 - if (this.previewEmojiCategoryIdx >= this.filteredCategories.length) { - this.previewEmojiCategoryIdx = this.filteredCategories.length - 1 - } else { - this.previewEmojiIdx = 0 - } - } - this.updatePreviewEmoji() + this.view.onArrowRight() }, onArrowDown() { - if (this.previewEmojiIdx == -1) { - return this.onArrowRight() - } - - const categoryLength = this.filteredCategories[ - this.previewEmojiCategoryIdx - ].emojis.length - - let diff = this.perLine - // If we jump to the next category and current category - // has the last row shorter than `perLine`, use the - // last row length as diff, so we go straight down, - // for example: - // - // 1 2 3 4 5 6 - // 7 8 9 - // A B C D E F - // - // If we go down from `8`, we need to move 3 emojis right - // to lend at `B` (and 3 is the length of the last row of - // this category). - // And if we used 6 instead (row length, `perLine`), we would - // lend up at `E`. - if (this.previewEmojiIdx + diff > categoryLength) { - diff = categoryLength % this.perLine - } - for (let i = 0; i < diff; i++) { - this.onArrowRight() - } - this.updatePreviewEmoji() + this.view.onArrowDown() }, onArrowUp($event) { - let diff = this.perLine - if (this.previewEmojiIdx - diff < 0) { - if (this.previewEmojiCategoryIdx > 0) { - // If the previous category is shorter and does not extend to - // the end of row, use the last row length as diff, so - // we go straight up, for example: - // - // 1 2 3 4 5 - // 6 7 8 - // 9 A B C D - // - // If we go up from `A`, we need to move 3 emojis left to get - // to `7` (and 3 is the length of the last row of the previous - // category). - const prevCategoryLastRowLength = - this.filteredCategories[this.previewEmojiCategoryIdx - 1].emojis - .length % this.perLine - // diff = this.previewEmojiIdx + prevCategoryLastRowLength - diff = prevCategoryLastRowLength - } else { - diff = 0 - } - } - for (let i = 0; i < diff; i++) { - this.onArrowLeft() - } + this.view.onArrowUp() // Prevent cursor movement inside the input $event.preventDefault() - this.updatePreviewEmoji() }, onEnter(emoji) { - this.$emit('select', this.previewEmoji) - frequently.add(this.previewEmoji) + this.$emit('select', this.view.previewEmoji) + frequently.add(this.view.previewEmoji) }, onEmojiClick(emoji) { this.$emit('select', emoji) @@ -402,35 +255,6 @@ export default { // Vue 3 does not support $refs as array. return component }, - updatePreviewEmoji() { - this.previewEmoji = this.filteredCategories[ - this.previewEmojiCategoryIdx - ].emojis[this.previewEmojiIdx] - - this.$nextTick(() => { - // Scroll the view if the `previewEmoji` goes out of the visible area. - const scrollEl = this.$refs.scroll - const emojiEl = scrollEl.querySelector('.emoji-mart-emoji-selected') - - const scrollHeight = scrollEl.offsetTop - scrollEl.offsetHeight - if ( - emojiEl && - emojiEl.offsetTop + emojiEl.offsetHeight > - scrollHeight + scrollEl.scrollTop - ) { - scrollEl.scrollTop += emojiEl.offsetHeight - } - if (emojiEl && emojiEl.offsetTop < scrollEl.scrollTop) { - scrollEl.scrollTop -= emojiEl.offsetHeight - } - }) - }, - emojisLength(categoryIdx) { - if (categoryIdx == -1) { - return 0 - } - return this.filteredCategories[categoryIdx].emojis.length - }, }, components: { Anchors, diff --git a/src/utils/picker.js b/src/utils/picker.js new file mode 100644 index 00000000..9cbfc97f --- /dev/null +++ b/src/utils/picker.js @@ -0,0 +1,242 @@ +export class PickerView { + constructor(pickerComponent) { + this._vm = pickerComponent + this._data = pickerComponent.data + this._perLine = pickerComponent.perLine + + this._categories = [] + this._categories.push(...this._data.categories()) + this._categories = this._categories.filter((category) => { + return category.emojis.length > 0 + }) + + this._categories[0].first = true + Object.freeze(this._categories) + + this.activeCategory = this._categories[0] + this.searchEmojis = null + + this.previewEmoji = null + this.previewEmojiCategoryIdx = 0 + this.previewEmojiIdx = -1 + } + + onScroll() { + const scrollElement = this._vm.$refs.scroll + const scrollTop = scrollElement.scrollTop + + let activeCategory = this.filteredCategories[0] + for (let i = 0, l = this.filteredCategories.length; i < l; i++) { + let category = this.filteredCategories[i] + let component = this._vm.getCategoryComponent(i) + // The `-50` offset switches active category (selected in the + // anchors bar) a bit eariler, before it actually reaches the top. + if (component && component.$el.offsetTop - 50 > scrollTop) { + break + } + activeCategory = category + } + this.activeCategory = activeCategory + } + + get allCategories() { + return this._categories + } + + get filteredCategories() { + if (this.searchEmojis) { + return [ + { + id: 'search', + name: 'Search', + emojis: this.searchEmojis, + }, + ] + } + return this._categories.filter((category) => { + let hasEmojis = category.emojis.length > 0 + return hasEmojis + }) + } + + get previewEmojiCategory() { + if (this.previewEmojiCategoryIdx >= 0) { + return this.filteredCategories[this.previewEmojiCategoryIdx] + } + return null + } + + onAnchorClick(category) { + if (this.searchEmojis) { + // No categories are shown when search is active. + return + } + let i = this.filteredCategories.indexOf(category) + let component = this._vm.getCategoryComponent(i) + let scrollToComponent = () => { + if (component) { + let top = component.$el.offsetTop + if (category.first) { + top = 0 + } + this._vm.$refs.scroll.scrollTop = top + } + } + if (this._vm.infiniteScroll) { + scrollToComponent() + } else { + this.activeCategory = this.filteredCategories[i] + } + } + + onSearch(value) { + let emojis = this._data.search(value, this.maxSearchResults) + this.searchEmojis = emojis + + this.previewEmojiCategoryIdx = 0 + this.previewEmojiIdx = 0 + this.updatePreviewEmoji() + } + + onEmojiEnter(emoji) { + this.previewEmoji = emoji + this.previewEmojiIdx = -1 + this.previewEmojiCategoryIdx = -1 + } + + onEmojiLeave(emoji) { + this.previewEmoji = null + } + + onArrowLeft() { + if (this.previewEmojiIdx > 0) { + this.previewEmojiIdx -= 1 + } else { + this.previewEmojiCategoryIdx -= 1 + if (this.previewEmojiCategoryIdx < 0) { + this.previewEmojiCategoryIdx = 0 + } else { + this.previewEmojiIdx = + this.filteredCategories[this.previewEmojiCategoryIdx].emojis.length - + 1 + } + } + this.updatePreviewEmoji() + } + + onArrowRight() { + if ( + this.previewEmojiIdx < + this.emojisLength(this.previewEmojiCategoryIdx) - 1 + ) { + this.previewEmojiIdx += 1 + } else { + this.previewEmojiCategoryIdx += 1 + if (this.previewEmojiCategoryIdx >= this.filteredCategories.length) { + this.previewEmojiCategoryIdx = this.filteredCategories.length - 1 + } else { + this.previewEmojiIdx = 0 + } + } + this.updatePreviewEmoji() + } + + onArrowDown() { + if (this.previewEmojiIdx == -1) { + return this.onArrowRight() + } + + const categoryLength = this.filteredCategories[this.previewEmojiCategoryIdx] + .emojis.length + + let diff = this._perLine + // If we jump to the next category and current category + // has the last row shorter than `_perLine`, use the + // last row length as diff, so we go straight down, + // for example: + // + // 1 2 3 4 5 6 + // 7 8 9 + // A B C D E F + // + // If we go down from `8`, we need to move 3 emojis right + // to lend at `B` (and 3 is the length of the last row of + // this category). + // And if we used 6 instead (row length, `_perLine`), we would + // lend up at `E`. + if (this.previewEmojiIdx + diff > categoryLength) { + diff = categoryLength % this._perLine + } + for (let i = 0; i < diff; i++) { + this.onArrowRight() + } + this.updatePreviewEmoji() + } + + onArrowUp() { + let diff = this._perLine + if (this.previewEmojiIdx - diff < 0) { + if (this.previewEmojiCategoryIdx > 0) { + // If the previous category is shorter and does not extend to + // the end of row, use the last row length as diff, so + // we go straight up, for example: + // + // 1 2 3 4 5 + // 6 7 8 + // 9 A B C D + // + // If we go up from `A`, we need to move 3 emojis left to get + // to `7` (and 3 is the length of the last row of the previous + // category). + const prevCategoryLastRowLength = + this.filteredCategories[this.previewEmojiCategoryIdx - 1].emojis + .length % this._perLine + // diff = this.previewEmojiIdx + prevCategoryLastRowLength + diff = prevCategoryLastRowLength + } else { + diff = 0 + } + } + for (let i = 0; i < diff; i++) { + this.onArrowLeft() + } + this.updatePreviewEmoji() + } + + updatePreviewEmoji() { + this.previewEmoji = this.filteredCategories[ + this.previewEmojiCategoryIdx + ].emojis[this.previewEmojiIdx] + + this._vm.$nextTick(() => { + // Scroll the view if the `previewEmoji` goes out of the visible area. + const scrollEl = this._vm.$refs.scroll + + // Note: it would be more Vue-ish to mark all emojis with `ref`s + // and then do something similar here to what we do in the + // `getCategories` instead of using `querySelector` directly, + // but I am not sure if having many refs would affect the performance + // (it might, so I use `querySelector` for now). + const emojiEl = scrollEl.querySelector('.emoji-mart-emoji-selected') + + const scrollHeight = scrollEl.offsetTop - scrollEl.offsetHeight + if ( + emojiEl && + emojiEl.offsetTop + emojiEl.offsetHeight > + scrollHeight + scrollEl.scrollTop + ) { + scrollEl.scrollTop += emojiEl.offsetHeight + } + if (emojiEl && emojiEl.offsetTop < scrollEl.scrollTop) { + scrollEl.scrollTop -= emojiEl.offsetHeight + } + }) + } + + emojisLength(categoryIdx) { + if (categoryIdx == -1) { + return 0 + } + return this.filteredCategories[categoryIdx].emojis.length + } +} From 0ec7238d6baf07f871c70f329ffb6b430dc9b9ac Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sun, 11 Apr 2021 22:57:32 +0300 Subject: [PATCH 29/32] Update tests --- spec/keyboard-spec.js | 62 +++++++++++++++++++++---------------------- spec/picker-spec.js | 4 +-- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/spec/keyboard-spec.js b/spec/keyboard-spec.js index 984186df..57f70ecf 100644 --- a/spec/keyboard-spec.js +++ b/spec/keyboard-spec.js @@ -17,27 +17,27 @@ describe('Picker keyboard control', () => { }) it('Arrow down selects emoji below', () => { - expect(picker.vm.previewEmoji).toEqual(null) + expect(picker.vm.view.previewEmoji).toEqual(null) const search = picker.find(Search) const input = search.find('input') input.trigger('click') input.trigger('keydown.down') - expect(picker.vm.previewEmoji.native).toEqual('👍') - expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + expect(picker.vm.view.previewEmoji.native).toEqual('👍') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('recent') input.trigger('keydown.down') - expect(picker.vm.previewEmoji.native).toEqual('😞') - expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + expect(picker.vm.view.previewEmoji.native).toEqual('😞') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('recent') input.trigger('keydown.down') - expect(picker.vm.previewEmoji.native).toEqual('😀') - expect(picker.vm.previewEmojiCategory.id).toEqual('smileys') + expect(picker.vm.view.previewEmoji.native).toEqual('😀') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('smileys') }) it('Arrow down selects emoji above', () => { - expect(picker.vm.previewEmoji).toEqual(null) + expect(picker.vm.view.previewEmoji).toEqual(null) const search = picker.find(Search) const input = search.find('input') @@ -47,40 +47,40 @@ describe('Picker keyboard control', () => { input.trigger('keydown.down') input.trigger('keydown.down') - expect(picker.vm.previewEmoji.native).toEqual('😀') - expect(picker.vm.previewEmojiCategory.id).toEqual('smileys') + expect(picker.vm.view.previewEmoji.native).toEqual('😀') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('smileys') input.trigger('keydown.up') - expect(picker.vm.previewEmoji.native).toEqual('😞') - expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + expect(picker.vm.view.previewEmoji.native).toEqual('😞') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('recent') input.trigger('keydown.up') - expect(picker.vm.previewEmoji.native).toEqual('👍') - expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + expect(picker.vm.view.previewEmoji.native).toEqual('👍') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('recent') }) it('Arrow right selects emoji on the right', () => { - expect(picker.vm.previewEmoji).toEqual(null) + expect(picker.vm.view.previewEmoji).toEqual(null) const search = picker.find(Search) const input = search.find('input') input.trigger('click') input.trigger('keydown.right') - expect(picker.vm.previewEmoji.native).toEqual('👍') - expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + expect(picker.vm.view.previewEmoji.native).toEqual('👍') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('recent') input.trigger('keydown.right') - expect(picker.vm.previewEmoji.native).toEqual('😀') - expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + expect(picker.vm.view.previewEmoji.native).toEqual('😀') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('recent') input.trigger('keydown.right') - expect(picker.vm.previewEmoji.native).toEqual('😘') - expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + expect(picker.vm.view.previewEmoji.native).toEqual('😘') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('recent') }) it('Arrow left selects emoji on the left', () => { - expect(picker.vm.previewEmoji).toEqual(null) + expect(picker.vm.view.previewEmoji).toEqual(null) const search = picker.find(Search) const input = search.find('input') @@ -90,20 +90,20 @@ describe('Picker keyboard control', () => { input.trigger('keydown.right') input.trigger('keydown.right') - expect(picker.vm.previewEmoji.native).toEqual('😘') - expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + expect(picker.vm.view.previewEmoji.native).toEqual('😘') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('recent') input.trigger('keydown.left') - expect(picker.vm.previewEmoji.native).toEqual('😀') - expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + expect(picker.vm.view.previewEmoji.native).toEqual('😀') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('recent') input.trigger('keydown.left') - expect(picker.vm.previewEmoji.native).toEqual('👍') - expect(picker.vm.previewEmojiCategory.id).toEqual('recent') + expect(picker.vm.view.previewEmoji.native).toEqual('👍') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('recent') }) it('Enter selects the emoji', () => { - expect(picker.vm.previewEmoji).toEqual(null) + expect(picker.vm.view.previewEmoji).toEqual(null) const search = picker.find(Search) const input = search.find('input') @@ -113,8 +113,8 @@ describe('Picker keyboard control', () => { input.trigger('keydown.down') input.trigger('keydown.down') - expect(picker.vm.previewEmoji.native).toEqual('😀') - expect(picker.vm.previewEmojiCategory.id).toEqual('smileys') + expect(picker.vm.view.previewEmoji.native).toEqual('😀') + expect(picker.vm.view.previewEmojiCategory.id).toEqual('smileys') input.trigger('keydown.enter') diff --git a/spec/picker-spec.js b/spec/picker-spec.js index d7cf9aab..76304fa8 100644 --- a/spec/picker-spec.js +++ b/spec/picker-spec.js @@ -207,7 +207,7 @@ describe('anchors', () => { expect(symbols.element.attributes['data-title'].value).toBe('Symbols') // The `recent` category is selected initially. - expect(picker.vm.activeCategory.id).toBe('recent') + expect(picker.vm.view.activeCategory.id).toBe('recent') expect(anchors.vm.activeCategory.id).toBe('recent') symbols.trigger('click') @@ -223,7 +223,7 @@ describe('anchors', () => { // Picker change - the check below fails (although works in demo app) // scrollTop if 0 for all categories and activeCategory is changed in the // onScroll handler, need to find a way to thes this. - // expect(picker.vm.activeCategory.id).toBe('symbols') + // expect(picker.vm.view.activeCategory.id).toBe('symbols') // expect(anchors.vm.activeCategory.id).toBe('symbols') }) }) From b977e4a48721dacd1952d8cec5819c070f3082a9 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sun, 11 Apr 2021 23:18:37 +0300 Subject: [PATCH 30/32] Add comments for arrow control functions --- src/utils/picker.js | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/utils/picker.js b/src/utils/picker.js index 9cbfc97f..0bd85947 100644 --- a/src/utils/picker.js +++ b/src/utils/picker.js @@ -16,7 +16,12 @@ export class PickerView { this.activeCategory = this._categories[0] this.searchEmojis = null + // Preview emoji, shown on mouse over or when we move + // with arrow keys. this.previewEmoji = null + // Indexes are used to keep the position when moving + // with arrows: current category and current emoji + // inside the category. this.previewEmojiCategoryIdx = 0 this.previewEmojiIdx = -1 } @@ -109,13 +114,18 @@ export class PickerView { } onArrowLeft() { + // Moving left, decrease emoji index. if (this.previewEmojiIdx > 0) { this.previewEmojiIdx -= 1 } else { + // If emoji index is zero, go to the previous category. this.previewEmojiCategoryIdx -= 1 if (this.previewEmojiCategoryIdx < 0) { + // If we reached first category, keep it. this.previewEmojiCategoryIdx = 0 } else { + // Update emoji index - we moved to the previous category, + // get the last emoji in it. this.previewEmojiIdx = this.filteredCategories[this.previewEmojiCategoryIdx].emojis.length - 1 @@ -129,12 +139,17 @@ export class PickerView { this.previewEmojiIdx < this.emojisLength(this.previewEmojiCategoryIdx) - 1 ) { + // Moving right within category, increase emoji index. this.previewEmojiIdx += 1 } else { + // Go to the next category. this.previewEmojiCategoryIdx += 1 if (this.previewEmojiCategoryIdx >= this.filteredCategories.length) { + // If we reached the last category - keep it. this.previewEmojiCategoryIdx = this.filteredCategories.length - 1 } else { + // If we moved to the next category, update emoji index to the + // first emoji in the new category. this.previewEmojiIdx = 0 } } @@ -142,6 +157,9 @@ export class PickerView { } onArrowDown() { + + // If we are out of the emoji control (index is -1), select the first + // emoji in the first category by calling `onArrowRight`. if (this.previewEmojiIdx == -1) { return this.onArrowRight() } @@ -149,11 +167,14 @@ export class PickerView { const categoryLength = this.filteredCategories[this.previewEmojiCategoryIdx] .emojis.length + // When moving down, we can move `_perLine` icons right to + // jump to the same position in the next row. let diff = this._perLine - // If we jump to the next category and current category - // has the last row shorter than `_perLine`, use the - // last row length as diff, so we go straight down, - // for example: + + // Unless if we are on the last row of the category and + // there are less then `_perLine` emojis in it. + // In this case we use the last row length as diff + // so we go straight down, for example: // // 1 2 3 4 5 6 // 7 8 9 @@ -165,6 +186,7 @@ export class PickerView { // And if we used 6 instead (row length, `_perLine`), we would // lend up at `E`. if (this.previewEmojiIdx + diff > categoryLength) { + // Calculate the last row length. diff = categoryLength % this._perLine } for (let i = 0; i < diff; i++) { @@ -174,11 +196,16 @@ export class PickerView { } onArrowUp() { + // Similar to `onArrowDown`, to move up we can move left + // by `_perLine` number of emojis. let diff = this._perLine + if (this.previewEmojiIdx - diff < 0) { if (this.previewEmojiCategoryIdx > 0) { - // If the previous category is shorter and does not extend to - // the end of row, use the last row length as diff, so + // Unless if we are on the first line of the category and + // the last line in the previous category is shorter than + // `_perLine`. + // In this case we use the last row length as diff, so // we go straight up, for example: // // 1 2 3 4 5 From 83950d14916105d03ad3436995dab78f2be02908 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sat, 17 Apr 2021 15:15:37 +0300 Subject: [PATCH 31/32] Fix view property initialization. --- src/components/Picker.vue | 5 +---- src/utils/picker.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/Picker.vue b/src/components/Picker.vue index 61f2be90..ac42c441 100644 --- a/src/components/Picker.vue +++ b/src/components/Picker.vue @@ -130,7 +130,7 @@ export default { data() { return { activeSkin: this.skin || store.get('skin') || this.defaultSkin, - view: this.view, + view: new PickerView(this), } }, computed: { @@ -187,9 +187,6 @@ export default { } }, }, - created() { - this.view = new PickerView(this) - }, methods: { onScroll() { if (this.infiniteScroll && !this.waitingForPaint) { diff --git a/src/utils/picker.js b/src/utils/picker.js index 0bd85947..4cb37546 100644 --- a/src/utils/picker.js +++ b/src/utils/picker.js @@ -171,6 +171,18 @@ export class PickerView { // jump to the same position in the next row. let diff = this._perLine + // TODO: previewCategory should match activeCategory + // (so it would be both highlighted in UI and used + // when we start moving with arrows after clicking + // the category). + + // Note: probably we can alwasy take current row length + // as a `diff` - it will fit both case of any row and + // special case of the last row. + // Note: it can be also easier to update indexes + // directly here instead of calling onArrowRight. + // Same is true for `onArrowUp`. + // Unless if we are on the last row of the category and // there are less then `_perLine` emojis in it. // In this case we use the last row length as diff From 07581e9b09c656385b433e1c4b346a441028b913 Mon Sep 17 00:00:00 2001 From: Boris Serebrov Date: Sat, 17 Apr 2021 15:17:49 +0300 Subject: [PATCH 32/32] Formatting --- src/utils/picker.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/picker.js b/src/utils/picker.js index 4cb37546..75f9dc04 100644 --- a/src/utils/picker.js +++ b/src/utils/picker.js @@ -157,7 +157,6 @@ export class PickerView { } onArrowDown() { - // If we are out of the emoji control (index is -1), select the first // emoji in the first category by calling `onArrowRight`. if (this.previewEmojiIdx == -1) {