Browse Source

Add types to the emoji picker

JC Brand 11 months ago
parent
commit
0908fae651

+ 61 - 21
src/shared/chat/emoji-picker.js

@@ -1,6 +1,7 @@
 /**
  * @module emoji-picker
  * @typedef {module:dom-navigator.DOMNavigatorOptions} DOMNavigatorOptions
+ * @typedef {module:dom-navigator.DOMNavigatorDirection} DOMNavigatorDirection
  */
 import debounce from 'lodash-es/debounce';
 import { api, converse, u, constants } from "@converse/headless";
@@ -44,7 +45,10 @@ export default class EmojiPicker extends CustomElement {
         this.model = null;
         this.query = '';
         this._search_results = [];
-        this.debouncedFilter = debounce(input => this.model.set({'query': input.value}), 250);
+        this.debouncedFilter = debounce(
+            /** @param {HTMLInputElement} input */(input) => this.model.set({'query': input.value}),
+            250
+        );
     }
 
     get search_results () {
@@ -70,7 +74,7 @@ export default class EmojiPicker extends CustomElement {
             'query': this.query,
             'search_results': this.search_results,
             'render_emojis': this.render_emojis,
-            'sn2Emoji': shortname => u.shortnamesToEmojis(this.getTonedShortname(shortname))
+            'sn2Emoji': /** @param {string} sn */(sn) => u.shortnamesToEmojis(this.getTonedShortname(sn))
         });
     }
 
@@ -133,6 +137,9 @@ export default class EmojiPicker extends CustomElement {
         super.disconnectedCallback();
     }
 
