UNPKG

@atlaskit/editor-plugin-emoji

Version:

Emoji plugin for @atlaskit/editor-core

226 lines 9.79 kB
import _readOnlyError from "@babel/runtime/helpers/readOnlyError"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _get from "@babel/runtime/helpers/get"; import _inherits from "@babel/runtime/helpers/inherits"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } function _superPropGet(t, o, e, r) { var p = _get(_getPrototypeOf(1 & r ? t.prototype : t), o, e); return 2 & r && "function" == typeof p ? function (t) { return p.apply(e, t); } : p; } import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics'; import { SafePlugin } from '@atlaskit/editor-common/safe-plugin'; import { createRule } from '@atlaskit/editor-common/utils'; import { createPlugin, leafNodeReplacementCharacter } from '@atlaskit/prosemirror-input-rules'; var matcher; export function inputRulePlugin(schema, editorAnalyticsAPI, pluginInjectionApi, disableAutoformat) { if (disableAutoformat) { return; } if (schema.nodes.emoji) { initMatcher(pluginInjectionApi); var asciiEmojiRule = createRule(AsciiEmojiMatcher.REGEX, inputRuleHandler(editorAnalyticsAPI)); return new SafePlugin(createPlugin('emoji', [asciiEmojiRule])); } return; } function initMatcher(pluginInjectionApi) { pluginInjectionApi === null || pluginInjectionApi === void 0 || pluginInjectionApi.emoji.sharedState.onChange(function (_ref) { var nextSharedState = _ref.nextSharedState; var emojiProvider = nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.emojiProvider; if (emojiProvider) { emojiProvider.getAsciiMap().then(function (map) { matcher = new RecordingAsciiEmojiMatcher(emojiProvider, map); }); } }); } var inputRuleHandler = function inputRuleHandler(editorAnalyticsAPI) { return function (state, matchParts, start, end) { if (!matcher) { return null; } var match = matcher.match(matchParts); if (match) { var transactionCreator = new AsciiEmojiTransactionCreator(state, match, start, end, editorAnalyticsAPI); return transactionCreator.create(); } return null; }; }; var REGEX_LEADING_CAPTURE_INDEX = 1; var REGEX_EMOJI_LEADING_PARENTHESES = 2; var REGEX_EMOJI_ASCII_CAPTURE_INDEX = 3; var REGEX_TRAILING_CAPTURE_INDEX = 4; var getLeadingString = function getLeadingString(match) { var withParenthesis = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; return match[REGEX_LEADING_CAPTURE_INDEX] + (withParenthesis ? match[REGEX_EMOJI_LEADING_PARENTHESES] : ''); }; var getLeadingStringWithoutParentheses = function getLeadingStringWithoutParentheses(match) { return getLeadingString(match, false); }; var getAscii = function getAscii(match) { var withParentheses = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; return (withParentheses ? match[REGEX_EMOJI_LEADING_PARENTHESES] : '') + match[REGEX_EMOJI_ASCII_CAPTURE_INDEX].trim(); }; var getAsciiWithParentheses = function getAsciiWithParentheses(matchParts) { return getAscii(matchParts, true); }; var getTrailingString = function getTrailingString(match) { return match[REGEX_TRAILING_CAPTURE_INDEX] || ''; }; var AsciiEmojiMatcher = /*#__PURE__*/function () { function AsciiEmojiMatcher(asciiToEmojiMap) { _classCallCheck(this, AsciiEmojiMatcher); this.asciiToEmojiMap = asciiToEmojiMap; } return _createClass(AsciiEmojiMatcher, [{ key: "match", value: function match(matchParts) { return this.getAsciiEmojiMatch(getLeadingStringWithoutParentheses(matchParts), getAsciiWithParentheses(matchParts), getTrailingString(matchParts)) || this.getAsciiEmojiMatch(getLeadingString(matchParts), getAscii(matchParts), getTrailingString(matchParts)); } }, { key: "getAsciiEmojiMatch", value: function getAsciiEmojiMatch(leading, ascii, trailing) { var emoji = this.asciiToEmojiMap.get(ascii); return emoji ? { emoji: emoji, leadingString: leading, trailingString: trailing } : undefined; } }]); }(); /** * A matcher that will record ascii matches as usages of the matched emoji. */ /** * This regex matches 2 scenarios: * 1. an emoticon starting with a colon character (e.g. :D => 😃) * 2. an emoticon not starting with a colon character (e.g. 8-D => 😎) * * Explanation (${leafNodeReplacementCharacter} is replaced with character \ufffc) * * 1st Capturing Group ((?:^|[\s\ufffc])(?:\(*?)) * Non-capturing group (?:^|[\s\ufffc]) * 1st Alternative ^ * ^ asserts position at start of the string * 2nd Alternative [\s\ufffc] * matches a single character present in [\s\ufffc] * Non-capturing group (?:\(*?) * matches the character ( literally between zero and unlimited times, as few times as possible, expanding as needed (lazy) * 2nd Capturing Group (\(?) * matches a single ( if present * 3rd Capturing Group ([^:\s\ufffc\(]\S{1,3}|:\S{1,3}( )) * 1st Alternative [^:\s\ufffc\(]\S{1,3} * matches a single character not present in [^:\s\ufffc\(] between 1 and 3 times, as many times as possible, giving back as needed (greedy) * 2nd Alternative :\S{1,3}( ) * : matches the character : literally * \S{1,3} matches any non-whitespace character between 1 and 3 times, as many times as possible, giving back as needed (greedy) * 4th Capturing Group ( ) * * See https://regex101.com/r/HRS9O2/4 */ // New behavior: All emoticons require whitespace after them // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp _defineProperty(AsciiEmojiMatcher, "REGEX", new RegExp("((?:^|[\\s".concat(leafNodeReplacementCharacter, "])(?:\\(*?))(\\(?)([^:\\s").concat(leafNodeReplacementCharacter, "\\(]\\S{1,3}|:\\S{1,3})([\\s\\t\\n])$"))); var RecordingAsciiEmojiMatcher = /*#__PURE__*/function (_AsciiEmojiMatcher2) { function RecordingAsciiEmojiMatcher(emojiProvider, asciiToEmojiMap) { var _this; _classCallCheck(this, RecordingAsciiEmojiMatcher); _this = _callSuper(this, RecordingAsciiEmojiMatcher, [asciiToEmojiMap]); _this.emojiProvider = emojiProvider; return _this; } _inherits(RecordingAsciiEmojiMatcher, _AsciiEmojiMatcher2); return _createClass(RecordingAsciiEmojiMatcher, [{ key: "match", value: function match(matchParts) { var match = _superPropGet(RecordingAsciiEmojiMatcher, "match", this, 3)([matchParts]); if (match && this.emojiProvider.recordSelection) { this.emojiProvider.recordSelection(match.emoji); } return match; } }]); }(AsciiEmojiMatcher); var AsciiEmojiTransactionCreator = /*#__PURE__*/function () { function AsciiEmojiTransactionCreator(state, match, start, end, editorAnalyticsAPI) { _classCallCheck(this, AsciiEmojiTransactionCreator); this.state = state; this.match = match; this.start = start; this.end = end; this.editorAnalyticsAPI = editorAnalyticsAPI; } return _createClass(AsciiEmojiTransactionCreator, [{ key: "create", value: function create() { var _this$editorAnalytics; var tr = this.state.tr.replaceWith(this.from, this.to, this.createNodes()); (_this$editorAnalytics = this.editorAnalyticsAPI) === null || _this$editorAnalytics === void 0 || _this$editorAnalytics.attachAnalyticsEvent({ action: ACTION.INSERTED, actionSubject: ACTION_SUBJECT.DOCUMENT, actionSubjectId: ACTION_SUBJECT_ID.EMOJI, attributes: { inputMethod: INPUT_METHOD.ASCII }, eventType: EVENT_TYPE.TRACK })(tr); return tr; } }, { key: "from", get: function get() { return this.start + this.match.leadingString.length; } }, { key: "to", get: function get() { return this.end; } }, { key: "createNodes", value: function createNodes() { var nodes = [this.createEmojiNode()]; if (this.trailingTextNodeRequired()) { nodes.push(this.createTrailingTextNode()); } return nodes; } }, { key: "createEmojiNode", value: function createEmojiNode() { var emojiTypeNode = this.state.schema.nodes.emoji; return emojiTypeNode.create(this.getEmojiNodeAttrs()); } }, { key: "getEmojiNodeAttrs", value: function getEmojiNodeAttrs() { var emoji = this.match.emoji; return { id: emoji.id, shortName: emoji.shortName, text: emoji.fallback || emoji.shortName }; } }, { key: "trailingTextNodeRequired", value: function trailingTextNodeRequired() { return this.match.trailingString.length > 0; } }, { key: "createTrailingTextNode", value: function createTrailingTextNode() { return this.state.schema.text(this.match.trailingString); } }]); }(); var plugins = function plugins(schema, providerFactory, featureFlags, editorAnalyticsAPI, pluginInjectionApi) { return [inputRulePlugin(schema, editorAnalyticsAPI, pluginInjectionApi)].filter(function (plugin) { return !!plugin; }); }; export default plugins;