@atlaskit/editor-plugin-extension
Version:
editor-plugin-extension plugin for @atlaskit/editor-core
306 lines (295 loc) • 14.6 kB
JavaScript
"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
};
};