UNPKG

@atlaskit/editor-common

Version:

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

311 lines • 12 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(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, 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 normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } import isEqual from 'lodash/isEqual'; import throttle from 'lodash/throttle'; import { corePlugin } from './core-plugin'; function hasGetSharedState(plugin) { return typeof plugin.getSharedState === 'function'; } function hasActions(plugin) { return _typeof(plugin.actions) === 'object'; } function hasCommands(plugin) { 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); } _createClass(ActionsAPI, [{ key: "createAPI", value: function createAPI(plugin) { 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); } }); } }]); return ActionsAPI; }(); var EditorCommandsAPI = /*#__PURE__*/function () { function EditorCommandsAPI() { _classCallCheck(this, EditorCommandsAPI); } _createClass(EditorCommandsAPI, [{ key: "createAPI", value: function createAPI(plugin) { 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); } }); } }]); return EditorCommandsAPI; }(); 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(); } _createClass(SharedStateAPI, [{ key: "createAPI", value: function createAPI(plugin) { 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(); } }]); return SharedStateAPI; }(); export var EditorPluginInjectionAPI = /*#__PURE__*/function () { function EditorPluginInjectionAPI(_ref6) { var _this2 = this; var getEditorState = _ref6.getEditorState, getEditorView = _ref6.getEditorView; _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 } })); } _createClass(EditorPluginInjectionAPI, [{ key: "api", value: function api() { var sharedStateAPI = this.sharedStateAPI, actionsAPI = this.actionsAPI, commandsAPI = this.commandsAPI, getPluginByName = this.getPluginByName; 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); var proxyCoreAPI = { sharedState: sharedState, actions: actions, commands: commands }; return proxyCoreAPI; } }); } }]); return EditorPluginInjectionAPI; }();