UNPKG

@atlaskit/editor-plugin-synced-block

Version:

SyncedBlock plugin for @atlaskit/editor-core

297 lines (296 loc) 14.9 kB
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _inherits from "@babel/runtime/helpers/inherits"; function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } import React from 'react'; import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics'; import { ErrorBoundary } from '@atlaskit/editor-common/error-boundary'; import ReactNodeView from '@atlaskit/editor-common/react-node-view'; import { BodiedSyncBlockSharedCssClassName } from '@atlaskit/editor-common/sync-block'; import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity'; import { DOMSerializer } from '@atlaskit/editor-prosemirror/model'; import { fg } from '@atlaskit/platform-feature-flags'; import { BodiedSyncBlockWrapper } from '../ui/BodiedSyncBlockWrapper'; import { SyncBlockLabel } from '../ui/SyncBlockLabel'; var toDOMOld = function toDOMOld() { return ['div', { class: BodiedSyncBlockSharedCssClassName.content, contenteditable: true }, 0]; }; var BodiedSyncBlockOld = /*#__PURE__*/function (_ReactNodeView) { function BodiedSyncBlockOld(props) { var _this; _classCallCheck(this, BodiedSyncBlockOld); _this = _callSuper(this, BodiedSyncBlockOld, [props.node, props.view, props.getPos, props.portalProviderAPI, props.eventDispatcher, props]); _this.api = props.api; _this.syncBlockStore = props.syncBlockStore; _this.handleConnectivityModeChange(); _this.handleViewModeChange(); return _this; } _inherits(BodiedSyncBlockOld, _ReactNodeView); return _createClass(BodiedSyncBlockOld, [{ key: "updateContentEditable", value: function updateContentEditable(_ref) { var _this$api, _this$api2; var contentDOM = _ref.contentDOM, nextConnectivityMode = _ref.nextConnectivityMode, nextViewMode = _ref.nextViewMode; var connectivityMode = nextConnectivityMode !== null && nextConnectivityMode !== void 0 ? nextConnectivityMode : (_this$api = this.api) === null || _this$api === void 0 || (_this$api = _this$api.connectivity) === null || _this$api === void 0 || (_this$api = _this$api.sharedState) === null || _this$api === void 0 || (_this$api = _this$api.currentState()) === null || _this$api === void 0 ? void 0 : _this$api.mode; var viewMode = nextViewMode !== null && nextViewMode !== void 0 ? nextViewMode : (_this$api2 = this.api) === null || _this$api2 === void 0 || (_this$api2 = _this$api2.editorViewMode) === null || _this$api2 === void 0 || (_this$api2 = _this$api2.sharedState) === null || _this$api2 === void 0 || (_this$api2 = _this$api2.currentState()) === null || _this$api2 === void 0 ? void 0 : _this$api2.mode; var isOnline = !isOfflineMode(connectivityMode); var isEditMode = viewMode !== 'view'; var shouldBeEditable = isOnline && isEditMode; contentDOM === null || contentDOM === void 0 || contentDOM.setAttribute('contenteditable', shouldBeEditable ? 'true' : 'false'); } }, { key: "handleConnectivityModeChange", value: function handleConnectivityModeChange() { var _this$api3, _this2 = this; if ((_this$api3 = this.api) !== null && _this$api3 !== void 0 && _this$api3.connectivity) { this.cleanupConnectivityModeListener = this.api.connectivity.sharedState.onChange(function (_ref2) { var nextSharedState = _ref2.nextSharedState; _this2.updateContentEditable({ contentDOM: _this2.contentDOM, nextConnectivityMode: nextSharedState.mode }); }); } } }, { key: "handleViewModeChange", value: function handleViewModeChange() { var _this$api4, _this3 = this; if ((_this$api4 = this.api) !== null && _this$api4 !== void 0 && _this$api4.editorViewMode) { this.cleanupViewModeListener = this.api.editorViewMode.sharedState.onChange(function (_ref3) { var nextSharedState = _ref3.nextSharedState; _this3.updateContentEditable({ contentDOM: _this3.contentDOM, nextViewMode: nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.mode }); }); } } }, { key: "createDomRef", value: function createDomRef() { var domRef = document.createElement('div'); domRef.classList.add(BodiedSyncBlockSharedCssClassName.prefix); return domRef; } }, { key: "render", value: function render(_props, forwardRef) { var _this$api$syncedBlock, _this$api5, _this$api6; // Use passed syncBlockStore for SSR where sharedState.currentState() is delayed var syncBlockStore = (_this$api$syncedBlock = (_this$api5 = this.api) === null || _this$api5 === void 0 || (_this$api5 = _this$api5.syncedBlock.sharedState) === null || _this$api5 === void 0 || (_this$api5 = _this$api5.currentState()) === null || _this$api5 === void 0 ? void 0 : _this$api5.syncBlockStore) !== null && _this$api$syncedBlock !== void 0 ? _this$api$syncedBlock : this.syncBlockStore; if (!syncBlockStore) { return null; } return /*#__PURE__*/React.createElement(ErrorBoundary, { component: ACTION_SUBJECT.SYNCED_BLOCK, dispatchAnalyticsEvent: (_this$api6 = this.api) === null || _this$api6 === void 0 || (_this$api6 = _this$api6.analytics) === null || _this$api6 === void 0 ? void 0 : _this$api6.actions.fireAnalyticsEvent, fallbackComponent: null }, /*#__PURE__*/React.createElement(BodiedSyncBlockWrapper, { ref: forwardRef, syncBlockStore: syncBlockStore, node: this.node })); } }, { key: "getContentDOM", value: function getContentDOM() { var _DOMSerializer$render = DOMSerializer.renderSpec(document, toDOMOld()), dom = _DOMSerializer$render.dom, contentDOM = _DOMSerializer$render.contentDOM; // In SSR, the first check won't work, so fallback to nodeType check if (dom instanceof HTMLElement || dom.nodeType === 1) { this.updateContentEditable({ contentDOM: contentDOM }); // eslint-disable-next-line @atlaskit/editor/no-as-casting return { dom: dom, contentDOM: contentDOM }; } return undefined; } }, { key: "destroy", value: function destroy() { if (this.cleanupConnectivityModeListener) { this.cleanupConnectivityModeListener(); } if (this.cleanupViewModeListener) { this.cleanupViewModeListener(); } } }]); }(ReactNodeView); export var bodiedSyncBlockNodeViewOld = function bodiedSyncBlockNodeViewOld(_ref4) { var pluginOptions = _ref4.pluginOptions, pmPluginFactoryParams = _ref4.pmPluginFactoryParams, api = _ref4.api, syncBlockStore = _ref4.syncBlockStore; return function (node, view, getPos) { var portalProviderAPI = pmPluginFactoryParams.portalProviderAPI, eventDispatcher = pmPluginFactoryParams.eventDispatcher; return new BodiedSyncBlockOld({ api: api, pluginOptions: pluginOptions, node: node, view: view, getPos: getPos, portalProviderAPI: portalProviderAPI, eventDispatcher: eventDispatcher, syncBlockStore: syncBlockStore }).init(); }; }; var toDOM = function toDOM(node) { return ['div', { class: "".concat(BodiedSyncBlockSharedCssClassName.prefix, " bodiedSyncBlockView-content-wrap"), localid: node.attrs.localId, resourceid: node.attrs.resourceId }, ['div', { class: BodiedSyncBlockSharedCssClassName.content, contenteditable: 'true' }, 0]]; }; export var BodiedSyncBlock = /*#__PURE__*/function () { function BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI, syncBlockStore) { var _this4 = this; _classCallCheck(this, BodiedSyncBlock); this.node = node; this.view = view; this.getPos = getPos; this.api = api; this.syncBlockStore = syncBlockStore; this.nodeViewPortalProviderAPI = nodeViewPortalProviderAPI; var _DOMSerializer$render2 = DOMSerializer.renderSpec(document, toDOM(this.node)), dom = _DOMSerializer$render2.dom, contentDOM = _DOMSerializer$render2.contentDOM; // eslint-disable-next-line @atlaskit/editor/no-as-casting this.dom = dom; // eslint-disable-next-line @atlaskit/editor/no-as-casting this.contentDOM = contentDOM; this.labelKey = crypto.randomUUID(); this.nodeViewPortalProviderAPI.render(function () { var _this4$api; return /*#__PURE__*/React.createElement(ErrorBoundary, { component: ACTION_SUBJECT.SYNCED_BLOCK, componentId: ACTION_SUBJECT_ID.SYNCED_BLOCK_LABEL, dispatchAnalyticsEvent: (_this4$api = _this4.api) === null || _this4$api === void 0 || (_this4$api = _this4$api.analytics) === null || _this4$api === void 0 ? void 0 : _this4$api.actions.fireAnalyticsEvent, fallbackComponent: null }, /*#__PURE__*/React.createElement(SyncBlockLabel, { isSource: true, localId: node.attrs.localId })); }, this.dom, this.labelKey); this.updateContentEditable({}); this.handleConnectivityModeChange(); this.handleViewModeChange(); // update sync block data on initial creation // When fg is ON, cache is populated in state.init() and updated in appendTransaction if (!fg('platform_synced_block_update_refactor')) { var _this$syncedBlockStor; (_this$syncedBlockStor = this.syncedBlockStore) === null || _this$syncedBlockStor === void 0 || _this$syncedBlockStor.sourceManager.updateSyncBlockData(node, false); } } return _createClass(BodiedSyncBlock, [{ key: "updateContentEditable", value: function updateContentEditable(_ref5) { var _this$api7, _this$api8; var nextConnectivityMode = _ref5.nextConnectivityMode, nextViewMode = _ref5.nextViewMode; var connectivityMode = nextConnectivityMode !== null && nextConnectivityMode !== void 0 ? nextConnectivityMode : (_this$api7 = this.api) === null || _this$api7 === void 0 || (_this$api7 = _this$api7.connectivity) === null || _this$api7 === void 0 || (_this$api7 = _this$api7.sharedState) === null || _this$api7 === void 0 || (_this$api7 = _this$api7.currentState()) === null || _this$api7 === void 0 ? void 0 : _this$api7.mode; var viewMode = nextViewMode !== null && nextViewMode !== void 0 ? nextViewMode : (_this$api8 = this.api) === null || _this$api8 === void 0 || (_this$api8 = _this$api8.editorViewMode) === null || _this$api8 === void 0 || (_this$api8 = _this$api8.sharedState) === null || _this$api8 === void 0 || (_this$api8 = _this$api8.currentState()) === null || _this$api8 === void 0 ? void 0 : _this$api8.mode; var isOnline = !isOfflineMode(connectivityMode); var isEditMode = viewMode !== 'view'; var shouldBeEditable = isOnline && isEditMode; this.contentDOM.setAttribute('contenteditable', shouldBeEditable ? 'true' : 'false'); } }, { key: "handleConnectivityModeChange", value: function handleConnectivityModeChange() { var _this$api9, _this5 = this; if ((_this$api9 = this.api) !== null && _this$api9 !== void 0 && _this$api9.connectivity) { this.cleanupConnectivityModeListener = this.api.connectivity.sharedState.onChange(function (_ref6) { var nextSharedState = _ref6.nextSharedState; _this5.updateContentEditable({ nextConnectivityMode: nextSharedState.mode }); }); } } }, { key: "handleViewModeChange", value: function handleViewModeChange() { var _this$api0, _this6 = this; if ((_this$api0 = this.api) !== null && _this$api0 !== void 0 && _this$api0.editorViewMode) { this.cleanupViewModeListener = this.api.editorViewMode.sharedState.onChange(function (_ref7) { var nextSharedState = _ref7.nextSharedState; _this6.updateContentEditable({ nextViewMode: nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.mode }); }); } } }, { key: "syncedBlockStore", get: function get() { var _this$api$syncedBlock2, _this$api1; return (_this$api$syncedBlock2 = (_this$api1 = this.api) === null || _this$api1 === void 0 || (_this$api1 = _this$api1.syncedBlock.sharedState) === null || _this$api1 === void 0 || (_this$api1 = _this$api1.currentState()) === null || _this$api1 === void 0 ? void 0 : _this$api1.syncBlockStore) !== null && _this$api$syncedBlock2 !== void 0 ? _this$api$syncedBlock2 : this.syncBlockStore; } }, { key: "update", value: function update(node) { if (this.node.type !== node.type) { return false; } if (node !== this.node) { // When fg is ON, cache updates are handled in appendTransaction where we can // filter out non-user changes (remote collab, table auto-scale, etc.) if (!fg('platform_synced_block_update_refactor')) { var _this$syncedBlockStor2; (_this$syncedBlockStor2 = this.syncedBlockStore) === null || _this$syncedBlockStor2 === void 0 || _this$syncedBlockStor2.sourceManager.updateSyncBlockData(node, false); } } this.node = node; return true; } }, { key: "ignoreMutation", value: function ignoreMutation(mutation) { if (mutation.type === 'selection') { return false; } return true; } }, { key: "destroy", value: function destroy() { var _this$cleanupConnecti, _this$cleanupViewMode; (_this$cleanupConnecti = this.cleanupConnectivityModeListener) === null || _this$cleanupConnecti === void 0 || _this$cleanupConnecti.call(this); (_this$cleanupViewMode = this.cleanupViewModeListener) === null || _this$cleanupViewMode === void 0 || _this$cleanupViewMode.call(this); this.nodeViewPortalProviderAPI.remove(this.labelKey); } }]); }(); export var bodiedSyncBlockNodeView = function bodiedSyncBlockNodeView(props) { var api = props.api, syncBlockStore = props.syncBlockStore, nodeViewPortalProviderAPI = props.pmPluginFactoryParams.nodeViewPortalProviderAPI; return function (node, view, getPos) { return new BodiedSyncBlock(node, view, getPos, api, nodeViewPortalProviderAPI, syncBlockStore); }; };