@atlaskit/editor-plugin-synced-block
Version:
SyncedBlock plugin for @atlaskit/editor-core
297 lines (296 loc) • 14.9 kB
JavaScript
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);
};
};