UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

374 lines (369 loc) • 14 kB
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _createClass from "@babel/runtime/helpers/createClass"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import _typeof from "@babel/runtime/helpers/typeof"; 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 isEqual from 'lodash/isEqual'; import throttle from 'lodash/throttle'; import { corePlugin } from './core-plugin'; // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any function hasGetSharedState( // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any plugin // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any ) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any return typeof plugin.getSharedState === 'function'; } function hasActions( // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any plugin // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any ) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any return _typeof(plugin.actions) === 'object'; } function hasCommands( // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any plugin // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any ) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any return _typeof(plugin.commands) === 'object'; } var filterPluginsWithListeners = function filterPluginsWithListeners(_ref) { var listeners = _ref.listeners, plugins = _ref.plugins; return Array.from(listeners.keys()).map(function (pluginName) { return plugins.get(pluginName); }).filter(function (plugin) { return plugin !== undefined && hasGetSharedState(plugin); }); }; var extractSharedStateFromPlugins = function extractSharedStateFromPlugins(_ref2) { var oldEditorState = _ref2.oldEditorState, newEditorState = _ref2.newEditorState, plugins = _ref2.plugins; var isInitialization = !oldEditorState && newEditorState; var result = new Map(); var _iterator = _createForOfIteratorHelper(plugins), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var plugin = _step.value; if (!plugin || !hasGetSharedState(plugin)) { continue; } var nextSharedState = plugin.getSharedState(newEditorState); var prevSharedState = !isInitialization && oldEditorState ? plugin.getSharedState(oldEditorState) : undefined; var isSamePluginState = isEqual(prevSharedState, nextSharedState); if (isInitialization || !isSamePluginState) { result.set(plugin.name, { nextSharedState: nextSharedState, prevSharedState: prevSharedState }); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return result; }; var THROTTLE_CALLS_FOR_MILLISECONDS = 0; var notifyListenersThrottled = throttle(function (_ref3) { var listeners = _ref3.listeners, updatesToNotifyQueue = _ref3.updatesToNotifyQueue; var callbacks = []; var _iterator2 = _createForOfIteratorHelper(updatesToNotifyQueue.entries()), _step2; try { var _loop = function _loop() { var _step2$value = _slicedToArray(_step2.value, 2), pluginName = _step2$value[0], diffs = _step2$value[1]; var pluginListeners = listeners.get(pluginName) || []; pluginListeners.forEach(function (callback) { diffs.forEach(function (diff) { callbacks.push(callback.bind(callback, diff)); }); }); }; for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { _loop(); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } updatesToNotifyQueue.clear(); if (callbacks.length === 0) { return; } callbacks.reverse().forEach(function (cb) { cb(); }); }, THROTTLE_CALLS_FOR_MILLISECONDS); export var PluginsData = /*#__PURE__*/_createClass(function PluginsData() { _classCallCheck(this, PluginsData); }); var ActionsAPI = /*#__PURE__*/function () { function ActionsAPI() { _classCallCheck(this, ActionsAPI); } return _createClass(ActionsAPI, [{ key: "createAPI", value: function createAPI( // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any plugin // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any ) { if (!plugin || !hasActions(plugin)) { return {}; } return new Proxy(plugin.actions || {}, { get: function get(target, prop, receiver) { // We will be able to track perfomance here return Reflect.get(target, prop); } }); } }]); }(); var EditorCommandsAPI = /*#__PURE__*/function () { function EditorCommandsAPI() { _classCallCheck(this, EditorCommandsAPI); } return _createClass(EditorCommandsAPI, [{ key: "createAPI", value: function createAPI( // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any plugin // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any ) { if (!plugin || !hasCommands(plugin)) { return {}; } return new Proxy(plugin.commands || {}, { get: function get(target, prop, receiver) { // We will be able to track perfomance here return Reflect.get(target, prop); } }); } }]); }(); export var SharedStateAPI = /*#__PURE__*/function () { function SharedStateAPI(_ref4) { var getEditorState = _ref4.getEditorState; _classCallCheck(this, SharedStateAPI); _defineProperty(this, "updatesToNotifyQueue", new Map()); this.getEditorState = getEditorState; this.listeners = new Map(); } return _createClass(SharedStateAPI, [{ key: "createAPI", value: function createAPI(plugin // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any ) { var _this = this; if (!plugin) { return { currentState: function currentState() { return undefined; }, onChange: function onChange(sub) { return function () {}; } }; } var pluginName = plugin.name; return { currentState: function currentState() { if (!hasGetSharedState(plugin)) { return undefined; } var state = _this.getEditorState(); return plugin.getSharedState(state); }, onChange: function onChange(sub) { var pluginListeners = _this.listeners.get(pluginName) || new Set(); pluginListeners.add(sub); _this.listeners.set(pluginName, pluginListeners); return function () { return _this.cleanupSubscription(pluginName, sub); }; } }; } }, { key: "cleanupSubscription", value: function cleanupSubscription(pluginName, sub) { (this.listeners.get(pluginName) || new Set()).delete(sub); } }, { key: "notifyListeners", value: function notifyListeners(_ref5) { var newEditorState = _ref5.newEditorState, oldEditorState = _ref5.oldEditorState, plugins = _ref5.plugins; var listeners = this.listeners, updatesToNotifyQueue = this.updatesToNotifyQueue; var pluginsFiltered = filterPluginsWithListeners({ plugins: plugins, listeners: listeners }); var sharedStateDiffs = extractSharedStateFromPlugins({ oldEditorState: oldEditorState, newEditorState: newEditorState, plugins: pluginsFiltered }); if (sharedStateDiffs.size === 0) { return; } var _iterator3 = _createForOfIteratorHelper(sharedStateDiffs), _step3; try { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { var _step3$value = _slicedToArray(_step3.value, 2), pluginName = _step3$value[0], nextDiff = _step3$value[1]; var currentDiffQueue = updatesToNotifyQueue.get(pluginName) || []; updatesToNotifyQueue.set(pluginName, [].concat(_toConsumableArray(currentDiffQueue), [nextDiff])); } } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } notifyListenersThrottled({ updatesToNotifyQueue: updatesToNotifyQueue, listeners: listeners }); } }, { key: "destroy", value: function destroy() { this.listeners.clear(); this.updatesToNotifyQueue.clear(); } }]); }(); var editorAPICache = new WeakMap(); export var EditorPluginInjectionAPI = /*#__PURE__*/function () { function EditorPluginInjectionAPI(_ref6) { var _this2 = this; var getEditorState = _ref6.getEditorState, getEditorView = _ref6.getEditorView, fireAnalyticsEvent = _ref6.fireAnalyticsEvent; _classCallCheck(this, EditorPluginInjectionAPI); _defineProperty(this, "onEditorViewUpdated", function (_ref7) { var newEditorState = _ref7.newEditorState, oldEditorState = _ref7.oldEditorState; _this2.sharedStateAPI.notifyListeners({ newEditorState: newEditorState, oldEditorState: oldEditorState, plugins: _this2.plugins }); }); _defineProperty(this, "onEditorPluginInitialized", function (plugin) { _this2.addPlugin(plugin); }); _defineProperty(this, "addPlugin", function (plugin) { // Plugins other than `core` are checked by the preset itself // For some reason in some tests we have duplicates that are missed. // To follow-up in ED-19611 if (plugin.name === 'core' && _this2.plugins.has(plugin.name)) { throw new Error("Plugin ".concat(plugin.name, " has already been initialised in the Editor API!\n There cannot be duplicate plugins or you will have unexpected behaviour")); } _this2.plugins.set(plugin.name, plugin); }); _defineProperty(this, "getPluginByName", function (pluginName) { var plugin = _this2.plugins.get(pluginName); return plugin; }); this.sharedStateAPI = new SharedStateAPI({ getEditorState: getEditorState }); this.plugins = new Map(); this.actionsAPI = new ActionsAPI(); this.commandsAPI = new EditorCommandsAPI(); // Special core plugin that is always added this.addPlugin(corePlugin({ config: { getEditorView: getEditorView, fireAnalyticsEvent: fireAnalyticsEvent } })); } return _createClass(EditorPluginInjectionAPI, [{ key: "createAPI", value: function createAPI() { var sharedStateAPI = this.sharedStateAPI, actionsAPI = this.actionsAPI, commandsAPI = this.commandsAPI, getPluginByName = this.getPluginByName; // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any return new Proxy({}, { get: function get(target, prop, receiver) { // If we pass this as a prop React hates us // Let's just reflect the result and ignore these if (prop === 'toJSON') { return Reflect.get(target, prop); } var plugin = getPluginByName(prop); if (!plugin) { return undefined; } var sharedState = sharedStateAPI.createAPI(plugin); var actions = actionsAPI.createAPI(plugin); var commands = commandsAPI.createAPI(plugin); // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any var proxyCoreAPI = { sharedState: sharedState, actions: actions, commands: commands }; return proxyCoreAPI; } }); } }, { key: "api", value: function api() { if (!editorAPICache.get(this)) { editorAPICache.set(this, this.createAPI()); } // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return editorAPICache.get(this); } }]); }();