UNPKG

@atlaskit/editor-core

Version:

A package contains Atlassian editor core functionality

442 lines (432 loc) • 18 kB
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _regeneratorRuntime from "@babel/runtime/regenerator"; import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics'; import { createDispatch } from '@atlaskit/editor-common/event-dispatcher'; import { processRawFragmentValue, processRawValue } from '@atlaskit/editor-common/process-raw-value'; import { analyticsEventKey } from '@atlaskit/editor-common/utils/analytics'; import { Node } from '@atlaskit/editor-prosemirror/model'; import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state'; import { findParentNode, safeInsert } from '@atlaskit/editor-prosemirror/utils'; import { getEditorValueWithMedia } from '../utils/action'; import deprecationWarnings from '../utils/deprecation-warnings'; import { findNodePosByFragmentLocalIds } from '../utils/nodes-by-localIds'; import { isEmptyDocument } from './temp-is-empty-document'; import { findNodePosByLocalIds } from './temp-nodes-by-localids'; // eslint-disable-next-line import/order import { toJSON } from './temp-to-json'; // eslint-disable-next-line import/order // Please, do not copy or use this kind of code below // @ts-ignore var fakePluginKey = { key: 'nativeCollabProviderPlugin$', getState: function getState(state) { // eslint-disable-next-line @typescript-eslint/no-explicit-any return state['nativeCollabProviderPlugin$']; } }; /** * @deprecated {@link https://hello.atlassian.net/browse/ENGHEALTH-26729 Internal documentation for deprecation (no external access)} Editor actions is no longer supported and will be removed in a future version. Please use the core actions, or Plugin APIs directly instead * @example If you were using editorActions.getValue() replace with: const { editorApi, preset } = usePreset(...); editorApi?.core.actions.requestDocument((doc) => { // use doc as desired }) * If you were using editorActions.getNodeByLocalId(localId) replace with: const { editorApi, preset } = usePreset(...); const extensionAPI = editorAPI?.extension?.actions?.api(); // Use nodeWithPos as desired const nodeWithPos = extensionAPI.getNodeWithPosByLocalId(localId); const node = nodeWithPos.node; const nodePos = nodeWithPos.pos; */ // eslint-disable-next-line @typescript-eslint/no-explicit-any var EditorActions = /*#__PURE__*/function () { function EditorActions() { var _this = this; _classCallCheck(this, EditorActions); _defineProperty(this, "listeners", []); _defineProperty(this, "dispatchAnalyticsEvent", function (payload) { if (_this.eventDispatcher) { var dispatch = createDispatch(_this.eventDispatcher); dispatch(analyticsEventKey, { payload: payload }); } }); /** * If editor is using new collab service, * we want editor to call the collab provider to * retrieve the final acknowledged state of the * editor. The final acknowledged editor state * refers to the latest state of editor with confirmed * steps. */ _defineProperty(this, "getResolvedEditorState", /*#__PURE__*/function () { var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(reason) { var _this$getFeatureFlags, useNativeCollabPlugin, editorValue, editorView, collabEditState; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _this$getFeatureFlags = _this.getFeatureFlags(), useNativeCollabPlugin = _this$getFeatureFlags.useNativeCollabPlugin; if (_this.editorView) { _context.next = 3; break; } throw new Error('Called getResolvedEditorState before editorView is ready'); case 3: if (useNativeCollabPlugin) { _context.next = 10; break; } _context.next = 6; return _this.getValue(); case 6: editorValue = _context.sent; if (editorValue) { _context.next = 9; break; } throw new Error('editorValue is undefined'); case 9: return _context.abrupt("return", { content: editorValue, title: null, stepVersion: -1 }); case 10: editorView = _this.editorView; _context.next = 13; return getEditorValueWithMedia(editorView); case 13: collabEditState = fakePluginKey.getState(editorView.state); return _context.abrupt("return", collabEditState === null || collabEditState === void 0 ? void 0 : collabEditState.getFinalAcknowledgedState(reason)); case 15: case "end": return _context.stop(); } }, _callee); })); return function (_x) { return _ref.apply(this, arguments); }; }()); } return _createClass(EditorActions, [{ key: "_privateGetEditorView", value: //#region private // This method needs to be public for context based helper components. function _privateGetEditorView() { return this.editorView; } }, { key: "_privateGetEventDispatcher", value: function _privateGetEventDispatcher() { return this.eventDispatcher; } }, { key: "getFeatureFlags", value: function getFeatureFlags() { return {}; } // This method needs to be public for EditorContext component. }, { key: "_privateRegisterEditor", value: function _privateRegisterEditor(editorView, eventDispatcher, contentTransformer) { var getFeatureFlags = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : function () { return {}; }; this.contentTransformer = contentTransformer; this.eventDispatcher = eventDispatcher; this.getFeatureFlags = getFeatureFlags; if (!this.editorView && editorView) { this.editorView = editorView; this.listeners.forEach(function (cb) { return cb(editorView, eventDispatcher); }); } else if (this.editorView !== editorView) { throw new Error("Editor has already been registered! It's not allowed to re-register editor with the new Editor instance."); } if (this.contentTransformer) { this.contentEncode = this.contentTransformer.encode.bind(this.contentTransformer); } } // This method needs to be public for EditorContext component. }, { key: "_privateUnregisterEditor", value: function _privateUnregisterEditor() { this.editorView = undefined; this.contentTransformer = undefined; this.contentEncode = undefined; this.eventDispatcher = undefined; this.getFeatureFlags = function () { return {}; }; } }, { key: "_privateSubscribe", value: function _privateSubscribe(cb) { // If editor is registered and somebody is trying to add a listener, // just call it first. if (this.editorView && this.eventDispatcher) { cb(this.editorView, this.eventDispatcher); } this.listeners.push(cb); } }, { key: "_privateUnsubscribe", value: function _privateUnsubscribe(cb) { this.listeners = this.listeners.filter(function (c) { return c !== cb; }); } //#endregion }, { key: "focus", value: function focus() { var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { scrollIntoView: true }, scrollIntoView = _ref2.scrollIntoView; if (!this.editorView || this.editorView.hasFocus()) { return false; } this.editorView.focus(); if (scrollIntoView !== null && scrollIntoView !== void 0 ? scrollIntoView : true) { this.editorView.dispatch(this.editorView.state.tr.scrollIntoView()); } return true; } }, { key: "blur", value: function blur() { if (!this.editorView || !this.editorView.hasFocus()) { return false; } // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting this.editorView.dom.blur(); return true; } }, { key: "clear", value: function clear() { if (!this.editorView) { return false; } var editorView = this.editorView; var state = editorView.state; var tr = editorView.state.tr.setSelection(TextSelection.create(state.doc, 0, state.doc.nodeSize - 2)).deleteSelection(); editorView.dispatch(tr); return true; } // eslint-disable-next-line @repo/internal/deprecations/deprecation-ticket-required -- Ignored via go/ED-25883 /** * @deprecated This is deprecated and is no longer maintained. * * Use the `requestDocument` API from `editorAPI` (ie. `editorApi?.core?.actions.requestDocument( ... )) * it has inbuilt throttling and is designed for use with `ComposableEditor`. * * Docs on its usage are available from: https://atlaskit.atlassian.com/packages/editor/editor-core * * WARNING: this may be called repeatedly, async with care */ }, { key: "getValue", value: function () { var _getValue = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() { var editorView, doc, json, nodeSanitized; return _regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: editorView = this.editorView; if (editorView) { _context2.next = 3; break; } return _context2.abrupt("return"); case 3: _context2.next = 5; return getEditorValueWithMedia(editorView); case 5: doc = _context2.sent; json = toJSON(doc); if (this.contentEncode) { _context2.next = 9; break; } return _context2.abrupt("return", json); case 9: // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion nodeSanitized = Node.fromJSON(this.editorView.state.schema, json); _context2.prev = 10; return _context2.abrupt("return", this.contentEncode(nodeSanitized)); case 14: _context2.prev = 14; _context2.t0 = _context2["catch"](10); this.dispatchAnalyticsEvent({ action: ACTION.DOCUMENT_PROCESSING_ERROR, actionSubject: ACTION_SUBJECT.EDITOR, eventType: EVENT_TYPE.OPERATIONAL, attributes: { errorMessage: "".concat(_context2.t0 instanceof Error && _context2.t0.name === 'NodeNestingTransformError' ? 'NodeNestingTransformError - Failed to encode one or more nested tables' : undefined) } }); throw _context2.t0; case 18: case "end": return _context2.stop(); } }, _callee2, this, [[10, 14]]); })); function getValue() { return _getValue.apply(this, arguments); } return getValue; }() // eslint-disable-next-line @repo/internal/deprecations/deprecation-ticket-required -- Ignored via go/ED-25883 /** * @deprecated - please use `getNodeWithPosByLocalId` found in the core plugin actions instead * @example const { editorApi, preset } = usePreset(...); const extensionAPI = editorAPI?.extension?.actions?.api(); // Use nodeWithPos as desired const nodeWithPos = extensionAPI.getNodeWithPosByLocalId(localId); const node = nodeWithPos.node; const nodePos = nodeWithPos.pos; */ }, { key: "getNodeByLocalId", value: function getNodeByLocalId(id) { var _this$editorView; if ((_this$editorView = this.editorView) !== null && _this$editorView !== void 0 && _this$editorView.state) { var _this$editorView2; var nodes = findNodePosByLocalIds((_this$editorView2 = this.editorView) === null || _this$editorView2 === void 0 ? void 0 : _this$editorView2.state, [id]); var node = nodes.length >= 1 ? nodes[0] : undefined; return node === null || node === void 0 ? void 0 : node.node; } } }, { key: "getNodeByFragmentLocalId", value: function getNodeByFragmentLocalId(id) { var _this$editorView3; if ((_this$editorView3 = this.editorView) !== null && _this$editorView3 !== void 0 && _this$editorView3.state) { var _this$editorView4; var nodes = findNodePosByFragmentLocalIds((_this$editorView4 = this.editorView) === null || _this$editorView4 === void 0 ? void 0 : _this$editorView4.state, [id]); return nodes.length > 0 ? nodes[0].node : undefined; } } /** * This method will return the currently selected `Node` if the selection is a `Node`. * Otherwise, if the selection is textual or a non-selectable `Node` within another selectable `Node`, the closest selectable parent `Node` will be returned. */ }, { key: "getSelectedNode", value: function getSelectedNode() { var _this$editorView5; if ((_this$editorView5 = this.editorView) !== null && _this$editorView5 !== void 0 && (_this$editorView5 = _this$editorView5.state) !== null && _this$editorView5 !== void 0 && _this$editorView5.selection) { var _findParentNode; var selection = this.editorView.state.selection; if (selection instanceof NodeSelection) { return selection.node; } return (_findParentNode = findParentNode(function (node) { return Boolean(node.type.spec.selectable); })(selection)) === null || _findParentNode === void 0 ? void 0 : _findParentNode.node; } } }, { key: "isDocumentEmpty", value: function isDocumentEmpty() { // Unlikely case when editorView has been destroyed before calling isDocumentEmpty, // we treat this case as if document was empty. if (!this.editorView) { return true; } return isEmptyDocument(this.editorView.state.doc); } // eslint-disable-next-line @repo/internal/deprecations/deprecation-ticket-required -- Ignored via go/ED-25883 /** * @deprecated - please use `replaceDocument` found in the core plugin actions instead * using this will reset your Editor State which could cause some things to break (like emojis) * @example - use the `replaceDocument` from the core plugin actions instead * ```ts * const { editorApi, preset } = usePreset(...); // where you need it editorApi?.core.actions.replaceDocument(value); return <ComposableEditor preset={preset} ... /> */ }, { key: "replaceDocument", value: function replaceDocument( // eslint-disable-next-line @typescript-eslint/no-explicit-any rawValue) { var shouldScrollToBottom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; var shouldAddToHistory = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; deprecationWarnings('EditorActions.replaceDocument', { shouldAddToHistory: shouldAddToHistory }, [{ property: 'shouldAddToHistory', description: '[ED-14158] EditorActions.replaceDocument does not use the shouldAddToHistory arg', type: 'removed' }]); if (!this.editorView || rawValue === undefined || rawValue === null) { return false; } if (this.eventDispatcher) { this.eventDispatcher.emit('resetEditorState', { doc: rawValue, shouldScrollToBottom: shouldScrollToBottom }); } return true; } }, { key: "replaceSelection", value: function replaceSelection(rawValue, tryToReplace, position) { if (!this.editorView) { return false; } var state = this.editorView.state; if (!rawValue) { var tr = state.tr.deleteSelection().scrollIntoView(); this.editorView.dispatch(tr); return true; } var schema = state.schema; var content = Array.isArray(rawValue) ? processRawFragmentValue(schema, rawValue, undefined, undefined, undefined, this.dispatchAnalyticsEvent) : processRawValue(schema, rawValue, undefined, undefined, undefined, this.dispatchAnalyticsEvent); if (!content) { return false; } // try to find a place in the document where to insert a node if its not allowed at the cursor position by schema this.editorView.dispatch(safeInsert(content, position, tryToReplace)(state.tr).scrollIntoView()); return true; } }, { key: "appendText", value: function appendText(text) { if (!this.editorView || !text) { return false; } var state = this.editorView.state; var lastChild = state.doc.lastChild; if (lastChild && lastChild.type !== state.schema.nodes.paragraph) { return false; } var tr = state.tr.insertText(text).scrollIntoView(); this.editorView.dispatch(tr); return true; } }], [{ key: "from", value: function from(view, eventDispatcher, transformer) { var editorActions = new EditorActions(); editorActions._privateRegisterEditor(view, eventDispatcher, transformer); return editorActions; } }]); }(); export { EditorActions as default };