interweave-emoji
Version:
Emoji support for Interweave.
422 lines (327 loc) • 11.5 kB
JavaScript
// Bundled with Packemon: https://packemon.dev
// Platform: browser, Support: stable, Format: lib
'use strict';
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; }
Object.defineProperties(exports, {
__esModule: {
value: true
},
[Symbol.toStringTag]: {
value: 'Module'
}
});
const React = require('react');
const EmojiDataManager = require('./bundle-9c6a7c11.js');
const EMOJI_REGEX = require('emojibase-regex');
const EMOTICON_REGEX = require('emojibase-regex/emoticon');
const SHORTCODE_REGEX = require('emojibase-regex/shortcode');
const interweave = require('interweave');
const emojibase = require('emojibase');
const _interopDefault = e => e && e.__esModule ? e : {
default: e
};
const React__default = /*#__PURE__*/_interopDefault(React);
const EMOJI_REGEX__default = /*#__PURE__*/_interopDefault(EMOJI_REGEX);
const EMOTICON_REGEX__default = /*#__PURE__*/_interopDefault(EMOTICON_REGEX);
const SHORTCODE_REGEX__default = /*#__PURE__*/_interopDefault(SHORTCODE_REGEX); // 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.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__default.default.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__default.default.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__default.default.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__default.default.source})(?=\\\s|\\\b|$)`);
class EmojiMatcher extends interweave.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__default.default.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__default.default, 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__default.default, 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.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__default.default.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__default.default.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.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
emojibase.fetchEmojis(locale, {
compact,
flat: false,
shortcodes,
version
}), emojibase.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 = React.useMemo(() => determinePresets(locale, shortcodes), shortcodes);
const [emojis, setEmojis] = React.useState([]);
const [error, setError] = React.useState();
React.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.EmojiDataManager.getInstance(locale, version)];
}
exports.EmojiDataManager = EmojiDataManager.EmojiDataManager;
exports.resetInstances = EmojiDataManager.resetInstances;
exports.Emoji = Emoji;
exports.EmojiMatcher = EmojiMatcher;
exports.LATEST_DATASET_VERSION = LATEST_DATASET_VERSION;
exports.MAX_EMOJI_VERSION = MAX_EMOJI_VERSION;
exports.determinePresets = determinePresets;
exports.resetLoaded = resetLoaded;
exports.useEmojiData = useEmojiData;
//# sourceMappingURL=index.js.map