UNPKG

@atlaskit/editor-plugin-extension

Version:

editor-plugin-extension plugin for @atlaskit/editor-core

282 lines (279 loc) 12.9 kB
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator"; import _regeneratorRuntime from "@babel/runtime/regenerator"; function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics'; import { copyToClipboard } from '@atlaskit/editor-common/clipboard'; import { closestElement, findNodePosByLocalIds } from '@atlaskit/editor-common/utils'; import { JSONTransformer } from '@atlaskit/editor-json-transformer'; import { findDomRefAtPos, findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils'; import { isResolvingMentionProvider } from '@atlaskit/mention/resource'; export var getSelectedExtension = function getSelectedExtension(state) { var searchParent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var _state$schema$nodes = state.schema.nodes, inlineExtension = _state$schema$nodes.inlineExtension, extension = _state$schema$nodes.extension, bodiedExtension = _state$schema$nodes.bodiedExtension, multiBodiedExtension = _state$schema$nodes.multiBodiedExtension; var nodeTypes = [extension, bodiedExtension, inlineExtension, multiBodiedExtension]; return findSelectedNodeOfType(nodeTypes)(state.selection) || searchParent && findParentNodeOfType(nodeTypes)(state.selection) || undefined; }; export var findExtensionWithLocalId = function findExtensionWithLocalId(state, localId) { var selectedExtension = getSelectedExtension(state, true); if (!localId) { return selectedExtension; } if (selectedExtension && selectedExtension.node.attrs.localId === localId) { return selectedExtension; } var _state$schema$nodes2 = state.schema.nodes, inlineExtension = _state$schema$nodes2.inlineExtension, extension = _state$schema$nodes2.extension, bodiedExtension = _state$schema$nodes2.bodiedExtension, multiBodiedExtension = _state$schema$nodes2.multiBodiedExtension; var nodeTypes = [extension, bodiedExtension, inlineExtension, multiBodiedExtension]; var matched; state.doc.descendants(function (node, pos) { if (nodeTypes.includes(node.type) && node.attrs.localId === localId) { matched = { node: node, pos: pos }; } }); return matched; }; export var getSelectedDomElement = function getSelectedDomElement(schema, domAtPos, selectedExtensionNode) { // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting var selectedExtensionDomNode = findDomRefAtPos(selectedExtensionNode.pos, domAtPos); var isContentExtension = selectedExtensionNode.node.type !== schema.nodes.bodiedExtension; return ( // Content extension can be nested in bodied-extension, the following check is necessary for that case (isContentExtension // Search down ? selectedExtensionDomNode.querySelector('.extension-container') // Try searching up and then down : closestElement(selectedExtensionDomNode, '.extension-container') || selectedExtensionDomNode.querySelector('.extension-container')) || selectedExtensionDomNode ); }; export var getDataConsumerMark = function getDataConsumerMark(newNode) { var _newNode$marks; return (_newNode$marks = newNode.marks) === null || _newNode$marks === void 0 ? void 0 : _newNode$marks.find(function (mark) { return mark.type.name === 'dataConsumer'; }); }; export var getNodeTypesReferenced = function getNodeTypesReferenced(ids, state) { return findNodePosByLocalIds(state, ids, { includeDocNode: true }).map(function (_ref) { var node = _ref.node; return node.type.name; }); }; export var findNodePosWithLocalId = function findNodePosWithLocalId(state, localId) { var nodes = findNodePosByLocalIds(state, [localId]); return nodes.length >= 1 ? nodes[0] : undefined; }; /** * Converts a ProseMirror node to its text representation. * Handles text nodes with marks (links) and inline nodes (status, mention, emoji). * Returns the content for this node, with a trailing separator for text blocks. */ var convertNodeToText = function convertNodeToText(node, mentionSet, parent, locale) { if (node.isInline) { var schema = node.type.schema; var finalText = ''; if (node.isText) { finalText = node.text || ''; if (node.marks.length > 0) { var _iterator = _createForOfIteratorHelper(node.marks), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var mark = _step.value; // if it's link, include the href in the text if (mark.type === schema.marks.link) { var href = mark.attrs.href; var text = node.text || ''; // If the text differs from the href, include both if (text && text !== href) { finalText = "".concat(text, " ").concat(href); } else { finalText = href; } } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } } else { switch (node.type) { case schema.nodes.status: finalText = node.attrs.text || ''; break; case schema.nodes.mention: mentionSet.add(node.attrs.id); finalText = "@".concat(node.attrs.id); break; case schema.nodes.emoji: finalText = node.attrs.shortName || ''; break; case schema.nodes.date: var timestamp = new Date(Number(node.attrs.timestamp)); finalText = !isNaN(timestamp.getTime()) ? timestamp.toLocaleDateString(locale !== null && locale !== void 0 ? locale : 'en-US') : String(node.attrs.timestamp); break; default: finalText = node.textContent; break; } } if (parent && parent.isTextblock && node === parent.lastChild && parent.childCount > 0) { finalText += '\n\n'; } return finalText; } return ''; }; /** * Resolves mention IDs to their display names and replaces them in the text. * Returns the text with resolved mentions, or the original text if the provider is unavailable. */ var resolveMentionsInText = /*#__PURE__*/function () { var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(text, mentionSet, api) { var _api$mention; var mentionProvider, resolvedText, _iterator2, _step2, id, mention; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: mentionProvider = api === null || api === void 0 || (_api$mention = api.mention) === null || _api$mention === void 0 || (_api$mention = _api$mention.sharedState) === null || _api$mention === void 0 || (_api$mention = _api$mention.currentState()) === null || _api$mention === void 0 ? void 0 : _api$mention.mentionProvider; if (!(!mentionProvider || !isResolvingMentionProvider(mentionProvider))) { _context.next = 3; break; } return _context.abrupt("return", text); case 3: resolvedText = text; _iterator2 = _createForOfIteratorHelper(mentionSet); _context.prev = 5; _iterator2.s(); case 7: if ((_step2 = _iterator2.n()).done) { _context.next = 15; break; } id = _step2.value; _context.next = 11; return mentionProvider.resolveMentionName(id); case 11: mention = _context.sent; // eslint-disable-next-line @atlassian/perf-linting/no-expensive-split-replace -- Ignored via go/ees017 (to be fixed) resolvedText = resolvedText.replace("@".concat(id), "@".concat(mention.name) || '@…'); case 13: _context.next = 7; break; case 15: _context.next = 20; break; case 17: _context.prev = 17; _context.t0 = _context["catch"](5); _iterator2.e(_context.t0); case 20: _context.prev = 20; _iterator2.f(); return _context.finish(20); case 23: return _context.abrupt("return", resolvedText); case 24: case "end": return _context.stop(); } }, _callee, null, [[5, 17, 20, 23]]); })); return function resolveMentionsInText(_x, _x2, _x3) { return _ref2.apply(this, arguments); }; }(); /** * copying ADF from the unsupported content extension as text to clipboard */ export var copyUnsupportedContentToClipboard = /*#__PURE__*/function () { var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(_ref3) { var locale, schema, unsupportedContent, api, transformer, pmNode, text, mentionSet; return _regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: locale = _ref3.locale, schema = _ref3.schema, unsupportedContent = _ref3.unsupportedContent, api = _ref3.api; _context2.prev = 1; if (unsupportedContent) { _context2.next = 4; break; } throw new Error('No nested content found'); case 4: if (unsupportedContent.type !== 'doc') { unsupportedContent = { version: 1, type: 'doc', content: [unsupportedContent] }; } transformer = new JSONTransformer(schema); pmNode = transformer.parse(unsupportedContent); text = ''; mentionSet = new Set(); pmNode.nodesBetween(0, pmNode.content.size, function (node, _pos, parent) { text += convertNodeToText(node, mentionSet, parent, locale); }); // Trim leading/trailing whitespace from the collected text text = text.trim(); _context2.next = 13; return resolveMentionsInText(text, mentionSet, api); case 13: text = _context2.sent; copyToClipboard(text); _context2.next = 20; break; case 17: _context2.prev = 17; _context2.t0 = _context2["catch"](1); throw _context2.t0 instanceof Error ? _context2.t0 : new Error('Failed to copy content'); case 20: case "end": return _context2.stop(); } }, _callee2, null, [[1, 17]]); })); return function copyUnsupportedContentToClipboard(_x4) { return _ref4.apply(this, arguments); }; }(); export var onCopyFailed = function onCopyFailed(_ref5) { var _extensionApi$analyti; var error = _ref5.error, extensionApi = _ref5.extensionApi, state = _ref5.state; var nodeWithPos = getSelectedExtension(state, true); if (!nodeWithPos) { return; } var node = nodeWithPos.node; var _node$attrs = node.attrs, extensionType = _node$attrs.extensionType, extensionKey = _node$attrs.extensionKey; extensionApi === null || extensionApi === void 0 || (_extensionApi$analyti = extensionApi.analytics) === null || _extensionApi$analyti === void 0 || _extensionApi$analyti.actions.fireAnalyticsEvent({ eventType: EVENT_TYPE.OPERATIONAL, action: ACTION.COPY_FAILED, actionSubject: ACTION_SUBJECT.EXTENSION, actionSubjectId: node.type.name, attributes: { extensionKey: extensionKey, extensionType: extensionType, errorMessage: error.message, errorStack: error.stack } }); };