@atlaskit/editor-plugin-synced-block
Version:
SyncedBlock plugin for @atlaskit/editor-core
214 lines (211 loc) • 13.1 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.syncedBlockPlugin = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireDefault(require("react"));
var _adfSchema = require("@atlaskit/adf-schema");
var _hooks = require("@atlaskit/editor-common/hooks");
var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider");
var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
var _editorActions = require("./editor-actions");
var _editorCommands = require("./editor-commands");
var _main = require("./pm-plugins/main");
var _menuAndToolbarExperiences = require("./pm-plugins/menu-and-toolbar-experiences");
var _blockMenuComponents = require("./ui/block-menu-components");
var _DeleteConfirmationModal = require("./ui/DeleteConfirmationModal");
var _Flag = require("./ui/Flag");
var _floatingToolbar = require("./ui/floating-toolbar");
var _quickInsert = require("./ui/quick-insert");
var _getSyncedBlockQuickInsertComponents = require("./ui/quick-insert/getSyncedBlockQuickInsertComponents");
var _SourceSyncBlockPlaceholder = require("./ui/SourceSyncBlockPlaceholder");
var _SyncBlockRefresher = require("./ui/SyncBlockRefresher");
var _toolbarComponents = require("./ui/toolbar-components");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
/**
* EDITOR-6929 / PR-G: Guard contentComponent rendering.
* When `hasSyncedBlocks` is false return null
* to avoid mounting SyncBlockRefresher, DeleteConfirmationModal, and Flag —
* their hooks (useSharedPluginStateWithSelector) would execute selectors on
* every transaction for no benefit on the ~99.98% of pages with zero synced
* blocks.
*/
var LazySyncedBlockUI = function LazySyncedBlockUI(_ref) {
var syncBlockStoreManager = _ref.syncBlockStore,
api = _ref.api,
editorView = _ref.editorView,
onFeedbackPromptShown = _ref.onFeedbackPromptShown,
onGiveFeedback = _ref.onGiveFeedback;
var hasSyncBlocks = (0, _hooks.useSharedPluginStateWithSelector)(api, ['syncedBlock'], function (states) {
var _states$syncedBlockSt;
return (_states$syncedBlockSt = states.syncedBlockState) === null || _states$syncedBlockSt === void 0 ? void 0 : _states$syncedBlockSt.hasSyncedBlocks;
});
if (!hasSyncBlocks) {
return null;
}
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_SyncBlockRefresher.SyncBlockRefresher, {
syncBlockStoreManager: syncBlockStoreManager,
api: api
}), /*#__PURE__*/_react.default.createElement(_DeleteConfirmationModal.DeleteConfirmationModal, {
syncBlockStoreManager: syncBlockStoreManager,
api: api,
editorView: editorView
}), /*#__PURE__*/_react.default.createElement(_Flag.Flag, {
api: api,
onFeedbackPromptShown: onFeedbackPromptShown,
onGiveFeedback: onGiveFeedback
}));
};
var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_ref2) {
var _api$editorViewMode, _api$analytics, _api$blockMenu, _config$enableSourceC, _api$toolbar, _config$enableSourceC2;
var config = _ref2.config,
api = _ref2.api;
var refs = {};
var viewMode = api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 || (_api$editorViewMode = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.mode;
var syncBlockStore = new _editorSyncedBlockProvider.SyncBlockStoreManager(config === null || config === void 0 ? void 0 : config.syncBlockDataProvider, viewMode, config === null || config === void 0 ? void 0 : config.__livePage);
syncBlockStore.setFireAnalyticsEvent(api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 ? void 0 : _api$analytics.fireAnalyticsEvent);
// --- Memoized getSharedState (EDITOR-6929 / PR-F) ---
// Cache the last returned shared state object. On each call, perform a
// shallow comparison of all fields against the cached value. If nothing
// changed, return the cached reference so SharedStateAPI subscribers
// (React components) skip re-rendering.
var cachedSharedState;
api === null || api === void 0 || (_api$blockMenu = api.blockMenu) === null || _api$blockMenu === void 0 || _api$blockMenu.actions.registerBlockMenuComponents((0, _blockMenuComponents.getBlockMenuComponents)(api, (_config$enableSourceC = config === null || config === void 0 ? void 0 : config.enableSourceCreation) !== null && _config$enableSourceC !== void 0 ? _config$enableSourceC : false));
api === null || api === void 0 || (_api$toolbar = api.toolbar) === null || _api$toolbar === void 0 || _api$toolbar.actions.registerComponents((0, _toolbarComponents.getToolbarComponents)(api, (_config$enableSourceC2 = config === null || config === void 0 ? void 0 : config.enableSourceCreation) !== null && _config$enableSourceC2 !== void 0 ? _config$enableSourceC2 : false));
var isRegisteredSlashCommandEnabled = (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_slash_command');
if (isRegisteredSlashCommandEnabled && config !== null && config !== void 0 && config.enableSourceCreation) {
var _api$uiControlRegistr;
api === null || api === void 0 || (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 || _api$uiControlRegistr.actions.register((0, _getSyncedBlockQuickInsertComponents.getSyncedBlockQuickInsertComponents)({
api: api,
syncBlockStore: syncBlockStore
}));
}
return {
name: 'syncedBlock',
nodes: function nodes() {
return [{
name: 'syncBlock',
node: _adfSchema.syncBlock
}, {
name: 'bodiedSyncBlock',
node: _adfSchema.bodiedSyncBlock
}];
},
pmPlugins: function pmPlugins() {
return [{
name: 'syncedBlockPlugin',
plugin: function plugin(params) {
return (0, _main.createPlugin)(config, params, syncBlockStore, api);
}
}, {
name: 'menuAndToolbarExperiencesPlugin',
plugin: function plugin() {
return (0, _menuAndToolbarExperiences.getMenuAndToolbarExperiencesPlugin)({
refs: refs,
dispatchAnalyticsEvent: function dispatchAnalyticsEvent(payload) {
var _api$analytics2;
return api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || (_api$analytics2 = _api$analytics2.actions) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.fireAnalyticsEvent(payload);
}
});
}
}];
},
commands: {
copySyncedBlockReferenceToClipboard: function copySyncedBlockReferenceToClipboard(inputMethod) {
return (0, _editorCommands.copySyncedBlockReferenceToClipboardEditorCommand)(syncBlockStore, inputMethod, api, config === null || config === void 0 ? void 0 : config.__livePage);
},
insertSyncedBlock: function insertSyncedBlock(inputMethod) {
return function (_ref3) {
var _api$analytics3;
var tr = _ref3.tr;
if (!(config !== null && config !== void 0 && config.enableSourceCreation)) {
return null;
}
return (0, _editorCommands.createSyncedBlock)({
tr: tr,
syncBlockStore: syncBlockStore,
fireAnalyticsEvent: api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.fireAnalyticsEvent,
inputMethod: inputMethod
}) || null;
};
}
},
actions: {
flushBodiedSyncBlocks: function flushBodiedSyncBlocks() {
return (0, _editorActions.flushBodiedSyncBlocks)(syncBlockStore);
},
flushSyncedBlocks: function flushSyncedBlocks() {
return (0, _editorActions.flushSyncBlocks)(syncBlockStore);
},
discardUnpublishedSyncBlocks: function discardUnpublishedSyncBlocks() {
return (0, _editorActions.discardUnpublishedSyncBlocks)(syncBlockStore);
}
},
pluginsOptions: _objectSpread(_objectSpread({}, !isRegisteredSlashCommandEnabled && {
quickInsert: (0, _quickInsert.getQuickInsertConfig)(config, api, syncBlockStore)
}), {}, {
floatingToolbar: function floatingToolbar(state, intl) {
var _syncedBlockPluginKey;
// When registered slash-command support is on and the document has no synced blocks,
// skip the toolbar config entirely to avoid the per-selection-change
// cost of findSyncBlockOrBodiedSyncBlock (EDITOR-6931).
if (!((_syncedBlockPluginKey = _main.syncedBlockPluginKey.getState(state)) !== null && _syncedBlockPluginKey !== void 0 && _syncedBlockPluginKey.hasSyncedBlocks)) {
return undefined;
}
return (0, _floatingToolbar.getToolbarConfig)(state, intl, api, syncBlockStore, config === null || config === void 0 ? void 0 : config.__livePage, config === null || config === void 0 ? void 0 : config.onGiveFeedback);
}
}),
contentComponent: function contentComponent(_ref4) {
var containerElement = _ref4.containerElement,
wrapperElement = _ref4.wrapperElement,
popupsMountPoint = _ref4.popupsMountPoint,
editorView = _ref4.editorView;
refs.containerElement = containerElement || undefined;
refs.popupsMountPoint = popupsMountPoint || undefined;
refs.wrapperElement = wrapperElement || undefined;
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, (0, _expValEquals.expValEquals)('platform_editor_sync_block_activation', 'isEnabled', true) && /*#__PURE__*/_react.default.createElement(_SourceSyncBlockPlaceholder.SourceSyncBlockPlaceholder, null), /*#__PURE__*/_react.default.createElement(LazySyncedBlockUI, {
syncBlockStore: syncBlockStore,
api: api,
editorView: editorView,
onFeedbackPromptShown: config === null || config === void 0 ? void 0 : config.onFeedbackPromptShown,
onGiveFeedback: config === null || config === void 0 ? void 0 : config.onGiveFeedback
}));
},
getSharedState: function getSharedState(editorState) {
if (!editorState) {
return;
}
var pluginState = _main.syncedBlockPluginKey.getState(editorState);
var activeFlag = pluginState.activeFlag,
currentSyncBlockStore = pluginState.syncBlockStore,
bodiedSyncBlockDeletionStatus = pluginState.bodiedSyncBlockDeletionStatus,
retryCreationPosMap = pluginState.retryCreationPosMap,
hasSyncedBlocks = pluginState.hasSyncedBlocks,
hasUnsavedBodiedSyncBlockChanges = pluginState.hasUnsavedBodiedSyncBlockChanges;
// --- EDITOR-6929 / PR-F: return a stable reference when all
// fields are unchanged to prevent unnecessary React re-renders. ---
if (cachedSharedState !== undefined && cachedSharedState.activeFlag === activeFlag && cachedSharedState.syncBlockStore === currentSyncBlockStore && cachedSharedState.bodiedSyncBlockDeletionStatus === bodiedSyncBlockDeletionStatus && cachedSharedState.retryCreationPosMap === retryCreationPosMap && cachedSharedState.hasSyncedBlocks === hasSyncedBlocks && cachedSharedState.hasUnsavedBodiedSyncBlockChanges === hasUnsavedBodiedSyncBlockChanges) {
return cachedSharedState;
}
var nextSharedState = {
activeFlag: activeFlag,
syncBlockStore: currentSyncBlockStore,
bodiedSyncBlockDeletionStatus: bodiedSyncBlockDeletionStatus,
retryCreationPosMap: retryCreationPosMap,
hasSyncedBlocks: hasSyncedBlocks,
hasUnsavedBodiedSyncBlockChanges: hasUnsavedBodiedSyncBlockChanges
};
cachedSharedState = nextSharedState;
return nextSharedState;
},
// Destroy the SyncBlockStoreManager on editor unmount to cancel
// pending timers, subscriptions, and in-flight fetches.
destroy: function destroy() {
syncBlockStore.destroy();
}
};
};