Forráskód Böngészése

Add search bar for emojis

JC Brand 5 éve
szülő
commit
73c8002b76
4 módosított fájl, 100 hozzáadás és 31 törlés
  1. 13 3
      sass/_emoji.scss
  2. 64 21
      src/converse-emoji-views.js
  3. 1 0
      src/headless/converse-emoji.js
  4. 22 7
      src/templates/emojis.html

+ 13 - 3
sass/_emoji.scss

@@ -21,8 +21,10 @@
                     overflow-y: hidden;
                     background: white;
                     .emoji-picker__lists {
+                        height: 100%;
                         overflow-y: auto;
                         .emoji-category__heading {
+                            cursor: auto;
                             color: var(--subdued-color);
                             font-size: var(--font-size);
                             padding: 0.5em 0 0 0.5em;
@@ -70,9 +72,17 @@
                         }
                     }
                 }
-                .emoji-category-picker {
+                .emoji-picker__header {
+                    display: flex;
+                    flex-direction: column;
                     padding-top: 0.5em;
                     background-color: var(--chat-head-color);
+                    .emoji-search {
+                        width: auto;
+                        margin: 0.25em;
+                        height: 2em;
+                        font-size: var(--font-size-small);
+                    }
                     ul {
                         display: flex;
                         flex-direction: row;
@@ -105,7 +115,7 @@
                     .emoji-skintone-picker {
                         background-color: var(--chatroom-head-color);
                     }
-                    .emoji-category-picker {
+                    .emoji-picker__header {
                         background-color: var(--chatroom-head-color);
                         .emoji-category {
                             &.picked {
@@ -145,7 +155,7 @@
                     .emoji-skintone-picker {
                         font-size: var(--font-size-small);
                     }
-                    .emoji-category-picker {
+                    .emoji-picker__header {
                         .emoji-category {
                             font-size: var(--font-size);
                         }

+ 64 - 21
src/converse-emoji-views.js

@@ -13,7 +13,7 @@ import BrowserStorage from "backbone.browserStorage";
 import bootstrap from "bootstrap.native";
 import tpl_emoji_button from "templates/emoji_button.html";
 import tpl_emojis from "templates/emojis.html";
-const { Backbone } = converse.env;
+const { Backbone, _ } = converse.env;
 const u = converse.env.utils;
 
 
@@ -106,32 +106,18 @@ converse.plugins.add('converse-emoji-views', {
         Object.assign(_converse.ChatBoxView.prototype, emoji_aware_chat_view);
 
 
-        function emojiShouldBeHidden (shortname, current_skintone, toned_emojis) {
-            // Helper method for the template which decides whether an
-            // emoji should be hidden, based on which skin tone is
-            // currently being applied.
-            if (shortname.includes('_tone')) {
-                if (!current_skintone || !shortname.includes(current_skintone)) {
-                    return true;
-                }
-            } else {
-                if (current_skintone && toned_emojis.includes(shortname)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-
         _converse.EmojiPickerView = Backbone.VDOMView.extend({
             className: 'emoji-picker__container',
             events: {
-                'click .emoji-category-picker li.emoji-category': 'chooseCategory',
+                'click .emoji-picker__header li.emoji-category': 'chooseCategory',
                 'click .emoji-skintone-picker li.emoji-skintone': 'chooseSkinTone',
-                'click .toggle-smiley ul.emoji-picker li': 'insertEmoji'
+                'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
+                'keydown .emoji-search': 'onKeyDown'
             },
 
             initialize () {
+                this.debouncedFilter = _.debounce(input => this.filter(input), 100);
+                this.model.on('change:query', this.render, this);
                 this.model.on('change:current_skintone', this.render, this);
                 this.model.on('change:current_category', () => {
                     this.render();
@@ -144,10 +130,11 @@ converse.plugins.add('converse-emoji-views', {
                 const html = tpl_emojis(
                     Object.assign(
                         this.model.toJSON(), {
+                            '__': __,
                             '_converse': _converse,
                             'emoji_categories': _converse.emoji_categories,
                             'emojis_by_category': u.getEmojisByCategory(),
-                            'shouldBeHidden': emojiShouldBeHidden,
+                            'shouldBeHidden': shortname => this.shouldBeHidden(shortname),
                             'skintones': ['tone1', 'tone2', 'tone3', 'tone4', 'tone5'],
                             'toned_emojis': _converse.emojis.toned,
                             'transform': u.getEmojiRenderer(),
@@ -158,6 +145,58 @@ converse.plugins.add('converse-emoji-views', {
                 return html;
             },
 
+            filter (input) {
+                this.model.set({'query': input.value});
+            },
+
+            onKeyDown (ev) {
+                if (ev.keyCode === _converse.keycodes.TAB) {
+                    ev.preventDefault();
+                    const match = _.find(_converse.emoji_shortnames, sn => _converse.FILTER_CONTAINS(sn, ev.target.value));
+                    if (match) {
+                        // XXX: Ideally we would set `query` on the model and
+                        // then let the view re-render, instead of doing it
+                        // manually here. Unfortunately this doesn't work, the
+                        // value gets set on the HTML element, but is not
+                        // visible to the new user.
+                        ev.target.value = match;
+                        this.filter(ev.target);
+                    }
+                } else if (ev.keyCode === _converse.keycodes.ENTER) {
+                    ev.preventDefault();
+                    ev.stopPropagation();
+                    if (_converse.emoji_shortnames.includes(ev.target.value)) {
+                        this.chatview.insertIntoTextArea(ev.target.value);
+                        // XXX: See above
+                        ev.target.value = '';
+                        this.filter(ev.target);
+                    }
+                } else {
+                    this.debouncedFilter(ev.target);
+                }
+            },
+
+            shouldBeHidden (shortname) {
+                // Helper method for the template which decides whether an
+                // emoji should be hidden, based on which skin tone is
+                // currently being applied.
+                const current_skintone = this.model.get('current_skintone');
+                if (shortname.includes('_tone')) {
+                    if (!current_skintone || !shortname.includes(current_skintone)) {
+                        return true;
+                    }
+                } else {
+                    if (current_skintone && _converse.emojis.toned.includes(shortname)) {
+                        return true;
+                    }
+                }
+                const query = this.model.get('query');
+                if (query && !_converse.FILTER_CONTAINS(shortname, query)) {
+                    return true;
+                }
+                return false;
+            },
+
             getTonedShortname (shortname) {
                 if (_converse.emojis.toned.includes(shortname) && this.model.get('current_skintone')) {
                     return `${shortname.slice(0, shortname.length-1)}_${this.model.get('current_skintone')}:`
@@ -197,6 +236,10 @@ converse.plugins.add('converse-emoji-views', {
                 ev.preventDefault();
                 ev.stopPropagation();
                 const target = ev.target.nodeName === 'IMG' ? ev.target.parentElement : ev.target;
+                // XXX: See above
+                const input = this.el.querySelector('.emoji-search');
+                input.value = '';
+                this.filter(input);
                 this.chatview.insertIntoTextArea(target.getAttribute('data-emoji'));
             }
         });

+ 1 - 0
src/headless/converse-emoji.js

@@ -382,6 +382,7 @@ converse.plugins.add('converse-emoji', {
         await fetchEmojiJSON();
         _converse.emojis.shortnames_regex = new RegExp("<object[^>]*>.*?<\/object>|<span[^>]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|("+getShortNames()+")", "gi");
         _converse.emojis_list = Object.values(_converse.emojis.json);
+        _converse.emoji_shortnames = _converse.emojis_list.map(m => m.sn);
 
         const excluded_categories = ['modifier', 'regional'];
         _converse.emojis.all_categories = _converse.emojis_list

+ 22 - 7
src/templates/emojis.html

@@ -1,5 +1,6 @@
 <div class="emoji-picker__container">
-    <div class="emoji-category-picker">
+    <div class="emoji-picker__header">
+        <input class="form-control emoji-search" name="emoji-search" placeholder="{{{o.__('Search')}}}"/>
         <ul>
             {[ Object.keys(o.emoji_categories).forEach(function (category) { ]}
                 <li data-category="{{{category}}}" class="emoji-category {[ if (o.current_category === category) { ]} picked {[ } ]}">
@@ -8,18 +9,32 @@
             {[ }); ]}
         </ul>
     </div>
+
+
     <div class="emoji-picker__lists">
-        {[ Object.keys(o.emoji_categories).forEach(function (category) { ]}
-            <a id="emoji-picker-{{{category}}}" class="emoji-category__heading">{{{o._converse.emoji_category_labels[category]}}}</a>
-            <ul class="emoji-picker emoji-picker-{{{category}}}">
-                {[ o.emojis_by_category[category].forEach(function (emoji) { ]}
-                <li class="emoji insert-emoji {[ if (o.shouldBeHidden(emoji.sn, o.current_skintone, o.toned_emojis)) { ]} hidden {[ }; ]}"
+        {[ if (o.query) { ]}
+            <a id="emoji-picker-search-results" class="emoji-category__heading">{{{o.__('Search results')}}}</a>
+            <ul class="emoji-picker">
+                {[ o._converse.emojis_list.forEach(function (emoji) { ]}
+                <li class="emoji insert-emoji {[ if (o.shouldBeHidden(emoji.sn)) { ]} hidden {[ }; ]}"
                     data-emoji="{{{emoji.sn}}}" title="{{{emoji.sn}}}">
                         <a href="#" data-emoji="{{{emoji.sn}}}"> {{ o.transform(emoji.sn) }}  </a>
                 </li>
                 {[ }); ]}
             </ul>
-        {[ }); ]}
+        {[ } else { ]}
+            {[ Object.keys(o.emoji_categories).forEach(function (category) { ]}
+                <a id="emoji-picker-{{{category}}}" class="emoji-category__heading">{{{o._converse.emoji_category_labels[category]}}}</a>
+                <ul class="emoji-picker">
+                    {[ o.emojis_by_category[category].forEach(function (emoji) { ]}
+                    <li class="emoji insert-emoji {[ if (o.shouldBeHidden(emoji.sn)) { ]} hidden {[ }; ]}"
+                        data-emoji="{{{emoji.sn}}}" title="{{{emoji.sn}}}">
+                            <a href="#" data-emoji="{{{emoji.sn}}}"> {{ o.transform(emoji.sn) }}  </a>
+                    </li>
+                    {[ }); ]}
+                </ul>
+            {[ }); ]}
+        {[ } ]}
     </div>
     <div class="emoji-skintone-picker">
         <label>Skin tone</label>