@atlaskit/editor-plugin-extension
Version:
editor-plugin-extension plugin for @atlaskit/editor-core
191 lines (188 loc) • 10.6 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
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) { _defineProperty(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; }
import _regeneratorRuntime from "@babel/runtime/regenerator";
import assert from 'assert';
import { ACTION, ACTION_SUBJECT, EVENT_TYPE, INPUT_METHOD, TARGET_SELECTION_SOURCE } from '@atlaskit/editor-common/analytics';
import { normaliseNestedLayout } from '@atlaskit/editor-common/insert';
import { getValidNode } from '@atlaskit/editor-common/validator';
import { NodeSelection, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
import { findSelectedNodeOfType, replaceParentNodeOfType, replaceSelectedNode, safeInsert } from '@atlaskit/editor-prosemirror/utils';
import { getPluginState as getExtensionPluginState } from '../plugin-factory';
import { pluginKey } from './plugin-key';
export var insertMacroFromMacroBrowser = function insertMacroFromMacroBrowser(editorAnalyticsAPI) {
return function (macroProvider, macroNode, isEditing) {
return /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(view) {
var newMacro, state, dispatch, currentLayout, node, selection, schema, _schema$nodes, extension, inlineExtension, bodiedExtension, multiBodiedExtension, updateSelectionsByNodeType, extensionState, targetSelectionSource, tr, isBodiedExtensionSelected, isMultiBodiedExtensionSelected, pos, _macroNode$attrs, extensionType, extensionKey, layout, localId;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
if (macroProvider) {
_context.next = 2;
break;
}
return _context.abrupt("return", false);
case 2:
_context.next = 4;
return macroProvider.openMacroBrowser(macroNode);
case 4:
newMacro = _context.sent;
if (!(newMacro && macroNode)) {
_context.next = 22;
break;
}
state = view.state, dispatch = view.dispatch;
currentLayout = macroNode && macroNode.attrs.layout || 'default';
node = resolveMacro(newMacro, state, {
layout: currentLayout
});
if (node) {
_context.next = 11;
break;
}
return _context.abrupt("return", false);
case 11:
selection = state.selection, schema = state.schema;
_schema$nodes = schema.nodes, extension = _schema$nodes.extension, inlineExtension = _schema$nodes.inlineExtension, bodiedExtension = _schema$nodes.bodiedExtension, multiBodiedExtension = _schema$nodes.multiBodiedExtension;
updateSelectionsByNodeType = function updateSelectionsByNodeType(nodeType) {
// `isEditing` is `false` when we are inserting from insert-block toolbar
tr = isEditing ? replaceParentNodeOfType(nodeType, node)(tr) : safeInsert(node)(tr);
// Replacing selected node doesn't update the selection. `selection.node` still returns the old node
tr.setSelection(TextSelection.create(tr.doc, state.selection.anchor));
};
extensionState = getExtensionPluginState(state);
targetSelectionSource = TARGET_SELECTION_SOURCE.CURRENT_SELECTION;
tr = state.tr;
isBodiedExtensionSelected = !!findSelectedNodeOfType([bodiedExtension])(selection);
isMultiBodiedExtensionSelected = !!findSelectedNodeOfType([multiBodiedExtension])(selection); // When it's a bodiedExtension but not selected
if (macroNode.type === bodiedExtension && !isBodiedExtensionSelected) {
updateSelectionsByNodeType(state.schema.nodes.bodiedExtension);
}
// When it's a multiBodiedExtension but not selected
else if (macroNode.type === multiBodiedExtension && !isMultiBodiedExtensionSelected) {
updateSelectionsByNodeType(state.schema.nodes.multiBodiedExtension);
}
// If any extension is currently selected
else if (findSelectedNodeOfType([extension, bodiedExtension, inlineExtension, multiBodiedExtension])(selection)) {
tr = replaceSelectedNode(node)(tr);
// Replacing selected node doesn't update the selection. `selection.node` still returns the old node
tr.setSelection(NodeSelection.create(tr.doc, tr.mapping.map(state.selection.anchor)));
}
// When we loose the selection. This usually happens when Synchrony resets or changes
// the selection when user is in the middle of updating an extension.
else if (extensionState.element) {
pos = view.posAtDOM(extensionState.element, -1);
if (pos > -1) {
tr = tr.replaceWith(pos, pos + macroNode.nodeSize, node);
tr.setSelection(Selection.near(tr.doc.resolve(pos)));
targetSelectionSource = TARGET_SELECTION_SOURCE.HTML_ELEMENT;
}
}
// Only scroll if we have anything to update, best to avoid surprise scroll
if (dispatch && tr.docChanged) {
_macroNode$attrs = macroNode.attrs, extensionType = _macroNode$attrs.extensionType, extensionKey = _macroNode$attrs.extensionKey, layout = _macroNode$attrs.layout, localId = _macroNode$attrs.localId;
editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent({
action: ACTION.UPDATED,
actionSubject: ACTION_SUBJECT.EXTENSION,
actionSubjectId: macroNode.type.name,
eventType: EVENT_TYPE.TRACK,
attributes: {
// @ts-expect-error - Type is not assignable to parameter of type 'AnalyticsEventPayload'
// This error was introduced after upgrading to TypeScript 5
inputMethod: isEditing ? INPUT_METHOD.MACRO_BROWSER : INPUT_METHOD.TOOLBAR,
extensionType: extensionType,
extensionKey: extensionKey,
layout: layout,
localId: localId,
selection: tr.selection.toJSON(),
targetSelectionSource: targetSelectionSource
}
})(tr);
dispatch(tr.scrollIntoView());
}
return _context.abrupt("return", true);
case 22:
return _context.abrupt("return", false);
case 23:
case "end":
return _context.stop();
}
}, _callee);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}();
};
};
export var resolveMacro = function resolveMacro(macro, state, optionalAttrs) {
if (!macro || !state) {
return null;
}
var schema = state.schema;
var _getValidNode = getValidNode(macro, schema),
type = _getValidNode.type,
attrs = _getValidNode.attrs;
var node = null;
if (type === 'extension') {
node = schema.nodes.extension.create(_objectSpread(_objectSpread({}, attrs), optionalAttrs));
} else if (type === 'bodiedExtension') {
node = schema.nodes.bodiedExtension.create(_objectSpread(_objectSpread({}, attrs), optionalAttrs), schema.nodeFromJSON(macro).content);
} else if (type === 'inlineExtension') {
node = schema.nodes.inlineExtension.create(attrs);
} else if (type === 'multiBodiedExtension') {
node = schema.nodes.multiBodiedExtension.create(_objectSpread(_objectSpread({}, attrs), optionalAttrs), schema.nodeFromJSON(macro).content);
}
return node && normaliseNestedLayout(state, node);
};
// gets the macroProvider from the state and tries to autoConvert a given text
export var runMacroAutoConvert = function runMacroAutoConvert(state, text) {
var macroPluginState = pluginKey.getState(state);
var macroProvider = macroPluginState && macroPluginState.macroProvider;
if (!macroProvider || !macroProvider.autoConvert) {
return null;
}
var macroAttributes = macroProvider.autoConvert(text);
if (!macroAttributes) {
return null;
}
// decides which kind of macro to render (inline|bodied|bodyless)
return resolveMacro(macroAttributes, state);
};
export var setMacroProvider = function setMacroProvider(provider) {
return /*#__PURE__*/function () {
var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(view) {
var resolvedProvider;
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
_context2.prev = 0;
_context2.next = 3;
return provider;
case 3:
resolvedProvider = _context2.sent;
assert(resolvedProvider && resolvedProvider.openMacroBrowser, "MacroProvider promise did not resolve to a valid instance of MacroProvider - ".concat(resolvedProvider));
_context2.next = 10;
break;
case 7:
_context2.prev = 7;
_context2.t0 = _context2["catch"](0);
resolvedProvider = null;
case 10:
view.dispatch(view.state.tr.setMeta(pluginKey, {
macroProvider: resolvedProvider
}));
return _context2.abrupt("return", true);
case 12:
case "end":
return _context2.stop();
}
}, _callee2, null, [[0, 7]]);
}));
return function (_x2) {
return _ref2.apply(this, arguments);
};
}();
};