UNPKG

interweave-emoji

Version:
385 lines (304 loc) 10.6 kB
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } // Bundled with Packemon: https://packemon.dev // Platform: browser, Support: stable, Format: esm import React, { useMemo, useState, useEffect } from 'react'; import { E as EmojiDataManager } from './bundle-2bf6afbb.js'; export { E as EmojiDataManager, r as resetInstances } from './bundle-2bf6afbb.js'; import EMOJI_REGEX from 'emojibase-regex'; import EMOTICON_REGEX from 'emojibase-regex/emoticon'; import SHORTCODE_REGEX from 'emojibase-regex/shortcode'; import { Matcher } from 'interweave'; import { fetchEmojis, fetchMessages } from 'emojibase'; // We hardcode these values so that newer emojis // are not displayed for devices that do not support // them yet. Support cycle is twice a year. const MAX_EMOJI_VERSION = 14; const LATEST_DATASET_VERSION = '7.0.1'; /* eslint-disable complexity */ function Emoji({ emojiLargeSize = '3em', emojiPath = '{{hexcode}}', emojiSize = '1em', emojiSource, emoticon, enlargeEmoji = false, hexcode, renderUnicode = false, shortcode, unicode }) { const data = EmojiDataManager.getInstance(emojiSource.locale, emojiSource.version); if (process.env.NODE_ENV !== "production" && !emoticon && !shortcode && !unicode && !hexcode) { throw new Error('Emoji component requires a `unicode` character, `emoticon`, `hexcode`, or a `shortcode`.'); } // Retrieve applicable unicode character let hex = hexcode; if (!hex && shortcode) { hex = data.SHORTCODE_TO_HEXCODE[shortcode]; } if (!hex && emoticon) { hex = data.EMOTICON_TO_HEXCODE[emoticon]; } if (!hex && unicode) { hex = data.UNICODE_TO_HEXCODE[unicode]; } // Return the invalid value instead of erroring if (!hex || !data.EMOJIS[hex]) { var _ref, _ref2; return /*#__PURE__*/React.createElement("span", null, (_ref = (_ref2 = unicode !== null && unicode !== void 0 ? unicode : emoticon) !== null && _ref2 !== void 0 ? _ref2 : shortcode) !== null && _ref !== void 0 ? _ref : hex); } const emoji = data.EMOJIS[hex]; if (renderUnicode) { return /*#__PURE__*/React.createElement("span", null, emoji.unicode); } // eslint-disable-next-line react-perf/jsx-no-new-object-as-prop const styles = { display: 'inline-block', verticalAlign: 'middle' }; // Handle large styles if (enlargeEmoji && emojiLargeSize) { styles.width = emojiLargeSize; styles.height = emojiLargeSize; // Only apply styles if a size is defined } else if (emojiSize) { styles.width = emojiSize; styles.height = emojiSize; } // Determine the path let path = emojiPath || '{{hexcode}}'; path = typeof path === 'function' ? path(emoji.hexcode, { enlarged: enlargeEmoji, largeSize: emojiLargeSize, size: enlargeEmoji ? emojiLargeSize : emojiSize, smallSize: emojiSize }) : path.replace('{{hexcode}}', emoji.hexcode); // http://git.emojione.com/demos/latest/sprites-png.html // http://git.emojione.com/demos/latest/sprites-svg.html // https://css-tricks.com/using-svg/ return /*#__PURE__*/React.createElement("img", { alt: emoji.unicode, "aria-label": emoji.label, "data-emoticon": emoji.emoticon, "data-hexcode": emoji.hexcode, "data-shortcodes": emoji.canonical_shortcodes.join(', '), "data-unicode": emoji.unicode, src: path, style: styles, title: emoji.label }); } const EMOTICON_BOUNDARY_REGEX = new RegExp( // eslint-disable-next-line no-useless-escape `(^|\\\b|\\\s)(${EMOTICON_REGEX.source})(?=\\\s|\\\b|$)`); class EmojiMatcher extends Matcher { constructor(name, options, factory) { super(name, { convertEmoticon: false, convertShortcode: false, convertUnicode: false, enlargeThreshold: 1, renderUnicode: false, ...options }, factory); _defineProperty(this, "data", null); _defineProperty(this, "greedy", true); } replaceWith(children, props) { return /*#__PURE__*/React.createElement(Emoji, { ...props, renderUnicode: this.options.renderUnicode }); } asTag() { return 'img'; } match(string) { let response = null; // Should we convert emoticons to unicode? if (this.options.convertEmoticon) { response = this.matchEmoticon(string); if (response) { return response; } } // Should we convert shortcodes to unicode? if (this.options.convertShortcode) { response = this.matchShortcode(string); if (response) { return response; } } // Should we convert unicode to SVG/PNG? if (this.options.convertUnicode) { response = this.matchUnicode(string); if (response) { return response; } } return null; } matchEmoticon(string) { const response = this.doMatch(string, EMOTICON_BOUNDARY_REGEX, matches => ({ emoticon: matches[0].trim() }), true); if (response !== null && response !== void 0 && response.emoticon && this.data && this.data.EMOTICON_TO_HEXCODE[response.emoticon]) { response.hexcode = this.data.EMOTICON_TO_HEXCODE[response.emoticon]; response.match = String(response.emoticon); // Remove padding return response; } return null; } matchShortcode(string) { const response = this.doMatch(string, SHORTCODE_REGEX, matches => ({ shortcode: matches[0].toLowerCase() }), true); if (response !== null && response !== void 0 && response.shortcode && this.data && this.data.SHORTCODE_TO_HEXCODE[response.shortcode]) { response.hexcode = this.data.SHORTCODE_TO_HEXCODE[response.shortcode]; return response; } return null; } matchUnicode(string) { const response = this.doMatch(string, EMOJI_REGEX, matches => ({ unicode: matches[0] }), true); if (response !== null && response !== void 0 && response.unicode && this.data && this.data.UNICODE_TO_HEXCODE[response.unicode]) { response.hexcode = this.data.UNICODE_TO_HEXCODE[response.unicode]; return response; } return null; } /** * Load emoji data before matching. */ onBeforeParse(content, props) { if (props.emojiSource) { this.data = EmojiDataManager.getInstance(props.emojiSource.locale, props.emojiSource.version); } else if (process.env.NODE_ENV !== "production") { throw new Error('Missing emoji source data. Have you loaded with the `useEmojiData` hook and passed the `emojiSource` prop?'); } return content; } /** * When a single `Emoji` is the only content, enlarge it! */ onAfterParse(content, props) { if (content.length === 0) { return content; } const { enlargeThreshold = 1 } = this.options; let valid = false; let count = 0; // Use a for-loop, as it's much cleaner than some() for (let i = 0, item = null; i < content.length; i += 1) { item = content[i]; if (typeof item === 'string') { // Allow whitespace but disallow strings if (!item.match(/^\s+$/)) { valid = false; break; } } else if ( /*#__PURE__*/React.isValidElement(item)) { // Only count towards emojis if (item && item.type === Emoji) { count += 1; valid = true; if (count > enlargeThreshold) { valid = false; break; } // Abort early for non-emoji components } else { valid = false; break; } } else { valid = false; break; } } if (!valid) { return content; } return content.map(item => { if (!item || typeof item === 'string') { return item; } const element = item; return /*#__PURE__*/React.cloneElement(element, { ...element.props, enlargeEmoji: true }); }); } } /* eslint-disable promise/prefer-await-to-then */ function determinePresets(locale, shortcodes) { const presets = []; if (shortcodes.length === 0) { presets.push('emojibase'); } else { presets.push(...shortcodes); } if (!locale.startsWith('en') && presets.includes('emojibase') && !presets.includes('en/emojibase')) { presets.push('en/emojibase'); } return presets; } const promises = new Map(); function resetLoaded() { if (process.env.NODE_ENV !== "production") { promises.clear(); } } async function loadEmojis(locale, version, shortcodes, compact) { const key = `${locale}:${version}:${compact ? 'compact' : 'data'}`; const instance = EmojiDataManager.getInstance(locale, version); // Return as we're currently loading data if (promises.has(key)) { return promises.get(key); } // Data has been loaded elsewhere if (instance.data.length > 0) { return instance.getData(); } // Otherwise, start loading emoji data from the CDN // eslint-disable-next-line compat/compat const request = Promise.all([// @ts-expect-error Mismatched types fetchEmojis(locale, { compact, flat: false, shortcodes, version }), fetchMessages(locale, { version })]); promises.set(key, request.then(([emojis, messages]) => { instance.parseEmojiData(emojis); instance.parseMessageData(messages); return instance.getData(); })); return promises.get(key); } function useEmojiData({ avoidFetch = false, compact = false, locale = 'en', shortcodes = ['emojibase'], throwErrors = false, version = LATEST_DATASET_VERSION } = {}) { // eslint-disable-next-line react-hooks/exhaustive-deps const shortcodePresets = useMemo(() => determinePresets(locale, shortcodes), shortcodes); const [emojis, setEmojis] = useState([]); const [error, setError] = useState(); useEffect(() => { let mounted = true; if (!avoidFetch) { loadEmojis(locale, version, shortcodePresets, compact).then(loadedEmojis => { if (mounted) { setEmojis(loadedEmojis); } return loadedEmojis; }).catch(setError); } return () => { mounted = false; }; }, [avoidFetch, compact, locale, shortcodePresets, version]); if (error && throwErrors) { throw error; } return [emojis, { compact, error, locale, version }, EmojiDataManager.getInstance(locale, version)]; } export { Emoji, EmojiMatcher, LATEST_DATASET_VERSION, MAX_EMOJI_VERSION, determinePresets, resetLoaded, useEmojiData }; //# sourceMappingURL=index.js.map