UNPKG

converse.js

Version:
118 lines (107 loc) 4.25 kB
/** * @typedef {module:emoji-picker.EmojiPicker} EmojiPicker */ import { html } from 'lit'; import { converse, api, u } from '@converse/headless'; import { CustomElement } from 'shared/components/element.js'; import { tplAllEmojis, tplSearchResults } from './templates/emoji-picker.js'; import { getTonedEmojis } from './utils.js'; import { FILTER_CONTAINS } from 'shared/autocomplete/utils.js'; const { sizzle } = converse.env; export default class EmojiPickerContent extends CustomElement { static get properties () { return { 'search_results': { type: Array }, 'current_skintone': { type: String }, 'model': { type: Object }, 'query': { type: String }, }; } constructor () { super(); this.model = null; this.current_skintone = null; this.query = null; this.search_results = null; } render () { const props = { 'current_skintone': this.current_skintone, 'insertEmoji': /** @param {MouseEvent} ev */(ev) => this.insertEmoji(ev), 'query': this.query, 'search_results': this.search_results, 'shouldBeHidden': /** @param {string} shortname */(shortname) => this.shouldBeHidden(shortname), }; return html` <div class="emoji-picker__lists">${tplSearchResults(props)} ${tplAllEmojis(props)}</div> `; } firstUpdated () { this.initIntersectionObserver(); } initIntersectionObserver () { if (!window.IntersectionObserver) { return; } if (this.observer) { this.observer.disconnect(); } else { const options = { root: this.querySelector('.emoji-picker__lists'), threshold: [0.1], }; const handler = (ev) => this.setCategoryOnVisibilityChange(ev); this.observer = new IntersectionObserver(handler, options); } sizzle('.emoji-picker', this).forEach((a) => this.observer.observe(a)); } setCategoryOnVisibilityChange (entries) { const selected = /** @type {EmojiPicker} */(this.parentElement).navigator.selected; const intersection_with_selected = entries.filter((i) => i.target.contains(selected)).pop(); let current; // Choose the intersection that contains the currently selected // element, or otherwise the one with the largest ratio. if (intersection_with_selected) { current = intersection_with_selected; } else { current = entries.reduce((p, c) => (c.intersectionRatio >= (p?.intersectionRatio || 0) ? c : p), null); } if (current && current.isIntersecting) { const category = current.target.getAttribute('data-category'); if (category !== this.model.get('current_category')) { /** @type {EmojiPicker} */(this.parentElement).preserve_scroll = true; u.safeSave(this.model, { 'current_category': category }); } } } /** * @param {MouseEvent} ev */ insertEmoji (ev) { ev.preventDefault(); ev.stopPropagation(); const target = /** @type {HTMLElement} */(ev.target); const emoji_el = target.nodeName === 'IMG' ? target.parentElement : target; /** @type EmojiPicker */(this.parentElement).selectEmoji(emoji_el.getAttribute('data-emoji')); } /** * @param {string} shortname */ shouldBeHidden (shortname) { // 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 (!this.current_skintone || !shortname.includes(this.current_skintone)) { return true; } } else { if (this.current_skintone && getTonedEmojis().includes(shortname)) { return true; } } if (this.query && !FILTER_CONTAINS(shortname, this.query)) { return true; } return false; } } api.elements.define('converse-emoji-picker-content', EmojiPickerContent);