+    /**
+     * @param {KeyboardEvent} ev
+     */
     _onGlobalKeyDown (ev) {
         if (!this.navigator) {
             return;
@@ -149,6 +156,9 @@ export default class EmojiPicker extends CustomElement {
         }
     }
 
+    /**
+     * @param {HTMLElement} el
+     */
     setCategoryForElement (el) {
         const old_category = this.current_category;
         const category = el?.getAttribute('data-category') || old_category;
@@ -157,6 +167,9 @@ export default class EmojiPicker extends CustomElement {
         }
     }
 
+    /**
+     * @param {string} value
+     */
     insertIntoTextArea (value) {
         const autocompleting = this.model.get('autocompleting');
         const ac_position = this.model.get('ac_position');
@@ -170,11 +183,15 @@ export default class EmojiPicker extends CustomElement {
         this.dispatchEvent(new CustomEvent("emojiSelected", options));
     }
 
+    /**
+     * @param {MouseEvent} ev
+     */
     chooseSkinTone (ev) {
         ev.preventDefault();
         ev.stopPropagation();
-        const target = ev.target.nodeName === 'IMG' ? ev.target.parentElement : ev.target;
-        const skintone = target.getAttribute("data-skintone").trim();
+        const target = /** @type {Element} */(ev.target);
+        const el = target.nodeName === 'IMG' ? target.parentElement : target;
+        const skintone = el.getAttribute("data-skintone").trim();
         if (this.current_skintone === skintone) {
             this.model.save({'current_skintone': ''});
         } else {
@@ -182,20 +199,28 @@ export default class EmojiPicker extends CustomElement {
         }
     }
 
+    /**
+     * @param {MouseEvent} ev
+     */
     chooseCategory (ev) {
         ev.preventDefault && ev.preventDefault();
         ev.stopPropagation && ev.stopPropagation();
-        const el = ev.target.matches('li') ? ev.target : u.ancestor(ev.target, 'li');
+        const target = /** @type {Element} */(ev.target);
+        const el = target.matches('li') ? ev.target : u.ancestor(target, 'li');
         this.setCategoryForElement(el);
         this.navigator.select(el);
         !this.navigator.enabled && this.navigator.enable();
     }
 
+    /**
+     * @param {KeyboardEvent} ev
+     */
     onSearchInputKeyDown (ev) {
         if (ev.keyCode === KEYCODES.TAB) {
-            if (ev.target.value) {
+            const target = /** @type {HTMLInputElement} */(ev.target);
+            if (target.value) {
                 ev.preventDefault();
-                const match = converse.emojis.shortnames.find(sn => FILTER_CONTAINS(sn, ev.target.value));
+                const match = converse.emojis.shortnames.find(sn => FILTER_CONTAINS(sn, target.value));
                 match && this.model.set({'query': match});
             } else if (!this.navigator.enabled) {
                 this.enableArrowNavigation(ev);
@@ -210,25 +235,35 @@ export default class EmojiPicker extends CustomElement {
         }
     }
 
+    /**
+     * @param {KeyboardEvent} ev
+     */
     onEnterPressed (ev) {
         ev.preventDefault();
         ev.stopPropagation();
-        if (converse.emojis.shortnames.includes(ev.target.value)) {
-            this.insertIntoTextArea(ev.target.value);
+        const target = /** @type {HTMLInputElement} */(ev.target);
+        if (converse.emojis.shortnames.includes(target.value)) {
+            this.insertIntoTextArea(target.value);
         } else if (this.search_results.length === 1) {
             this.insertIntoTextArea(this.search_results[0].sn);
         } else if (this.navigator.selected && this.navigator.selected.matches('.insert-emoji')) {
             this.insertIntoTextArea(this.navigator.selected.getAttribute('data-emoji'));
         } else if (this.navigator.selected && this.navigator.selected.matches('.emoji-category')) {
-            this.chooseCategory({'target': this.navigator.selected});
+            this.chooseCategory(new MouseEvent('click', {relatedTarget: this.navigator.selected}));
         }
     }
 
+    /**
+     * @param {FocusEvent} ev
+     */
     onSearchInputFocus (ev) {
         this.chatview.emitBlurred(ev);
         this.disableArrowNavigation();
     }
 
+    /**
+     * @param {string} shortname
+     */
     getTonedShortname (shortname) {
         if (getTonedEmojis().includes(shortname) && this.current_skintone) {
             return `${shortname.slice(0, shortname.length-1)}_${this.current_skintone}:`
@@ -240,23 +275,25 @@ export default class EmojiPicker extends CustomElement {
         if (!this.navigator) {
             const default_selector = 'li:not(.hidden):not(.emoji-skintone), .emoji-search';
             const options = /** @type DOMNavigatorOptions */({
-                'jump_to_picked': '.emoji-category',
-                'jump_to_picked_selector': '.emoji-category.picked',
-                'jump_to_picked_direction': DOMNavigator.DIRECTION.down,
-                'picked_selector': '.picked',
-                'scroll_container': this.querySelector('.emoji-picker__lists'),
-                'getSelector': direction => {
-                    if (direction === DOMNavigator.DIRECTION.down) {
+                jump_to_picked: '.emoji-category',
+                jump_to_picked_selector: '.emoji-category.picked',
+                jump_to_picked_direction: DOMNavigator.DIRECTION.down,
+                picked_selector: '.picked',
+                scroll_container: this.querySelector('.emoji-picker__lists'),
+                getSelector: /** @param {keyof(DOMNavigatorDirection)} dir */(dir) => {
+                    if (dir === DOMNavigator.DIRECTION.down) {
                         const c = this.navigator.selected && this.navigator.selected.getAttribute('data-category');
                         return c ? `ul[data-category="${c}"] li:not(.hidden):not(.emoji-skintone), .emoji-search` : default_selector;
                     } else {
                         return default_selector;
                     }
                 },
-                'onSelected': el => {
-                    el.matches('.insert-emoji') && this.setCategoryForElement(el.parentElement);
-                    el.matches('.insert-emoji, .emoji-category') && el.firstElementChild.focus();
-                    el.matches('.emoji-search') && el.focus();
+                onSelected: /** @param {HTMLElement} el */(el) => {
+                    if (el.matches('.insert-emoji')) this.setCategoryForElement(el.parentElement);
+                    if (el.matches('.insert-emoji, .emoji-category')) {
+                        /** @type {HTMLInputElement} */(el.firstElementChild).focus();
+                    }
+                    if (el.matches('.emoji-search')) el.focus();
                 }
             });
             this.navigator = new DOMNavigator(this, options);
@@ -267,6 +304,9 @@ export default class EmojiPicker extends CustomElement {
         this.navigator?.disable();
     }
 
+    /**
+     * @param {KeyboardEvent} ev
+     */
     enableArrowNavigation (ev) {
         ev?.preventDefault?.();
         ev?.stopPropagation?.();

+ 0 - 1
src/shared/dom-navigator.js

@@ -73,7 +73,6 @@ class DOMNavigator {
     /**
      * Directions.
      * @returns {DOMNavigatorDirection}
-     * @constructor
      */
     static get DIRECTION () {
         return ({

+ 41 - 10
src/types/shared/chat/emoji-picker.d.ts

@@ -42,21 +42,52 @@ export default class EmojiPicker extends CustomElement {
     registerEvents(): void;
     onGlobalKeyDown: (ev: any) => void;
     connectedCallback(): void;
-    _onGlobalKeyDown(ev: any): void;
-    setCategoryForElement(el: any): void;
-    insertIntoTextArea(value: any): void;
-    chooseSkinTone(ev: any): void;
-    chooseCategory(ev: any): void;
-    onSearchInputKeyDown(ev: any): void;
-    onEnterPressed(ev: any): void;
-    onSearchInputFocus(ev: any): void;
-    getTonedShortname(shortname: any): any;
+    /**
+     * @param {KeyboardEvent} ev
+     */
+    _onGlobalKeyDown(ev: KeyboardEvent): void;
+    /**
+     * @param {HTMLElement} el
+     */
+    setCategoryForElement(el: HTMLElement): void;
+    /**
+     * @param {string} value
+     */
+    insertIntoTextArea(value: string): void;
+    /**
+     * @param {MouseEvent} ev
+     */
+    chooseSkinTone(ev: MouseEvent): void;
+    /**
+     * @param {MouseEvent} ev
+     */
+    chooseCategory(ev: MouseEvent): void;
+    /**
+     * @param {KeyboardEvent} ev
+     */
+    onSearchInputKeyDown(ev: KeyboardEvent): void;
+    /**
+     * @param {KeyboardEvent} ev
+     */
+    onEnterPressed(ev: KeyboardEvent): void;
+    /**
+     * @param {FocusEvent} ev
+     */
+    onSearchInputFocus(ev: FocusEvent): void;
+    /**
+     * @param {string} shortname
+     */
+    getTonedShortname(shortname: string): string;
     initArrowNavigation(): void;
     navigator: DOMNavigator;
     disableArrowNavigation(): void;
-    enableArrowNavigation(ev: any): void;
+    /**
+     * @param {KeyboardEvent} ev
+     */
+    enableArrowNavigation(ev: KeyboardEvent): void;
 }
 export type DOMNavigatorOptions = any;
+export type DOMNavigatorDirection = any;
 import { CustomElement } from "shared/components/element.js";
 import DOMNavigator from "shared/dom-navigator";
 //# sourceMappingURL=emoji-picker.d.ts.map

+ 5 - 0
src/types/shared/dom-navigator.d.ts

@@ -21,6 +21,11 @@ export type DOMNavigatorDirection = {
  * @class DOMNavigator
  */
 declare class DOMNavigator {
+    /**
+     * Directions.
+     * @returns {DOMNavigatorDirection}
+     */
+    static get DIRECTION(): DOMNavigatorDirection;
     /**
      * The default options for the DOM navigator.
      * @returns {{