UNPKG

@atlaskit/editor-plugin-extension

Version:

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

306 lines (295 loc) 14.6 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.getEditInLegacyMacroBrowser = exports.createExtensionAPI = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof")); var _validator = require("@atlaskit/adf-utils/validator"); var _analytics = require("@atlaskit/editor-common/analytics"); var _utils = require("@atlaskit/editor-common/utils"); var _editorJsonTransformer = require("@atlaskit/editor-json-transformer"); var _model = require("@atlaskit/editor-prosemirror/model"); var _state = require("@atlaskit/editor-prosemirror/state"); var _utils2 = require("@atlaskit/editor-prosemirror/utils"); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); var _commands = require("../editor-commands/commands"); var _actions = require("./macro/actions"); var _pluginKey = require("./macro/plugin-key"); var _utils3 = require("./utils"); function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } var getEditInLegacyMacroBrowser = exports.getEditInLegacyMacroBrowser = function getEditInLegacyMacroBrowser(_ref) { var view = _ref.view, macroProvider = _ref.macroProvider, editorAnalyticsAPI = _ref.editorAnalyticsAPI; return function () { if (!view) { throw new Error("Missing view. Can't update without EditorView"); } if (!macroProvider) { throw new Error("Missing macroProvider. Can't use the macro browser for updates"); } var nodeWithPos = (0, _utils3.getSelectedExtension)(view.state, true); if (!nodeWithPos) { throw new Error("Missing nodeWithPos. Can't determine position of node"); } (0, _actions.insertMacroFromMacroBrowser)(editorAnalyticsAPI)(macroProvider, nodeWithPos.node, true)(view); }; }; var extensionAPICallPayload = function extensionAPICallPayload(functionName) { return { action: _analytics.ACTION.INVOKED, actionSubject: _analytics.ACTION_SUBJECT.EXTENSION, actionSubjectId: _analytics.ACTION_SUBJECT_ID.EXTENSION_API, attributes: { functionName: functionName }, eventType: _analytics.EVENT_TYPE.TRACK }; }; var createExtensionAPI = exports.createExtensionAPI = function createExtensionAPI(options) { var schema = options.editorView.state.schema, editorAnalyticsAPI = options.editorAnalyticsAPI; var nodes = Object.keys(schema.nodes); var marks = Object.keys(schema.marks); var validate = (0, _validator.validator)(nodes, marks, { allowPrivateAttributes: true }); /** * Finds the node and its position by `localId`. Throws if the node could not be found. * * @returns {NodeWithPos} */ var ensureNodePosByLocalId = function ensureNodePosByLocalId(localId, _ref2) { var opName = _ref2.opName; // Be extra cautious since 3rd party devs can use regular JS without type safety if (typeof localId !== 'string' || localId === '') { throw new Error("".concat(opName, "(): Invalid localId '").concat(localId, "'.")); } // Find the node + position matching the given ID var state = options.editorView.state; var nodePos = (0, _utils3.findNodePosWithLocalId)(state, localId); if (!nodePos) { throw new Error("".concat(opName, "(): Could not find node with ID '").concat(localId, "'.")); } return nodePos; }; var doc = { insertAfter: function insertAfter(localId, adf, opt) { try { validate(adf); } catch (e) { throw new Error("insertAfter(): Invalid ADF given."); } var nodePos = ensureNodePosByLocalId(localId, { opName: 'insertAfter' }); var editorView = options.editorView; var dispatch = editorView.dispatch, state = editorView.state; // Validate the given ADF var tr = state.tr, schema = state.schema; var nodeType = schema.nodes[adf.type]; if (!nodeType) { throw new Error("insertAfter(): Invalid ADF type '".concat(adf.type, "'.")); } var fragment = _model.Fragment.fromJSON(schema, adf.content); var marks = (adf.marks || []).map(function (markEntity) { return _model.Mark.fromJSON(schema, markEntity); }); var newNode = nodeType === null || nodeType === void 0 ? void 0 : nodeType.createChecked(adf.attrs, fragment, marks); if (!newNode) { throw new Error('insertAfter(): Could not create a node for given ADFEntity.'); } var insertPosition = nodePos.pos + nodePos.node.nodeSize; tr.insert(insertPosition, newNode); // Validate if the document is valid at this point try { tr.doc.check(); } catch (err) { throw new Error("insertAfter(): The given ADFEntity cannot be inserted in the current position.\n".concat(err)); } // Analytics - tracking the api call var apiCallPayload = extensionAPICallPayload('insertAfter'); editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(apiCallPayload)(tr); // Analytics - tracking node types added var nodesAdded = [newNode]; newNode.descendants(function (node) { nodesAdded.push(node); return true; }); nodesAdded.forEach(function (node) { var _node$attrs = node.attrs, extensionKey = _node$attrs.extensionKey, extensionType = _node$attrs.extensionType; var dataConsumerMark = (0, _utils3.getDataConsumerMark)(node); var stringIds = (dataConsumerMark === null || dataConsumerMark === void 0 ? void 0 : dataConsumerMark.attrs.sources.map(function (sourceId) { return sourceId; })) || []; var hasReferentiality = !!dataConsumerMark; var nodeTypesReferenced = hasReferentiality ? (0, _utils3.getNodeTypesReferenced)(stringIds, state) : undefined; // fire off analytics for this ADF var payload = { action: _analytics.ACTION.INSERTED, actionSubject: _analytics.ACTION_SUBJECT.DOCUMENT, attributes: { nodeType: node.type.name, inputMethod: _analytics.INPUT_METHOD.EXTENSION_API, hasReferentiality: hasReferentiality, nodeTypesReferenced: nodeTypesReferenced, layout: node.attrs.layout, extensionType: extensionType, extensionKey: extensionKey }, eventType: _analytics.EVENT_TYPE.TRACK }; editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(payload)(tr); }); if (opt) { if (opt.allowSelectionToNewNode) { tr.setSelection(new _state.NodeSelection(tr.doc.resolve(insertPosition))); } else if (opt.allowSelectionNearNewNode) { tr.setSelection(_state.TextSelection.near(tr.doc.resolve(insertPosition))); } } dispatch(tr); }, scrollTo: function scrollTo(localId) { var nodePos = ensureNodePosByLocalId(localId, { opName: 'scrollTo' }); // Analytics - tracking the api call var apiCallPayload = extensionAPICallPayload('scrollTo'); var _options$editorView = options.editorView, dispatch = _options$editorView.dispatch, state = _options$editorView.state; var tr = state.tr; editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(apiCallPayload)(tr); tr = (0, _utils2.setTextSelection)(nodePos.pos)(tr); var useScrollIntoView = true; if (nodePos.node.type.name === 'table' && (0, _expValEquals.expValEquals)('platform_editor_table_sticky_header_improvements', 'cohort', 'test_with_overflow') && (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true)) { var tableDOM = options.editorView.nodeDOM(nodePos.pos); if (tableDOM instanceof HTMLElement) { tableDOM.scrollIntoView({ block: 'start', behavior: 'smooth' }); useScrollIntoView = false; } } if (useScrollIntoView) { tr.scrollIntoView(); } dispatch(tr); }, update: function update(localId, mutationCallback, opts) { var _changedValues$marks; var _ensureNodePosByLocal = ensureNodePosByLocalId(localId, { opName: 'update' }), node = _ensureNodePosByLocal.node, pos = _ensureNodePosByLocal.pos; var _options$editorView2 = options.editorView, dispatch = _options$editorView2.dispatch, state = _options$editorView2.state; var tr = state.tr, schema = state.schema; var changedValues = mutationCallback({ content: (0, _utils.nodeToJSON)(node).content, attrs: node.attrs, marks: node.marks.map(function (pmMark) { return { type: pmMark.type.name, attrs: pmMark.attrs }; }) }); var ensureValidMark = function ensureValidMark(mark) { if ((0, _typeof2.default)(mark) !== 'object' || Array.isArray(mark)) { throw new Error("update(): Invalid mark given."); } var _state$doc$resolve = state.doc.resolve(pos), parent = _state$doc$resolve.parent; // Ensure that the given mark is present in the schema var markType = schema.marks[mark.type]; if (!markType) { throw new Error("update(): Invalid ADF mark type '".concat(mark.type, "'.")); } if (!parent.type.allowsMarkType(markType)) { throw new Error("update(): Parent of type '".concat(parent.type.name, "' does not allow marks of type '").concat(mark.type, "'.")); } return { mark: markType, attrs: mark.attrs }; }; var newMarks = changedValues.hasOwnProperty('marks') ? (_changedValues$marks = changedValues.marks) === null || _changedValues$marks === void 0 ? void 0 : _changedValues$marks.map(ensureValidMark).map(function (_ref3) { var mark = _ref3.mark, attrs = _ref3.attrs; return mark.create(attrs); }) : node.marks; var newContent = changedValues.hasOwnProperty('content') ? _model.Fragment.fromJSON(schema, changedValues.content) : node.content; var newAttrs = changedValues.hasOwnProperty('attrs') ? changedValues.attrs : node.attrs; if (node.type.name === 'multiBodiedExtension') { var _changedValues$attrs, _node$attrs$parameter, _changedValues$attrs2; newAttrs = _objectSpread(_objectSpread(_objectSpread({}, node.attrs), changedValues.attrs), {}, { parameters: _objectSpread(_objectSpread(_objectSpread({}, node.attrs.parameters), (_changedValues$attrs = changedValues.attrs) === null || _changedValues$attrs === void 0 ? void 0 : _changedValues$attrs.parameters), {}, { macroParams: _objectSpread(_objectSpread({}, (_node$attrs$parameter = node.attrs.parameters) === null || _node$attrs$parameter === void 0 ? void 0 : _node$attrs$parameter.macroParams), (_changedValues$attrs2 = changedValues.attrs) === null || _changedValues$attrs2 === void 0 || (_changedValues$attrs2 = _changedValues$attrs2.parameters) === null || _changedValues$attrs2 === void 0 ? void 0 : _changedValues$attrs2.macroParams) }) }); // console.log('newAttrs', newAttrs); } // Validate if the new attributes, content and marks result in a valid node and adf. try { var newNode = node.type.createChecked(newAttrs, newContent, newMarks); var newNodeAdf = new _editorJsonTransformer.JSONTransformer().encodeNode(newNode); validate(newNodeAdf); tr.replaceWith(pos, pos + node.nodeSize, newNode); // Keep selection if content does not change if (newContent === node.content) { tr.setSelection(_state.Selection.fromJSON(tr.doc, state.selection.toJSON())); } } catch (err) { throw new Error("update(): The given ADFEntity cannot be inserted in the current position.\n".concat(err)); } // Analytics - tracking the api call var apiCallPayload = extensionAPICallPayload('update'); editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(apiCallPayload)(tr); if (typeof (opts === null || opts === void 0 ? void 0 : opts.addToHistory) === 'boolean') { tr.setMeta('addToHistory', opts.addToHistory); } if (typeof (opts === null || opts === void 0 ? void 0 : opts.scrollIntoView) === 'boolean') { tr.setMeta('scrollIntoView', opts.scrollIntoView); } dispatch(tr); } }; return { editInContextPanel: function editInContextPanel(transformBefore, transformAfter) { var editorView = options.editorView; (0, _commands.setEditingContextToContextPanel)(transformBefore, transformAfter, options.applyChange)(editorView.state, editorView.dispatch, editorView); }, _editInLegacyMacroBrowser: function _editInLegacyMacroBrowser() { var editorView = options.editorView; var editInLegacy = options.editInLegacyMacroBrowser; if (!editInLegacy) { var macroState = _pluginKey.pluginKey.getState(editorView.state); editInLegacy = getEditInLegacyMacroBrowser({ view: options.editorView, macroProvider: (macroState === null || macroState === void 0 ? void 0 : macroState.macroProvider) || undefined, editorAnalyticsAPI: editorAnalyticsAPI }); } editInLegacy(); }, getNodeWithPosByLocalId: function getNodeWithPosByLocalId(localId) { return ensureNodePosByLocalId(localId, { opName: 'getNodeWithPosByLocalId' }); }, doc: doc }; };